mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
app/internal/window: replace X11 input handling with xkb
Unifies Wayland and X11 keyboard handling. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -628,18 +628,14 @@ func (w *window) resetFling() {
|
||||
func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) {
|
||||
defer syscall.Close(int(fd))
|
||||
conn.repeat.Stop(0)
|
||||
if conn.xkb != nil {
|
||||
conn.xkb.Destroy()
|
||||
}
|
||||
conn.xkb.DestroyKeymapState()
|
||||
if format != C.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 {
|
||||
return
|
||||
}
|
||||
xkb, err := xkb.New(int(format), int(fd), int(size))
|
||||
if err != nil {
|
||||
if err := conn.xkb.LoadKeymap(int(format), int(fd), int(size)); err != nil {
|
||||
// TODO: Do better.
|
||||
panic(err)
|
||||
}
|
||||
conn.xkb = xkb
|
||||
}
|
||||
|
||||
//export gio_onKeyboardEnter
|
||||
@@ -663,10 +659,10 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
|
||||
w := winMap[keyboard]
|
||||
w.resetFling()
|
||||
conn.repeat.Stop(t)
|
||||
if state != C.WL_KEYBOARD_KEY_STATE_PRESSED || conn.xkb == nil {
|
||||
if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
|
||||
return
|
||||
}
|
||||
kc := uint32(keyCode)
|
||||
kc := mapXKBKeycode(uint32(keyCode))
|
||||
for _, e := range conn.xkb.DispatchKey(kc) {
|
||||
w.w.Event(e)
|
||||
}
|
||||
@@ -675,6 +671,11 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) {
|
||||
if r.rate <= 0 {
|
||||
return
|
||||
@@ -867,7 +868,7 @@ func gio_onKeyboardModifiers(data unsafe.Pointer, keyboard *C.struct_wl_keyboard
|
||||
if conn.xkb == nil {
|
||||
return
|
||||
}
|
||||
conn.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group))
|
||||
conn.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group), uint32(group), uint32(group))
|
||||
}
|
||||
|
||||
//export gio_onKeyboardRepeatInfo
|
||||
@@ -1082,6 +1083,12 @@ func detectUIScale() float32 {
|
||||
func waylandConnect() error {
|
||||
c := new(wlConn)
|
||||
conn = c
|
||||
xkb, err := xkb.New()
|
||||
if err != nil {
|
||||
c.destroy()
|
||||
return fmt.Errorf("wayland: %v", err)
|
||||
}
|
||||
c.xkb = xkb
|
||||
c.disp = C.wl_display_connect(nil)
|
||||
if c.disp == nil {
|
||||
c.destroy()
|
||||
|
||||
+79
-188
@@ -5,38 +5,17 @@
|
||||
package window
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lX11
|
||||
#cgo LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
|
||||
void gio_x11_init_ime(Display *dpy, Window win, XIM *xim, XIC *xic) {
|
||||
// adjust locale temporarily for XOpenIM
|
||||
char *lc = setlocale(LC_CTYPE, NULL);
|
||||
setlocale(LC_CTYPE, "");
|
||||
XSetLocaleModifiers("");
|
||||
|
||||
*xim = XOpenIM(dpy, 0, 0, 0);
|
||||
if (!*xim) {
|
||||
// fallback to internal input method
|
||||
XSetLocaleModifiers("@im=none");
|
||||
*xim = XOpenIM(dpy, 0, 0, 0);
|
||||
}
|
||||
|
||||
// revert locale to prevent any unexpected side effects
|
||||
setlocale(LC_CTYPE, lc);
|
||||
|
||||
*xic = XCreateIC(*xim,
|
||||
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
||||
XNClientWindow, win,
|
||||
XNFocusWindow, win,
|
||||
NULL);
|
||||
|
||||
XSetICFocus(*xic);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
@@ -46,29 +25,29 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/system"
|
||||
|
||||
"gioui.org/app/internal/xkb"
|
||||
syscall "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type x11Window struct {
|
||||
w Callbacks
|
||||
x *C.Display
|
||||
xw C.Window
|
||||
w Callbacks
|
||||
x *C.Display
|
||||
xkb *xkb.Context
|
||||
xkbEventBase C.int
|
||||
xw C.Window
|
||||
|
||||
evDelWindow C.Atom
|
||||
stage system.Stage
|
||||
cfg config
|
||||
width int
|
||||
height int
|
||||
xim C.XIM
|
||||
xic C.XIC
|
||||
notify struct {
|
||||
read, write int
|
||||
}
|
||||
@@ -192,8 +171,10 @@ func (w *x11Window) destroy() {
|
||||
syscall.Close(w.notify.read)
|
||||
w.notify.read = 0
|
||||
}
|
||||
C.XDestroyIC(w.xic)
|
||||
C.XCloseIM(w.xim)
|
||||
if w.xkb != nil {
|
||||
w.xkb.Destroy()
|
||||
w.xkb = nil
|
||||
}
|
||||
C.XDestroyWindow(w.x, w.xw)
|
||||
C.XCloseDisplay(w.x)
|
||||
}
|
||||
@@ -235,60 +216,22 @@ func (h *x11EventHandler) handleEvents() bool {
|
||||
continue
|
||||
}
|
||||
switch _type := (*C.XAnyEvent)(unsafe.Pointer(xev))._type; _type {
|
||||
case h.w.xkbEventBase:
|
||||
xkbEvent := (*C.XkbAnyEvent)(unsafe.Pointer(xev))
|
||||
switch xkbEvent.xkb_type {
|
||||
case C.XkbNewKeyboardNotify, C.XkbMapNotify:
|
||||
if err := h.w.updateXkbKeymap(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
case C.XkbStateNotify:
|
||||
state := (*C.XkbStateNotifyEvent)(unsafe.Pointer(xev))
|
||||
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:
|
||||
kevt := (*C.XKeyPressedEvent)(unsafe.Pointer(xev))
|
||||
lookup:
|
||||
// Save state then clear CTRL & Shift bits in order to have
|
||||
// Xutf8LookupString return the unmodified key name in text[:l].
|
||||
// This addresses an issue on some non US keyboard layouts where
|
||||
// CTRL-[0..9] do not behave consistently.
|
||||
//
|
||||
// Note that this enables sending a key.Event for key combinations
|
||||
// like CTRL-SHIFT-/ on QWERTY layouts, but CTRL-? is completely
|
||||
// masked. The same applies to AZERTY layouts where CTRL-SHIFT-É is
|
||||
// available but not CTRL-2.
|
||||
state := kevt.state
|
||||
mods := x11KeyStateToModifiers(state)
|
||||
if mods.Contain(key.ModCtrl) {
|
||||
kevt.state &^= (C.uint(C.ControlMask) | C.uint(C.ShiftMask))
|
||||
}
|
||||
l := int(C.Xutf8LookupString(w.xic, kevt,
|
||||
(*C.char)(unsafe.Pointer(&h.text[0])), C.int(len(h.text)),
|
||||
&h.keysym, &h.status))
|
||||
switch h.status {
|
||||
case C.XBufferOverflow:
|
||||
h.text = make([]byte, l)
|
||||
goto lookup
|
||||
case C.XLookupChars:
|
||||
// Synthetic event from XIM.
|
||||
w.w.Event(key.EditEvent{Text: string(h.text[:l])})
|
||||
case C.XLookupKeySym:
|
||||
// Special keys.
|
||||
if n, m, ok := x11ConvertKeysym(h.keysym, mods); ok {
|
||||
w.w.Event(key.Event{Name: n, Modifiers: m})
|
||||
}
|
||||
case C.XLookupBoth:
|
||||
if n, m, ok := x11ConvertKeysym(h.keysym, mods); ok {
|
||||
w.w.Event(key.Event{Name: n, Modifiers: m})
|
||||
}
|
||||
// Do not send EditEvent for CTRL key combinations.
|
||||
if mods.Contain(key.ModCtrl) {
|
||||
break
|
||||
}
|
||||
// Report only printable runes.
|
||||
str := h.text[:l]
|
||||
for n := 0; n < len(str); {
|
||||
r, s := utf8.DecodeRune(str)
|
||||
if unicode.IsPrint(r) {
|
||||
n += s
|
||||
} else {
|
||||
copy(str[n:], str[n+s:])
|
||||
str = str[:len(str)-s]
|
||||
}
|
||||
}
|
||||
if len(str) > 0 {
|
||||
w.w.Event(key.EditEvent{Text: string(str)})
|
||||
}
|
||||
for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode)) {
|
||||
w.w.Event(e)
|
||||
}
|
||||
case C.KeyRelease:
|
||||
case C.ButtonPress, C.ButtonRelease:
|
||||
@@ -369,98 +312,6 @@ func (h *x11EventHandler) handleEvents() bool {
|
||||
return redraw
|
||||
}
|
||||
|
||||
func x11KeyStateToModifiers(s C.uint) key.Modifiers {
|
||||
var m key.Modifiers
|
||||
if s&C.Mod1Mask != 0 {
|
||||
m |= key.ModAlt
|
||||
}
|
||||
if s&C.Mod4Mask != 0 {
|
||||
m |= key.ModSuper
|
||||
}
|
||||
if s&C.ControlMask != 0 {
|
||||
m |= key.ModCtrl
|
||||
}
|
||||
if s&C.ShiftMask != 0 {
|
||||
m |= key.ModShift
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// x11ConvertKeysym returns the Gio special key that matches keysym s. For
|
||||
// portability reasons, some keysyms might be translated and modifiers changed
|
||||
// (like BackTab -> ModShift+Tab)
|
||||
func x11ConvertKeysym(s C.KeySym, mods key.Modifiers) (string, key.Modifiers, bool) {
|
||||
if '0' <= s && s <= '9' || 'A' <= s && s <= 'Z' {
|
||||
return string(s), mods, true
|
||||
}
|
||||
if 'a' <= s && s <= 'z' {
|
||||
return string(s - 0x20), mods, true
|
||||
}
|
||||
var n string
|
||||
switch s {
|
||||
case C.XK_Escape:
|
||||
n = key.NameEscape
|
||||
case C.XK_Left, C.XK_KP_Left:
|
||||
n = key.NameLeftArrow
|
||||
case C.XK_Right, C.XK_KP_Right:
|
||||
n = key.NameRightArrow
|
||||
case C.XK_Return:
|
||||
n = key.NameReturn
|
||||
case C.XK_KP_Enter:
|
||||
n = key.NameEnter
|
||||
case C.XK_Up, C.XK_KP_Up:
|
||||
n = key.NameUpArrow
|
||||
case C.XK_Down, C.XK_KP_Down:
|
||||
n = key.NameDownArrow
|
||||
case C.XK_Home, C.XK_KP_Home:
|
||||
n = key.NameHome
|
||||
case C.XK_End, C.XK_KP_End:
|
||||
n = key.NameEnd
|
||||
case C.XK_BackSpace:
|
||||
n = key.NameDeleteBackward
|
||||
case C.XK_Delete, C.XK_KP_Delete:
|
||||
n = key.NameDeleteForward
|
||||
case C.XK_Page_Up, C.XK_KP_Prior:
|
||||
n = key.NamePageUp
|
||||
case C.XK_Page_Down, C.XK_KP_Next:
|
||||
n = key.NamePageDown
|
||||
case C.XK_F1:
|
||||
n = "F1"
|
||||
case C.XK_F2:
|
||||
n = "F2"
|
||||
case C.XK_F3:
|
||||
n = "F3"
|
||||
case C.XK_F4:
|
||||
n = "F4"
|
||||
case C.XK_F5:
|
||||
n = "F5"
|
||||
case C.XK_F6:
|
||||
n = "F6"
|
||||
case C.XK_F7:
|
||||
n = "F7"
|
||||
case C.XK_F8:
|
||||
n = "F8"
|
||||
case C.XK_F9:
|
||||
n = "F9"
|
||||
case C.XK_F10:
|
||||
n = "F10"
|
||||
case C.XK_F11:
|
||||
n = "F11"
|
||||
case C.XK_F12:
|
||||
n = "F12"
|
||||
case C.XK_ISO_Left_Tab:
|
||||
mods |= key.ModShift
|
||||
fallthrough
|
||||
case C.XK_Tab:
|
||||
n = key.NameTab
|
||||
case 0x20, C.XK_KP_Space:
|
||||
n = "Space"
|
||||
default:
|
||||
return "", mods, false
|
||||
}
|
||||
return n, mods, true
|
||||
}
|
||||
|
||||
var (
|
||||
x11Threads sync.Once
|
||||
)
|
||||
@@ -490,6 +341,22 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
|
||||
if dpy == nil {
|
||||
return errors.New("x11: cannot connect to the X server")
|
||||
}
|
||||
var major, minor C.int = C.XkbMajorVersion, C.XkbMinorVersion
|
||||
var xkbEventBase C.int
|
||||
if C.XkbQueryExtension(dpy, nil, &xkbEventBase, nil, &major, &minor) != C.True {
|
||||
C.XCloseDisplay(dpy)
|
||||
return errors.New("x11: XkbQueryExtension failed")
|
||||
}
|
||||
const bits = C.uint(C.XkbNewKeyboardNotifyMask | C.XkbMapNotifyMask | C.XkbStateNotifyMask)
|
||||
if C.XkbSelectEvents(dpy, C.XkbUseCoreKbd, bits, bits) != C.True {
|
||||
C.XCloseDisplay(dpy)
|
||||
return errors.New("x11: XkbSelectEvents failed")
|
||||
}
|
||||
xkb, err := xkb.New()
|
||||
if err != nil {
|
||||
C.XCloseDisplay(dpy)
|
||||
return fmt.Errorf("x11: %v", err)
|
||||
}
|
||||
|
||||
ppsp := x11DetectUIScale(dpy)
|
||||
cfg := config{pxPerDp: ppsp, pxPerSp: ppsp}
|
||||
@@ -506,23 +373,23 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
|
||||
0, 0, C.uint(cfg.Px(opts.Width)), C.uint(cfg.Px(opts.Height)),
|
||||
0, C.CopyFromParent, C.InputOutput, nil,
|
||||
C.CWEventMask|C.CWBackPixmap|C.CWOverrideRedirect, &swa)
|
||||
var (
|
||||
xim C.XIM
|
||||
xic C.XIC
|
||||
)
|
||||
C.gio_x11_init_ime(dpy, win, &xim, &xic)
|
||||
|
||||
w := &x11Window{
|
||||
w: gioWin, x: dpy, xw: win,
|
||||
width: cfg.Px(opts.Width),
|
||||
height: cfg.Px(opts.Height),
|
||||
cfg: cfg,
|
||||
xim: xim,
|
||||
xic: xic,
|
||||
width: cfg.Px(opts.Width),
|
||||
height: cfg.Px(opts.Height),
|
||||
cfg: cfg,
|
||||
xkb: xkb,
|
||||
xkbEventBase: xkbEventBase,
|
||||
}
|
||||
w.notify.read = pipe[0]
|
||||
w.notify.write = pipe[1]
|
||||
|
||||
if err := w.updateXkbKeymap(); err != nil {
|
||||
w.destroy()
|
||||
return err
|
||||
}
|
||||
|
||||
var hints C.XWMHints
|
||||
hints.input = C.True
|
||||
hints.flags = C.InputHint
|
||||
@@ -591,3 +458,27 @@ func x11DetectUIScale(dpy *C.Display) float32 {
|
||||
|
||||
return scale
|
||||
}
|
||||
|
||||
func (w *x11Window) updateXkbKeymap() error {
|
||||
w.xkb.DestroyKeymapState()
|
||||
ctx := (*C.struct_xkb_context)(unsafe.Pointer(w.xkb.Ctx))
|
||||
xcb := C.XGetXCBConnection(w.x)
|
||||
if xcb == nil {
|
||||
return errors.New("x11: XGetXCBConnection failed")
|
||||
}
|
||||
xkbDevID := C.xkb_x11_get_core_keyboard_device_id(xcb)
|
||||
if xkbDevID == -1 {
|
||||
return errors.New("x11: xkb_x11_get_core_keyboard_device_id failed")
|
||||
}
|
||||
keymap := C.xkb_x11_keymap_new_from_device(ctx, xcb, xkbDevID, C.XKB_KEYMAP_COMPILE_NO_FLAGS)
|
||||
if keymap == nil {
|
||||
return errors.New("x11: xkb_x11_keymap_new_from_device failed")
|
||||
}
|
||||
state := C.xkb_x11_state_new_from_device(keymap, xcb, xkbDevID)
|
||||
if state == nil {
|
||||
C.xkb_keymap_unref(keymap)
|
||||
return errors.New("x11: xkb_x11_keymap_new_from_device failed")
|
||||
}
|
||||
w.xkb.SetKeymap(unsafe.Pointer(keymap), unsafe.Pointer(state))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"unicode"
|
||||
"syscall"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
import "C"
|
||||
|
||||
type Context struct {
|
||||
ctx *C.struct_xkb_context
|
||||
Ctx *C.struct_xkb_context
|
||||
keyMap *C.struct_xkb_keymap
|
||||
state *C.struct_xkb_state
|
||||
compTable *C.struct_xkb_compose_table
|
||||
@@ -54,25 +54,18 @@ func (x *Context) Destroy() {
|
||||
C.xkb_compose_table_unref(x.compTable)
|
||||
x.compTable = nil
|
||||
}
|
||||
if x.state != nil {
|
||||
C.xkb_state_unref(x.state)
|
||||
x.state = nil
|
||||
}
|
||||
if x.keyMap != nil {
|
||||
C.xkb_keymap_unref(x.keyMap)
|
||||
x.keyMap = nil
|
||||
}
|
||||
if x.ctx != nil {
|
||||
C.xkb_context_unref(x.ctx)
|
||||
x.ctx = nil
|
||||
x.DestroyKeymapState()
|
||||
if x.Ctx != nil {
|
||||
C.xkb_context_unref(x.Ctx)
|
||||
x.Ctx = nil
|
||||
}
|
||||
}
|
||||
|
||||
func New(format int, fd int, size int) (*Context, error) {
|
||||
func New() (*Context, error) {
|
||||
ctx := &Context{
|
||||
ctx: C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS),
|
||||
Ctx: C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS),
|
||||
}
|
||||
if ctx.ctx == nil {
|
||||
if ctx.Ctx == nil {
|
||||
return nil, errors.New("newXKB: xkb_context_new failed")
|
||||
}
|
||||
locale := os.Getenv("LC_ALL")
|
||||
@@ -87,7 +80,7 @@ func New(format int, fd int, size int) (*Context, error) {
|
||||
}
|
||||
cloc := C.CString(locale)
|
||||
defer C.free(unsafe.Pointer(cloc))
|
||||
ctx.compTable = C.xkb_compose_table_new_from_locale(ctx.ctx, cloc, C.XKB_COMPOSE_COMPILE_NO_FLAGS)
|
||||
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")
|
||||
@@ -97,27 +90,54 @@ func New(format int, fd int, size int) (*Context, error) {
|
||||
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 {
|
||||
ctx.Destroy()
|
||||
return nil, fmt.Errorf("newXKB: mmap of keymap failed: %v", err)
|
||||
}
|
||||
defer syscall.Munmap(mapData)
|
||||
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")
|
||||
}
|
||||
ctx.state = C.xkb_state_new(ctx.keyMap)
|
||||
if ctx.state == nil {
|
||||
ctx.Destroy()
|
||||
return nil, errors.New("newXKB: xkb_state_new failed")
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (x *Context) DestroyKeymapState() {
|
||||
if x.state != nil {
|
||||
C.xkb_state_unref(x.state)
|
||||
x.state = nil
|
||||
}
|
||||
if x.keyMap != nil {
|
||||
C.xkb_keymap_unref(x.keyMap)
|
||||
x.keyMap = nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetKeymap sets the keymap and state. The context takes ownership of the
|
||||
// keymap and state and frees them in Destroy.
|
||||
func (x *Context) SetKeymap(xkbKeyMap, xkbState unsafe.Pointer) {
|
||||
x.DestroyKeymapState()
|
||||
x.keyMap = (*C.struct_xkb_keymap)(xkbKeyMap)
|
||||
x.state = (*C.struct_xkb_state)(xkbState)
|
||||
}
|
||||
|
||||
func (x *Context) LoadKeymap(format int, fd int, size int) error {
|
||||
x.DestroyKeymapState()
|
||||
mapData, err := syscall.Mmap(int(fd), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
return fmt.Errorf("newXKB: mmap of keymap failed: %v", err)
|
||||
}
|
||||
defer syscall.Munmap(mapData)
|
||||
keyMap := C.xkb_keymap_new_from_buffer(x.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 keyMap == nil {
|
||||
return errors.New("newXKB: xkb_keymap_new_from_buffer failed")
|
||||
}
|
||||
state := C.xkb_state_new(keyMap)
|
||||
if state == nil {
|
||||
C.xkb_keymap_unref(keyMap)
|
||||
return errors.New("newXKB: xkb_state_new failed")
|
||||
}
|
||||
x.keyMap = keyMap
|
||||
x.state = state
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) {
|
||||
kc := mapXKBKeyCode(keyCode)
|
||||
if x.state == nil {
|
||||
return
|
||||
}
|
||||
kc := C.xkb_keycode_t(keyCode)
|
||||
if len(x.utf8Buf) == 0 {
|
||||
x.utf8Buf = make([]byte, 1)
|
||||
}
|
||||
@@ -186,18 +206,16 @@ func (x *Context) charsForKeycode(keyCode C.xkb_keycode_t) []byte {
|
||||
}
|
||||
|
||||
func (x *Context) IsRepeatKey(keyCode uint32) bool {
|
||||
kc := mapXKBKeyCode(keyCode)
|
||||
kc := C.xkb_keycode_t(keyCode)
|
||||
return C.xkb_keymap_key_repeats(x.keyMap, kc) == 1
|
||||
}
|
||||
|
||||
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 uint32) C.xkb_keycode_t {
|
||||
// According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode."
|
||||
return C.xkb_keycode_t(keyCode + 8)
|
||||
func (x *Context) UpdateMask(depressed, latched, locked, depressedGroup, latchedGroup, lockedGroup uint32) {
|
||||
if x.state == nil {
|
||||
return
|
||||
}
|
||||
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),
|
||||
C.xkb_layout_index_t(depressedGroup), C.xkb_layout_index_t(latchedGroup), C.xkb_layout_index_t(lockedGroup))
|
||||
}
|
||||
|
||||
func convertKeysym(s C.xkb_keysym_t) (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user