| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build windows |
| |
| package win32 |
| |
| import ( |
| "fmt" |
| "syscall" |
| "unicode/utf16" |
| |
| "golang.org/x/mobile/event/key" |
| ) |
| |
| // convVirtualKeyCode converts a Win32 virtual key code number |
| // into the standard keycodes used by the key package. |
| func convVirtualKeyCode(vKey uint32) key.Code { |
| switch vKey { |
| case 0x01: // VK_LBUTTON left mouse button |
| case 0x02: // VK_RBUTTON right mouse button |
| case 0x03: // VK_CANCEL control-break processing |
| case 0x04: // VK_MBUTTON middle mouse button |
| case 0x05: // VK_XBUTTON1 X1 mouse button |
| case 0x06: // VK_XBUTTON2 X2 mouse button |
| case 0x08: // VK_BACK |
| return key.CodeDeleteBackspace |
| case 0x09: // VK_TAB |
| return key.CodeTab |
| case 0x0C: // VK_CLEAR |
| case 0x0D: // VK_RETURN |
| return key.CodeReturnEnter |
| case 0x10: // VK_SHIFT |
| return key.CodeLeftShift |
| case 0x11: // VK_CONTROL |
| return key.CodeLeftControl |
| case 0x12: // VK_MENU |
| return key.CodeLeftAlt |
| case 0x13: // VK_PAUSE |
| case 0x14: // VK_CAPITAL |
| return key.CodeCapsLock |
| case 0x15: // VK_KANA, VK_HANGUEL, VK_HANGUL |
| case 0x17: // VK_JUNJA |
| case 0x18: // VK_FINA, L |
| case 0x19: // VK_HANJA, VK_KANJI |
| case 0x1B: // VK_ESCAPE |
| return key.CodeEscape |
| case 0x1C: // VK_CONVERT |
| case 0x1D: // VK_NONCONVERT |
| case 0x1E: // VK_ACCEPT |
| case 0x1F: // VK_MODECHANGE |
| case 0x20: // VK_SPACE |
| return key.CodeSpacebar |
| case 0x21: // VK_PRIOR |
| return key.CodePageUp |
| case 0x22: // VK_NEXT |
| return key.CodePageDown |
| case 0x23: // VK_END |
| return key.CodeEnd |
| case 0x24: // VK_HOME |
| return key.CodeHome |
| case 0x25: // VK_LEFT |
| return key.CodeLeftArrow |
| case 0x26: // VK_UP |
| return key.CodeUpArrow |
| case 0x27: // VK_RIGHT |
| return key.CodeRightArrow |
| case 0x28: // VK_DOWN |
| return key.CodeDownArrow |
| case 0x29: // VK_SELECT |
| case 0x2A: // VK_PRINT |
| case 0x2B: // VK_EXECUTE |
| case 0x2C: // VK_SNAPSHOT |
| case 0x2D: // VK_INSERT |
| case 0x2E: // VK_DELETE |
| return key.CodeDeleteForward |
| case 0x2F: // VK_HELP |
| return key.CodeHelp |
| case 0x30: |
| return key.Code0 |
| case 0x31: |
| return key.Code1 |
| case 0x32: |
| return key.Code2 |
| case 0x33: |
| return key.Code3 |
| case 0x34: |
| return key.Code4 |
| case 0x35: |
| return key.Code5 |
| case 0x36: |
| return key.Code6 |
| case 0x37: |
| return key.Code7 |
| case 0x38: |
| return key.Code8 |
| case 0x39: |
| return key.Code9 |
| case 0x41: |
| return key.CodeA |
| case 0x42: |
| return key.CodeB |
| case 0x43: |
| return key.CodeC |
| case 0x44: |
| return key.CodeD |
| case 0x45: |
| return key.CodeE |
| case 0x46: |
| return key.CodeF |
| case 0x47: |
| return key.CodeG |
| case 0x48: |
| return key.CodeH |
| case 0x49: |
| return key.CodeI |
| case 0x4A: |
| return key.CodeJ |
| case 0x4B: |
| return key.CodeK |
| case 0x4C: |
| return key.CodeL |
| case 0x4D: |
| return key.CodeM |
| case 0x4E: |
| return key.CodeN |
| case 0x4F: |
| return key.CodeO |
| case 0x50: |
| return key.CodeP |
| case 0x51: |
| return key.CodeQ |
| case 0x52: |
| return key.CodeR |
| case 0x53: |
| return key.CodeS |
| case 0x54: |
| return key.CodeT |
| case 0x55: |
| return key.CodeU |
| case 0x56: |
| return key.CodeV |
| case 0x57: |
| return key.CodeW |
| case 0x58: |
| return key.CodeX |
| case 0x59: |
| return key.CodeY |
| case 0x5A: |
| return key.CodeZ |
| case 0x5B: // VK_LWIN |
| return key.CodeLeftGUI |
| case 0x5C: // VK_RWIN |
| return key.CodeRightGUI |
| case 0x5D: // VK_APPS |
| case 0x5F: // VK_SLEEP |
| case 0x60: // VK_NUMPAD0 |
| return key.CodeKeypad0 |
| case 0x61: // VK_NUMPAD1 |
| return key.CodeKeypad1 |
| case 0x62: // VK_NUMPAD2 |
| return key.CodeKeypad2 |
| case 0x63: // VK_NUMPAD3 |
| return key.CodeKeypad3 |
| case 0x64: // VK_NUMPAD4 |
| return key.CodeKeypad4 |
| case 0x65: // VK_NUMPAD5 |
| return key.CodeKeypad5 |
| case 0x66: // VK_NUMPAD6 |
| return key.CodeKeypad6 |
| case 0x67: // VK_NUMPAD7 |
| return key.CodeKeypad7 |
| case 0x68: // VK_NUMPAD8 |
| return key.CodeKeypad8 |
| case 0x69: // VK_NUMPAD9 |
| return key.CodeKeypad9 |
| case 0x6A: // VK_MULTIPLY |
| return key.CodeKeypadAsterisk |
| case 0x6B: // VK_ADD |
| return key.CodeKeypadPlusSign |
| case 0x6C: // VK_SEPARATOR |
| case 0x6D: // VK_SUBTRACT |
| return key.CodeKeypadHyphenMinus |
| case 0x6E: // VK_DECIMAL |
| return key.CodeFullStop |
| case 0x6F: // VK_DIVIDE |
| return key.CodeKeypadSlash |
| case 0x70: // VK_F1 |
| return key.CodeF1 |
| case 0x71: // VK_F2 |
| return key.CodeF2 |
| case 0x72: // VK_F3 |
| return key.CodeF3 |
| case 0x73: // VK_F4 |
| return key.CodeF4 |
| case 0x74: // VK_F5 |
| return key.CodeF5 |
| case 0x75: // VK_F6 |
| return key.CodeF6 |
| case 0x76: // VK_F7 |
| return key.CodeF7 |
| case 0x77: // VK_F8 |
| return key.CodeF8 |
| case 0x78: // VK_F9 |
| return key.CodeF9 |
| case 0x79: // VK_F10 |
| return key.CodeF10 |
| case 0x7A: // VK_F11 |
| return key.CodeF11 |
| case 0x7B: // VK_F12 |
| return key.CodeF12 |
| case 0x7C: // VK_F13 |
| return key.CodeF13 |
| case 0x7D: // VK_F14 |
| return key.CodeF14 |
| case 0x7E: // VK_F15 |
| return key.CodeF15 |
| case 0x7F: // VK_F16 |
| return key.CodeF16 |
| case 0x80: // VK_F17 |
| return key.CodeF17 |
| case 0x81: // VK_F18 |
| return key.CodeF18 |
| case 0x82: // VK_F19 |
| return key.CodeF19 |
| case 0x83: // VK_F20 |
| return key.CodeF20 |
| case 0x84: // VK_F21 |
| return key.CodeF21 |
| case 0x85: // VK_F22 |
| return key.CodeF22 |
| case 0x86: // VK_F23 |
| return key.CodeF23 |
| case 0x87: // VK_F24 |
| return key.CodeF24 |
| case 0x90: // VK_NUMLOCK |
| return key.CodeKeypadNumLock |
| case 0x91: // VK_SCROLL |
| case 0xA0: // VK_LSHIFT |
| return key.CodeLeftShift |
| case 0xA1: // VK_RSHIFT |
| return key.CodeRightShift |
| case 0xA2: // VK_LCONTROL |
| return key.CodeLeftControl |
| case 0xA3: // VK_RCONTROL |
| return key.CodeRightControl |
| case 0xA4: // VK_LMENU |
| case 0xA5: // VK_RMENU |
| case 0xA6: // VK_BROWSER_BACK |
| case 0xA7: // VK_BROWSER_FORWARD |
| case 0xA8: // VK_BROWSER_REFRESH |
| case 0xA9: // VK_BROWSER_STOP |
| case 0xAA: // VK_BROWSER_SEARCH |
| case 0xAB: // VK_BROWSER_FAVORITES |
| case 0xAC: // VK_BROWSER_HOME |
| case 0xAD: // VK_VOLUME_MUTE |
| return key.CodeMute |
| case 0xAE: // VK_VOLUME_DOWN |
| return key.CodeVolumeDown |
| case 0xAF: // VK_VOLUME_UP |
| return key.CodeVolumeUp |
| case 0xB0: // VK_MEDIA_NEXT_TRACK |
| case 0xB1: // VK_MEDIA_PREV_TRACK |
| case 0xB2: // VK_MEDIA_STOP |
| case 0xB3: // VK_MEDIA_PLAY_PAUSE |
| case 0xB4: // VK_LAUNCH_MAIL |
| case 0xB5: // VK_LAUNCH_MEDIA_SELECT |
| case 0xB6: // VK_LAUNCH_APP1 |
| case 0xB7: // VK_LAUNCH_APP2 |
| case 0xBA: // VK_OEM_1 ';:' |
| return key.CodeSemicolon |
| case 0xBB: // VK_OEM_PLUS '+' |
| return key.CodeEqualSign |
| case 0xBC: // VK_OEM_COMMA ',' |
| return key.CodeComma |
| case 0xBD: // VK_OEM_MINUS '-' |
| return key.CodeHyphenMinus |
| case 0xBE: // VK_OEM_PERIOD '.' |
| return key.CodeFullStop |
| case 0xBF: // VK_OEM_2 '/?' |
| return key.CodeSlash |
| case 0xC0: // VK_OEM_3 '`~' |
| return key.CodeGraveAccent |
| case 0xDB: // VK_OEM_4 '[{' |
| return key.CodeLeftSquareBracket |
| case 0xDC: // VK_OEM_5 '\|' |
| return key.CodeBackslash |
| case 0xDD: // VK_OEM_6 ']}' |
| return key.CodeRightSquareBracket |
| case 0xDE: // VK_OEM_7 'single-quote/double-quote' |
| return key.CodeApostrophe |
| case 0xDF: // VK_OEM_8 |
| return key.CodeUnknown |
| case 0xE2: // VK_OEM_102 |
| case 0xE5: // VK_PROCESSKEY |
| case 0xE7: // VK_PACKET |
| case 0xF6: // VK_ATTN |
| case 0xF7: // VK_CRSEL |
| case 0xF8: // VK_EXSEL |
| case 0xF9: // VK_EREOF |
| case 0xFA: // VK_PLAY |
| case 0xFB: // VK_ZOOM |
| case 0xFC: // VK_NONAME |
| case 0xFD: // VK_PA1 |
| case 0xFE: // VK_OEM_CLEAR |
| } |
| return key.CodeUnknown |
| } |
| |
| func readRune(vKey uint32, scanCode uint8) rune { |
| var ( |
| keystate [256]byte |
| buf [4]uint16 |
| ) |
| if err := _GetKeyboardState(&keystate[0]); err != nil { |
| panic(fmt.Sprintf("win32: %v", err)) |
| } |
| // TODO: cache GetKeyboardLayout result, update on WM_INPUTLANGCHANGE |
| layout := _GetKeyboardLayout(0) |
| ret := _ToUnicodeEx(vKey, uint32(scanCode), &keystate[0], &buf[0], int32(len(buf)), 0, layout) |
| if ret < 1 { |
| return -1 |
| } |
| return utf16.Decode(buf[:ret])[0] |
| } |
| |
| func sendKeyEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { |
| e := key.Event{ |
| Rune: readRune(uint32(wParam), uint8(lParam>>16)), |
| Code: convVirtualKeyCode(uint32(wParam)), |
| Modifiers: keyModifiers(), |
| } |
| switch uMsg { |
| case _WM_KEYDOWN, _WM_SYSKEYDOWN: |
| const prevMask = 1 << 30 |
| if repeat := lParam&prevMask == prevMask; repeat { |
| e.Direction = key.DirNone |
| } else { |
| e.Direction = key.DirPress |
| } |
| case _WM_KEYUP, _WM_SYSKEYUP: |
| e.Direction = key.DirRelease |
| default: |
| panic(fmt.Sprintf("win32: unexpected key message: %d", uMsg)) |
| } |
| |
| KeyEvent(hwnd, e) |
| return 0 |
| } |