mirror of
https://github.com/jtgans/g13gui.git
synced 2025-06-20 00:14:09 -04:00
g13gui: Actually finish the migration
Add an evdev listener for input so we can use linux "keysyms" for keyboard events, since uinput uses it.
This commit is contained in:
parent
0a0ecfc9bc
commit
6ddee4ae88
63
g13gui/g13gui/input.py
Normal file
63
g13gui/g13gui/input.py
Normal file
@ -0,0 +1,63 @@
|
||||
import threading
|
||||
import gi
|
||||
import glob
|
||||
import asyncio
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import GObject
|
||||
|
||||
import evdev
|
||||
from evdev import InputDevice
|
||||
from evdev import ecodes
|
||||
from select import select
|
||||
|
||||
|
||||
class InputReader(threading.Thread, GObject.GObject):
|
||||
INPUT_PATH = '/dev/input'
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self, daemon=True)
|
||||
GObject.GObject.__init__(self)
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self._isRunning = False
|
||||
self._keyStates = {}
|
||||
|
||||
@GObject.Signal(name='evdev-key-released')
|
||||
def keyReleased(self, keyCode, time):
|
||||
print('key released: %d')
|
||||
|
||||
@GObject.Signal(name='evdev-key-pressed')
|
||||
def keyPressed(self, keyCode, time):
|
||||
print('key pressed: %d')
|
||||
|
||||
def run(self):
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
devices = [InputDevice(path) for path in evdev.list_devices()]
|
||||
keyboards = [dev for dev in devices if ecodes.EV_KEY in dev.capabilities()]
|
||||
print('Have keyboards: %s' % (keyboards))
|
||||
|
||||
while self._isRunning:
|
||||
r, w, x = select(keyboards, [], [])
|
||||
print('keyboards listening!')
|
||||
for fd in r:
|
||||
for event in keyboards[fd].read():
|
||||
if event.type != ecodes.EV_KEY:
|
||||
continue
|
||||
|
||||
keyCode = event.keycode
|
||||
keyDown = event.keystate == event.key_down
|
||||
lastKeyState = self._keystates.get(keyCode, False)
|
||||
|
||||
if keyDown and not lastKeyState:
|
||||
self.emit('evdev-key-pressed',
|
||||
keyCode, event.event.timestamp())
|
||||
elif not keyDown and lastKeyState:
|
||||
self.emit('evdev-key-released',
|
||||
keyCode, event.event.timestamp())
|
||||
|
||||
def shutdown(self):
|
||||
self._isRunning = False
|
76
g13gui/g13gui/observer/subject.py
Normal file
76
g13gui/g13gui/observer/subject.py
Normal file
@ -0,0 +1,76 @@
|
||||
import enum
|
||||
|
||||
|
||||
class ChangeType(enum.Enum):
|
||||
ADD = 0
|
||||
REMOVE = 1
|
||||
MODIFY = 2
|
||||
|
||||
|
||||
class Subject(object):
|
||||
"""Simple class to handle the subject-side of the Observer pattern."""
|
||||
|
||||
AllKeys = ''
|
||||
|
||||
def registerObserver(self, observer, subscribedKeys=AllKeys):
|
||||
"""Registers an Observer class as an observer of this object"""
|
||||
if subscribedKeys != Subject.AllKeys:
|
||||
subscribedKeys = frozenset(subscribedKeys)
|
||||
if '_observers' not in self.__dict__:
|
||||
self._observers = {observer: subscribedKeys}
|
||||
else:
|
||||
self._observers[observer] = subscribedKeys
|
||||
|
||||
def removeObserver(self, observer):
|
||||
"""Removes an observer from this object"""
|
||||
if '_observers' in self.__dict__:
|
||||
if observer in self._observers:
|
||||
del self._observers[observer]
|
||||
|
||||
def addChange(self, type, key, data=None):
|
||||
"""Schedules a change notification for transmitting later.
|
||||
|
||||
type[ChangeType]: the type of change that occurred.
|
||||
key[string]: a required name for what field changed.
|
||||
data[object]: an optional context-dependent object, dict, or
|
||||
None, specifying what changed. In the case of an ADD or MODIFY,
|
||||
whatChanged should be the new data. In the case of a DELETE, it should
|
||||
be the old data (or None).
|
||||
"""
|
||||
if '_changes' not in self.__dict__:
|
||||
self._changes = [(type, key, data)]
|
||||
else:
|
||||
self._changes.append((type, key, data))
|
||||
|
||||
def clearChanges(self):
|
||||
"""Removes all scheduled changes from the change buffer."""
|
||||
self._changes = []
|
||||
|
||||
def notifyChange(self):
|
||||
raise NotImplementedError('Use Subject.notifyChanged instead')
|
||||
|
||||
def notifyChanged(self):
|
||||
"""Notifies all observers of scheduled changes in the change buffer.
|
||||
|
||||
This method actually does the work of iterating through all observers
|
||||
and all changes and delivering them to the Observer's onSubjectChanged
|
||||
method.
|
||||
|
||||
It is safe to call this if there are no changes to send in the buffer,
|
||||
or there are no observers to send changes to. Note that calling this
|
||||
when no observers are registered will still flush the change buffer.
|
||||
"""
|
||||
if '_observers' in self.__dict__ and '_changes' in self.__dict__:
|
||||
for observer, subscribedKeys in self._observers.items():
|
||||
for type, key, data in self._changes:
|
||||
if subscribedKeys == Subject.AllKeys or key in subscribedKeys:
|
||||
observer.onSubjectChanged(self, type, key, data)
|
||||
|
||||
self._changes = []
|
||||
|
||||
def setProperty(self, propertyName, value, notify=True):
|
||||
propertyName = '_' + propertyName
|
||||
self.__dict__[propertyName] = value
|
||||
self.addChange(ChangeType.MODIFY, propertyName, value)
|
||||
if notify:
|
||||
self.notifyChanged()
|
@ -8,6 +8,8 @@ from g13gui.bitwidgets.fonts_tests import *
|
||||
from g13gui.bitwidgets.label_tests import *
|
||||
from g13gui.bitwidgets.display_tests import *
|
||||
from g13gui.bitwidgets.screen_tests import *
|
||||
from g13gui.g13.displaydevice_tests import *
|
||||
from g13gui.g13.manager_tests import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -44,7 +44,7 @@ class G13Button(Gtk.MenuButton, GtkObserver):
|
||||
bindings = self._prefs.selectedProfile().keyBinding(self._keyName)
|
||||
|
||||
if len(bindings) > 0:
|
||||
keybinds = G13ToGDK(bindings)
|
||||
keybinds = BindsToKeynames(bindings)
|
||||
accelerator = '+'.join(keybinds)
|
||||
shortcut = Gtk.ShortcutsShortcut(
|
||||
shortcut_type=Gtk.ShortcutType.ACCELERATOR,
|
||||
|
@ -1,17 +1,16 @@
|
||||
import gi
|
||||
|
||||
from g13gui.observer import GtkObserver
|
||||
|
||||
from g13gui.model.bindings import G13ToGDK
|
||||
from g13gui.model.bindings import GDKToG13
|
||||
from g13gui.model.bindings import G13DKeyIsModifier
|
||||
from g13gui.observer.gtkobserver import GtkObserver
|
||||
from g13gui.model.bindings import BindsToKeynames
|
||||
from g13gui.model.bindings import KeycodeIsModifier
|
||||
from g13gui.input import InputReader
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, GObject, Gdk
|
||||
|
||||
|
||||
MAX_DELAY_BETWEEN_PRESSES_MILLIS = 250
|
||||
MAX_DELAY_BETWEEN_PRESSES_SECONDS = 0.250
|
||||
|
||||
|
||||
class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
@ -21,6 +20,11 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
|
||||
self._prefs = prefs
|
||||
self._prefs.registerObserver(self, {'selectedProfile'})
|
||||
|
||||
self._inputReader = InputReader()
|
||||
self._inputReader.connect('evdev-key-pressed', self.keypress)
|
||||
self._inputReader.connect('evdev-key-released', self.keyrelease)
|
||||
|
||||
self._keyName = keyName
|
||||
|
||||
self._modifiers = set()
|
||||
@ -52,8 +56,6 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
self._box.pack_start(button, True, True, 6)
|
||||
self._box.show_all()
|
||||
|
||||
self.connect("key-press-event", self.keypress)
|
||||
self.connect("key-release-event", self.keyrelease)
|
||||
self.connect("show", self.shown)
|
||||
self.connect("closed", self.closed)
|
||||
button.connect("pressed", self.clear)
|
||||
@ -61,7 +63,7 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
self.buildBindingDisplay()
|
||||
|
||||
def shown(self, widget):
|
||||
self.grab_add()
|
||||
self._inputReader.start()
|
||||
|
||||
def rebuildBindingDisplay(self):
|
||||
if self._bindingBox:
|
||||
@ -76,13 +78,11 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
self._box.reorder_child(self._bindingBox, 1)
|
||||
|
||||
if len(self._currentBindings) > 0:
|
||||
keybinds = G13ToGDK(self._currentBindings)
|
||||
keybinds = BindsToKeynames(self._currentBindings)
|
||||
accelerator = '+'.join(keybinds)
|
||||
shortcut = Gtk.ShortcutsShortcut(
|
||||
shortcut_type=Gtk.ShortcutType.ACCELERATOR,
|
||||
accelerator=accelerator)
|
||||
shortcut.set_halign(Gtk.Align.CENTER)
|
||||
self._bindingBox.pack_start(shortcut, True, True, 6)
|
||||
label = Gtk.Label(accelerator)
|
||||
label.set_halign(Gtk.Align.CENTER)
|
||||
self._bindingBox.pack_start(label, True, True, 6)
|
||||
else:
|
||||
label = Gtk.Label()
|
||||
label.set_markup("<i>No binding. Press a key to bind.</i>")
|
||||
@ -90,31 +90,26 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
|
||||
self._bindingBox.show_all()
|
||||
|
||||
def keypress(self, buttonMenu, eventKey):
|
||||
pressDelta = eventKey.time - self._lastPressTime
|
||||
if pressDelta > MAX_DELAY_BETWEEN_PRESSES_MILLIS:
|
||||
def keypress(self, keyCode, timestamp):
|
||||
print('keypress: %s' % repr(keyCode))
|
||||
|
||||
pressDelta = timestamp - self._lastPressTime
|
||||
if pressDelta > MAX_DELAY_BETWEEN_PRESSES_SECONDS:
|
||||
self._modifiers = set()
|
||||
self._consonantKey = None
|
||||
|
||||
binding = Gdk.keyval_name(eventKey.keyval)
|
||||
if len(binding) == 1:
|
||||
binding = binding.upper()
|
||||
if binding == 'Meta_L':
|
||||
binding = 'Alt_L'
|
||||
if binding == 'Meta_R':
|
||||
binding = 'Alt_R'
|
||||
key = keyCode
|
||||
print('Hardware keycode: %s' % (key))
|
||||
|
||||
binding = GDKToG13(binding)
|
||||
|
||||
if G13DKeyIsModifier(binding):
|
||||
self._modifiers.add(binding)
|
||||
if KeycodeIsModifier(key):
|
||||
self._modifiers.add(key)
|
||||
else:
|
||||
self._consonantKey = binding
|
||||
self._consonantKey = key
|
||||
|
||||
self._lastPressTime = eventKey.time
|
||||
self._lastPressTime = timestamp
|
||||
return True
|
||||
|
||||
def keyrelease(self, buttonMenu, eventKey):
|
||||
def keyrelease(self, keyCode):
|
||||
self._currentBindings = sorted(list(self._modifiers))
|
||||
if self._consonantKey:
|
||||
self._currentBindings += [self._consonantKey]
|
||||
@ -128,7 +123,7 @@ class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
self.hide()
|
||||
|
||||
def closed(self, buttonMenu):
|
||||
self.grab_remove()
|
||||
self._inputReader.shutdown()
|
||||
self._prefs.selectedProfile().bindKey(self._keyName,
|
||||
self._currentBindings)
|
||||
self.hide()
|
||||
|
Loading…
Reference in New Issue
Block a user