win32: keyboard support
For golang/go#9306 Change-Id: I2e4029e261307e08748b7088c495512247f10d21 Reviewed-on: https://go-review.googlesource.com/17722 Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
This commit is contained in:
Родитель
fb8fbd2b13
Коммит
7ee4134c09
|
@ -0,0 +1,347 @@
|
|||
// 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.
|
||||
|
||||
package win32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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 HWND, 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:
|
||||
const prevMask = 1 << 30
|
||||
if repeat := lParam&prevMask == prevMask; repeat {
|
||||
e.Direction = key.DirNone
|
||||
} else {
|
||||
e.Direction = key.DirPress
|
||||
}
|
||||
case _WM_KEYUP:
|
||||
e.Direction = key.DirRelease
|
||||
default:
|
||||
panic(fmt.Sprintf("win32: unexpected key message: %d", uMsg))
|
||||
}
|
||||
|
||||
KeyEvent(hwnd, e)
|
||||
return 0
|
||||
}
|
|
@ -182,3 +182,6 @@ func _HIWORD(l uintptr) uint16 {
|
|||
//sys _GetClientRect(hwnd HWND, rect *_RECT) (err error) = user32.GetClientRect
|
||||
//sys _GetKeyState(virtkey int32) (keystatus int16) = user32.GetKeyState
|
||||
//sys _PostQuitMessage(exitCode int32) = user32.PostQuitMessage
|
||||
//sys _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) = user32.GetKeyboardLayout
|
||||
//sys _GetKeyboardState(lpKeyState *byte) (err error) = user32.GetKeyboardState
|
||||
//sys _ToUnicodeEx(wVirtKey uint32, wScanCode uint32, lpKeyState *byte, pwszBuff *uint16, cchBuff int32, wFlags uint32, dwhkl syscall.Handle) (ret int32) = user32.ToUnicodeEx
|
||||
|
|
|
@ -217,6 +217,7 @@ var (
|
|||
MouseEvent func(hwnd HWND, e mouse.Event)
|
||||
PaintEvent func(hwnd HWND, e paint.Event)
|
||||
SizeEvent func(hwnd HWND, e size.Event)
|
||||
KeyEvent func(hwnd HWND, e key.Event)
|
||||
LifecycleEvent func(hwnd HWND, e lifecycle.Stage)
|
||||
)
|
||||
|
||||
|
@ -258,7 +259,10 @@ var windowMsgs = map[uint32]func(hwnd HWND, uMsg uint32, wParam, lParam uintptr)
|
|||
_WM_RBUTTONUP: sendMouseEvent,
|
||||
_WM_MOUSEMOVE: sendMouseEvent,
|
||||
_WM_MOUSEWHEEL: sendMouseEvent,
|
||||
// TODO case _WM_KEYDOWN, _WM_KEYUP, _WM_SYSKEYDOWN, _WM_SYSKEYUP:
|
||||
|
||||
_WM_KEYDOWN: sendKeyEvent,
|
||||
_WM_KEYUP: sendKeyEvent,
|
||||
// TODO case _WM_SYSKEYDOWN, _WM_SYSKEYUP:
|
||||
}
|
||||
|
||||
func AddWindowMsg(fn func(hwnd HWND, uMsg uint32, wParam, lParam uintptr)) uint32 {
|
||||
|
|
|
@ -27,6 +27,9 @@ var (
|
|||
procGetClientRect = moduser32.NewProc("GetClientRect")
|
||||
procGetKeyState = moduser32.NewProc("GetKeyState")
|
||||
procPostQuitMessage = moduser32.NewProc("PostQuitMessage")
|
||||
procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout")
|
||||
procGetKeyboardState = moduser32.NewProc("GetKeyboardState")
|
||||
procToUnicodeEx = moduser32.NewProc("ToUnicodeEx")
|
||||
)
|
||||
|
||||
func GetDC(hwnd HWND) (dc HDC, err error) {
|
||||
|
@ -189,3 +192,27 @@ func _PostQuitMessage(exitCode int32) {
|
|||
syscall.Syscall(procPostQuitMessage.Addr(), 1, uintptr(exitCode), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(threadID), 0, 0)
|
||||
locale = syscall.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetKeyboardState(lpKeyState *byte) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetKeyboardState.Addr(), 1, uintptr(unsafe.Pointer(lpKeyState)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ToUnicodeEx(wVirtKey uint32, wScanCode uint32, lpKeyState *byte, pwszBuff *uint16, cchBuff int32, wFlags uint32, dwhkl syscall.Handle) (ret int32) {
|
||||
r0, _, _ := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(wVirtKey), uintptr(wScanCode), uintptr(unsafe.Pointer(lpKeyState)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(wFlags), uintptr(dwhkl), 0, 0)
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"golang.org/x/exp/shiny/driver/internal/win32"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/image/math/f64"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"golang.org/x/mobile/event/lifecycle"
|
||||
"golang.org/x/mobile/event/mouse"
|
||||
"golang.org/x/mobile/event/paint"
|
||||
|
@ -149,6 +150,7 @@ func init() {
|
|||
}
|
||||
win32.MouseEvent = func(hwnd win32.HWND, e mouse.Event) { send(hwnd, e) }
|
||||
win32.PaintEvent = func(hwnd win32.HWND, e paint.Event) { send(hwnd, e) }
|
||||
win32.KeyEvent = func(hwnd win32.HWND, e key.Event) { send(hwnd, e) }
|
||||
win32.LifecycleEvent = lifecycleEvent
|
||||
win32.SizeEvent = sizeEvent
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче