Last active
February 1, 2024 15:26
-
-
Save cs8425/d166d4dd640129581f4e27bab8cc0706 to your computer and use it in GitHub Desktop.
simple keyboard & mouse binding over web in golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// build: | |
// GOOS=windows GOARCH=386 go build -o keyemu.exe keyemu.go | |
// GOOS=windows GOARCH=amd64 go build -o keyemu-x64.exe keyemu.go | |
// pre-build: https://mega.nz/#F!c9M0VaqK!sjUh5jwAU1tYkVv8z9YqaQ | |
// ref: https://github.com/micmonay/keybd_event | |
package main | |
import ( | |
"io" | |
"io/ioutil" | |
"net/http" | |
"syscall" | |
// "time" | |
"fmt" | |
"flag" | |
"strconv" | |
"strings" | |
) | |
var localAddr = flag.String("l", ":9999", "") | |
var ( | |
moduser32 = syscall.NewLazyDLL("user32.dll") | |
procKeyBd = moduser32.NewProc("keybd_event") | |
procMouse = moduser32.NewProc("mouse_event") | |
) | |
var html = `<!doctype html> | |
<head> | |
<title>XD</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> | |
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script> | |
<style> | |
html, body { | |
height: 100%; | |
margin: 0px; | |
} | |
body > div { | |
float: left; | |
width: 50%; | |
height: 100%; | |
} | |
#mouse { | |
border: 2px solid #000; | |
height: calc(100% - 4px); | |
} | |
#btns { | |
margin: 8px; | |
height: calc(100% - 16px); | |
} | |
button { | |
border: 1px solid gray; | |
padding: 5px 10px; | |
border-radius: 5px; | |
background-color: #efefef; | |
} | |
</style> | |
</head> | |
<body> | |
<div> | |
<div id="mouse"></div> | |
</div> | |
<div> | |
<div id="btns"> | |
<button id="mouseL">mouseL</button> | |
<button id="mouseR">mouseR</button> | |
<button id="up">^</button> | |
<button id="down">V</button> | |
<button id="left"><</button> | |
<button id="right">></button> | |
<button id="esc">esc</button> | |
<button id="ent">enter</button> | |
<button id="tab">Tab</button> | |
<button id="altf4">Alt-F4</button> | |
<button id="ctrlc">Ctrl-C</button> | |
<button id="alt-tab">Alt-Tab</button> | |
</div> | |
</div> | |
</body> | |
<script type="text/javascript"> | |
var isdrag = false; | |
var t = null; | |
var pos = {}; | |
var queue = []; | |
var delaypost = null; | |
var mousemove = function(){ | |
delaypost = setTimeout(mousemove, 50); | |
if(queue.length == 0) return; | |
var out = ''; | |
for(var i=0; i<queue.length; i++){ | |
var dx = queue[i][0]; | |
var dy = queue[i][1]; | |
var part = queue.length / 4; | |
for(var j=0; j<part && i<queue.length-1; j++){ | |
dx += queue[++i][0]; | |
dy += queue[i][1]; | |
} | |
out += Math.round(dx) + ',' + Math.round(dy) + '\n'; | |
} | |
// console.log('mousemove', out); | |
$.post('/mousemove', out, null, 'text'); | |
queue = []; | |
} | |
$('#mouse').bind('mousedown touchstart', function(e){ | |
isdrag = true; | |
t = new Date(); | |
if(typeof e.touches != 'undefined'){ | |
pos.x = e.touches[0].clientX; | |
pos.y = e.touches[0].clientY; | |
}else{ | |
pos.x = e.clientX; | |
pos.y = e.clientY; | |
} | |
}).bind('mouseup touchend', function(e){ | |
isdrag = false; | |
}).bind('mousemove touchmove', function(e){ | |
if(!isdrag) return; | |
e.preventDefault(); | |
var x,y; | |
if(typeof e.touches != 'undefined'){ | |
x = e.touches[0].clientX; | |
y = e.touches[0].clientY; | |
}else{ | |
x = e.clientX; | |
y = e.clientY; | |
} | |
var dx = x - pos.x; | |
var dy = y - pos.y; | |
pos.x = x; | |
pos.y = y; | |
queue.push([dx, dy]); | |
if(!delaypost) delaypost = setTimeout(mousemove, 50); | |
}).bind('click', function(e){ | |
if(((new Date()) - t) > 120) return; | |
$.get('/mouseL'); | |
}); | |
var bindlist = ['up', 'down', 'left', 'right', 'esc', 'ent', 'tab', 'altf4', 'ctrlc', 'alt-tab', 'mouseL', 'mouseR']; | |
for(idx in bindlist){ | |
var ele = bindlist[idx]; | |
(function(ele){ | |
$('#' + ele).bind('click', function(e){$.get('/' + ele)}); | |
})(ele); | |
} | |
</script> | |
</html>` | |
var keymap map[string]int = map[string]int{ | |
"/up": VK_UP, | |
"/down": VK_DOWN, | |
"/left": VK_LEFT, | |
"/right": VK_RIGHT, | |
"/esc": VK_ESCAPE, | |
"/ent": VK_RETURN, | |
"/tab": VK_TAB, | |
} | |
func mousemvs(data io.ReadCloser) { | |
defer data.Close() | |
body, err := ioutil.ReadAll(data) | |
// fmt.Printf("%v:%v\n", err, body) | |
if err != nil { | |
return | |
} | |
mvs := strings.Split(string(body), "\n") | |
for _, line := range mvs { | |
d := strings.Split(line, ",") | |
dx, err := strconv.ParseInt(d[0], 10, 32) | |
if err != nil { | |
return | |
} | |
dy, err := strconv.ParseInt(d[1], 10, 32) | |
if err != nil { | |
return | |
} | |
mouse_move(false, int32(dx), int32(dy)) | |
} | |
} | |
func keys(w http.ResponseWriter, r *http.Request) { | |
key, ok := keymap[r.URL.Path] | |
if ok { | |
fmt.Printf("got key: %v\n", r.URL.Path) | |
sendkey(key) | |
io.WriteString(w, r.URL.Path + ", ok") | |
} else { | |
switch r.URL.Path { | |
case "/altf4": | |
fmt.Printf("got alt-F4\n") | |
downKey(_VK_ALT) | |
sendkey(VK_F4) | |
upKey(_VK_ALT) | |
case "/ctrlc": | |
fmt.Printf("got ctrl-C\n") | |
downKey(_VK_CTRL) | |
sendkeys("C") | |
upKey(_VK_CTRL) | |
case "/alt-tab": | |
fmt.Printf("got alt-tab\n") | |
downKey(_VK_ALT) | |
sendkey(VK_TAB) | |
upKey(_VK_ALT) | |
case "/mousemove": | |
// fmt.Printf("got mousemove event\n") | |
mousemvs(r.Body) | |
case "/mouseL": | |
fmt.Printf("got mouseL event\n") | |
mouse_click(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP) | |
case "/mouseR": | |
fmt.Printf("got mouseR event\n") | |
mouse_click(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP) | |
default: | |
io.WriteString(w, html) | |
} | |
} | |
} | |
func main() { | |
flag.Parse() | |
fmt.Printf("server Listen @ %v\n", *localAddr) | |
http.HandleFunc("/", keys) | |
http.ListenAndServe(*localAddr, nil) | |
} | |
func mouse_click(intype uint32) { | |
procMouse.Call(uintptr(intype), 0, 0, 0, 0) | |
} | |
func mouse_move(abs bool, x int32, y int32) { | |
var intype uint32 = MOUSEEVENTF_MOVE | |
if abs { | |
intype |= MOUSEEVENTF_ABSOLUTE | |
} | |
procMouse.Call(uintptr(intype), uintptr(x), uintptr(y), 0, 0) | |
} | |
func mouse_wheel(delta int32, vh bool) { | |
var intype uint32 | |
if vh { | |
intype = MOUSEEVENTF_HWHEEL | |
} else { | |
intype = MOUSEEVENTF_WHEEL | |
} | |
procMouse.Call(uintptr(intype), 0, 0, uintptr(delta), 0) | |
} | |
const ( | |
MOUSEEVENTF_ABSOLUTE = 0x8000; | |
MOUSEEVENTF_MOVE = 0x0001; | |
MOUSEEVENTF_LEFTDOWN = 0x0002; | |
MOUSEEVENTF_LEFTUP = 0x0004; | |
MOUSEEVENTF_MIDDLEDOWN = 0x0020; | |
MOUSEEVENTF_MIDDLEUP = 0x0040; | |
MOUSEEVENTF_RIGHTDOWN = 0x0008; | |
MOUSEEVENTF_RIGHTUP = 0x0010; | |
MOUSEEVENTF_WHEEL = 0x0800; | |
MOUSEEVENTF_HWHEEL = 0x01000; | |
MOUSEEVENTF_XDOWN = 0x0080; | |
MOUSEEVENTF_XUP = 0x0100; | |
) | |
func sendkey(vk int) { | |
downKey(vk) | |
upKey(vk) | |
} | |
func sendkeys(str string) { | |
for _, s := range str { | |
if s >= 'a' && s <= 'z' { | |
s -= 'a' - 'A' | |
} | |
fmt.Printf("sendkey(): %x\n", int(s)) | |
downKey(int(s)) | |
upKey(int(s)) | |
} | |
} | |
func downKey(key int) { | |
vkey := key + 0x80 | |
procKeyBd.Call(uintptr(key), uintptr(vkey), 0, 0) | |
} | |
func upKey(key int) { | |
vkey := key + 0x80 | |
procKeyBd.Call(uintptr(key), uintptr(vkey), _KEYEVENTF_KEYUP, 0) | |
} | |
const ( | |
_VK_SHIFT = 0x10 | |
_VK_CTRL = 0x11 | |
_VK_ALT = 0x12 | |
_VK_LSHIFT = 0xA0 | |
_VK_RSHIFT = 0xA1 | |
_VK_LCONTROL = 0xA2 | |
_VK_RCONTROL = 0xA3 | |
_KEYEVENTF_KEYUP = 0x0002 | |
) | |
// Virtual-Key Codes | |
const ( | |
VK_LBUTTON = 0x01 | |
VK_RBUTTON = 0x02 | |
VK_CANCEL = 0x03 | |
VK_MBUTTON = 0x04 | |
VK_XBUTTON1 = 0x05 | |
VK_XBUTTON2 = 0x06 | |
VK_BACK = 0x08 | |
VK_TAB = 0x09 | |
VK_CLEAR = 0x0C | |
VK_RETURN = 0x0D | |
VK_SHIFT = 0x10 | |
VK_CONTROL = 0x11 | |
VK_MENU = 0x12 | |
VK_PAUSE = 0x13 | |
VK_CAPITAL = 0x14 | |
VK_KANA = 0x15 | |
VK_HANGEUL = 0x15 | |
VK_HANGUL = 0x15 | |
VK_JUNJA = 0x17 | |
VK_FINAL = 0x18 | |
VK_HANJA = 0x19 | |
VK_KANJI = 0x19 | |
VK_ESCAPE = 0x1B | |
VK_CONVERT = 0x1C | |
VK_NONCONVERT = 0x1D | |
VK_ACCEPT = 0x1E | |
VK_MODECHANGE = 0x1F | |
VK_SPACE = 0x20 | |
VK_PRIOR = 0x21 | |
VK_NEXT = 0x22 | |
VK_END = 0x23 | |
VK_HOME = 0x24 | |
VK_LEFT = 0x25 | |
VK_UP = 0x26 | |
VK_RIGHT = 0x27 | |
VK_DOWN = 0x28 | |
VK_SELECT = 0x29 | |
VK_PRINT = 0x2A | |
VK_EXECUTE = 0x2B | |
VK_SNAPSHOT = 0x2C | |
VK_INSERT = 0x2D | |
VK_DELETE = 0x2E | |
VK_HELP = 0x2F | |
VK_LWIN = 0x5B | |
VK_RWIN = 0x5C | |
VK_APPS = 0x5D | |
VK_SLEEP = 0x5F | |
VK_NUMPAD0 = 0x60 | |
VK_NUMPAD1 = 0x61 | |
VK_NUMPAD2 = 0x62 | |
VK_NUMPAD3 = 0x63 | |
VK_NUMPAD4 = 0x64 | |
VK_NUMPAD5 = 0x65 | |
VK_NUMPAD6 = 0x66 | |
VK_NUMPAD7 = 0x67 | |
VK_NUMPAD8 = 0x68 | |
VK_NUMPAD9 = 0x69 | |
VK_MULTIPLY = 0x6A | |
VK_ADD = 0x6B | |
VK_SEPARATOR = 0x6C | |
VK_SUBTRACT = 0x6D | |
VK_DECIMAL = 0x6E | |
VK_DIVIDE = 0x6F | |
VK_F1 = 0x70 | |
VK_F2 = 0x71 | |
VK_F3 = 0x72 | |
VK_F4 = 0x73 | |
VK_F5 = 0x74 | |
VK_F6 = 0x75 | |
VK_F7 = 0x76 | |
VK_F8 = 0x77 | |
VK_F9 = 0x78 | |
VK_F10 = 0x79 | |
VK_F11 = 0x7A | |
VK_F12 = 0x7B | |
VK_F13 = 0x7C | |
VK_F14 = 0x7D | |
VK_F15 = 0x7E | |
VK_F16 = 0x7F | |
VK_F17 = 0x80 | |
VK_F18 = 0x81 | |
VK_F19 = 0x82 | |
VK_F20 = 0x83 | |
VK_F21 = 0x84 | |
VK_F22 = 0x85 | |
VK_F23 = 0x86 | |
VK_F24 = 0x87 | |
VK_NUMLOCK = 0x90 | |
VK_SCROLL = 0x91 | |
VK_OEM_NEC_EQUAL = 0x92 | |
VK_OEM_FJ_JISHO = 0x92 | |
VK_OEM_FJ_MASSHOU = 0x93 | |
VK_OEM_FJ_TOUROKU = 0x94 | |
VK_OEM_FJ_LOYA = 0x95 | |
VK_OEM_FJ_ROYA = 0x96 | |
VK_LSHIFT = 0xA0 | |
VK_RSHIFT = 0xA1 | |
VK_LCONTROL = 0xA2 | |
VK_RCONTROL = 0xA3 | |
VK_LMENU = 0xA4 | |
VK_RMENU = 0xA5 | |
VK_BROWSER_BACK = 0xA6 | |
VK_BROWSER_FORWARD = 0xA7 | |
VK_BROWSER_REFRESH = 0xA8 | |
VK_BROWSER_STOP = 0xA9 | |
VK_BROWSER_SEARCH = 0xAA | |
VK_BROWSER_FAVORITES = 0xAB | |
VK_BROWSER_HOME = 0xAC | |
VK_VOLUME_MUTE = 0xAD | |
VK_VOLUME_DOWN = 0xAE | |
VK_VOLUME_UP = 0xAF | |
VK_MEDIA_NEXT_TRACK = 0xB0 | |
VK_MEDIA_PREV_TRACK = 0xB1 | |
VK_MEDIA_STOP = 0xB2 | |
VK_MEDIA_PLAY_PAUSE = 0xB3 | |
VK_LAUNCH_MAIL = 0xB4 | |
VK_LAUNCH_MEDIA_SELECT = 0xB5 | |
VK_LAUNCH_APP1 = 0xB6 | |
VK_LAUNCH_APP2 = 0xB7 | |
VK_OEM_1 = 0xBA | |
VK_OEM_PLUS = 0xBB | |
VK_OEM_COMMA = 0xBC | |
VK_OEM_MINUS = 0xBD | |
VK_OEM_PERIOD = 0xBE | |
VK_OEM_2 = 0xBF | |
VK_OEM_3 = 0xC0 | |
VK_OEM_4 = 0xDB | |
VK_OEM_5 = 0xDC | |
VK_OEM_6 = 0xDD | |
VK_OEM_7 = 0xDE | |
VK_OEM_8 = 0xDF | |
VK_OEM_AX = 0xE1 | |
VK_OEM_102 = 0xE2 | |
VK_ICO_HELP = 0xE3 | |
VK_ICO_00 = 0xE4 | |
VK_PROCESSKEY = 0xE5 | |
VK_ICO_CLEAR = 0xE6 | |
VK_OEM_RESET = 0xE9 | |
VK_OEM_JUMP = 0xEA | |
VK_OEM_PA1 = 0xEB | |
VK_OEM_PA2 = 0xEC | |
VK_OEM_PA3 = 0xED | |
VK_OEM_WSCTRL = 0xEE | |
VK_OEM_CUSEL = 0xEF | |
VK_OEM_ATTN = 0xF0 | |
VK_OEM_FINISH = 0xF1 | |
VK_OEM_COPY = 0xF2 | |
VK_OEM_AUTO = 0xF3 | |
VK_OEM_ENLW = 0xF4 | |
VK_OEM_BACKTAB = 0xF5 | |
VK_ATTN = 0xF6 | |
VK_CRSEL = 0xF7 | |
VK_EXSEL = 0xF8 | |
VK_EREOF = 0xF9 | |
VK_PLAY = 0xFA | |
VK_ZOOM = 0xFB | |
VK_NONAME = 0xFC | |
VK_PA1 = 0xFD | |
VK_OEM_CLEAR = 0xFE | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment