g13buttonpopover: Read from evdev to capture keys

Since GDK/GTK filter events through Xorg, libinput, and everything else in
between, we can't use their keypress detection code to actually figure out what
the linux key codes are, instead we read directly from all keyboard-like evdev
devices on the system.

This is less than ideal, but directly due to Linux/Xorg/libinput/XKB/GDK/GTK/GNOME's
completely batshit insane input model. On Wayland it gets better, but not by
much since most compositors still use XKB and libinput.

*sigh*
This commit is contained in:
June Tate-Gans 2021-05-03 18:14:32 -05:00
parent 240be20b63
commit 244368ede3

View File

@ -7,7 +7,7 @@ from g13gui.input import InputReader
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, GObject, Gdk from gi.repository import Gtk, GObject, Gdk, GLib
MAX_DELAY_BETWEEN_PRESSES_SECONDS = 0.250 MAX_DELAY_BETWEEN_PRESSES_SECONDS = 0.250
@ -24,6 +24,7 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self._inputReader = InputReader() self._inputReader = InputReader()
self._inputReader.connect('evdev-key-pressed', self.keypress) self._inputReader.connect('evdev-key-pressed', self.keypress)
self._inputReader.connect('evdev-key-released', self.keyrelease) self._inputReader.connect('evdev-key-released', self.keyrelease)
self.connect('key-press-event', self.ignoreKeypress)
self._keyName = keyName self._keyName = keyName
@ -35,6 +36,9 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self.updateBinding() self.updateBinding()
self.build() self.build()
def ignoreKeypress(self, *args, **kwargs):
return True
def updateBinding(self): def updateBinding(self):
selectedProfile = self._prefs.selectedProfile() selectedProfile = self._prefs.selectedProfile()
self._currentBindings = selectedProfile.keyBinding(self._keyName) self._currentBindings = selectedProfile.keyBinding(self._keyName)
@ -63,7 +67,7 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self.buildBindingDisplay() self.buildBindingDisplay()
def shown(self, widget): def shown(self, widget):
self._inputReader.start() self._inputReader.capture()
def rebuildBindingDisplay(self): def rebuildBindingDisplay(self):
if self._bindingBox: if self._bindingBox:
@ -90,16 +94,13 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self._bindingBox.show_all() self._bindingBox.show_all()
def keypress(self, keyCode, timestamp): def keypress(self, obj, keyCode, timestamp):
print('keypress: %s' % repr(keyCode))
pressDelta = timestamp - self._lastPressTime pressDelta = timestamp - self._lastPressTime
if pressDelta > MAX_DELAY_BETWEEN_PRESSES_SECONDS: if pressDelta > MAX_DELAY_BETWEEN_PRESSES_SECONDS:
self._modifiers = set() self._modifiers = set()
self._consonantKey = None self._consonantKey = None
key = keyCode key = keyCode
print('Hardware keycode: %s' % (key))
if KeycodeIsModifier(key): if KeycodeIsModifier(key):
self._modifiers.add(key) self._modifiers.add(key)
@ -107,15 +108,13 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self._consonantKey = key self._consonantKey = key
self._lastPressTime = timestamp self._lastPressTime = timestamp
return True
def keyrelease(self, keyCode): def keyrelease(self, obj, keyCode, timestamp):
self._currentBindings = sorted(list(self._modifiers)) self._currentBindings = sorted(list(self._modifiers))
if self._consonantKey: if self._consonantKey:
self._currentBindings += [self._consonantKey] self._currentBindings += [self._consonantKey]
self.rebuildBindingDisplay() self.rebuildBindingDisplay()
self.hide() self.hide()
return True
def clear(self, button): def clear(self, button):
self._currentBindings = [] self._currentBindings = []
@ -123,7 +122,8 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
self.hide() self.hide()
def closed(self, buttonMenu): def closed(self, buttonMenu):
self._inputReader.shutdown()
self._prefs.selectedProfile().bindKey(self._keyName, self._prefs.selectedProfile().bindKey(self._keyName,
self._currentBindings) self._currentBindings)
self.hide() self.hide()
self._inputReader.stop()