mouse.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package loop
  3. import (
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. )
  8. var _ = fmt.Print
  9. type MouseEventType uint
  10. type MouseButtonFlag uint
  11. const (
  12. MOUSE_PRESS MouseEventType = iota
  13. MOUSE_RELEASE
  14. MOUSE_MOVE
  15. MOUSE_CLICK
  16. )
  17. func (e MouseEventType) String() string {
  18. switch e {
  19. case MOUSE_PRESS:
  20. return "press"
  21. case MOUSE_RELEASE:
  22. return "release"
  23. case MOUSE_MOVE:
  24. return "move"
  25. case MOUSE_CLICK:
  26. return "click"
  27. }
  28. return strconv.Itoa(int(e))
  29. }
  30. type PointerShape uint8
  31. const (
  32. // start pointer shape enum (auto generated by gen-key-constants.py do not edit)
  33. DEFAULT_POINTER PointerShape = 0
  34. TEXT_POINTER PointerShape = 1
  35. POINTER_POINTER PointerShape = 2
  36. HELP_POINTER PointerShape = 3
  37. WAIT_POINTER PointerShape = 4
  38. PROGRESS_POINTER PointerShape = 5
  39. CROSSHAIR_POINTER PointerShape = 6
  40. CELL_POINTER PointerShape = 7
  41. VERTICAL_TEXT_POINTER PointerShape = 8
  42. MOVE_POINTER PointerShape = 9
  43. E_RESIZE_POINTER PointerShape = 10
  44. NE_RESIZE_POINTER PointerShape = 11
  45. NW_RESIZE_POINTER PointerShape = 12
  46. N_RESIZE_POINTER PointerShape = 13
  47. SE_RESIZE_POINTER PointerShape = 14
  48. SW_RESIZE_POINTER PointerShape = 15
  49. S_RESIZE_POINTER PointerShape = 16
  50. W_RESIZE_POINTER PointerShape = 17
  51. EW_RESIZE_POINTER PointerShape = 18
  52. NS_RESIZE_POINTER PointerShape = 19
  53. NESW_RESIZE_POINTER PointerShape = 20
  54. NWSE_RESIZE_POINTER PointerShape = 21
  55. ZOOM_IN_POINTER PointerShape = 22
  56. ZOOM_OUT_POINTER PointerShape = 23
  57. ALIAS_POINTER PointerShape = 24
  58. COPY_POINTER PointerShape = 25
  59. NOT_ALLOWED_POINTER PointerShape = 26
  60. NO_DROP_POINTER PointerShape = 27
  61. GRAB_POINTER PointerShape = 28
  62. GRABBING_POINTER PointerShape = 29
  63. // end pointer shape enum
  64. )
  65. func (e PointerShape) String() string {
  66. switch e {
  67. // start pointer shape tostring (auto generated by gen-key-constants.py do not edit)
  68. case DEFAULT_POINTER:
  69. return "default"
  70. case TEXT_POINTER:
  71. return "text"
  72. case POINTER_POINTER:
  73. return "pointer"
  74. case HELP_POINTER:
  75. return "help"
  76. case WAIT_POINTER:
  77. return "wait"
  78. case PROGRESS_POINTER:
  79. return "progress"
  80. case CROSSHAIR_POINTER:
  81. return "crosshair"
  82. case CELL_POINTER:
  83. return "cell"
  84. case VERTICAL_TEXT_POINTER:
  85. return "vertical-text"
  86. case MOVE_POINTER:
  87. return "move"
  88. case E_RESIZE_POINTER:
  89. return "e-resize"
  90. case NE_RESIZE_POINTER:
  91. return "ne-resize"
  92. case NW_RESIZE_POINTER:
  93. return "nw-resize"
  94. case N_RESIZE_POINTER:
  95. return "n-resize"
  96. case SE_RESIZE_POINTER:
  97. return "se-resize"
  98. case SW_RESIZE_POINTER:
  99. return "sw-resize"
  100. case S_RESIZE_POINTER:
  101. return "s-resize"
  102. case W_RESIZE_POINTER:
  103. return "w-resize"
  104. case EW_RESIZE_POINTER:
  105. return "ew-resize"
  106. case NS_RESIZE_POINTER:
  107. return "ns-resize"
  108. case NESW_RESIZE_POINTER:
  109. return "nesw-resize"
  110. case NWSE_RESIZE_POINTER:
  111. return "nwse-resize"
  112. case ZOOM_IN_POINTER:
  113. return "zoom-in"
  114. case ZOOM_OUT_POINTER:
  115. return "zoom-out"
  116. case ALIAS_POINTER:
  117. return "alias"
  118. case COPY_POINTER:
  119. return "copy"
  120. case NOT_ALLOWED_POINTER:
  121. return "not-allowed"
  122. case NO_DROP_POINTER:
  123. return "no-drop"
  124. case GRAB_POINTER:
  125. return "grab"
  126. case GRABBING_POINTER:
  127. return "grabbing"
  128. // end pointer shape tostring
  129. }
  130. return strconv.Itoa(int(e))
  131. }
  132. const (
  133. SHIFT_INDICATOR int = 1 << 2
  134. ALT_INDICATOR = 1 << 3
  135. CTRL_INDICATOR = 1 << 4
  136. MOTION_INDICATOR = 1 << 5
  137. )
  138. const (
  139. NO_MOUSE_BUTTON MouseButtonFlag = 0
  140. LEFT_MOUSE_BUTTON MouseButtonFlag = 1 << iota
  141. MIDDLE_MOUSE_BUTTON
  142. RIGHT_MOUSE_BUTTON
  143. FOURTH_MOUSE_BUTTON
  144. FIFTH_MOUSE_BUTTON
  145. SIXTH_MOUSE_BUTTON
  146. SEVENTH_MOUSE_BUTTON
  147. MOUSE_WHEEL_UP
  148. MOUSE_WHEEL_DOWN
  149. MOUSE_WHEEL_LEFT
  150. MOUSE_WHEEL_RIGHT
  151. )
  152. var bmap = [...]MouseButtonFlag{LEFT_MOUSE_BUTTON, MIDDLE_MOUSE_BUTTON, RIGHT_MOUSE_BUTTON}
  153. var ebmap = [...]MouseButtonFlag{FOURTH_MOUSE_BUTTON, FIFTH_MOUSE_BUTTON, SIXTH_MOUSE_BUTTON, SEVENTH_MOUSE_BUTTON}
  154. var wbmap = [...]MouseButtonFlag{MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, MOUSE_WHEEL_LEFT, MOUSE_WHEEL_RIGHT}
  155. func (b MouseButtonFlag) String() string {
  156. ans := ""
  157. switch {
  158. case b&LEFT_MOUSE_BUTTON != 0:
  159. ans += "|LEFT"
  160. case b&MIDDLE_MOUSE_BUTTON != 0:
  161. ans += "|MIDDLE"
  162. case b&RIGHT_MOUSE_BUTTON != 0:
  163. ans += "|RIGHT"
  164. case b&FOURTH_MOUSE_BUTTON != 0:
  165. ans += "|FOURTH"
  166. case b&FIFTH_MOUSE_BUTTON != 0:
  167. ans += "|FIFTH"
  168. case b&SIXTH_MOUSE_BUTTON != 0:
  169. ans += "|SIXTH"
  170. case b&SEVENTH_MOUSE_BUTTON != 0:
  171. ans += "|SEVENTH"
  172. case b&MOUSE_WHEEL_UP != 0:
  173. ans += "|WHEEL_UP"
  174. case b&MOUSE_WHEEL_DOWN != 0:
  175. ans += "|WHEEL_DOWN"
  176. case b&MOUSE_WHEEL_LEFT != 0:
  177. ans += "|WHEEL_LEFT"
  178. case b&MOUSE_WHEEL_RIGHT != 0:
  179. ans += "|WHEEL_RIGHT"
  180. }
  181. ans = strings.TrimLeft(ans, "|")
  182. if ans == "" {
  183. ans = "NONE"
  184. }
  185. return ans
  186. }
  187. type MouseEvent struct {
  188. Event_type MouseEventType
  189. Buttons MouseButtonFlag
  190. Mods KeyModifiers
  191. Cell, Pixel struct{ X, Y int }
  192. }
  193. func (e MouseEvent) String() string {
  194. return fmt.Sprintf("MouseEvent{%s %s %s Cell:%v Pixel:%v}", e.Event_type, e.Buttons, e.Mods, e.Cell, e.Pixel)
  195. }
  196. func pixel_to_cell(px, length, cell_length int) int {
  197. px = max(0, min(px, length-1))
  198. if cell_length > 0 {
  199. return px / cell_length
  200. }
  201. return 0
  202. }
  203. func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent {
  204. last_letter := text[len(text)-1]
  205. text = text[:len(text)-1]
  206. parts := strings.Split(text, ";")
  207. if len(parts) != 3 {
  208. return nil
  209. }
  210. cb, err := strconv.Atoi(parts[0])
  211. if err != nil {
  212. return nil
  213. }
  214. ans := MouseEvent{}
  215. ans.Pixel.X, err = strconv.Atoi(parts[1])
  216. if err != nil {
  217. return nil
  218. }
  219. if len(parts[2]) < 1 {
  220. return nil
  221. }
  222. if ans.Pixel.Y, err = strconv.Atoi(parts[2]); err != nil {
  223. return nil
  224. }
  225. if last_letter == 'm' {
  226. ans.Event_type = MOUSE_RELEASE
  227. } else if cb&MOTION_INDICATOR != 0 {
  228. ans.Event_type = MOUSE_MOVE
  229. }
  230. cb3 := cb & 3
  231. if cb >= 128 {
  232. ans.Buttons |= ebmap[cb3]
  233. } else if cb >= 64 {
  234. ans.Buttons |= wbmap[cb3]
  235. } else if cb3 < 3 {
  236. ans.Buttons |= bmap[cb3]
  237. }
  238. if cb&SHIFT_INDICATOR != 0 {
  239. ans.Mods |= SHIFT
  240. }
  241. if cb&ALT_INDICATOR != 0 {
  242. ans.Mods |= ALT
  243. }
  244. if cb&CTRL_INDICATOR != 0 {
  245. ans.Mods |= CTRL
  246. }
  247. ans.Cell.X = pixel_to_cell(ans.Pixel.X, int(screen_size.WidthPx), int(screen_size.CellWidth))
  248. ans.Cell.Y = pixel_to_cell(ans.Pixel.Y, int(screen_size.HeightPx), int(screen_size.CellHeight))
  249. return &ans
  250. }
  251. func MouseEventFromCSI(csi string, screen_size ScreenSize) *MouseEvent {
  252. if len(csi) == 0 {
  253. return nil
  254. }
  255. last_char := csi[len(csi)-1]
  256. if last_char != 'm' && last_char != 'M' {
  257. return nil
  258. }
  259. if !strings.HasPrefix(csi, "<") {
  260. return nil
  261. }
  262. return decode_sgr_mouse(csi[1:], screen_size)
  263. }