diff --git a/app/xkb_linux.go b/app/internal/xkb/xkb_linux.go similarity index 82% rename from app/xkb_linux.go rename to app/internal/xkb/xkb_linux.go index 737b3a3e..ca004c02 100644 --- a/app/xkb_linux.go +++ b/app/internal/xkb/xkb_linux.go @@ -2,7 +2,10 @@ // +build !android -package app +// Package xkb implements a Go interface for the X Keyboard Extension library. +package xkb + +import "gioui.org/io/event" /* #cgo LDFLAGS: -lxkbcommon @@ -25,7 +28,7 @@ import ( "gioui.org/io/key" ) -type xkb struct { +type Context struct { ctx *C.struct_xkb_context keyMap *C.struct_xkb_keymap state *C.struct_xkb_state @@ -39,7 +42,7 @@ var ( _XKB_MOD_NAME_SHIFT = []byte("Shift\x00") ) -func (x *xkb) Destroy() { +func (x *Context) Destroy() { if x.state != nil { C.xkb_compose_state_unref(x.compState) x.compState = nil @@ -62,11 +65,11 @@ func (x *xkb) Destroy() { } } -func newXKB(format C.uint32_t, fd C.int32_t, size C.uint32_t) (*xkb, error) { - xkb := &xkb{ +func New(format int, fd int, size int) (*Context, error) { + ctx := &Context{ ctx: C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS), } - if xkb.ctx == nil { + if ctx.ctx == nil { return nil, errors.New("newXKB: xkb_context_new failed") } locale := os.Getenv("LC_ALL") @@ -81,36 +84,36 @@ func newXKB(format C.uint32_t, fd C.int32_t, size C.uint32_t) (*xkb, error) { } cloc := C.CString(locale) defer C.free(unsafe.Pointer(cloc)) - xkb.compTable = C.xkb_compose_table_new_from_locale(xkb.ctx, cloc, C.XKB_COMPOSE_COMPILE_NO_FLAGS) - if xkb.compTable == nil { - xkb.Destroy() + ctx.compTable = C.xkb_compose_table_new_from_locale(ctx.ctx, cloc, C.XKB_COMPOSE_COMPILE_NO_FLAGS) + if ctx.compTable == nil { + ctx.Destroy() return nil, errors.New("newXKB: xkb_compose_table_new_from_locale failed") } - xkb.compState = C.xkb_compose_state_new(xkb.compTable, C.XKB_COMPOSE_STATE_NO_FLAGS) - if xkb.compState == nil { - xkb.Destroy() + ctx.compState = C.xkb_compose_state_new(ctx.compTable, C.XKB_COMPOSE_STATE_NO_FLAGS) + if ctx.compState == nil { + ctx.Destroy() return nil, errors.New("newXKB: xkb_compose_state_new failed") } mapData, err := syscall.Mmap(int(fd), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) if err != nil { - xkb.Destroy() + ctx.Destroy() return nil, fmt.Errorf("newXKB: mmap of keymap failed: %v", err) } defer syscall.Munmap(mapData) - xkb.keyMap = C.xkb_keymap_new_from_buffer(xkb.ctx, (*C.char)(unsafe.Pointer(&mapData[0])), C.size_t(size-1), C.XKB_KEYMAP_FORMAT_TEXT_V1, C.XKB_KEYMAP_COMPILE_NO_FLAGS) - if xkb.keyMap == nil { - xkb.Destroy() + ctx.keyMap = C.xkb_keymap_new_from_buffer(ctx.ctx, (*C.char)(unsafe.Pointer(&mapData[0])), C.size_t(size-1), C.XKB_KEYMAP_FORMAT_TEXT_V1, C.XKB_KEYMAP_COMPILE_NO_FLAGS) + if ctx.keyMap == nil { + ctx.Destroy() return nil, errors.New("newXKB: xkb_keymap_new_from_buffer failed") } - xkb.state = C.xkb_state_new(xkb.keyMap) - if xkb.state == nil { - xkb.Destroy() + ctx.state = C.xkb_state_new(ctx.keyMap) + if ctx.state == nil { + ctx.Destroy() return nil, errors.New("newXKB: xkb_state_new failed") } - return xkb, nil + return ctx, nil } -func (x *xkb) dispatchKey(w *Window, keyCode C.uint32_t) { +func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { keyCode = mapXKBKeyCode(keyCode) if len(x.utf8Buf) == 0 { x.utf8Buf = make([]byte, 1) @@ -124,7 +127,7 @@ func (x *xkb) dispatchKey(w *Window, keyCode C.uint32_t) { if C.xkb_state_mod_name_is_active(x.state, (*C.char)(unsafe.Pointer(&_XKB_MOD_NAME_SHIFT[0])), C.XKB_STATE_MODS_EFFECTIVE) == 1 { cmd.Modifiers |= key.ModShift } - w.event(cmd) + events = append(events, cmd) } C.xkb_compose_state_feed(x.compState, sym) var size C.int @@ -158,21 +161,22 @@ func (x *xkb) dispatchKey(w *Window, keyCode C.uint32_t) { } } if len(str) > 0 { - w.event(key.EditEvent{Text: string(str)}) + events = append(events, key.EditEvent{Text: string(str)}) } + return } -func (x *xkb) isRepeatKey(keyCode C.uint32_t) bool { +func (x *Context) IsRepeatKey(keyCode uint32) bool { keyCode = mapXKBKeyCode(keyCode) return C.xkb_keymap_key_repeats(x.keyMap, C.xkb_keycode_t(keyCode)) == 1 } -func (x *xkb) updateMask(depressed, latched, locked, group C.uint32_t) { +func (x *Context) UpdateMask(depressed, latched, locked, group uint32) { xkbGrp := C.xkb_layout_index_t(group) C.xkb_state_update_mask(x.state, C.xkb_mod_mask_t(depressed), C.xkb_mod_mask_t(latched), C.xkb_mod_mask_t(locked), xkbGrp, xkbGrp, xkbGrp) } -func mapXKBKeyCode(keyCode C.uint32_t) C.uint32_t { +func mapXKBKeyCode(keyCode uint32) uint32 { // According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode." return keyCode + 8 } diff --git a/app/os_wayland.go b/app/os_wayland.go index 868a5844..5cf77168 100644 --- a/app/os_wayland.go +++ b/app/os_wayland.go @@ -16,6 +16,7 @@ import ( "time" "unsafe" + "gioui.org/app/internal/xkb" "gioui.org/f32" "gioui.org/internal/fling" "gioui.org/io/key" @@ -68,7 +69,7 @@ type wlConn struct { pointer *C.struct_wl_pointer touch *C.struct_wl_touch keyboard *C.struct_wl_keyboard - xkb *xkb + xkb *xkb.Context repeat repeatState } @@ -77,7 +78,7 @@ type repeatState struct { rate int delay time.Duration - key C.uint32_t + key uint32 win *Window stopC chan struct{} @@ -630,7 +631,7 @@ func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, f if format != C.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 { return } - xkb, err := newXKB(format, fd, size) + xkb, err := xkb.New(int(format), int(fd), int(size)) if err != nil { // TODO: Do better. panic(err) @@ -662,13 +663,16 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri if state != C.WL_KEYBOARD_KEY_STATE_PRESSED || conn.xkb == nil { return } - conn.xkb.dispatchKey(w.w, keyCode) - if conn.xkb.isRepeatKey(keyCode) { - conn.repeat.Start(w, keyCode, t) + kc := uint32(keyCode) + for _, e := range conn.xkb.DispatchKey(kc) { + w.w.event(e) + } + if conn.xkb.IsRepeatKey(kc) { + conn.repeat.Start(w, kc, t) } } -func (r *repeatState) Start(w *window, keyCode C.uint32_t, t time.Duration) { +func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) { if r.rate <= 0 { return } @@ -733,7 +737,9 @@ func (r *repeatState) Repeat() { if r.last+delay > now { break } - conn.xkb.dispatchKey(r.win, r.key) + for _, e := range conn.xkb.DispatchKey(r.key) { + r.win.event(e) + } r.last += delay } } @@ -852,7 +858,7 @@ func gio_onKeyboardModifiers(data unsafe.Pointer, keyboard *C.struct_wl_keyboard if conn.xkb == nil { return } - conn.xkb.updateMask(depressed, latched, locked, group) + conn.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group)) } //export gio_onKeyboardRepeatInfo