Skip to content

Commit

Permalink
Event handling refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Egil committed May 28, 2024
1 parent b7d58ea commit f1deb76
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 139 deletions.
6 changes: 3 additions & 3 deletions glass-input/glass_input/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ def handle(event):

mode.push_by_name(display, "base_mode")

display.root.xinput_select_events([
(Xlib.ext.xinput.AllDevices, Xlib.ext.xinput.RawMotionMask),
])
# display.root.xinput_select_events([
# (Xlib.ext.xinput.AllDevices, Xlib.ext.xinput.RawMotionMask),
# ])

InfiniteGlass.DEBUG("init", "Input handler started\n")
86 changes: 86 additions & 0 deletions glass-input/glass_input/event_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import InfiniteGlass
import datetime
import operator

class EventStatePattern(InfiniteGlass.EventPattern):
def __init__(self, pattern, display, state, config):
self.state = state
self.config = config
pattern = pattern.split(",")
self.filters = []
self.statefilters = []
self.parse_state_pattern(pattern)

if not self.statefilters:
self.state_filter = self.state_filter_none
else:
has_time = False
for name, op, value in self.statefilters:
cvalue = self.state.get(name, 0)
if isinstance(cvalue, datetime.datetime):
has_time = True

if has_time:
self.state_filter = self.state_filter_time
else:
self.state_filter = self.state_filter_notime
InfiniteGlass.EventPattern.__init__(self, self.filters, display)
self.pattern = pattern

def state_filter_time(self):
now = datetime.datetime.now()
for name, op, value in self.statefilters:
cvalue = self.state.get(name, 0)
if isinstance(cvalue, datetime.datetime):
cvalue = (now - cvalue).total_seconds()
if not op(cvalue, value):
return False
return True

def state_filter_notime(self):
for name, op, value in self.statefilters:
cvalue = self.state.get(name, 0)
if not op(cvalue, value):
return False
return True

def state_filter_none(self):
return True

def parse_state_pattern(self, items, negate=False):
for item in items:
if '==' in item:
name, value = item.split('==')
self.statefilters.append((name, operator.ne if negate else operator.eq, float(value)))
elif '%' in item:
name, value = item.split('%')
self.statefilters.append((name, modulo, int(value)))
elif '>=' in item:
name, value = item.split('>=')
self.statefilters.append((name, operator.lt if negate else operator.ge, float(value)))
elif '<=' in item:
name, value = item.split('<=')
self.statefilters.append((name, operator.gt if negate else operator.le, float(value)))
elif '>' in item:
name, value = item.split('>')
self.statefilters.append((name, operator.le if negate else operator.gt, float(value)))
elif '<' in item:
name, value = item.split('<')
self.statefilters.append((name, operator.ge if negate else operator.lt, float(value)))
elif item in self.config.get("eventfilters", {}):
self.parse_state_pattern(self.config["eventfilters"][item].split(","), negate)
elif item.startswith("!") and item[1:] in self.config.get("eventfilters", {}):
self.parse_state_pattern(self.config["eventfilters"][item[1:]].split(","), not negate)
else:
if negate:
if item.startswith("!"):
item = item[1:]
else:
item = "!" + item
self.filters.append(item)

def equal(self, event):
return self.state_filter() and InfiniteGlass.EventPattern.equal(self, event)

def contained_by(self, event):
return self.state_filter() and InfiniteGlass.EventPattern.contained_by(self, event)
72 changes: 22 additions & 50 deletions glass-input/glass_input/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import types
import pkg_resources
import yaml
from .event_state import EventStatePattern

config = {}
functions = {}
Expand Down Expand Up @@ -83,6 +84,7 @@ def __getitem__(self, name):
return res.__window__()
return res


class Mode(object):
def __init__(self, **kw):
self.first_event = None
Expand All @@ -91,6 +93,22 @@ def __init__(self, **kw):
for key, value in kw.items():
setattr(self, key, value)

self.keymap_compiled = self.compile_keymap(self.keymap)

def compile_action_keymap(self, action):
if isinstance(action, dict) and "keymap" in action:
action = dict(action)
action["keymap"] = self.compile_keymap(action.pop("keymap"))
return action

def compile_keymap(self, keymap):
return [(EventStatePattern(eventfilter,
self.display,
self.state,
config),
self.compile_action_keymap(action))
for eventfilter, action in keymap.items()]

def enter(self):
self.window = self.get_event_window(self.first_event)
self.x = 0
Expand All @@ -117,64 +135,18 @@ def get_event_window(self, event=None):
def exit(self):
pass

def handle_state_filter(self, eventfilter):
eventfilter = eventfilter.split(",")
filters = []
statefilters = []
def parse(items, negate=False):
for item in items:
if '==' in item:
name, value = item.split('==')
statefilters.append((name, operator.ne if negate else operator.eq, float(value)))
elif '%' in item:
name, value = item.split('%')
statefilters.append((name, modulo, int(value)))
elif '>=' in item:
name, value = item.split('>=')
statefilters.append((name, operator.lt if negate else operator.ge, float(value)))
elif '<=' in item:
name, value = item.split('<=')
statefilters.append((name, operator.gt if negate else operator.le, float(value)))
elif '>' in item:
name, value = item.split('>')
statefilters.append((name, operator.le if negate else operator.gt, float(value)))
elif '<' in item:
name, value = item.split('<')
statefilters.append((name, operator.ge if negate else operator.lt, float(value)))
elif item in config.get("eventfilters", {}):
parse(config["eventfilters"][item].split(","), negate)
elif item.startswith("!") and item[1:] in config.get("eventfilters", {}):
parse(config["eventfilters"][item[1:]].split(","), not negate)
else:
if negate:
if item.startswith("!"):
item = item[1:]
else:
item = "!" + item
filters.append(item)
parse(eventfilter)
now = datetime.datetime.now()
for name, op, value in statefilters:
cvalue = self.state.get(name, 0)
if isinstance(cvalue, datetime.datetime):
cvalue = (now - cvalue).total_seconds()
# print("%s %s %s == %s" % (cvalue, op, value, op(cvalue, value)))
if not op(cvalue, value):
return None
return filters

def handle(self, event, keymap=None):
self.last_event = event

if keymap is None:
keymap = self.keymap
for eventfilter, action in keymap.items():
filters = self.handle_state_filter(eventfilter)
keymap = self.keymap_compiled
for eventfilter, action in keymap:
try:
if filters is None or filters and not event[filters]:
if not event[eventfilter]:
continue
except Exception as e:
print(eventfilter, "=>", filters)
print(eventfilter, "=>", eventfilter)
print(e)
raise
try:
Expand Down
87 changes: 1 addition & 86 deletions glass-lib/InfiniteGlass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,94 +12,9 @@
from .valueencoding import *
from .debug import *
from .profile import *

from .event import EventPattern
from Xlib.display import Display

def parse_event_pattern(pattern):
if isinstance(pattern, str):
pattern = (pattern,)
if not isinstance(pattern, (list, tuple)):
return None
res = {"buttons": [],
"masks": [],
"keys": [],
"types": [],
"flags": []}
for item in pattern:
include = True
if item.startswith("!"):
include = False
item = item[1:]
try:
item = int(item)
except:
pass
if isinstance(item, int):
res["buttons"].append((include, item))
elif item.endswith("Mask"):
res["masks"].append((include, item))
elif item.startswith("XK_"):
res["keys"].append((include, item))
elif item == "AutoRepeat":
res["flags"].append((include, item))
else:
res["types"].append((include, item))
return res

orig_event_eq = Xlib.protocol.rq.Event.__eq__
def event_eq(self, other):
pattern = parse_event_pattern(other)
if pattern is None:
return orig_event_eq(self, other)
for i, t in pattern["types"]:
if hasattr(Xlib.X, t):
t = getattr(Xlib.X, t)
elif hasattr(Xlib.ext.ge, t):
t = getattr(Xlib.ext.ge, t)
else:
raise Exception("Unknown event type specified in on(): %s" % t)
if i != (self.type == t):
return False
if pattern["masks"] and self.state != sum((getattr(Xlib.X, item) for i, item in pattern["masks"] if i), 0):
return False
for i, k in pattern["keys"]:
if i != (self.detail == self.window.display.real_display.keycode(k)):
return False
for i, b in pattern["buttons"]:
if i != (self.detail == b):
return False
for i, f in pattern["flags"]:
if i != (hasattr(self, f)):
return False
return True
Xlib.protocol.rq.Event.__eq__ = event_eq

def event_getitem(self, item):
pattern = parse_event_pattern(item)
for i, t in pattern["types"]:
if hasattr(Xlib.X, t):
t = getattr(Xlib.X, t)
elif hasattr(Xlib.ext.ge, t):
t = getattr(Xlib.ext.ge, t)
else:
raise Exception("Unknown event type specified in on(): %s" % t)
if i != (self.type == t):
return False
for i, s in pattern["masks"]:
if i != (not not self.state & getattr(Xlib.X, s)):
return False
for i, k in pattern["keys"]:
if i != (self.detail == self.window.display.real_display.keycode(k)):
return False
for i, b in pattern["buttons"]:
if i != (self.detail == b):
return False
for i, f in pattern["flags"]:
if i != (hasattr(self, f)):
return False
return True
Xlib.protocol.rq.Event.__getitem__ = event_getitem

def client_message_parse(self, *types):
disp = self.window.display.real_display
format, data = self.data
Expand Down
Loading

0 comments on commit f1deb76

Please sign in to comment.