mouse_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. package input
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/charmbracelet/x/ansi"
  6. "github.com/charmbracelet/x/ansi/parser"
  7. )
  8. func TestMouseEvent_String(t *testing.T) {
  9. tt := []struct {
  10. name string
  11. event Event
  12. expected string
  13. }{
  14. {
  15. name: "unknown",
  16. event: MouseClickEvent{Button: MouseButton(0xff)},
  17. expected: "unknown",
  18. },
  19. {
  20. name: "left",
  21. event: MouseClickEvent{Button: MouseLeft},
  22. expected: "left",
  23. },
  24. {
  25. name: "right",
  26. event: MouseClickEvent{Button: MouseRight},
  27. expected: "right",
  28. },
  29. {
  30. name: "middle",
  31. event: MouseClickEvent{Button: MouseMiddle},
  32. expected: "middle",
  33. },
  34. {
  35. name: "release",
  36. event: MouseReleaseEvent{Button: MouseNone},
  37. expected: "",
  38. },
  39. {
  40. name: "wheelup",
  41. event: MouseWheelEvent{Button: MouseWheelUp},
  42. expected: "wheelup",
  43. },
  44. {
  45. name: "wheeldown",
  46. event: MouseWheelEvent{Button: MouseWheelDown},
  47. expected: "wheeldown",
  48. },
  49. {
  50. name: "wheelleft",
  51. event: MouseWheelEvent{Button: MouseWheelLeft},
  52. expected: "wheelleft",
  53. },
  54. {
  55. name: "wheelright",
  56. event: MouseWheelEvent{Button: MouseWheelRight},
  57. expected: "wheelright",
  58. },
  59. {
  60. name: "motion",
  61. event: MouseMotionEvent{Button: MouseNone},
  62. expected: "motion",
  63. },
  64. {
  65. name: "shift+left",
  66. event: MouseReleaseEvent{Button: MouseLeft, Mod: ModShift},
  67. expected: "shift+left",
  68. },
  69. {
  70. name: "shift+left", event: MouseClickEvent{Button: MouseLeft, Mod: ModShift},
  71. expected: "shift+left",
  72. },
  73. {
  74. name: "ctrl+shift+left",
  75. event: MouseClickEvent{Button: MouseLeft, Mod: ModCtrl | ModShift},
  76. expected: "ctrl+shift+left",
  77. },
  78. {
  79. name: "alt+left",
  80. event: MouseClickEvent{Button: MouseLeft, Mod: ModAlt},
  81. expected: "alt+left",
  82. },
  83. {
  84. name: "ctrl+left",
  85. event: MouseClickEvent{Button: MouseLeft, Mod: ModCtrl},
  86. expected: "ctrl+left",
  87. },
  88. {
  89. name: "ctrl+alt+left",
  90. event: MouseClickEvent{Button: MouseLeft, Mod: ModAlt | ModCtrl},
  91. expected: "ctrl+alt+left",
  92. },
  93. {
  94. name: "ctrl+alt+shift+left",
  95. event: MouseClickEvent{Button: MouseLeft, Mod: ModAlt | ModCtrl | ModShift},
  96. expected: "ctrl+alt+shift+left",
  97. },
  98. {
  99. name: "ignore coordinates",
  100. event: MouseClickEvent{X: 100, Y: 200, Button: MouseLeft},
  101. expected: "left",
  102. },
  103. {
  104. name: "broken type",
  105. event: MouseClickEvent{Button: MouseButton(120)},
  106. expected: "unknown",
  107. },
  108. }
  109. for i := range tt {
  110. tc := tt[i]
  111. t.Run(tc.name, func(t *testing.T) {
  112. actual := fmt.Sprint(tc.event)
  113. if tc.expected != actual {
  114. t.Fatalf("expected %q but got %q",
  115. tc.expected,
  116. actual,
  117. )
  118. }
  119. })
  120. }
  121. }
  122. func TestParseX10MouseDownEvent(t *testing.T) {
  123. encode := func(b byte, x, y int) []byte {
  124. return []byte{
  125. '\x1b',
  126. '[',
  127. 'M',
  128. byte(32) + b,
  129. byte(x + 32 + 1),
  130. byte(y + 32 + 1),
  131. }
  132. }
  133. tt := []struct {
  134. name string
  135. buf []byte
  136. expected Event
  137. }{
  138. // Position.
  139. {
  140. name: "zero position",
  141. buf: encode(0b0000_0000, 0, 0),
  142. expected: MouseClickEvent{X: 0, Y: 0, Button: MouseLeft},
  143. },
  144. {
  145. name: "max position",
  146. buf: encode(0b0000_0000, 222, 222), // Because 255 (max int8) - 32 - 1.
  147. expected: MouseClickEvent{X: 222, Y: 222, Button: MouseLeft},
  148. },
  149. // Simple.
  150. {
  151. name: "left",
  152. buf: encode(0b0000_0000, 32, 16),
  153. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseLeft},
  154. },
  155. {
  156. name: "left in motion",
  157. buf: encode(0b0010_0000, 32, 16),
  158. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseLeft},
  159. },
  160. {
  161. name: "middle",
  162. buf: encode(0b0000_0001, 32, 16),
  163. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseMiddle},
  164. },
  165. {
  166. name: "middle in motion",
  167. buf: encode(0b0010_0001, 32, 16),
  168. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseMiddle},
  169. },
  170. {
  171. name: "right",
  172. buf: encode(0b0000_0010, 32, 16),
  173. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseRight},
  174. },
  175. {
  176. name: "right in motion",
  177. buf: encode(0b0010_0010, 32, 16),
  178. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseRight},
  179. },
  180. {
  181. name: "motion",
  182. buf: encode(0b0010_0011, 32, 16),
  183. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseNone},
  184. },
  185. {
  186. name: "wheel up",
  187. buf: encode(0b0100_0000, 32, 16),
  188. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp},
  189. },
  190. {
  191. name: "wheel down",
  192. buf: encode(0b0100_0001, 32, 16),
  193. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelDown},
  194. },
  195. {
  196. name: "wheel left",
  197. buf: encode(0b0100_0010, 32, 16),
  198. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelLeft},
  199. },
  200. {
  201. name: "wheel right",
  202. buf: encode(0b0100_0011, 32, 16),
  203. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelRight},
  204. },
  205. {
  206. name: "release",
  207. buf: encode(0b0000_0011, 32, 16),
  208. expected: MouseReleaseEvent{X: 32, Y: 16, Button: MouseNone},
  209. },
  210. {
  211. name: "backward",
  212. buf: encode(0b1000_0000, 32, 16),
  213. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseBackward},
  214. },
  215. {
  216. name: "forward",
  217. buf: encode(0b1000_0001, 32, 16),
  218. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseForward},
  219. },
  220. {
  221. name: "button 10",
  222. buf: encode(0b1000_0010, 32, 16),
  223. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseButton10},
  224. },
  225. {
  226. name: "button 11",
  227. buf: encode(0b1000_0011, 32, 16),
  228. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseButton11},
  229. },
  230. // Combinations.
  231. {
  232. name: "alt+right",
  233. buf: encode(0b0000_1010, 32, 16),
  234. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModAlt, Button: MouseRight},
  235. },
  236. {
  237. name: "ctrl+right",
  238. buf: encode(0b0001_0010, 32, 16),
  239. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModCtrl, Button: MouseRight},
  240. },
  241. {
  242. name: "left in motion",
  243. buf: encode(0b0010_0000, 32, 16),
  244. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseLeft},
  245. },
  246. {
  247. name: "alt+right in motion",
  248. buf: encode(0b0010_1010, 32, 16),
  249. expected: MouseMotionEvent{X: 32, Y: 16, Mod: ModAlt, Button: MouseRight},
  250. },
  251. {
  252. name: "ctrl+right in motion",
  253. buf: encode(0b0011_0010, 32, 16),
  254. expected: MouseMotionEvent{X: 32, Y: 16, Mod: ModCtrl, Button: MouseRight},
  255. },
  256. {
  257. name: "ctrl+alt+right",
  258. buf: encode(0b0001_1010, 32, 16),
  259. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModAlt | ModCtrl, Button: MouseRight},
  260. },
  261. {
  262. name: "ctrl+wheel up",
  263. buf: encode(0b0101_0000, 32, 16),
  264. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModCtrl, Button: MouseWheelUp},
  265. },
  266. {
  267. name: "alt+wheel down",
  268. buf: encode(0b0100_1001, 32, 16),
  269. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModAlt, Button: MouseWheelDown},
  270. },
  271. {
  272. name: "ctrl+alt+wheel down",
  273. buf: encode(0b0101_1001, 32, 16),
  274. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModAlt | ModCtrl, Button: MouseWheelDown},
  275. },
  276. // Overflow position.
  277. {
  278. name: "overflow position",
  279. buf: encode(0b0010_0000, 250, 223), // Because 255 (max int8) - 32 - 1.
  280. expected: MouseMotionEvent{X: -6, Y: -33, Button: MouseLeft},
  281. },
  282. }
  283. for i := range tt {
  284. tc := tt[i]
  285. t.Run(tc.name, func(t *testing.T) {
  286. actual := parseX10MouseEvent(tc.buf)
  287. if tc.expected != actual {
  288. t.Fatalf("expected %#v but got %#v",
  289. tc.expected,
  290. actual,
  291. )
  292. }
  293. })
  294. }
  295. }
  296. func TestParseSGRMouseEvent(t *testing.T) {
  297. type csiSequence struct {
  298. params []ansi.Param
  299. cmd ansi.Cmd
  300. }
  301. encode := func(b, x, y int, r bool) *csiSequence {
  302. re := 'M'
  303. if r {
  304. re = 'm'
  305. }
  306. return &csiSequence{
  307. params: []ansi.Param{
  308. ansi.Param(b),
  309. ansi.Param(x + 1),
  310. ansi.Param(y + 1),
  311. },
  312. cmd: ansi.Cmd(re) | ('<' << parser.PrefixShift),
  313. }
  314. }
  315. tt := []struct {
  316. name string
  317. buf *csiSequence
  318. expected Event
  319. }{
  320. // Position.
  321. {
  322. name: "zero position",
  323. buf: encode(0, 0, 0, false),
  324. expected: MouseClickEvent{X: 0, Y: 0, Button: MouseLeft},
  325. },
  326. {
  327. name: "225 position",
  328. buf: encode(0, 225, 225, false),
  329. expected: MouseClickEvent{X: 225, Y: 225, Button: MouseLeft},
  330. },
  331. // Simple.
  332. {
  333. name: "left",
  334. buf: encode(0, 32, 16, false),
  335. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseLeft},
  336. },
  337. {
  338. name: "left in motion",
  339. buf: encode(32, 32, 16, false),
  340. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseLeft},
  341. },
  342. {
  343. name: "left",
  344. buf: encode(0, 32, 16, true),
  345. expected: MouseReleaseEvent{X: 32, Y: 16, Button: MouseLeft},
  346. },
  347. {
  348. name: "middle",
  349. buf: encode(1, 32, 16, false),
  350. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseMiddle},
  351. },
  352. {
  353. name: "middle in motion",
  354. buf: encode(33, 32, 16, false),
  355. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseMiddle},
  356. },
  357. {
  358. name: "middle",
  359. buf: encode(1, 32, 16, true),
  360. expected: MouseReleaseEvent{X: 32, Y: 16, Button: MouseMiddle},
  361. },
  362. {
  363. name: "right",
  364. buf: encode(2, 32, 16, false),
  365. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseRight},
  366. },
  367. {
  368. name: "right",
  369. buf: encode(2, 32, 16, true),
  370. expected: MouseReleaseEvent{X: 32, Y: 16, Button: MouseRight},
  371. },
  372. {
  373. name: "motion",
  374. buf: encode(35, 32, 16, false),
  375. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseNone},
  376. },
  377. {
  378. name: "wheel up",
  379. buf: encode(64, 32, 16, false),
  380. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp},
  381. },
  382. {
  383. name: "wheel down",
  384. buf: encode(65, 32, 16, false),
  385. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelDown},
  386. },
  387. {
  388. name: "wheel left",
  389. buf: encode(66, 32, 16, false),
  390. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelLeft},
  391. },
  392. {
  393. name: "wheel right",
  394. buf: encode(67, 32, 16, false),
  395. expected: MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelRight},
  396. },
  397. {
  398. name: "backward",
  399. buf: encode(128, 32, 16, false),
  400. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseBackward},
  401. },
  402. {
  403. name: "backward in motion",
  404. buf: encode(160, 32, 16, false),
  405. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseBackward},
  406. },
  407. {
  408. name: "forward",
  409. buf: encode(129, 32, 16, false),
  410. expected: MouseClickEvent{X: 32, Y: 16, Button: MouseForward},
  411. },
  412. {
  413. name: "forward in motion",
  414. buf: encode(161, 32, 16, false),
  415. expected: MouseMotionEvent{X: 32, Y: 16, Button: MouseForward},
  416. },
  417. // Combinations.
  418. {
  419. name: "alt+right",
  420. buf: encode(10, 32, 16, false),
  421. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModAlt, Button: MouseRight},
  422. },
  423. {
  424. name: "ctrl+right",
  425. buf: encode(18, 32, 16, false),
  426. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModCtrl, Button: MouseRight},
  427. },
  428. {
  429. name: "ctrl+alt+right",
  430. buf: encode(26, 32, 16, false),
  431. expected: MouseClickEvent{X: 32, Y: 16, Mod: ModAlt | ModCtrl, Button: MouseRight},
  432. },
  433. {
  434. name: "alt+wheel",
  435. buf: encode(73, 32, 16, false),
  436. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModAlt, Button: MouseWheelDown},
  437. },
  438. {
  439. name: "ctrl+wheel",
  440. buf: encode(81, 32, 16, false),
  441. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModCtrl, Button: MouseWheelDown},
  442. },
  443. {
  444. name: "ctrl+alt+wheel",
  445. buf: encode(89, 32, 16, false),
  446. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModAlt | ModCtrl, Button: MouseWheelDown},
  447. },
  448. {
  449. name: "ctrl+alt+shift+wheel",
  450. buf: encode(93, 32, 16, false),
  451. expected: MouseWheelEvent{X: 32, Y: 16, Mod: ModAlt | ModShift | ModCtrl, Button: MouseWheelDown},
  452. },
  453. }
  454. for i := range tt {
  455. tc := tt[i]
  456. t.Run(tc.name, func(t *testing.T) {
  457. actual := parseSGRMouseEvent(tc.buf.cmd, tc.buf.params)
  458. if tc.expected != actual {
  459. t.Fatalf("expected %#v but got %#v",
  460. tc.expected,
  461. actual,
  462. )
  463. }
  464. })
  465. }
  466. }