From 42e568775cf53e5aa93ba5100371dad5e3fc191e Mon Sep 17 00:00:00 2001 From: Josiah Niedrauer Date: Thu, 29 Oct 2020 15:46:41 -0700 Subject: [PATCH] io/key: implement key.Release state Implement key state for the following platforms: js, wayland, windows, x11. Unsupported platforms will continue to function as before, sending key.Press for all key events. Signed-off-by: Josiah Niedrauer --- app/internal/window/os_js.go | 9 +++++++-- app/internal/window/os_wayland.go | 20 +++++++++++++++----- app/internal/window/os_windows.go | 12 ++++++++++-- app/internal/window/os_x11.go | 9 ++++++--- app/internal/windows/windows.go | 1 + app/internal/xkb/xkb_unix.go | 5 +++-- io/key/key.go | 15 +++++++++++++++ 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/app/internal/window/os_js.go b/app/internal/window/os_js.go index 1032cec3..7f3ffc50 100644 --- a/app/internal/window/os_js.go +++ b/app/internal/window/os_js.go @@ -180,7 +180,11 @@ func (w *window) addEventListeners() { return nil }) w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} { - w.keyEvent(args[0]) + w.keyEvent(args[0], key.Press) + return nil + }) + w.addEventListener(w.tarea, "keyup", func(this js.Value, args []js.Value) interface{} { + w.keyEvent(args[0], key.Release) return nil }) w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} { @@ -215,12 +219,13 @@ func (w *window) focus() { w.tarea.Call("focus") } -func (w *window) keyEvent(e js.Value) { +func (w *window) keyEvent(e js.Value, ks key.State) { k := e.Get("key").String() if n, ok := translateKey(k); ok { cmd := key.Event{ Name: n, Modifiers: modifiersFor(e), + State: ks, } w.w.Event(cmd) } diff --git a/app/internal/window/os_wayland.go b/app/internal/window/os_wayland.go index cebf4e5d..60f59b15 100644 --- a/app/internal/window/os_wayland.go +++ b/app/internal/window/os_wayland.go @@ -964,13 +964,14 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri t := time.Duration(timestamp) * time.Millisecond s.disp.repeat.Stop(t) w.resetFling() + kc := mapXKBKeycode(uint32(keyCode)) + ks := mapXKBKeyState(uint32(state)) + for _, e := range w.disp.xkb.DispatchKey(kc, ks) { + w.w.Event(e) + } if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { return } - kc := mapXKBKeycode(uint32(keyCode)) - for _, e := range w.disp.xkb.DispatchKey(kc) { - w.w.Event(e) - } if w.disp.xkb.IsRepeatKey(kc) { w.disp.repeat.Start(w, kc, t) } @@ -981,6 +982,15 @@ func mapXKBKeycode(keyCode uint32) uint32 { return keyCode + 8 } +func mapXKBKeyState(state uint32) key.State { + switch state { + case C.WL_KEYBOARD_KEY_STATE_RELEASED: + return key.Release + default: + return key.Press + } +} + func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) { if r.rate <= 0 { return @@ -1046,7 +1056,7 @@ func (r *repeatState) Repeat(d *wlDisplay) { if r.last+delay > now { break } - for _, e := range d.xkb.DispatchKey(r.key) { + for _, e := range d.xkb.DispatchKey(r.key, key.Press) { r.win.Event(e) } r.last += delay diff --git a/app/internal/window/os_windows.go b/app/internal/window/os_windows.go index ca8501d7..fe8e3aac 100644 --- a/app/internal/window/os_windows.go +++ b/app/internal/window/os_windows.go @@ -228,9 +228,17 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr case windows.WM_ERASEBKGND: // Avoid flickering between GPU content and background color. return 1 - case windows.WM_KEYDOWN, windows.WM_SYSKEYDOWN: + case windows.WM_KEYDOWN, windows.WM_KEYUP, windows.WM_SYSKEYDOWN, windows.WM_SYSKEYUP: if n, ok := convertKeyCode(wParam); ok { - w.w.Event(key.Event{Name: n, Modifiers: getModifiers()}) + e := key.Event{ + Name: n, + Modifiers: getModifiers(), + State: key.Press, + } + if msg == windows.WM_KEYUP || msg == windows.WM_SYSKEYUP { + e.State = key.Release + } + w.w.Event(e) } case windows.WM_LBUTTONDOWN: w.pointerButton(pointer.ButtonLeft, true, lParam, getModifiers()) diff --git a/app/internal/window/os_x11.go b/app/internal/window/os_x11.go index 8d60a0bd..17bdd5a8 100644 --- a/app/internal/window/os_x11.go +++ b/app/internal/window/os_x11.go @@ -305,12 +305,15 @@ func (h *x11EventHandler) handleEvents() bool { h.w.xkb.UpdateMask(uint32(state.base_mods), uint32(state.latched_mods), uint32(state.locked_mods), uint32(state.base_group), uint32(state.latched_group), uint32(state.locked_group)) } - case C.KeyPress: + case C.KeyPress, C.KeyRelease: + ks := key.Press + if _type == C.KeyRelease { + ks = key.Release + } kevt := (*C.XKeyPressedEvent)(unsafe.Pointer(xev)) - for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode)) { + for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode), ks) { w.w.Event(e) } - case C.KeyRelease: case C.ButtonPress, C.ButtonRelease: bevt := (*C.XButtonEvent)(unsafe.Pointer(xev)) ev := pointer.Event{ diff --git a/app/internal/windows/windows.go b/app/internal/windows/windows.go index 209871be..29e7f998 100644 --- a/app/internal/windows/windows.go +++ b/app/internal/windows/windows.go @@ -150,6 +150,7 @@ const ( WM_SHOWWINDOW = 0x0018 WM_SIZE = 0x0005 WM_SYSKEYDOWN = 0x0104 + WM_SYSKEYUP = 0x0105 WM_RBUTTONDOWN = 0x0204 WM_RBUTTONUP = 0x0205 WM_TIMER = 0x0113 diff --git a/app/internal/xkb/xkb_unix.go b/app/internal/xkb/xkb_unix.go index b4a9ff75..2f0fefac 100644 --- a/app/internal/xkb/xkb_unix.go +++ b/app/internal/xkb/xkb_unix.go @@ -150,7 +150,7 @@ func (x *Context) Modifiers() key.Modifiers { return mods } -func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { +func (x *Context) DispatchKey(keyCode uint32, state key.State) (events []event.Event) { if x.state == nil { return } @@ -163,6 +163,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { cmd := key.Event{ Name: name, Modifiers: x.Modifiers(), + State: state, } // Ensure that a physical backtab key is translated to // Shift-Tab. @@ -201,7 +202,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { str = str[:len(str)-s] } } - if len(str) > 0 { + if state == key.Press && len(str) > 0 { events = append(events, key.EditEvent{Text: string(str)}) } return diff --git a/io/key/key.go b/io/key/key.go index 5729ae4e..07892e99 100644 --- a/io/key/key.go +++ b/io/key/key.go @@ -46,6 +46,8 @@ type Event struct { Name string // Modifiers is the set of active modifiers when the key was pressed. Modifiers Modifiers + // State is the state of the key when the event was fired. + State State } // An EditEvent is generated when text is input. @@ -53,6 +55,19 @@ type EditEvent struct { Text string } +// State is the state of a key during an event. +type State uint8 + +const ( + // Press is the state of a pressed key. + Press State = iota + // Release is the state of a key that has been released. + // + // Note: release events are only implemented on the following platforms: + // Linux, Windows, WebAssembly. + Release +) + // Modifiers type Modifiers uint32