mouse.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. from __future__ import annotations
  2. import sys
  3. from typing import TYPE_CHECKING
  4. from prompt_toolkit.data_structures import Point
  5. from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent
  6. from prompt_toolkit.keys import Keys
  7. from prompt_toolkit.mouse_events import (
  8. MouseButton,
  9. MouseEvent,
  10. MouseEventType,
  11. MouseModifier,
  12. )
  13. from ..key_bindings import KeyBindings
  14. if TYPE_CHECKING:
  15. from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
  16. __all__ = [
  17. "load_mouse_bindings",
  18. ]
  19. E = KeyPressEvent
  20. # fmt: off
  21. SCROLL_UP = MouseEventType.SCROLL_UP
  22. SCROLL_DOWN = MouseEventType.SCROLL_DOWN
  23. MOUSE_DOWN = MouseEventType.MOUSE_DOWN
  24. MOUSE_MOVE = MouseEventType.MOUSE_MOVE
  25. MOUSE_UP = MouseEventType.MOUSE_UP
  26. NO_MODIFIER : frozenset[MouseModifier] = frozenset()
  27. SHIFT : frozenset[MouseModifier] = frozenset({MouseModifier.SHIFT})
  28. ALT : frozenset[MouseModifier] = frozenset({MouseModifier.ALT})
  29. SHIFT_ALT : frozenset[MouseModifier] = frozenset({MouseModifier.SHIFT, MouseModifier.ALT})
  30. CONTROL : frozenset[MouseModifier] = frozenset({MouseModifier.CONTROL})
  31. SHIFT_CONTROL : frozenset[MouseModifier] = frozenset({MouseModifier.SHIFT, MouseModifier.CONTROL})
  32. ALT_CONTROL : frozenset[MouseModifier] = frozenset({MouseModifier.ALT, MouseModifier.CONTROL})
  33. SHIFT_ALT_CONTROL: frozenset[MouseModifier] = frozenset({MouseModifier.SHIFT, MouseModifier.ALT, MouseModifier.CONTROL})
  34. UNKNOWN_MODIFIER : frozenset[MouseModifier] = frozenset()
  35. LEFT = MouseButton.LEFT
  36. MIDDLE = MouseButton.MIDDLE
  37. RIGHT = MouseButton.RIGHT
  38. NO_BUTTON = MouseButton.NONE
  39. UNKNOWN_BUTTON = MouseButton.UNKNOWN
  40. xterm_sgr_mouse_events = {
  41. ( 0, "m") : (LEFT, MOUSE_UP, NO_MODIFIER), # left_up 0+ + + =0
  42. ( 4, "m") : (LEFT, MOUSE_UP, SHIFT), # left_up Shift 0+4+ + =4
  43. ( 8, "m") : (LEFT, MOUSE_UP, ALT), # left_up Alt 0+ +8+ =8
  44. (12, "m") : (LEFT, MOUSE_UP, SHIFT_ALT), # left_up Shift Alt 0+4+8+ =12
  45. (16, "m") : (LEFT, MOUSE_UP, CONTROL), # left_up Control 0+ + +16=16
  46. (20, "m") : (LEFT, MOUSE_UP, SHIFT_CONTROL), # left_up Shift Control 0+4+ +16=20
  47. (24, "m") : (LEFT, MOUSE_UP, ALT_CONTROL), # left_up Alt Control 0+ +8+16=24
  48. (28, "m") : (LEFT, MOUSE_UP, SHIFT_ALT_CONTROL), # left_up Shift Alt Control 0+4+8+16=28
  49. ( 1, "m") : (MIDDLE, MOUSE_UP, NO_MODIFIER), # middle_up 1+ + + =1
  50. ( 5, "m") : (MIDDLE, MOUSE_UP, SHIFT), # middle_up Shift 1+4+ + =5
  51. ( 9, "m") : (MIDDLE, MOUSE_UP, ALT), # middle_up Alt 1+ +8+ =9
  52. (13, "m") : (MIDDLE, MOUSE_UP, SHIFT_ALT), # middle_up Shift Alt 1+4+8+ =13
  53. (17, "m") : (MIDDLE, MOUSE_UP, CONTROL), # middle_up Control 1+ + +16=17
  54. (21, "m") : (MIDDLE, MOUSE_UP, SHIFT_CONTROL), # middle_up Shift Control 1+4+ +16=21
  55. (25, "m") : (MIDDLE, MOUSE_UP, ALT_CONTROL), # middle_up Alt Control 1+ +8+16=25
  56. (29, "m") : (MIDDLE, MOUSE_UP, SHIFT_ALT_CONTROL), # middle_up Shift Alt Control 1+4+8+16=29
  57. ( 2, "m") : (RIGHT, MOUSE_UP, NO_MODIFIER), # right_up 2+ + + =2
  58. ( 6, "m") : (RIGHT, MOUSE_UP, SHIFT), # right_up Shift 2+4+ + =6
  59. (10, "m") : (RIGHT, MOUSE_UP, ALT), # right_up Alt 2+ +8+ =10
  60. (14, "m") : (RIGHT, MOUSE_UP, SHIFT_ALT), # right_up Shift Alt 2+4+8+ =14
  61. (18, "m") : (RIGHT, MOUSE_UP, CONTROL), # right_up Control 2+ + +16=18
  62. (22, "m") : (RIGHT, MOUSE_UP, SHIFT_CONTROL), # right_up Shift Control 2+4+ +16=22
  63. (26, "m") : (RIGHT, MOUSE_UP, ALT_CONTROL), # right_up Alt Control 2+ +8+16=26
  64. (30, "m") : (RIGHT, MOUSE_UP, SHIFT_ALT_CONTROL), # right_up Shift Alt Control 2+4+8+16=30
  65. ( 0, "M") : (LEFT, MOUSE_DOWN, NO_MODIFIER), # left_down 0+ + + =0
  66. ( 4, "M") : (LEFT, MOUSE_DOWN, SHIFT), # left_down Shift 0+4+ + =4
  67. ( 8, "M") : (LEFT, MOUSE_DOWN, ALT), # left_down Alt 0+ +8+ =8
  68. (12, "M") : (LEFT, MOUSE_DOWN, SHIFT_ALT), # left_down Shift Alt 0+4+8+ =12
  69. (16, "M") : (LEFT, MOUSE_DOWN, CONTROL), # left_down Control 0+ + +16=16
  70. (20, "M") : (LEFT, MOUSE_DOWN, SHIFT_CONTROL), # left_down Shift Control 0+4+ +16=20
  71. (24, "M") : (LEFT, MOUSE_DOWN, ALT_CONTROL), # left_down Alt Control 0+ +8+16=24
  72. (28, "M") : (LEFT, MOUSE_DOWN, SHIFT_ALT_CONTROL), # left_down Shift Alt Control 0+4+8+16=28
  73. ( 1, "M") : (MIDDLE, MOUSE_DOWN, NO_MODIFIER), # middle_down 1+ + + =1
  74. ( 5, "M") : (MIDDLE, MOUSE_DOWN, SHIFT), # middle_down Shift 1+4+ + =5
  75. ( 9, "M") : (MIDDLE, MOUSE_DOWN, ALT), # middle_down Alt 1+ +8+ =9
  76. (13, "M") : (MIDDLE, MOUSE_DOWN, SHIFT_ALT), # middle_down Shift Alt 1+4+8+ =13
  77. (17, "M") : (MIDDLE, MOUSE_DOWN, CONTROL), # middle_down Control 1+ + +16=17
  78. (21, "M") : (MIDDLE, MOUSE_DOWN, SHIFT_CONTROL), # middle_down Shift Control 1+4+ +16=21
  79. (25, "M") : (MIDDLE, MOUSE_DOWN, ALT_CONTROL), # middle_down Alt Control 1+ +8+16=25
  80. (29, "M") : (MIDDLE, MOUSE_DOWN, SHIFT_ALT_CONTROL), # middle_down Shift Alt Control 1+4+8+16=29
  81. ( 2, "M") : (RIGHT, MOUSE_DOWN, NO_MODIFIER), # right_down 2+ + + =2
  82. ( 6, "M") : (RIGHT, MOUSE_DOWN, SHIFT), # right_down Shift 2+4+ + =6
  83. (10, "M") : (RIGHT, MOUSE_DOWN, ALT), # right_down Alt 2+ +8+ =10
  84. (14, "M") : (RIGHT, MOUSE_DOWN, SHIFT_ALT), # right_down Shift Alt 2+4+8+ =14
  85. (18, "M") : (RIGHT, MOUSE_DOWN, CONTROL), # right_down Control 2+ + +16=18
  86. (22, "M") : (RIGHT, MOUSE_DOWN, SHIFT_CONTROL), # right_down Shift Control 2+4+ +16=22
  87. (26, "M") : (RIGHT, MOUSE_DOWN, ALT_CONTROL), # right_down Alt Control 2+ +8+16=26
  88. (30, "M") : (RIGHT, MOUSE_DOWN, SHIFT_ALT_CONTROL), # right_down Shift Alt Control 2+4+8+16=30
  89. (32, "M") : (LEFT, MOUSE_MOVE, NO_MODIFIER), # left_drag 32+ + + =32
  90. (36, "M") : (LEFT, MOUSE_MOVE, SHIFT), # left_drag Shift 32+4+ + =36
  91. (40, "M") : (LEFT, MOUSE_MOVE, ALT), # left_drag Alt 32+ +8+ =40
  92. (44, "M") : (LEFT, MOUSE_MOVE, SHIFT_ALT), # left_drag Shift Alt 32+4+8+ =44
  93. (48, "M") : (LEFT, MOUSE_MOVE, CONTROL), # left_drag Control 32+ + +16=48
  94. (52, "M") : (LEFT, MOUSE_MOVE, SHIFT_CONTROL), # left_drag Shift Control 32+4+ +16=52
  95. (56, "M") : (LEFT, MOUSE_MOVE, ALT_CONTROL), # left_drag Alt Control 32+ +8+16=56
  96. (60, "M") : (LEFT, MOUSE_MOVE, SHIFT_ALT_CONTROL), # left_drag Shift Alt Control 32+4+8+16=60
  97. (33, "M") : (MIDDLE, MOUSE_MOVE, NO_MODIFIER), # middle_drag 33+ + + =33
  98. (37, "M") : (MIDDLE, MOUSE_MOVE, SHIFT), # middle_drag Shift 33+4+ + =37
  99. (41, "M") : (MIDDLE, MOUSE_MOVE, ALT), # middle_drag Alt 33+ +8+ =41
  100. (45, "M") : (MIDDLE, MOUSE_MOVE, SHIFT_ALT), # middle_drag Shift Alt 33+4+8+ =45
  101. (49, "M") : (MIDDLE, MOUSE_MOVE, CONTROL), # middle_drag Control 33+ + +16=49
  102. (53, "M") : (MIDDLE, MOUSE_MOVE, SHIFT_CONTROL), # middle_drag Shift Control 33+4+ +16=53
  103. (57, "M") : (MIDDLE, MOUSE_MOVE, ALT_CONTROL), # middle_drag Alt Control 33+ +8+16=57
  104. (61, "M") : (MIDDLE, MOUSE_MOVE, SHIFT_ALT_CONTROL), # middle_drag Shift Alt Control 33+4+8+16=61
  105. (34, "M") : (RIGHT, MOUSE_MOVE, NO_MODIFIER), # right_drag 34+ + + =34
  106. (38, "M") : (RIGHT, MOUSE_MOVE, SHIFT), # right_drag Shift 34+4+ + =38
  107. (42, "M") : (RIGHT, MOUSE_MOVE, ALT), # right_drag Alt 34+ +8+ =42
  108. (46, "M") : (RIGHT, MOUSE_MOVE, SHIFT_ALT), # right_drag Shift Alt 34+4+8+ =46
  109. (50, "M") : (RIGHT, MOUSE_MOVE, CONTROL), # right_drag Control 34+ + +16=50
  110. (54, "M") : (RIGHT, MOUSE_MOVE, SHIFT_CONTROL), # right_drag Shift Control 34+4+ +16=54
  111. (58, "M") : (RIGHT, MOUSE_MOVE, ALT_CONTROL), # right_drag Alt Control 34+ +8+16=58
  112. (62, "M") : (RIGHT, MOUSE_MOVE, SHIFT_ALT_CONTROL), # right_drag Shift Alt Control 34+4+8+16=62
  113. (35, "M") : (NO_BUTTON, MOUSE_MOVE, NO_MODIFIER), # none_drag 35+ + + =35
  114. (39, "M") : (NO_BUTTON, MOUSE_MOVE, SHIFT), # none_drag Shift 35+4+ + =39
  115. (43, "M") : (NO_BUTTON, MOUSE_MOVE, ALT), # none_drag Alt 35+ +8+ =43
  116. (47, "M") : (NO_BUTTON, MOUSE_MOVE, SHIFT_ALT), # none_drag Shift Alt 35+4+8+ =47
  117. (51, "M") : (NO_BUTTON, MOUSE_MOVE, CONTROL), # none_drag Control 35+ + +16=51
  118. (55, "M") : (NO_BUTTON, MOUSE_MOVE, SHIFT_CONTROL), # none_drag Shift Control 35+4+ +16=55
  119. (59, "M") : (NO_BUTTON, MOUSE_MOVE, ALT_CONTROL), # none_drag Alt Control 35+ +8+16=59
  120. (63, "M") : (NO_BUTTON, MOUSE_MOVE, SHIFT_ALT_CONTROL), # none_drag Shift Alt Control 35+4+8+16=63
  121. (64, "M") : (NO_BUTTON, SCROLL_UP, NO_MODIFIER), # scroll_up 64+ + + =64
  122. (68, "M") : (NO_BUTTON, SCROLL_UP, SHIFT), # scroll_up Shift 64+4+ + =68
  123. (72, "M") : (NO_BUTTON, SCROLL_UP, ALT), # scroll_up Alt 64+ +8+ =72
  124. (76, "M") : (NO_BUTTON, SCROLL_UP, SHIFT_ALT), # scroll_up Shift Alt 64+4+8+ =76
  125. (80, "M") : (NO_BUTTON, SCROLL_UP, CONTROL), # scroll_up Control 64+ + +16=80
  126. (84, "M") : (NO_BUTTON, SCROLL_UP, SHIFT_CONTROL), # scroll_up Shift Control 64+4+ +16=84
  127. (88, "M") : (NO_BUTTON, SCROLL_UP, ALT_CONTROL), # scroll_up Alt Control 64+ +8+16=88
  128. (92, "M") : (NO_BUTTON, SCROLL_UP, SHIFT_ALT_CONTROL), # scroll_up Shift Alt Control 64+4+8+16=92
  129. (65, "M") : (NO_BUTTON, SCROLL_DOWN, NO_MODIFIER), # scroll_down 64+ + + =65
  130. (69, "M") : (NO_BUTTON, SCROLL_DOWN, SHIFT), # scroll_down Shift 64+4+ + =69
  131. (73, "M") : (NO_BUTTON, SCROLL_DOWN, ALT), # scroll_down Alt 64+ +8+ =73
  132. (77, "M") : (NO_BUTTON, SCROLL_DOWN, SHIFT_ALT), # scroll_down Shift Alt 64+4+8+ =77
  133. (81, "M") : (NO_BUTTON, SCROLL_DOWN, CONTROL), # scroll_down Control 64+ + +16=81
  134. (85, "M") : (NO_BUTTON, SCROLL_DOWN, SHIFT_CONTROL), # scroll_down Shift Control 64+4+ +16=85
  135. (89, "M") : (NO_BUTTON, SCROLL_DOWN, ALT_CONTROL), # scroll_down Alt Control 64+ +8+16=89
  136. (93, "M") : (NO_BUTTON, SCROLL_DOWN, SHIFT_ALT_CONTROL), # scroll_down Shift Alt Control 64+4+8+16=93
  137. }
  138. typical_mouse_events = {
  139. 32: (LEFT , MOUSE_DOWN , UNKNOWN_MODIFIER),
  140. 33: (MIDDLE , MOUSE_DOWN , UNKNOWN_MODIFIER),
  141. 34: (RIGHT , MOUSE_DOWN , UNKNOWN_MODIFIER),
  142. 35: (UNKNOWN_BUTTON , MOUSE_UP , UNKNOWN_MODIFIER),
  143. 64: (LEFT , MOUSE_MOVE , UNKNOWN_MODIFIER),
  144. 65: (MIDDLE , MOUSE_MOVE , UNKNOWN_MODIFIER),
  145. 66: (RIGHT , MOUSE_MOVE , UNKNOWN_MODIFIER),
  146. 67: (NO_BUTTON , MOUSE_MOVE , UNKNOWN_MODIFIER),
  147. 96: (NO_BUTTON , SCROLL_UP , UNKNOWN_MODIFIER),
  148. 97: (NO_BUTTON , SCROLL_DOWN, UNKNOWN_MODIFIER),
  149. }
  150. urxvt_mouse_events={
  151. 32: (UNKNOWN_BUTTON, MOUSE_DOWN , UNKNOWN_MODIFIER),
  152. 35: (UNKNOWN_BUTTON, MOUSE_UP , UNKNOWN_MODIFIER),
  153. 96: (NO_BUTTON , SCROLL_UP , UNKNOWN_MODIFIER),
  154. 97: (NO_BUTTON , SCROLL_DOWN, UNKNOWN_MODIFIER),
  155. }
  156. # fmt:on
  157. def load_mouse_bindings() -> KeyBindings:
  158. """
  159. Key bindings, required for mouse support.
  160. (Mouse events enter through the key binding system.)
  161. """
  162. key_bindings = KeyBindings()
  163. @key_bindings.add(Keys.Vt100MouseEvent)
  164. def _(event: E) -> NotImplementedOrNone:
  165. """
  166. Handling of incoming mouse event.
  167. """
  168. # TypicaL: "eSC[MaB*"
  169. # Urxvt: "Esc[96;14;13M"
  170. # Xterm SGR: "Esc[<64;85;12M"
  171. # Parse incoming packet.
  172. if event.data[2] == "M":
  173. # Typical.
  174. mouse_event, x, y = map(ord, event.data[3:])
  175. # TODO: Is it possible to add modifiers here?
  176. mouse_button, mouse_event_type, mouse_modifiers = typical_mouse_events[
  177. mouse_event
  178. ]
  179. # Handle situations where `PosixStdinReader` used surrogateescapes.
  180. if x >= 0xDC00:
  181. x -= 0xDC00
  182. if y >= 0xDC00:
  183. y -= 0xDC00
  184. x -= 32
  185. y -= 32
  186. else:
  187. # Urxvt and Xterm SGR.
  188. # When the '<' is not present, we are not using the Xterm SGR mode,
  189. # but Urxvt instead.
  190. data = event.data[2:]
  191. if data[:1] == "<":
  192. sgr = True
  193. data = data[1:]
  194. else:
  195. sgr = False
  196. # Extract coordinates.
  197. mouse_event, x, y = map(int, data[:-1].split(";"))
  198. m = data[-1]
  199. # Parse event type.
  200. if sgr:
  201. try:
  202. (
  203. mouse_button,
  204. mouse_event_type,
  205. mouse_modifiers,
  206. ) = xterm_sgr_mouse_events[mouse_event, m]
  207. except KeyError:
  208. return NotImplemented
  209. else:
  210. # Some other terminals, like urxvt, Hyper terminal, ...
  211. (
  212. mouse_button,
  213. mouse_event_type,
  214. mouse_modifiers,
  215. ) = urxvt_mouse_events.get(
  216. mouse_event, (UNKNOWN_BUTTON, MOUSE_MOVE, UNKNOWN_MODIFIER)
  217. )
  218. x -= 1
  219. y -= 1
  220. # Only handle mouse events when we know the window height.
  221. if event.app.renderer.height_is_known and mouse_event_type is not None:
  222. # Take region above the layout into account. The reported
  223. # coordinates are absolute to the visible part of the terminal.
  224. from prompt_toolkit.renderer import HeightIsUnknownError
  225. try:
  226. y -= event.app.renderer.rows_above_layout
  227. except HeightIsUnknownError:
  228. return NotImplemented
  229. # Call the mouse handler from the renderer.
  230. # Note: This can return `NotImplemented` if no mouse handler was
  231. # found for this position, or if no repainting needs to
  232. # happen. this way, we avoid excessive repaints during mouse
  233. # movements.
  234. handler = event.app.renderer.mouse_handlers.mouse_handlers[y][x]
  235. return handler(
  236. MouseEvent(
  237. position=Point(x=x, y=y),
  238. event_type=mouse_event_type,
  239. button=mouse_button,
  240. modifiers=mouse_modifiers,
  241. )
  242. )
  243. return NotImplemented
  244. @key_bindings.add(Keys.ScrollUp)
  245. def _scroll_up(event: E) -> None:
  246. """
  247. Scroll up event without cursor position.
  248. """
  249. # We don't receive a cursor position, so we don't know which window to
  250. # scroll. Just send an 'up' key press instead.
  251. event.key_processor.feed(KeyPress(Keys.Up), first=True)
  252. @key_bindings.add(Keys.ScrollDown)
  253. def _scroll_down(event: E) -> None:
  254. """
  255. Scroll down event without cursor position.
  256. """
  257. event.key_processor.feed(KeyPress(Keys.Down), first=True)
  258. @key_bindings.add(Keys.WindowsMouseEvent)
  259. def _mouse(event: E) -> NotImplementedOrNone:
  260. """
  261. Handling of mouse events for Windows.
  262. """
  263. # This key binding should only exist for Windows.
  264. if sys.platform == "win32":
  265. # Parse data.
  266. pieces = event.data.split(";")
  267. button = MouseButton(pieces[0])
  268. event_type = MouseEventType(pieces[1])
  269. x = int(pieces[2])
  270. y = int(pieces[3])
  271. # Make coordinates absolute to the visible part of the terminal.
  272. output = event.app.renderer.output
  273. from prompt_toolkit.output.win32 import Win32Output
  274. from prompt_toolkit.output.windows10 import Windows10_Output
  275. if isinstance(output, (Win32Output, Windows10_Output)):
  276. screen_buffer_info = output.get_win32_screen_buffer_info()
  277. rows_above_cursor = (
  278. screen_buffer_info.dwCursorPosition.Y
  279. - event.app.renderer._cursor_pos.y
  280. )
  281. y -= rows_above_cursor
  282. # Call the mouse event handler.
  283. # (Can return `NotImplemented`.)
  284. handler = event.app.renderer.mouse_handlers.mouse_handlers[y][x]
  285. return handler(
  286. MouseEvent(
  287. position=Point(x=x, y=y),
  288. event_type=event_type,
  289. button=button,
  290. modifiers=UNKNOWN_MODIFIER,
  291. )
  292. )
  293. # No mouse handler found. Return `NotImplemented` so that we don't
  294. # invalidate the UI.
  295. return NotImplemented
  296. return key_bindings