You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1598 lines
50 KiB

  1. #include "csi.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <assert.h>
  6. #include <errno.h>
  7. #if defined(_DEBUG)
  8. #include <stdio.h>
  9. #endif
  10. #include <sys/timerfd.h>
  11. #define LOG_MODULE "csi"
  12. #define LOG_ENABLE_DBG 0
  13. #include "log.h"
  14. #include "grid.h"
  15. #include "selection.h"
  16. #include "sixel.h"
  17. #include "util.h"
  18. #include "version.h"
  19. #include "vt.h"
  20. #include "xmalloc.h"
  21. #define UNHANDLED() LOG_DBG("unhandled: %s", csi_as_string(term, final, -1))
  22. #define UNHANDLED_SGR(idx) LOG_DBG("unhandled: %s", csi_as_string(term, 'm', idx))
  23. static void
  24. sgr_reset(struct terminal *term)
  25. {
  26. memset(&term->vt.attrs, 0, sizeof(term->vt.attrs));
  27. term->vt.attrs.fg = term->colors.fg;
  28. term->vt.attrs.bg = term->colors.bg;
  29. }
  30. static const char *
  31. csi_as_string(struct terminal *term, uint8_t final, int idx)
  32. {
  33. static char msg[1024];
  34. int c = snprintf(msg, sizeof(msg), "CSI: ");
  35. for (size_t i = idx >= 0 ? idx : 0;
  36. i < (idx >= 0 ? idx + 1 : term->vt.params.idx);
  37. i++)
  38. {
  39. c += snprintf(&msg[c], sizeof(msg) - c, "%u",
  40. term->vt.params.v[i].value);
  41. for (size_t j = 0; j < term->vt.params.v[i].sub.idx; j++) {
  42. c += snprintf(&msg[c], sizeof(msg) - c, ":%u",
  43. term->vt.params.v[i].sub.value[j]);
  44. }
  45. c += snprintf(&msg[c], sizeof(msg) - c, "%s",
  46. i == term->vt.params.idx - 1 ? "" : ";");
  47. }
  48. for (size_t i = 0; i < sizeof(term->vt.private) / sizeof(term->vt.private[0]); i++) {
  49. if (term->vt.private[i] == 0)
  50. break;
  51. c += snprintf(&msg[c], sizeof(msg) - c, "%c", term->vt.private[i]);
  52. }
  53. snprintf(&msg[c], sizeof(msg) - c, "%c (%u parameters)",
  54. final, idx >= 0 ? 1 : term->vt.params.idx);
  55. return msg;
  56. }
  57. static void
  58. csi_sgr(struct terminal *term)
  59. {
  60. if (term->vt.params.idx == 0) {
  61. sgr_reset(term);
  62. return;
  63. }
  64. for (size_t i = 0; i < term->vt.params.idx; i++) {
  65. const int param = term->vt.params.v[i].value;
  66. switch (param) {
  67. case 0:
  68. sgr_reset(term);
  69. break;
  70. case 1: term->vt.attrs.bold = true; break;
  71. case 2: term->vt.attrs.dim = true; break;
  72. case 3: term->vt.attrs.italic = true; break;
  73. case 4: term->vt.attrs.underline = true; break;
  74. case 5: term->vt.attrs.blink = true; break;
  75. case 6: LOG_WARN("ignored: rapid blink"); break;
  76. case 7: term->vt.attrs.reverse = true; break;
  77. case 8: term->vt.attrs.conceal = true; break;
  78. case 9: term->vt.attrs.strikethrough = true; break;
  79. case 21: term->vt.attrs.bold = false; break;
  80. case 22: term->vt.attrs.bold = term->vt.attrs.dim = false; break;
  81. case 23: term->vt.attrs.italic = false; break;
  82. case 24: term->vt.attrs.underline = false; break;
  83. case 25: term->vt.attrs.blink = false; break;
  84. case 26: break; /* rapid blink, ignored */
  85. case 27: term->vt.attrs.reverse = false; break;
  86. case 28: term->vt.attrs.conceal = false; break;
  87. case 29: term->vt.attrs.strikethrough = false; break;
  88. /* Regular foreground colors */
  89. case 30:
  90. case 31:
  91. case 32:
  92. case 33:
  93. case 34:
  94. case 35:
  95. case 36:
  96. case 37:
  97. term->vt.attrs.have_fg = 1;
  98. term->vt.attrs.fg = term->colors.table[param - 30];
  99. break;
  100. case 38: {
  101. /* Indexed: 38;5;<idx> */
  102. if (term->vt.params.idx - i - 1 >= 2 &&
  103. term->vt.params.v[i + 1].value == 5)
  104. {
  105. uint8_t idx = term->vt.params.v[i + 2].value;
  106. term->vt.attrs.have_fg = 1;
  107. term->vt.attrs.fg = term->colors.table[idx];
  108. i += 2;
  109. }
  110. /* RGB: 38;2;<r>;<g>;<b> */
  111. else if (term->vt.params.idx - i - 1 >= 4 &&
  112. term->vt.params.v[i + 1].value == 2)
  113. {
  114. uint8_t r = term->vt.params.v[i + 2].value;
  115. uint8_t g = term->vt.params.v[i + 3].value;
  116. uint8_t b = term->vt.params.v[i + 4].value;
  117. term->vt.attrs.have_fg = 1;
  118. term->vt.attrs.fg = r << 16 | g << 8 | b;
  119. i += 4;
  120. }
  121. /* Sub-parameter style: 38:2:... */
  122. else if (term->vt.params.v[i].sub.idx >= 2 &&
  123. term->vt.params.v[i].sub.value[0] == 2)
  124. {
  125. const struct vt_param *param = &term->vt.params.v[i];
  126. const int color_space_id = param->sub.value[1];
  127. switch (color_space_id) {
  128. case 0: /* Implementation defined - we map it to '2' */
  129. case 2: { /* RGB - 38:2:2:<r>:<g>:<b> */
  130. if (param->sub.idx < 5) {
  131. UNHANDLED_SGR(i);
  132. break;
  133. }
  134. uint8_t r = param->sub.value[2];
  135. uint8_t g = param->sub.value[3];
  136. uint8_t b = param->sub.value[4];
  137. /* 5 - unused */
  138. /* 6 - CS tolerance */
  139. /* 7 - color space associated with tolerance */
  140. term->vt.attrs.have_fg = 1;
  141. term->vt.attrs.fg = r << 16 | g << 8 | b;
  142. break;
  143. }
  144. case 5: { /* Indexed - 38:2:5:<idx> */
  145. if (param->sub.idx < 3) {
  146. UNHANDLED_SGR(i);
  147. break;
  148. }
  149. uint8_t idx = param->sub.value[2];
  150. term->vt.attrs.have_fg = 1;
  151. term->vt.attrs.fg = term->colors.table[idx];
  152. break;
  153. }
  154. case 1: /* Transparent */
  155. case 3: /* CMY */
  156. case 4: /* CMYK */
  157. UNHANDLED_SGR(i);
  158. break;
  159. }
  160. }
  161. /* Unrecognized */
  162. else
  163. UNHANDLED_SGR(i);
  164. break;
  165. }
  166. case 39:
  167. term->vt.attrs.have_fg = 0;
  168. break;
  169. /* Regular background colors */
  170. case 40:
  171. case 41:
  172. case 42:
  173. case 43:
  174. case 44:
  175. case 45:
  176. case 46:
  177. case 47:
  178. term->vt.attrs.have_bg = 1;
  179. term->vt.attrs.bg = term->colors.table[param - 40];
  180. break;
  181. case 48: {
  182. /* Indexed: 48;5;<idx> */
  183. if (term->vt.params.idx - i - 1 >= 2 &&
  184. term->vt.params.v[i + 1].value == 5)
  185. {
  186. uint8_t idx = term->vt.params.v[i + 2].value;
  187. term->vt.attrs.have_bg = 1;
  188. term->vt.attrs.bg = term->colors.table[idx];
  189. i += 2;
  190. }
  191. /* RGB: 48;2;<r>;<g>;<b> */
  192. else if (term->vt.params.idx - i - 1 >= 4 &&
  193. term->vt.params.v[i + 1].value == 2)
  194. {
  195. uint8_t r = term->vt.params.v[i + 2].value;
  196. uint8_t g = term->vt.params.v[i + 3].value;
  197. uint8_t b = term->vt.params.v[i + 4].value;
  198. term->vt.attrs.have_bg = 1;
  199. term->vt.attrs.bg = r << 16 | g << 8 | b;
  200. i += 4;
  201. }
  202. /* Sub-parameter style: 48:2:... */
  203. else if (term->vt.params.v[i].sub.idx >= 2 &&
  204. term->vt.params.v[i].sub.value[0] == 2)
  205. {
  206. const struct vt_param *param = &term->vt.params.v[i];
  207. const int color_space_id = param->sub.value[1];
  208. switch (color_space_id) {
  209. case 0: /* Implementation defined - we map it to '2' */
  210. case 2: { /* RGB - 48:2:2:<r>:<g>:<b> */
  211. if (param->sub.idx < 5) {
  212. UNHANDLED_SGR(i);
  213. break;
  214. }
  215. uint8_t r = param->sub.value[2];
  216. uint8_t g = param->sub.value[3];
  217. uint8_t b = param->sub.value[4];
  218. /* 5 - unused */
  219. /* 6 - CS tolerance */
  220. /* 7 - color space associated with tolerance */
  221. term->vt.attrs.have_bg = 1;
  222. term->vt.attrs.bg = r << 16 | g << 8 | b;
  223. break;
  224. }
  225. case 5: { /* Indexed - 48:2:5:<idx> */
  226. if (param->sub.idx < 3) {
  227. UNHANDLED_SGR(i);
  228. break;
  229. }
  230. uint8_t idx = param->sub.value[2];
  231. term->vt.attrs.have_bg = 1;
  232. term->vt.attrs.bg = term->colors.table[idx];
  233. break;
  234. }
  235. case 1: /* Transparent */
  236. case 3: /* CMY */
  237. case 4: /* CMYK */
  238. UNHANDLED_SGR(i);
  239. break;
  240. }
  241. }
  242. else
  243. UNHANDLED_SGR(i);
  244. break;
  245. }
  246. case 49:
  247. term->vt.attrs.have_bg = 0;
  248. break;
  249. /* Bright foreground colors */
  250. case 90:
  251. case 91:
  252. case 92:
  253. case 93:
  254. case 94:
  255. case 95:
  256. case 96:
  257. case 97:
  258. term->vt.attrs.have_fg = 1;
  259. term->vt.attrs.fg = term->colors.table[param - 90 + 8];
  260. break;
  261. /* Bright background colors */
  262. case 100:
  263. case 101:
  264. case 102:
  265. case 103:
  266. case 104:
  267. case 105:
  268. case 106:
  269. case 107:
  270. term->vt.attrs.have_bg = 1;
  271. term->vt.attrs.bg = term->colors.table[param - 100 + 8];
  272. break;
  273. default:
  274. UNHANDLED_SGR(i);
  275. break;
  276. }
  277. }
  278. }
  279. static void
  280. decset_decrst(struct terminal *term, unsigned param, bool enable)
  281. {
  282. #if defined(_DEBUG)
  283. /* For UNHANDLED() */
  284. int UNUSED final = enable ? 'h' : 'l';
  285. #endif
  286. /* Note: update XTSAVE/XTRESTORE if adding/removing things here */
  287. switch (param) {
  288. case 1:
  289. /* DECCKM */
  290. term->cursor_keys_mode =
  291. enable ? CURSOR_KEYS_APPLICATION : CURSOR_KEYS_NORMAL;
  292. break;
  293. case 3:
  294. /* DECCOLM */
  295. if (enable)
  296. LOG_WARN("unimplemented: 132 column mode (DECCOLM)");
  297. term_erase(
  298. term,
  299. &(struct coord){0, 0},
  300. &(struct coord){term->cols - 1, term->rows - 1});
  301. term_cursor_home(term);
  302. break;
  303. case 4:
  304. /* DECSCLM - Smooth scroll */
  305. if (enable)
  306. LOG_WARN("unimplemented: Smooth (Slow) Scroll (DECSCLM)");
  307. break;
  308. case 5:
  309. /* DECSCNM */
  310. term->reverse = enable;
  311. term_damage_all(term);
  312. term_damage_margins(term);
  313. break;
  314. case 6: {
  315. /* DECOM */
  316. term->origin = enable ? ORIGIN_RELATIVE : ORIGIN_ABSOLUTE;
  317. term_cursor_home(term);
  318. break;
  319. }
  320. case 7:
  321. /* DECAWM */
  322. term->auto_margin = enable;
  323. term->grid->cursor.lcf = false;
  324. break;
  325. case 9:
  326. if (enable)
  327. LOG_WARN("unimplemented: X10 mouse tracking mode");
  328. #if 0
  329. else if (term->mouse_tracking == MOUSE_X10)
  330. term->mouse_tracking = MOUSE_NONE;
  331. #endif
  332. break;
  333. case 12:
  334. if (enable)
  335. term_cursor_blink_enable(term);
  336. else
  337. term_cursor_blink_disable(term);
  338. break;
  339. case 25:
  340. /* DECTCEM */
  341. term->hide_cursor = !enable;
  342. break;
  343. case 45:
  344. term->reverse_wrap = enable;
  345. break;
  346. case 1000:
  347. if (enable)
  348. term->mouse_tracking = MOUSE_CLICK;
  349. else if (term->mouse_tracking == MOUSE_CLICK)
  350. term->mouse_tracking = MOUSE_NONE;
  351. term_xcursor_update(term);
  352. break;
  353. case 1001:
  354. if (enable)
  355. LOG_WARN("unimplemented: highlight mouse tracking");
  356. break;
  357. case 1002:
  358. if (enable)
  359. term->mouse_tracking = MOUSE_DRAG;
  360. else if (term->mouse_tracking == MOUSE_DRAG)
  361. term->mouse_tracking = MOUSE_NONE;
  362. term_xcursor_update(term);
  363. break;
  364. case 1003:
  365. if (enable)
  366. term->mouse_tracking = MOUSE_MOTION;
  367. else if (term->mouse_tracking == MOUSE_MOTION)
  368. term->mouse_tracking = MOUSE_NONE;
  369. term_xcursor_update(term);
  370. break;
  371. case 1004:
  372. term->focus_events = enable;
  373. break;
  374. case 1005:
  375. if (enable)
  376. LOG_WARN("unimplemented: mouse reporting mode: UTF-8");
  377. #if 0
  378. else if (term->mouse_reporting == MOUSE_UTF8)
  379. term->mouse_reporting = MOUSE_NONE;
  380. #endif
  381. break;
  382. case 1006:
  383. if (enable)
  384. term->mouse_reporting = MOUSE_SGR;
  385. else if (term->mouse_reporting == MOUSE_SGR)
  386. term->mouse_reporting = MOUSE_NORMAL;
  387. break;
  388. case 1007:
  389. term->alt_scrolling = enable;
  390. break;
  391. case 1015:
  392. if (enable)
  393. term->mouse_reporting = MOUSE_URXVT;
  394. else if (term->mouse_reporting == MOUSE_URXVT)
  395. term->mouse_reporting = MOUSE_NORMAL;
  396. break;
  397. case 1034:
  398. /* smm */
  399. LOG_DBG("%s 8-bit meta mode", enable ? "enabling" : "disabling");
  400. term->meta.eight_bit = enable;
  401. break;
  402. case 1036:
  403. /* metaSendsEscape */
  404. LOG_DBG("%s meta-sends-escape", enable ? "enabling" : "disabling");
  405. term->meta.esc_prefix = enable;
  406. break;
  407. case 1042:
  408. term->bell_is_urgent = enable;
  409. break;
  410. #if 0
  411. case 1043:
  412. LOG_WARN("unimplemented: raise window on ctrl-g");
  413. break;
  414. #endif
  415. case 1049:
  416. if (enable && term->grid != &term->alt) {
  417. selection_cancel(term);
  418. term->grid = &term->alt;
  419. term_cursor_to(
  420. term,
  421. min(term->grid->cursor.point.row, term->rows - 1),
  422. min(term->grid->cursor.point.col, term->cols - 1));
  423. tll_free(term->alt.scroll_damage);
  424. term_erase(
  425. term,
  426. &(struct coord){0, 0},
  427. &(struct coord){term->cols - 1, term->rows - 1});
  428. }
  429. else if (!enable && term->grid == &term->alt) {
  430. selection_cancel(term);
  431. term->grid = &term->normal;
  432. term_cursor_to(
  433. term,
  434. min(term->grid->cursor.point.row, term->rows - 1),
  435. min(term->grid->cursor.point.col, term->cols - 1));
  436. tll_free(term->alt.scroll_damage);
  437. /* Delete all sixel images on the alt screen */
  438. tll_foreach(term->alt.sixel_images, it) {
  439. sixel_destroy(&it->item);
  440. tll_remove(term->alt.sixel_images, it);
  441. }
  442. term_damage_all(term);
  443. }
  444. break;
  445. case 2004:
  446. term->bracketed_paste = enable;
  447. break;
  448. default:
  449. UNHANDLED();
  450. break;
  451. }
  452. }
  453. static void
  454. decset(struct terminal *term, unsigned param)
  455. {
  456. decset_decrst(term, param, true);
  457. }
  458. static void
  459. decrst(struct terminal *term, unsigned param)
  460. {
  461. decset_decrst(term, param, false);
  462. }
  463. static bool
  464. timespecs_equal(const struct timespec *a, const struct timespec *b)
  465. {
  466. return a->tv_sec == b->tv_sec && a->tv_nsec == b->tv_nsec;
  467. }
  468. static void
  469. xtsave(struct terminal *term, unsigned param)
  470. {
  471. switch (param) {
  472. case 1: term->xtsave.application_cursor_keys = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; break;
  473. case 3: break;
  474. case 4: break;
  475. case 5: term->xtsave.reverse = term->reverse; break;
  476. case 6: term->xtsave.origin = term->origin; break;
  477. case 7: term->xtsave.auto_margin = term->auto_margin; break;
  478. case 9: /* term->xtsave.mouse_x10 = term->mouse_tracking == MOUSE_X10; */ break;
  479. case 12: {
  480. struct itimerspec current_value;
  481. if (timerfd_gettime(term->cursor_blink.fd, &current_value) < 0)
  482. LOG_WARN("xtsave: failed to read cursor blink timer: %s", strerror(errno));
  483. else {
  484. const struct timespec zero = {.tv_sec = 0, .tv_nsec = 0};
  485. term->xtsave.cursor_blink =
  486. !(timespecs_equal(&current_value.it_interval, &zero) &&
  487. timespecs_equal(&current_value.it_value, &zero));
  488. }
  489. break;
  490. }
  491. case 25: term->xtsave.show_cursor = !term->hide_cursor; break;
  492. case 45: term->xtsave.reverse_wrap = term->reverse_wrap; break;
  493. case 1000: term->xtsave.mouse_click = term->mouse_tracking == MOUSE_CLICK; break;
  494. case 1001: break;
  495. case 1002: term->xtsave.mouse_drag = term->mouse_tracking == MOUSE_DRAG; break;
  496. case 1003: term->xtsave.mouse_motion = term->mouse_tracking == MOUSE_MOTION; break;
  497. case 1004: term->xtsave.focus_events = term->focus_events; break;
  498. case 1005: /* term->xtsave.mouse_utf8 = term->mouse_reporting == MOUSE_UTF8; */ break;
  499. case 1006: term->xtsave.mouse_sgr = term->mouse_reporting == MOUSE_SGR; break;
  500. case 1007: term->xtsave.alt_scrolling = term->alt_scrolling; break;
  501. case 1015: term->xtsave.mouse_urxvt = term->mouse_reporting == MOUSE_URXVT; break;
  502. case 1034: term->xtsave.meta_eight_bit = term->meta.eight_bit; break;
  503. case 1036: term->xtsave.meta_esc_prefix = term->meta.esc_prefix; break;
  504. case 1042: term->xtsave.bell_is_urgent = term->bell_is_urgent; break;
  505. case 1049: term->xtsave.alt_screen = term->grid == &term->alt; break;
  506. case 2004: term->xtsave.bracketed_paste = term->bracketed_paste; break;
  507. }
  508. }
  509. static void
  510. xtrestore(struct terminal *term, unsigned param)
  511. {
  512. bool enable;
  513. switch (param) {
  514. case 1: enable = term->xtsave.application_cursor_keys; break;
  515. case 3: return;
  516. case 4: return;
  517. case 5: enable = term->xtsave.reverse; break;
  518. case 6: enable = term->xtsave.origin; break;
  519. case 7: enable = term->xtsave.auto_margin; break;
  520. case 9: /* enable = term->xtsave.mouse_x10; break; */ return;
  521. case 12: enable = term->xtsave.cursor_blink; break;
  522. case 25: enable = term->xtsave.show_cursor; break;
  523. case 45: enable = term->xtsave.reverse_wrap; break;
  524. case 1000: enable = term->xtsave.mouse_click; break;
  525. case 1001: return;
  526. case 1002: enable = term->xtsave.mouse_drag; break;
  527. case 1003: enable = term->xtsave.mouse_motion; break;
  528. case 1004: enable = term->xtsave.focus_events; break;
  529. case 1005: /* enable = term->xtsave.mouse_utf8; break; */ return;
  530. case 1006: enable = term->xtsave.mouse_sgr; break;
  531. case 1007: enable = term->xtsave.alt_scrolling; break;
  532. case 1015: enable = term->xtsave.mouse_urxvt; break;
  533. case 1034: enable = term->xtsave.meta_eight_bit; break;
  534. case 1036: enable = term->xtsave.meta_esc_prefix; break;
  535. case 1042: enable = term->xtsave.bell_is_urgent; break;
  536. case 1049: enable = term->xtsave.alt_screen; break;
  537. case 2004: enable = term->xtsave.bracketed_paste; break;
  538. default: return;
  539. }
  540. decset_decrst(term, param, enable);
  541. }
  542. void
  543. csi_dispatch(struct terminal *term, uint8_t final)
  544. {
  545. LOG_DBG("%s", csi_as_string(term, final, -1));
  546. switch (term->vt.private[0]) {
  547. case 0: {
  548. switch (final) {
  549. case 'b':
  550. if (term->vt.last_printed != 0) {
  551. /*
  552. * Note: we never reset 'last-printed'. According to
  553. * ECMA-48, the behaviour is undefined if REP was
  554. * _not_ preceded by a graphical character.
  555. */
  556. int count = vt_param_get(term, 0, 1);
  557. LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count);
  558. const int width = wcwidth(term->vt.last_printed);
  559. if (width > 0) {
  560. for (int i = 0; i < count; i++)
  561. term_print(term, term->vt.last_printed, width);
  562. }
  563. }
  564. break;
  565. case 'c': {
  566. if (vt_param_get(term, 0, 0) != 0) {
  567. UNHANDLED();
  568. break;
  569. }
  570. /* Send Device Attributes (Primary DA) */
  571. /*
  572. * Responses:
  573. * - CSI?1;2c vt100 with advanced video option
  574. * - CSI?1;0c vt101 with no options
  575. * - CSI?6c vt102
  576. * - CSI?62;<Ps>c vt220
  577. * - CSI?63;<Ps>c vt320
  578. * - CSI?64;<Ps>c vt420
  579. *
  580. * Ps (response may contain multiple):
  581. * - 1 132 columns
  582. * - 2 Printer.
  583. * - 3 ReGIS graphics.
  584. * - 4 Sixel graphics.
  585. * - 6 Selective erase.
  586. * - 8 User-defined keys.
  587. * - 9 National Replacement Character sets.
  588. * - 15 Technical characters.
  589. * - 16 Locator port.
  590. * - 17 Terminal state interrogation.
  591. * - 18 User windows.
  592. * - 21 Horizontal scrolling.
  593. * - 22 ANSI color, e.g., VT525.
  594. * - 28 Rectangular editing.
  595. * - 29 ANSI text locator (i.e., DEC Locator mode).
  596. *
  597. * Note: we report ourselves as a VT220, mainly to be able
  598. * to pass parameters, to indicate we support sixel, and
  599. * ANSI colors.
  600. *
  601. * The VT level must be synchronized with the secondary DA
  602. * response.
  603. *
  604. * Note: tertiary DA responds with "FOOT".
  605. */
  606. const char *reply = "\033[?62;4;22c";
  607. term_to_slave(term, reply, strlen(reply));
  608. break;
  609. }
  610. case 'd': {
  611. /* VPA - vertical line position absolute */
  612. int rel_row = vt_param_get(term, 0, 1) - 1;
  613. int row = term_row_rel_to_abs(term, rel_row);
  614. term_cursor_to(term, row, term->grid->cursor.point.col);
  615. break;
  616. }
  617. case 'm':
  618. csi_sgr(term);
  619. break;
  620. case 'A':
  621. term_cursor_up(term, vt_param_get(term, 0, 1));
  622. break;
  623. case 'e':
  624. case 'B':
  625. term_cursor_down(term, vt_param_get(term, 0, 1));
  626. break;
  627. case 'a':
  628. case 'C':
  629. term_cursor_right(term, vt_param_get(term, 0, 1));
  630. break;
  631. case 'D':
  632. term_cursor_left(term, vt_param_get(term, 0, 1));
  633. break;
  634. case 'E':
  635. /* CNL - Cursor Next Line */
  636. term_cursor_down(term, vt_param_get(term, 0, 1));
  637. term_cursor_left(term, term->grid->cursor.point.col);
  638. break;
  639. case 'F':
  640. /* CPL - Cursor Previous Line */
  641. term_cursor_up(term, vt_param_get(term, 0, 1));
  642. term_cursor_left(term, term->grid->cursor.point.col);
  643. break;
  644. case 'g': {
  645. int param = vt_param_get(term, 0, 0);
  646. switch (param) {
  647. case 0:
  648. /* Clear tab stop at *current* column */
  649. tll_foreach(term->tab_stops, it) {
  650. if (it->item == term->grid->cursor.point.col)
  651. tll_remove(term->tab_stops, it);
  652. else if (it->item > term->grid->cursor.point.col)
  653. break;
  654. }
  655. break;
  656. case 3:
  657. /* Clear *all* tabs */
  658. tll_free(term->tab_stops);
  659. break;
  660. default:
  661. UNHANDLED();
  662. break;
  663. }
  664. break;
  665. }
  666. case '`':
  667. case 'G': {
  668. /* Cursor horizontal absolute */
  669. int col = min(vt_param_get(term, 0, 1), term->cols) - 1;
  670. term_cursor_to(term, term->grid->cursor.point.row, col);
  671. break;
  672. }
  673. case 'f':
  674. case 'H': {
  675. /* Move cursor */
  676. int rel_row = vt_param_get(term, 0, 1) - 1;
  677. int row = term_row_rel_to_abs(term, rel_row);
  678. int col = min(vt_param_get(term, 1, 1), term->cols) - 1;
  679. term_cursor_to(term, row, col);
  680. break;
  681. }
  682. case 'J': {
  683. /* Erase screen */
  684. int param = vt_param_get(term, 0, 0);
  685. switch (param) {
  686. case 0:
  687. /* From cursor to end of screen */
  688. term_erase(
  689. term,
  690. &term->grid->cursor.point,
  691. &(struct coord){term->cols - 1, term->rows - 1});
  692. term->grid->cursor.lcf = false;
  693. break;
  694. case 1:
  695. /* From start of screen to cursor */
  696. term_erase(term, &(struct coord){0, 0}, &term->grid->cursor.point);
  697. term->grid->cursor.lcf = false;
  698. break;
  699. case 2:
  700. /* Erase entire screen */
  701. term_erase(
  702. term,
  703. &(struct coord){0, 0},
  704. &(struct coord){term->cols - 1, term->rows - 1});
  705. term->grid->cursor.lcf = false;
  706. break;
  707. case 3: {
  708. /* Erase scrollback */
  709. int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
  710. for (size_t i = 0; i < term->grid->num_rows; i++) {
  711. if (end >= term->grid->offset) {
  712. /* Not wrapped */
  713. if (i >= term->grid->offset && i <= end)
  714. continue;
  715. } else {
  716. /* Wrapped */
  717. if (i >= term->grid->offset || i <= end)
  718. continue;
  719. }
  720. if (term->render.last_cursor.row == term->grid->rows[i])
  721. term->render.last_cursor.row = NULL;
  722. grid_row_free(term->grid->rows[i]);
  723. term->grid->rows[i] = NULL;
  724. }
  725. term->grid->view = term->grid->offset;
  726. term_damage_view(term);
  727. break;
  728. }
  729. default:
  730. UNHANDLED();
  731. break;
  732. }
  733. break;
  734. }
  735. case 'K': {
  736. /* Erase line */
  737. int param = vt_param_get(term, 0, 0);
  738. switch (param) {
  739. case 0:
  740. /* From cursor to end of line */
  741. term_erase(
  742. term,
  743. &term->grid->cursor.point,
  744. &(struct coord){term->cols - 1, term->grid->cursor.point.row});
  745. term->grid->cursor.lcf = false;
  746. break;
  747. case 1:
  748. /* From start of line to cursor */
  749. term_erase(
  750. term, &(struct coord){0, term->grid->cursor.point.row}, &term->grid->cursor.point);
  751. term->grid->cursor.lcf = false;
  752. break;
  753. case 2:
  754. /* Entire line */
  755. term_erase(
  756. term,
  757. &(struct coord){0, term->grid->cursor.point.row},
  758. &(struct coord){term->cols - 1, term->grid->cursor.point.row});
  759. term->grid->cursor.lcf = false;
  760. break;
  761. default:
  762. UNHANDLED();
  763. break;
  764. }
  765. break;
  766. }
  767. case 'L': {
  768. if (term->grid->cursor.point.row < term->scroll_region.start ||
  769. term->grid->cursor.point.row >= term->scroll_region.end)
  770. break;
  771. int count = min(
  772. vt_param_get(term, 0, 1),
  773. term->scroll_region.end - term->grid->cursor.point.row);
  774. term_scroll_reverse_partial(
  775. term,
  776. (struct scroll_region){
  777. .start = term->grid->cursor.point.row,
  778. .end = term->scroll_region.end},
  779. count);
  780. term->grid->cursor.lcf = false;
  781. break;
  782. }
  783. case 'M': {
  784. if (term->grid->cursor.point.row < term->scroll_region.start ||
  785. term->grid->cursor.point.row >= term->scroll_region.end)
  786. break;
  787. int count = min(
  788. vt_param_get(term, 0, 1),
  789. term->scroll_region.end - term->grid->cursor.point.row);
  790. term_scroll_partial(
  791. term,
  792. (struct scroll_region){
  793. .start = term->grid->cursor.point.row,
  794. .end = term->scroll_region.end},
  795. count);
  796. term->grid->cursor.lcf = false;
  797. break;
  798. }
  799. case 'P': {
  800. /* DCH: Delete character(s) */
  801. /* Number of characters to delete */
  802. int count = min(
  803. vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
  804. /* Number of characters left after deletion (on current line) */
  805. int remaining = term->cols - (term->grid->cursor.point.col + count);
  806. /* 'Delete' characters by moving the remaining ones */
  807. memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col],
  808. &term->grid->cur_row->cells[term->grid->cursor.point.col + count],
  809. remaining * sizeof(term->grid->cur_row->cells[0]));
  810. for (size_t c = 0; c < remaining; c++)
  811. term->grid->cur_row->cells[term->grid->cursor.point.col + c].attrs.clean = 0;
  812. term->grid->cur_row->dirty = true;
  813. /* Erase the remainder of the line */
  814. term_erase(
  815. term,
  816. &(struct coord){term->grid->cursor.point.col + remaining, term->grid->cursor.point.row},
  817. &(struct coord){term->cols - 1, term->grid->cursor.point.row});
  818. term->grid->cursor.lcf = false;
  819. break;
  820. }
  821. case '@': {
  822. /* ICH: insert character(s) */
  823. /* Number of characters to insert */
  824. int count = min(
  825. vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
  826. /* Characters to move */
  827. int remaining = term->cols - (term->grid->cursor.point.col + count);
  828. /* Push existing characters */
  829. memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col + count],
  830. &term->grid->cur_row->cells[term->grid->cursor.point.col],
  831. remaining * sizeof(term->grid->cur_row->cells[0]));
  832. for (size_t c = 0; c < remaining; c++)
  833. term->grid->cur_row->cells[term->grid->cursor.point.col + count + c].attrs.clean = 0;
  834. term->grid->cur_row->dirty = true;
  835. /* Erase (insert space characters) */
  836. term_erase(
  837. term,
  838. &term->grid->cursor.point,
  839. &(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
  840. term->grid->cursor.lcf = false;
  841. break;
  842. }
  843. case 'S': {
  844. int amount = min(
  845. vt_param_get(term, 0, 1),
  846. term->scroll_region.end - term->scroll_region.start);
  847. term_scroll(term, amount);
  848. break;
  849. }
  850. case 'T': {
  851. int amount = min(
  852. vt_param_get(term, 0, 1),
  853. term->scroll_region.end - term->scroll_region.start);
  854. term_scroll_reverse(term, amount);
  855. break;
  856. }
  857. case 'X': {
  858. /* Erase chars */
  859. int count = min(
  860. vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
  861. term_erase(
  862. term,
  863. &term->grid->cursor.point,
  864. &(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
  865. term->grid->cursor.lcf = false;
  866. break;
  867. }
  868. case 'I': {
  869. /* CHT - Tab Forward (param is number of tab stops to move through) */
  870. for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
  871. int new_col = term->cols - 1;
  872. tll_foreach(term->tab_stops, it) {
  873. if (it->item > term->grid->cursor.point.col) {
  874. new_col = it->item;
  875. break;
  876. }
  877. }
  878. assert(new_col >= term->grid->cursor.point.col);
  879. term_cursor_right(term, new_col - term->grid->cursor.point.col);
  880. }
  881. break;
  882. }
  883. case 'Z':
  884. /* CBT - Back tab (param is number of tab stops to move back through) */
  885. for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
  886. int new_col = 0;
  887. tll_rforeach(term->tab_stops, it) {
  888. if (it->item < term->grid->cursor.point.col) {
  889. new_col = it->item;
  890. break;
  891. }
  892. }
  893. assert(term->grid->cursor.point.col >= new_col);
  894. term_cursor_left(term, term->grid->cursor.point.col - new_col);
  895. }
  896. break;
  897. case 'h':
  898. /* Set mode */
  899. switch (vt_param_get(term, 0, 0)) {
  900. case 2: /* Keyboard Action Mode - AM */
  901. LOG_WARN("unimplemented: keyboard action mode (AM)");
  902. break;
  903. case 4: /* Insert Mode - IRM */
  904. term->insert_mode = true;
  905. break;
  906. case 12: /* Send/receive Mode - SRM */
  907. LOG_WARN("unimplemented: send/receive mode (SRM)");
  908. break;
  909. case 20: /* Automatic Newline Mode - LNM */
  910. /* TODO: would be easy to implemented; when active
  911. * term_linefeed() would _also_ do a
  912. * term_carriage_return() */
  913. LOG_WARN("unimplemented: automatic newline mode (LNM)");
  914. break;
  915. }
  916. break;
  917. case 'l':
  918. /* Reset mode */
  919. switch (vt_param_get(term, 0, 0)) {
  920. case 4: /* Insert Mode - IRM */
  921. term->insert_mode = false;
  922. break;
  923. case 2: /* Keyboard Action Mode - AM */
  924. case 12: /* Send/receive Mode - SRM */
  925. case 20: /* Automatic Newline Mode - LNM */
  926. break;
  927. }
  928. break;
  929. case 'r': {
  930. int start = vt_param_get(term, 0, 1);
  931. int end = min(vt_param_get(term, 1, term->rows), term->rows);
  932. if (end > start) {
  933. /* 1-based */
  934. term->scroll_region.start = start - 1;
  935. term->scroll_region.end = end;
  936. term_cursor_home(term);
  937. LOG_DBG("scroll region: %d-%d",
  938. term->scroll_region.start,
  939. term->scroll_region.end);
  940. }
  941. break;
  942. }
  943. case 's':
  944. term->grid->saved_cursor = term->grid->cursor;
  945. break;
  946. case 'u':
  947. term_restore_cursor(term, &term->grid->saved_cursor);
  948. break;
  949. case 't': {
  950. /*
  951. * Window operations
  952. */
  953. const unsigned param = vt_param_get(term, 0, 0);
  954. switch (param) {
  955. case 1: LOG_WARN("unimplemented: de-iconify"); break;
  956. case 2: LOG_WARN("unimplemented: iconify"); break;
  957. case 3: LOG_WARN("unimplemented: move window to pixel position"); break;
  958. case 4: LOG_WARN("unimplemented: resize window in pixels"); break;
  959. case 5: LOG_WARN("unimplemented: raise window to front of stack"); break;
  960. case 6: LOG_WARN("unimplemented: raise window to back of stack"); break;
  961. case 7: LOG_WARN("unimplemented: refresh window"); break;
  962. case 8: LOG_WARN("unimplemented: resize window in chars"); break;
  963. case 9: LOG_WARN("unimplemented: maximize/unmaximize window"); break;
  964. case 10: LOG_WARN("unimplemented: to/from full screen"); break;
  965. case 20: LOG_WARN("unimplemented: report icon label"); break;
  966. case 21: LOG_WARN("unimplemented: report window title"); break;
  967. case 24: LOG_WARN("unimplemented: resize window (DECSLPP)"); break;
  968. case 11: /* report if window is iconified */
  969. /* We don't know - always report *not* iconified */
  970. /* 1=not iconified, 2=iconified */
  971. term_to_slave(term, "\033[1t", 4);
  972. break;
  973. case 13: { /* report window position */
  974. /* We don't know our position - always report (0,0) */
  975. int x = -1;
  976. int y = -1;
  977. switch (vt_param_get(term, 1, 0)) {
  978. case 0:
  979. /* window position */
  980. x = y = 0;
  981. break;
  982. case 2:
  983. /* text area position */
  984. x = term->margins.left;
  985. y = term->margins.top;
  986. break;
  987. default:
  988. UNHANDLED();
  989. break;
  990. }
  991. if (x >= 0 && y >= 0) {
  992. char reply[64];
  993. snprintf(reply, sizeof(reply), "\033[3;%d;%dt",
  994. x / term->scale, y / term->scale);
  995. term_to_slave(term, reply, strlen(reply));
  996. }
  997. break;
  998. }
  999. case 14: { /* report window size in pixels */
  1000. int width = -1;
  1001. int height = -1;
  1002. switch (vt_param_get(term, 1, 0)) {
  1003. case 0:
  1004. /* text area size */
  1005. width = term->width - term->margins.left - term->margins.right;
  1006. height = term->height - term->margins.top - term->margins.bottom;
  1007. break;
  1008. case 2:
  1009. /* window size */
  1010. width = term->width;
  1011. height = term->height;
  1012. break;
  1013. default:
  1014. UNHANDLED();
  1015. break;
  1016. }
  1017. if (width >= 0 && height >= 0) {
  1018. char reply[64];
  1019. snprintf(reply, sizeof(reply), "\033[4;%d;%dt",
  1020. height / term->scale, width / term->scale);
  1021. term_to_slave(term, reply, strlen(reply));
  1022. }
  1023. break;
  1024. }
  1025. case 15: /* report screen size in pixels */
  1026. tll_foreach(term->window->on_outputs, it) {
  1027. char reply[64];
  1028. snprintf(reply, sizeof(reply), "\033[5;%d;%dt",
  1029. it->item->dim.px_scaled.height,
  1030. it->item->dim.px_scaled.width);
  1031. term_to_slave(term, reply, strlen(reply));
  1032. break;
  1033. }
  1034. if (tll_length(term->window->on_outputs) == 0)
  1035. term_to_slave(term, "\033[5;0;0t", 8);
  1036. break;
  1037. case 16: { /* report cell size in pixels */
  1038. char reply[64];
  1039. snprintf(reply, sizeof(reply), "\033[6;%d;%dt",
  1040. term->cell_height / term->scale,
  1041. term->cell_width / term->scale);
  1042. term_to_slave(term, reply, strlen(reply));
  1043. break;
  1044. }
  1045. case 18: { /* text area size in chars */
  1046. char reply[64];
  1047. snprintf(reply, sizeof(reply), "\033[8;%d;%dt",
  1048. term->rows, term->cols);
  1049. term_to_slave(term, reply, strlen(reply));
  1050. break;
  1051. }
  1052. case 19: { /* report screen size in chars */
  1053. tll_foreach(term->window->on_outputs, it) {
  1054. char reply[64];
  1055. snprintf(reply, sizeof(reply), "\033[9;%d;%dt",
  1056. it->item->dim.px_real.height / term->cell_height / term->scale,
  1057. it->item->dim.px_real.width / term->cell_width / term->scale);
  1058. term_to_slave(term, reply, strlen(reply));
  1059. break;
  1060. }
  1061. if (tll_length(term->window->on_outputs) == 0)
  1062. term_to_slave(term, "\033[9;0;0t", 8);
  1063. break;
  1064. }
  1065. case 22: { /* push window title */
  1066. /* 0 - icon + title, 1 - icon, 2 - title */
  1067. unsigned what = vt_param_get(term, 1, 0);
  1068. if (what == 0 || what == 2) {
  1069. tll_push_back(
  1070. term->window_title_stack, xstrdup(term->window_title));
  1071. }
  1072. break;
  1073. }
  1074. case 23: { /* pop window title */
  1075. /* 0 - icon + title, 1 - icon, 2 - title */
  1076. unsigned what = vt_param_get(term, 1, 0);
  1077. if (what == 0 || what == 2) {
  1078. if (tll_length(term->window_title_stack) > 0) {
  1079. char *title = tll_pop_back(term->window_title_stack);
  1080. term_set_window_title(term, title);
  1081. free(title);
  1082. }
  1083. }
  1084. break;
  1085. }
  1086. case 1001: {
  1087. }
  1088. default:
  1089. LOG_DBG("ignoring %s", csi_as_string(term, final, -1));
  1090. break;
  1091. }
  1092. break;
  1093. }
  1094. case 'n': {
  1095. if (term->vt.params.idx > 0) {
  1096. int param = vt_param_get(term, 0, 0);
  1097. switch (param) {
  1098. case 5:
  1099. /* Query device status */
  1100. term_to_slave(term, "\x1b[0n", 4); /* "Device OK" */
  1101. break;
  1102. case 6: {
  1103. /* u7 - cursor position query */
  1104. int row = term->origin == ORIGIN_ABSOLUTE
  1105. ? term->grid->cursor.point.row
  1106. : term->grid->cursor.point.row - term->scroll_region.start;
  1107. /* TODO: we use 0-based position, while the xterm
  1108. * terminfo says the receiver of the reply should
  1109. * decrement, hence we must add 1 */
  1110. char reply[64];
  1111. snprintf(reply, sizeof(reply), "\x1b[%d;%dR",
  1112. row + 1, term->grid->cursor.point.col + 1);
  1113. term_to_slave(term, reply, strlen(reply));
  1114. break;
  1115. }
  1116. default:
  1117. UNHANDLED();
  1118. break;
  1119. }
  1120. } else
  1121. UNHANDLED();
  1122. break;
  1123. }
  1124. default:
  1125. UNHANDLED();
  1126. break;
  1127. }
  1128. break; /* private == 0 */
  1129. }
  1130. case '?': {
  1131. switch (final) {
  1132. case 'h':
  1133. /* DECSET - DEC private mode set */
  1134. for (size_t i = 0; i < term->vt.params.idx; i++)
  1135. decset(term, term->vt.params.v[i].value);
  1136. break;
  1137. case 'l':
  1138. /* DECRST - DEC private mode reset */
  1139. for (size_t i = 0; i < term->vt.params.idx; i++)
  1140. decrst(term, term->vt.params.v[i].value);
  1141. break;
  1142. case 'p': {
  1143. if (term->vt.private[1] != '$') {
  1144. UNHANDLED();
  1145. break;
  1146. }
  1147. unsigned param = vt_param_get(term, 0, 0);
  1148. /*
  1149. * Request DEC private mode (DECRQM)
  1150. * Reply:
  1151. * 0 - not recognized
  1152. * 1 - set
  1153. * 2 - reset
  1154. * 3 - permanently set
  1155. * 4 - permantently reset
  1156. */
  1157. char reply[32];
  1158. snprintf(reply, sizeof(reply), "\033[?%u;2$y", param);
  1159. term_to_slave(term, reply, strlen(reply));
  1160. break;
  1161. }
  1162. case 's':
  1163. for (size_t i = 0; i < term->vt.params.idx; i++)
  1164. xtsave(term, term->vt.params.v[i].value);
  1165. break;
  1166. case 'r':
  1167. for (size_t i = 0; i < term->vt.params.idx; i++)
  1168. xtrestore(term, term->vt.params.v[i].value);
  1169. break;
  1170. case 'S': {
  1171. unsigned target = vt_param_get(term, 0, 0);
  1172. unsigned operation = vt_param_get(term, 1, 0);
  1173. switch (target) {
  1174. case 1:
  1175. switch (operation) {
  1176. case 1: sixel_colors_report_current(term); break;
  1177. case 2: sixel_colors_reset(term); break;
  1178. case 3: sixel_colors_set(term, vt_param_get(term, 2, 0)); break;
  1179. case 4: sixel_colors_report_max(term); break;
  1180. default: UNHANDLED(); break;
  1181. }
  1182. break;
  1183. case 2:
  1184. switch (operation) {
  1185. case 1: sixel_geometry_report_current(term); break;
  1186. case 2: sixel_geometry_reset(term); break;
  1187. case 3: sixel_geometry_set(term, vt_param_get(term, 2, 0), vt_param_get(term, 3, 0)); break;
  1188. case 4: sixel_geometry_report_max(term); break;
  1189. default: UNHANDLED(); break;
  1190. }
  1191. break;
  1192. default:
  1193. UNHANDLED();
  1194. break;
  1195. }
  1196. break;
  1197. }
  1198. default:
  1199. UNHANDLED();
  1200. break;
  1201. }
  1202. break; /* private == '?' */
  1203. }
  1204. case '>': {
  1205. switch (final) {
  1206. case 'c':
  1207. /* Send Device Attributes (Secondary DA) */
  1208. if (vt_param_get(term, 0, 0) != 0) {
  1209. UNHANDLED();
  1210. break;
  1211. }
  1212. /*
  1213. * Param 1 - terminal type:
  1214. * 0 - vt100
  1215. * 1 - vt220
  1216. * 2 - vt240
  1217. * 18 - vt330
  1218. * 19 - vt340
  1219. * 24 - vt320
  1220. * 41 - vt420
  1221. * 61 - vt510
  1222. * 64 - vt520
  1223. * 65 - vt525
  1224. *
  1225. * Param 2 - firmware version
  1226. * xterm uses its version number. We use an xterm
  1227. * version number too, since e.g. Emacs uses this to
  1228. * determine level of support.
  1229. *
  1230. * We report ourselves as a VT220. This must be
  1231. * synchronized with the primary DA response.
  1232. *
  1233. * Note: tertiary DA replies with "FOOT".
  1234. */
  1235. static_assert(FOOT_MAJOR < 100, "Major version must not exceed 99");
  1236. static_assert(FOOT_MINOR < 100, "Minor version must not exceed 99");
  1237. static_assert(FOOT_PATCH < 100, "Patch version must not exceed 99");
  1238. char reply[64];
  1239. snprintf(reply, sizeof(reply), "\033[>1;%02u%02u%02u;0c",
  1240. FOOT_MAJOR, FOOT_MINOR, FOOT_PATCH);
  1241. term_to_slave(term, reply, strlen(reply));
  1242. break;
  1243. case 'm':
  1244. if (term->vt.params.idx == 0) {
  1245. /* Reset all */
  1246. } else {
  1247. int resource = vt_param_get(term, 0, 0);
  1248. int value = vt_param_get(term, 1, -1);
  1249. switch (resource) {
  1250. case 0: /* modifyKeyboard */
  1251. break;
  1252. case 1: /* modifyCursorKeys */
  1253. case 2: /* modifyFunctionKeys */
  1254. case 4: /* modifyOtherKeys */
  1255. /* Ignored, we always report modifiers */
  1256. if (value != 2 && value != -1 &&
  1257. !(resource == 4 && value == 1))
  1258. {
  1259. LOG_WARN("unimplemented: %s = %d",
  1260. resource == 1 ? "modifyCursorKeys" :
  1261. resource == 2 ? "modifyFunctionKeys" :
  1262. resource == 4 ? "modifyOtherKeys" : "<invalid>",
  1263. value);
  1264. }
  1265. break;
  1266. default:
  1267. LOG_WARN("invalid resource %d in %s",
  1268. resource, csi_as_string(term, final, -1));
  1269. break;
  1270. }
  1271. }
  1272. break; /* final == 'm' */
  1273. default:
  1274. UNHANDLED();
  1275. break;
  1276. }
  1277. break; /* private == '>' */
  1278. }
  1279. case ' ': {
  1280. switch (final) {
  1281. case 'q': {
  1282. int param = vt_param_get(term, 0, 0);
  1283. switch (param) {
  1284. case 0: /* blinking block, but we use it to reset to configured default */
  1285. term->cursor_style = term->default_cursor_style;
  1286. if (term->default_cursor_blink)
  1287. term_cursor_blink_enable(term);
  1288. else
  1289. term_cursor_blink_disable(term);
  1290. break;
  1291. case 1: /* blinking block */
  1292. case 2: /* steady block */
  1293. term->cursor_style = CURSOR_BLOCK;
  1294. break;
  1295. case 3: /* blinking underline */
  1296. case 4: /* steady underline */
  1297. term->cursor_style = CURSOR_UNDERLINE;
  1298. break;
  1299. case 5: /* blinking bar */
  1300. case 6: /* steady bar */
  1301. term->cursor_style = CURSOR_BAR;
  1302. break;
  1303. default:
  1304. UNHANDLED();
  1305. break;
  1306. }
  1307. if (param > 0 && param <= 6) {
  1308. if (param & 1)
  1309. term_cursor_blink_enable(term);
  1310. else
  1311. term_cursor_blink_disable(term);
  1312. }
  1313. break;
  1314. }
  1315. default:
  1316. UNHANDLED();
  1317. break;
  1318. }
  1319. break; /* private == ' ' */
  1320. }
  1321. case '!': {
  1322. switch (final) {
  1323. case 'p':
  1324. term_reset(term, false);
  1325. break;
  1326. default:
  1327. UNHANDLED();
  1328. break;
  1329. }
  1330. break; /* private == '!' */
  1331. }
  1332. case '=': {
  1333. switch (final) {
  1334. case 'c':
  1335. if (vt_param_get(term, 0, 0) != 0) {
  1336. UNHANDLED();
  1337. break;
  1338. }
  1339. /*
  1340. * Send Device Attributes (Tertiary DA)
  1341. *
  1342. * Reply format is "DCS ! | DDDDDDDD ST"
  1343. *
  1344. * D..D is the unit ID of the terminal, consisting of four
  1345. * hexadecimal pairs. The first pair represents the
  1346. * manufacturing site code. This code can be any
  1347. * hexadecimal value from 00 through FF.
  1348. */
  1349. term_to_slave(term, "\033P!|464f4f54\033\\", 14); /* FOOT */
  1350. break;
  1351. default:
  1352. UNHANDLED();
  1353. break;
  1354. }
  1355. break; /* private == '=' */
  1356. }
  1357. default:
  1358. UNHANDLED();
  1359. break;
  1360. }
  1361. }