X11: reduce typing delay for non-Latin characters (Greek, etc.)
Previously, typing non-Latin characters (e.g. Greek) via the X11 backend was extremely slow (~1000ms per character) because the server had to dynamically remap an unused keycode to the desired keysym via XChangeKeyboardMapping. A blind 500ms sleep was used twice per character: once before sending the key event (to wait for the mapping to propagate), and once after (to wait for clients to process MappingNotify). The root cause is that X11 key events only carry keycodes, not keysyms. When a keysym has no existing keycode in the keyboard mapping, the app must temporarily remap a keycode. Clients cache the keyboard mapping locally and re-read it upon receiving a MappingNotify event from the server. The race condition is that a client may process the key event before it has processed the MappingNotify, interpreting the keycode with the stale (old) mapping. This patch replaces the blind pre-send sleep with a server-side verification loop: after XChangeKeyboardMapping, it polls XGetKeyboardMapping to confirm the server has applied the new mapping before sending the key event. This is safe because XGetKeyboardMapping reads directly from the server, which is the authority on the current mapping. The post-send delay is reduced from 500ms to 50ms, which is sufficient for clients to process MappingNotify on modern systems. Expected improvement: non-Latin characters go from ~1000ms/char to ~55-60ms/char (verification ~5ms + post-send 50ms). Latin characters remain instant as they already have existing keycode mappings. The 500ms constant was overly conservative. The verification loop provides a correct and responsive solution: it returns as soon as the server confirms the mapping, and times out after 200ms as a safety net. Fixes #46
This commit is contained in:
parent
996edb0501
commit
f2f3cbceed
1 changed files with 34 additions and 4 deletions
|
|
@ -42,7 +42,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
keyboardMappingDelay time.Duration = 500 * time.Millisecond
|
||||
keyboardMappingDelay time.Duration = 50 * time.Millisecond
|
||||
keyMappingTimeout time.Duration = 200 * time.Millisecond
|
||||
keyMappingPoll time.Duration = 5 * time.Millisecond
|
||||
scrollDiv int = 20
|
||||
)
|
||||
|
||||
|
|
@ -150,6 +152,35 @@ func (p *x11Controller) changeKeyMappingLocked(keysymsPerKeycode C.int,
|
|||
C.XFlush(p.display)
|
||||
}
|
||||
|
||||
func (p *x11Controller) verifyKeyMappingLocked(keycode C.KeyCode,
|
||||
keysymsPerKeycode C.int, keysym Keysym) {
|
||||
deadline := time.Now().Add(keyMappingTimeout)
|
||||
for {
|
||||
var currentKeysymsPerKeycode C.int
|
||||
currentKeysyms := C.XGetKeyboardMapping(p.display, keycode, 1,
|
||||
¤tKeysymsPerKeycode)
|
||||
if currentKeysyms != nil {
|
||||
found := false
|
||||
for i := 0; i < int(currentKeysymsPerKeycode); i++ {
|
||||
ks := *(*C.KeySym)(unsafe.Pointer(uintptr(unsafe.Pointer(currentKeysyms)) +
|
||||
uintptr(i)*unsafe.Sizeof(*currentKeysyms)))
|
||||
if ks == C.KeySym(keysym) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
C.XFree(unsafe.Pointer(currentKeysyms))
|
||||
if found {
|
||||
return
|
||||
}
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
return
|
||||
}
|
||||
time.Sleep(keyMappingPoll)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *x11Controller) getModKeycodesLocked() map[uint]C.KeyCode {
|
||||
modKeymap := C.XGetModifierMapping(p.display)
|
||||
defer C.XFreeModifiermap(modKeymap)
|
||||
|
|
@ -253,8 +284,7 @@ func (p *x11Controller) keyboardKeys(keys []Keysym) error {
|
|||
}
|
||||
keycode = emptyKeycode
|
||||
p.changeKeyMappingLocked(keysymsPerKeycode, keycode, keysym)
|
||||
// race condition!
|
||||
time.Sleep(keyboardMappingDelay)
|
||||
p.verifyKeyMappingLocked(keycode, keysymsPerKeycode, keysym)
|
||||
} else {
|
||||
pressMods = mods & ^activeMods
|
||||
releaseMods = activeMods & ^mods
|
||||
|
|
@ -267,7 +297,7 @@ func (p *x11Controller) keyboardKeys(keys []Keysym) error {
|
|||
p.sendModsLocked(modKeycodes, releaseMods, true)
|
||||
C.XFlush(p.display)
|
||||
if keycode == emptyKeycode {
|
||||
// race condition!
|
||||
// Wait for clients to process MappingNotify before next remap
|
||||
time.Sleep(keyboardMappingDelay)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue