_actions.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import abc
  2. import logging
  3. import typing
  4. import Xlib.protocol.event
  5. import Xlib.X
  6. class EngineAction:
  7. def __init__(self, target_engine_index: typing.Optional[int] = None):
  8. self._target_engine_index = target_engine_index
  9. @abc.abstractmethod
  10. def execute_on_window(
  11. self,
  12. overlay: "rescriptoon.Overlay",
  13. xkeyevent: typing.Union[
  14. Xlib.protocol.event.KeyPress, Xlib.protocol.event.KeyRelease
  15. ],
  16. engine_window: "Xlib.display.Window",
  17. ):
  18. raise NotImplementedError()
  19. def execute(
  20. self,
  21. overlay: "rescriptoon.Overlay",
  22. xkeyevent: typing.Union[
  23. Xlib.protocol.event.KeyPress, Xlib.protocol.event.KeyRelease
  24. ],
  25. ):
  26. if self._target_engine_index is None:
  27. for target_window in overlay.engine_windows:
  28. self.execute_on_window(overlay, xkeyevent, target_window)
  29. elif self._target_engine_index >= len(overlay.engine_windows):
  30. logging.warning("target engine index out of bounds")
  31. else:
  32. self.execute_on_window(
  33. overlay, xkeyevent, overlay.engine_windows[self._target_engine_index]
  34. )
  35. class CenterClickAction(EngineAction):
  36. def __init__(self, target_engine_index: int, factor_x: float, factor_y: float):
  37. super().__init__(target_engine_index=target_engine_index,)
  38. self._button = Xlib.X.Button1
  39. self._factor_x = factor_x
  40. self._factor_y = factor_y
  41. def execute_on_window(
  42. self,
  43. overlay: "rescriptoon.Overlay",
  44. xkeyevent: typing.Union[
  45. Xlib.protocol.event.KeyPress, Xlib.protocol.event.KeyRelease
  46. ],
  47. engine_window: "Xlib.display.Window",
  48. ):
  49. engine_geometry = engine_window.get_geometry()
  50. smaller_dimension = min(engine_geometry.width, engine_geometry.height)
  51. attr = dict(
  52. window=engine_window,
  53. detail=self._button,
  54. state=xkeyevent.state,
  55. event_x=int(engine_geometry.width / 2 + smaller_dimension * self._factor_x),
  56. event_y=int(
  57. engine_geometry.height / 2 + smaller_dimension * self._factor_y
  58. ),
  59. # apparently root_x & root_y do not need to correspond with event_x/y.
  60. # attributes are still required to be set.
  61. root_x=0, # xkeyevent.root_x,
  62. root_y=0, # xkeyevent.root_y,
  63. child=xkeyevent.child,
  64. root=xkeyevent.root,
  65. time=xkeyevent.time, # X.CurrentTime
  66. same_screen=xkeyevent.same_screen,
  67. )
  68. if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
  69. button_event = Xlib.protocol.event.ButtonPress(**attr)
  70. else:
  71. button_event = Xlib.protocol.event.ButtonRelease(**attr)
  72. engine_window.send_event(button_event)
  73. class SelectGagAction(CenterClickAction):
  74. X_OFFSET = -0.286
  75. X_FACTOR = 0.081
  76. def __init__(self, target_engine_index: int, column_index: int, factor_y: float):
  77. super().__init__(
  78. target_engine_index=target_engine_index,
  79. factor_x=self.X_OFFSET + self.X_FACTOR * column_index,
  80. factor_y=factor_y,
  81. )
  82. class RewriteKeyEventAction(EngineAction):
  83. def __init__(self, target_engine_index: typing.Optional[int] = None, keysym=None):
  84. super().__init__(target_engine_index=target_engine_index,)
  85. self._keysym = keysym
  86. def execute_on_window(
  87. self,
  88. overlay: "rescriptoon.Overlay",
  89. xkeyevent: typing.Union[
  90. Xlib.protocol.event.KeyPress, Xlib.protocol.event.KeyRelease
  91. ],
  92. engine_window: "Xlib.display.Window",
  93. ):
  94. engine_window.send_event(
  95. type(xkeyevent)(
  96. window=engine_window,
  97. detail=overlay.xdisplay.keysym_to_keycode(self._keysym)
  98. if self._keysym
  99. else xkeyevent.detail,
  100. state=xkeyevent.state,
  101. root_x=xkeyevent.root_x,
  102. root_y=xkeyevent.root_y,
  103. event_x=xkeyevent.event_x,
  104. event_y=xkeyevent.event_y,
  105. child=xkeyevent.child,
  106. root=xkeyevent.root,
  107. time=xkeyevent.time, # X.CurrentTime
  108. same_screen=xkeyevent.same_screen,
  109. )
  110. )
  111. class ToggleOverlayAction:
  112. def execute(self, overlay: "rescriptoon.Overlay", xkeyevent: "Xlib.display.Window"):
  113. if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
  114. overlay.toggle()