import abc import logging import typing import Xlib.protocol.event import Xlib.X class EngineAction: def __init__(self, target_engine_index: typing.Optional[int] = None): self._target_engine_index = target_engine_index @abc.abstractmethod def execute_on_window(self, overlay, xkeyevent, engine_window): raise NotImplementedError() def execute(self, overlay, xkeyevent): if not self._target_engine_index: for target_window in overlay.engine_windows: self.execute_on_window(overlay, xkeyevent, target_window) elif self._target_engine_index >= len(overlay.engine_windows): logging.warning("target engine index out of bounds") else: self.execute_on_window( overlay, xkeyevent, overlay.engine_windows[self._target_engine_index] ) class CenterClickAction(EngineAction): def __init__(self, target_engine_index, factor_x, factor_y): super().__init__(target_engine_index=target_engine_index,) self._button = Xlib.X.Button1 self._factor_x = factor_x self._factor_y = factor_y def execute_on_window(self, overlay, xkeyevent, engine_window): engine_geometry = engine_window.get_geometry() smaller_dimension = min(engine_geometry.width, engine_geometry.height) attr = dict( window=engine_window, detail=self._button, state=xkeyevent.state, event_x=int(engine_geometry.width / 2 + smaller_dimension * self._factor_x), event_y=int( engine_geometry.height / 2 + smaller_dimension * self._factor_y ), # apparently root_x & root_y do not need to correspond with event_x/y. # attributes are still required to be set. root_x=0, # xkeyevent.root_x, root_y=0, # xkeyevent.root_y, child=xkeyevent.child, root=xkeyevent.root, time=xkeyevent.time, # X.CurrentTime same_screen=xkeyevent.same_screen, ) if isinstance(xkeyevent, Xlib.protocol.event.KeyPress): e = Xlib.protocol.event.ButtonPress(**attr) else: e = Xlib.protocol.event.ButtonRelease(**attr) engine_window.send_event(e) class SelectGagAction(CenterClickAction): X_OFFSET = -0.286 X_FACTOR = 0.081 def __init__(self, target_engine_index, column_index, factor_y): super().__init__( target_engine_index=target_engine_index, factor_x=self.X_OFFSET + self.X_FACTOR * column_index, factor_y=factor_y, ) class RewriteKeyEventAction(EngineAction): def __init__(self, target_engine_index: typing.Optional[int] = None, keysym=None): super().__init__(target_engine_index=target_engine_index,) self._keysym = keysym def execute_on_window(self, overlay, xkeyevent, engine_window): engine_window.send_event( type(xkeyevent)( window=engine_window, detail=overlay.xdisplay.keysym_to_keycode(self._keysym) if self._keysym else xkeyevent.detail, state=xkeyevent.state, root_x=xkeyevent.root_x, root_y=xkeyevent.root_y, event_x=xkeyevent.event_x, event_y=xkeyevent.event_y, child=xkeyevent.child, root=xkeyevent.root, time=xkeyevent.time, # X.CurrentTime same_screen=xkeyevent.same_screen, ) ) class ToggleOverlayAction: def execute(self, overlay, xkeyevent): if isinstance(xkeyevent, Xlib.protocol.event.KeyPress): overlay.toggle()