Terminal.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "Terminal.h"
  7. #include <AK/Debug.h>
  8. #include <AK/StringBuilder.h>
  9. #include <AK/StringView.h>
  10. #include <LibVT/Terminal.h>
  11. namespace VT {
  12. Terminal::Terminal(TerminalClient& client)
  13. : m_client(client)
  14. , m_parser(*this)
  15. {
  16. }
  17. Terminal::~Terminal()
  18. {
  19. }
  20. void Terminal::clear()
  21. {
  22. for (size_t i = 0; i < rows(); ++i)
  23. m_lines[i].clear(m_current_attribute);
  24. set_cursor(0, 0);
  25. }
  26. void Terminal::clear_including_history()
  27. {
  28. m_history.clear();
  29. m_history_start = 0;
  30. clear();
  31. m_client.terminal_history_changed();
  32. }
  33. void Terminal::alter_mode(bool should_set, bool question_param, Parameters params)
  34. {
  35. int mode = 2;
  36. if (params.size() > 0) {
  37. mode = params[0];
  38. }
  39. if (!question_param) {
  40. switch (mode) {
  41. // FIXME: implement *something* for this
  42. default:
  43. dbgln("Terminal::alter_mode: Unimplemented mode {} (set={})", mode, should_set);
  44. break;
  45. }
  46. } else {
  47. switch (mode) {
  48. case 3: {
  49. // 80/132-column mode (DECCOLM)
  50. unsigned new_columns = should_set ? 80 : 132;
  51. dbgln_if(TERMINAL_DEBUG, "Setting {}-column mode", new_columns);
  52. set_size(new_columns, rows());
  53. clear();
  54. break;
  55. }
  56. case 25:
  57. // Hide cursor command, but doesn't need to be run (for now, because
  58. // we don't do inverse control codes anyways)
  59. if (should_set)
  60. dbgln("Terminal: Hide Cursor escapecode received. Not needed: ignored.");
  61. else
  62. dbgln("Terminal: Show Cursor escapecode received. Not needed: ignored.");
  63. break;
  64. default:
  65. dbgln("Terminal::alter_mode: Unimplemented private mode {}", mode);
  66. break;
  67. }
  68. }
  69. }
  70. void Terminal::RM(Parameters params)
  71. {
  72. bool question_param = false;
  73. if (params.size() > 0 && params[0] == '?') {
  74. question_param = true;
  75. params = params.slice(1);
  76. }
  77. alter_mode(true, question_param, params);
  78. }
  79. void Terminal::SM(Parameters params)
  80. {
  81. bool question_param = false;
  82. if (params.size() > 0 && params[0] == '?') {
  83. question_param = true;
  84. params = params.slice(1);
  85. }
  86. alter_mode(false, question_param, params);
  87. }
  88. void Terminal::SGR(Parameters params)
  89. {
  90. if (params.is_empty()) {
  91. m_current_attribute.reset();
  92. return;
  93. }
  94. if (params.size() >= 3) {
  95. bool should_set = true;
  96. auto kind = params[1];
  97. u32 color = 0;
  98. switch (kind) {
  99. case 5: // 8-bit
  100. color = xterm_colors[params[2]];
  101. break;
  102. case 2: // 24-bit
  103. for (size_t i = 0; i < 3; ++i) {
  104. u8 component = 0;
  105. if (params.size() - 2 > i) {
  106. component = params[i + 2];
  107. }
  108. color <<= 8;
  109. color |= component;
  110. }
  111. break;
  112. default:
  113. should_set = false;
  114. break;
  115. }
  116. if (should_set) {
  117. if (params[0] == 38) {
  118. m_current_attribute.foreground_color = color;
  119. return;
  120. } else if (params[0] == 48) {
  121. m_current_attribute.background_color = color;
  122. return;
  123. }
  124. }
  125. }
  126. for (auto param : params) {
  127. switch (param) {
  128. case 0:
  129. // Reset
  130. m_current_attribute.reset();
  131. break;
  132. case 1:
  133. m_current_attribute.flags |= Attribute::Bold;
  134. break;
  135. case 3:
  136. m_current_attribute.flags |= Attribute::Italic;
  137. break;
  138. case 4:
  139. m_current_attribute.flags |= Attribute::Underline;
  140. break;
  141. case 5:
  142. m_current_attribute.flags |= Attribute::Blink;
  143. break;
  144. case 7:
  145. m_current_attribute.flags |= Attribute::Negative;
  146. break;
  147. case 22:
  148. m_current_attribute.flags &= ~Attribute::Bold;
  149. break;
  150. case 23:
  151. m_current_attribute.flags &= ~Attribute::Italic;
  152. break;
  153. case 24:
  154. m_current_attribute.flags &= ~Attribute::Underline;
  155. break;
  156. case 25:
  157. m_current_attribute.flags &= ~Attribute::Blink;
  158. break;
  159. case 27:
  160. m_current_attribute.flags &= ~Attribute::Negative;
  161. break;
  162. case 30:
  163. case 31:
  164. case 32:
  165. case 33:
  166. case 34:
  167. case 35:
  168. case 36:
  169. case 37:
  170. // Foreground color
  171. if (m_current_attribute.flags & Attribute::Bold)
  172. param += 8;
  173. m_current_attribute.foreground_color = xterm_colors[param - 30];
  174. break;
  175. case 39:
  176. // reset foreground
  177. m_current_attribute.foreground_color = Attribute::default_foreground_color;
  178. break;
  179. case 40:
  180. case 41:
  181. case 42:
  182. case 43:
  183. case 44:
  184. case 45:
  185. case 46:
  186. case 47:
  187. // Background color
  188. if (m_current_attribute.flags & Attribute::Bold)
  189. param += 8;
  190. m_current_attribute.background_color = xterm_colors[param - 40];
  191. break;
  192. case 49:
  193. // reset background
  194. m_current_attribute.background_color = Attribute::default_background_color;
  195. break;
  196. default:
  197. dbgln("FIXME: SGR: p: {}", param);
  198. }
  199. }
  200. }
  201. void Terminal::SCOSC()
  202. {
  203. m_saved_cursor_row = m_cursor_row;
  204. m_saved_cursor_column = m_cursor_column;
  205. m_saved_attribute = m_current_attribute;
  206. }
  207. void Terminal::SCORC(Parameters)
  208. {
  209. set_cursor(m_saved_cursor_row, m_saved_cursor_column);
  210. }
  211. void Terminal::XTERM_WM(Parameters params)
  212. {
  213. if (params.size() < 1)
  214. return;
  215. dbgln("FIXME: XTERM_WM: Ps: {} (param count: {})", params[0], params.size());
  216. }
  217. void Terminal::DECSTBM(Parameters params)
  218. {
  219. unsigned top = 1;
  220. unsigned bottom = m_rows;
  221. if (params.size() >= 1)
  222. top = params[0];
  223. if (params.size() >= 2)
  224. bottom = params[1];
  225. if ((bottom - top) < 2 || bottom > m_rows) {
  226. dbgln("Error: DECSTBM: scrolling region invalid: {}-{}", top, bottom);
  227. return;
  228. }
  229. m_scroll_region_top = top - 1;
  230. m_scroll_region_bottom = bottom - 1;
  231. set_cursor(0, 0);
  232. }
  233. void Terminal::CUP(Parameters params)
  234. {
  235. // CUP – Cursor Position
  236. unsigned row = 1;
  237. unsigned col = 1;
  238. if (params.size() >= 1)
  239. row = params[0];
  240. if (params.size() >= 2)
  241. col = params[1];
  242. set_cursor(row - 1, col - 1);
  243. }
  244. void Terminal::HVP(Parameters params)
  245. {
  246. unsigned row = 1;
  247. unsigned col = 1;
  248. if (params.size() >= 1)
  249. row = params[0];
  250. if (params.size() >= 2)
  251. col = params[1];
  252. set_cursor(row - 1, col - 1);
  253. }
  254. void Terminal::CUU(Parameters params)
  255. {
  256. int num = 1;
  257. if (params.size() >= 1)
  258. num = params[0];
  259. if (num == 0)
  260. num = 1;
  261. int new_row = (int)m_cursor_row - num;
  262. if (new_row < 0)
  263. new_row = 0;
  264. set_cursor(new_row, m_cursor_column);
  265. }
  266. void Terminal::CUD(Parameters params)
  267. {
  268. int num = 1;
  269. if (params.size() >= 1)
  270. num = params[0];
  271. if (num == 0)
  272. num = 1;
  273. int new_row = (int)m_cursor_row + num;
  274. if (new_row >= m_rows)
  275. new_row = m_rows - 1;
  276. set_cursor(new_row, m_cursor_column);
  277. }
  278. void Terminal::CUF(Parameters params)
  279. {
  280. int num = 1;
  281. if (params.size() >= 1)
  282. num = params[0];
  283. if (num == 0)
  284. num = 1;
  285. int new_column = (int)m_cursor_column + num;
  286. if (new_column >= m_columns)
  287. new_column = m_columns - 1;
  288. set_cursor(m_cursor_row, new_column);
  289. }
  290. void Terminal::CUB(Parameters params)
  291. {
  292. int num = 1;
  293. if (params.size() >= 1)
  294. num = params[0];
  295. if (num == 0)
  296. num = 1;
  297. int new_column = (int)m_cursor_column - num;
  298. if (new_column < 0)
  299. new_column = 0;
  300. set_cursor(m_cursor_row, new_column);
  301. }
  302. void Terminal::CHA(Parameters params)
  303. {
  304. int new_column = 1;
  305. if (params.size() >= 1)
  306. new_column = params[0] - 1;
  307. if (new_column < 0)
  308. new_column = 0;
  309. set_cursor(m_cursor_row, new_column);
  310. }
  311. void Terminal::REP(Parameters params)
  312. {
  313. if (params.size() < 1)
  314. return;
  315. for (unsigned i = 0; i < params[0]; ++i)
  316. put_character_at(m_cursor_row, m_cursor_column++, m_last_code_point);
  317. }
  318. void Terminal::VPA(Parameters params)
  319. {
  320. int new_row = 1;
  321. if (params.size() >= 1)
  322. new_row = params[0] - 1;
  323. if (new_row < 0)
  324. new_row = 0;
  325. set_cursor(new_row, m_cursor_column);
  326. }
  327. void Terminal::ECH(Parameters params)
  328. {
  329. // Erase characters (without moving cursor)
  330. int num = 1;
  331. if (params.size() >= 1)
  332. num = params[0];
  333. if (num == 0)
  334. num = 1;
  335. // Clear from cursor to end of line.
  336. for (int i = m_cursor_column; i < num; ++i) {
  337. put_character_at(m_cursor_row, i, ' ');
  338. }
  339. }
  340. void Terminal::EL(Parameters params)
  341. {
  342. int mode = 0;
  343. if (params.size() >= 1)
  344. mode = params[0];
  345. switch (mode) {
  346. case 0:
  347. // Clear from cursor to end of line.
  348. for (int i = m_cursor_column; i < m_columns; ++i) {
  349. put_character_at(m_cursor_row, i, ' ');
  350. }
  351. break;
  352. case 1:
  353. // Clear from cursor to beginning of line.
  354. for (int i = 0; i <= m_cursor_column; ++i) {
  355. put_character_at(m_cursor_row, i, ' ');
  356. }
  357. break;
  358. case 2:
  359. // Clear the complete line
  360. for (int i = 0; i < m_columns; ++i) {
  361. put_character_at(m_cursor_row, i, ' ');
  362. }
  363. break;
  364. default:
  365. unimplemented_csi_sequence(params, {}, 'K');
  366. break;
  367. }
  368. }
  369. void Terminal::ED(Parameters params)
  370. {
  371. int mode = 0;
  372. if (params.size() >= 1)
  373. mode = params[0];
  374. switch (mode) {
  375. case 0:
  376. // Clear from cursor to end of screen.
  377. for (int i = m_cursor_column; i < m_columns; ++i)
  378. put_character_at(m_cursor_row, i, ' ');
  379. for (int row = m_cursor_row + 1; row < m_rows; ++row) {
  380. for (int column = 0; column < m_columns; ++column) {
  381. put_character_at(row, column, ' ');
  382. }
  383. }
  384. break;
  385. case 1:
  386. // Clear from cursor to beginning of screen.
  387. for (int i = m_cursor_column; i >= 0; --i)
  388. put_character_at(m_cursor_row, i, ' ');
  389. for (int row = m_cursor_row - 1; row >= 0; --row) {
  390. for (int column = 0; column < m_columns; ++column) {
  391. put_character_at(row, column, ' ');
  392. }
  393. }
  394. break;
  395. case 2:
  396. clear();
  397. break;
  398. case 3:
  399. // FIXME: <esc>[3J should also clear the scrollback buffer.
  400. clear();
  401. break;
  402. default:
  403. unimplemented_csi_sequence(params, {}, 'J');
  404. break;
  405. }
  406. }
  407. void Terminal::SU(Parameters params)
  408. {
  409. int count = 1;
  410. if (params.size() >= 1)
  411. count = params[0];
  412. for (u16 i = 0; i < count; i++)
  413. scroll_up();
  414. }
  415. void Terminal::SD(Parameters params)
  416. {
  417. int count = 1;
  418. if (params.size() >= 1)
  419. count = params[0];
  420. for (u16 i = 0; i < count; i++)
  421. scroll_down();
  422. }
  423. void Terminal::IL(Parameters params)
  424. {
  425. int count = 1;
  426. if (params.size() >= 1)
  427. count = params[0];
  428. invalidate_cursor();
  429. for (; count > 0; --count) {
  430. m_lines.insert(m_cursor_row + m_scroll_region_top, make<Line>(m_columns));
  431. if (m_scroll_region_bottom + 1 < m_lines.size())
  432. m_lines.remove(m_scroll_region_bottom + 1);
  433. else
  434. m_lines.remove(m_lines.size() - 1);
  435. }
  436. m_need_full_flush = true;
  437. }
  438. void Terminal::DA(Parameters)
  439. {
  440. emit_string("\033[?1;0c");
  441. }
  442. void Terminal::DL(Parameters params)
  443. {
  444. int count = 1;
  445. if (params.size() >= 1)
  446. count = params[0];
  447. if (count == 1 && m_cursor_row == 0) {
  448. scroll_up();
  449. return;
  450. }
  451. int max_count = m_rows - (m_scroll_region_top + m_cursor_row);
  452. count = min(count, max_count);
  453. for (int c = count; c > 0; --c) {
  454. m_lines.remove(m_cursor_row + m_scroll_region_top);
  455. if (m_scroll_region_bottom < m_lines.size())
  456. m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns));
  457. else
  458. m_lines.append(make<Line>(m_columns));
  459. }
  460. }
  461. void Terminal::DCH(Parameters params)
  462. {
  463. int num = 1;
  464. if (params.size() >= 1)
  465. num = params[0];
  466. if (num == 0)
  467. num = 1;
  468. auto& line = m_lines[m_cursor_row];
  469. // Move n characters of line to the left
  470. for (size_t i = m_cursor_column; i < line.length() - num; i++)
  471. line.set_code_point(i, line.code_point(i + num));
  472. // Fill remainder of line with blanks
  473. for (size_t i = line.length() - num; i < line.length(); i++)
  474. line.set_code_point(i, ' ');
  475. line.set_dirty(true);
  476. }
  477. void Terminal::newline()
  478. {
  479. u16 new_row = m_cursor_row;
  480. if (m_cursor_row == m_scroll_region_bottom) {
  481. scroll_up();
  482. } else {
  483. ++new_row;
  484. };
  485. set_cursor(new_row, 0);
  486. }
  487. void Terminal::carriage_return()
  488. {
  489. set_cursor(m_cursor_row, 0);
  490. }
  491. void Terminal::scroll_up()
  492. {
  493. // NOTE: We have to invalidate the cursor first.
  494. invalidate_cursor();
  495. if (m_scroll_region_top == 0) {
  496. auto line = move(m_lines.ptr_at(m_scroll_region_top));
  497. add_line_to_history(move(line));
  498. m_client.terminal_history_changed();
  499. }
  500. m_lines.remove(m_scroll_region_top);
  501. m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns));
  502. m_need_full_flush = true;
  503. }
  504. void Terminal::scroll_down()
  505. {
  506. // NOTE: We have to invalidate the cursor first.
  507. invalidate_cursor();
  508. m_lines.remove(m_scroll_region_bottom);
  509. m_lines.insert(m_scroll_region_top, make<Line>(m_columns));
  510. m_need_full_flush = true;
  511. }
  512. void Terminal::set_cursor(unsigned a_row, unsigned a_column)
  513. {
  514. unsigned row = min(a_row, m_rows - 1u);
  515. unsigned column = min(a_column, m_columns - 1u);
  516. if (row == m_cursor_row && column == m_cursor_column)
  517. return;
  518. VERIFY(row < rows());
  519. VERIFY(column < columns());
  520. invalidate_cursor();
  521. m_cursor_row = row;
  522. m_cursor_column = column;
  523. m_stomp = false;
  524. invalidate_cursor();
  525. }
  526. void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point)
  527. {
  528. VERIFY(row < rows());
  529. VERIFY(column < columns());
  530. auto& line = m_lines[row];
  531. line.set_code_point(column, code_point);
  532. line.attribute_at(column) = m_current_attribute;
  533. line.attribute_at(column).flags |= Attribute::Touched;
  534. line.set_dirty(true);
  535. m_last_code_point = code_point;
  536. }
  537. void Terminal::NEL()
  538. {
  539. newline();
  540. carriage_return();
  541. }
  542. void Terminal::IND()
  543. {
  544. CUD({});
  545. }
  546. void Terminal::RI()
  547. {
  548. CUU({});
  549. }
  550. void Terminal::DSR(Parameters params)
  551. {
  552. if (params.size() == 1 && params[0] == 5) {
  553. // Device status
  554. emit_string("\033[0n"); // Terminal status OK!
  555. } else if (params.size() == 1 && params[0] == 6) {
  556. // Cursor position query
  557. emit_string(String::formatted("\e[{};{}R", m_cursor_row + 1, m_cursor_column + 1));
  558. } else {
  559. dbgln("Unknown DSR");
  560. }
  561. }
  562. void Terminal::ICH(Parameters params)
  563. {
  564. int num = 0;
  565. if (params.size() >= 1) {
  566. num = params[0];
  567. }
  568. if (num == 0)
  569. num = 1;
  570. auto& line = m_lines[m_cursor_row];
  571. // Move characters after cursor to the right
  572. for (int i = line.length() - num; i >= m_cursor_column; --i)
  573. line.set_code_point(i + num, line.code_point(i));
  574. // Fill n characters after cursor with blanks
  575. for (int i = 0; i < num; i++)
  576. line.set_code_point(m_cursor_column + i, ' ');
  577. line.set_dirty(true);
  578. }
  579. void Terminal::on_input(u8 byte)
  580. {
  581. m_parser.on_input(byte);
  582. }
  583. void Terminal::emit_code_point(u32 code_point)
  584. {
  585. auto new_column = m_cursor_column + 1;
  586. if (new_column < columns()) {
  587. put_character_at(m_cursor_row, m_cursor_column, code_point);
  588. set_cursor(m_cursor_row, new_column);
  589. return;
  590. }
  591. if (m_stomp) {
  592. m_stomp = false;
  593. carriage_return();
  594. newline();
  595. put_character_at(m_cursor_row, m_cursor_column, code_point);
  596. set_cursor(m_cursor_row, 1);
  597. } else {
  598. // Curious: We wait once on the right-hand side
  599. m_stomp = true;
  600. put_character_at(m_cursor_row, m_cursor_column, code_point);
  601. }
  602. }
  603. void Terminal::execute_control_code(u8 code)
  604. {
  605. switch (code) {
  606. case '\a':
  607. m_client.beep();
  608. return;
  609. case '\b':
  610. if (m_cursor_column) {
  611. set_cursor(m_cursor_row, m_cursor_column - 1);
  612. return;
  613. }
  614. return;
  615. case '\t': {
  616. for (unsigned i = m_cursor_column + 1; i < columns(); ++i) {
  617. if (m_horizontal_tabs[i]) {
  618. set_cursor(m_cursor_row, i);
  619. return;
  620. }
  621. }
  622. return;
  623. }
  624. case '\n':
  625. newline();
  626. return;
  627. case '\r':
  628. carriage_return();
  629. return;
  630. default:
  631. unimplemented_control_code(code);
  632. }
  633. }
  634. void Terminal::execute_escape_sequence(Intermediates intermediates, bool ignore, u8 last_byte)
  635. {
  636. // FIXME: Handle it somehow?
  637. if (ignore)
  638. dbgln("Escape sequence has its ignore flag set.");
  639. if (intermediates.size() == 0) {
  640. switch (last_byte) {
  641. case 'D':
  642. IND();
  643. return;
  644. case 'E':
  645. NEL();
  646. return;
  647. case 'M':
  648. RI();
  649. return;
  650. }
  651. } else if (intermediates[0] == '#') {
  652. switch (last_byte) {
  653. case '8':
  654. // Confidence Test - Fill screen with E's
  655. for (size_t row = 0; row < m_rows; ++row) {
  656. for (size_t column = 0; column < m_columns; ++column) {
  657. put_character_at(row, column, 'E');
  658. }
  659. }
  660. return;
  661. }
  662. }
  663. unimplemented_escape_sequence(intermediates, last_byte);
  664. }
  665. void Terminal::execute_csi_sequence(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte)
  666. {
  667. // FIXME: Handle it somehow?
  668. if (ignore)
  669. dbgln("CSI sequence has its ignore flag set.");
  670. switch (last_byte) {
  671. case '@':
  672. ICH(parameters);
  673. break;
  674. case 'A':
  675. CUU(parameters);
  676. break;
  677. case 'B':
  678. CUD(parameters);
  679. break;
  680. case 'C':
  681. CUF(parameters);
  682. break;
  683. case 'D':
  684. CUB(parameters);
  685. break;
  686. case 'G':
  687. CHA(parameters);
  688. break;
  689. case 'H':
  690. CUP(parameters);
  691. break;
  692. case 'J':
  693. ED(parameters);
  694. break;
  695. case 'K':
  696. EL(parameters);
  697. break;
  698. case 'L':
  699. IL(parameters);
  700. break;
  701. case 'M':
  702. DL(parameters);
  703. break;
  704. case 'P':
  705. DCH(parameters);
  706. break;
  707. case 'S':
  708. SU(parameters);
  709. break;
  710. case 'T':
  711. SD(parameters);
  712. break;
  713. case 'X':
  714. ECH(parameters);
  715. break;
  716. case 'b':
  717. REP(parameters);
  718. break;
  719. case 'd':
  720. VPA(parameters);
  721. break;
  722. case 'm':
  723. SGR(parameters);
  724. break;
  725. case 's':
  726. SCOSC();
  727. break;
  728. case 'u':
  729. SCORC(parameters);
  730. break;
  731. case 't':
  732. XTERM_WM(parameters);
  733. break;
  734. case 'r':
  735. DECSTBM(parameters);
  736. break;
  737. case 'l':
  738. RM(parameters);
  739. break;
  740. case 'h':
  741. SM(parameters);
  742. break;
  743. case 'c':
  744. DA(parameters);
  745. break;
  746. case 'f':
  747. HVP(parameters);
  748. break;
  749. case 'n':
  750. DSR(parameters);
  751. break;
  752. default:
  753. unimplemented_csi_sequence(parameters, intermediates, last_byte);
  754. }
  755. }
  756. void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte)
  757. {
  758. auto stringview_ify = [&](size_t param_idx) {
  759. return StringView((const char*)(&parameters[param_idx][0]), parameters[param_idx].size());
  760. };
  761. if (parameters.size() > 0 && !parameters[0].is_empty()) {
  762. auto command_number = stringview_ify(0).to_uint();
  763. if (command_number.has_value()) {
  764. switch (command_number.value()) {
  765. case 0:
  766. case 1:
  767. case 2:
  768. if (parameters[1].is_empty())
  769. dbgln("Attempted to set window title without any parameters");
  770. else
  771. m_client.set_window_title(stringview_ify(1));
  772. // FIXME: the split breaks titles containing semicolons.
  773. // Should we expose the raw OSC string from the parser? Or join by semicolon?
  774. break;
  775. case 8:
  776. if (parameters.size() < 2) {
  777. dbgln("Attempted to set href but gave too few parameters");
  778. } else if (parameters[2].is_empty()) {
  779. m_current_attribute.href = String();
  780. m_current_attribute.href_id = String();
  781. } else {
  782. m_current_attribute.href = stringview_ify(2);
  783. // FIXME: Respect the provided ID
  784. m_current_attribute.href_id = String::number(m_next_href_id++);
  785. }
  786. break;
  787. case 9:
  788. if (parameters.size() < 2 || parameters[1].is_empty() || parameters[2].is_empty())
  789. dbgln("Atttempted to set window progress but gave too few parameters");
  790. else
  791. m_client.set_window_progress(stringview_ify(1).to_int().value_or(0), stringview_ify(2).to_int().value_or(0));
  792. break;
  793. default:
  794. unimplemented_osc_sequence(parameters, last_byte);
  795. }
  796. } else {
  797. unimplemented_osc_sequence(parameters, last_byte);
  798. }
  799. } else {
  800. unimplemented_osc_sequence(parameters, last_byte);
  801. }
  802. }
  803. void Terminal::dcs_hook(Parameters parameters, Intermediates intermediates, bool ignore, u8 last_byte)
  804. {
  805. dbgln("Received DCS parameters, but we don't support it yet");
  806. (void)parameters;
  807. (void)last_byte;
  808. (void)intermediates;
  809. (void)ignore;
  810. }
  811. void Terminal::receive_dcs_char(u8 byte)
  812. {
  813. dbgln_if(TERMINAL_DEBUG, "DCS string character {:c}", byte);
  814. (void)byte;
  815. }
  816. void Terminal::execute_dcs_sequence()
  817. {
  818. }
  819. void Terminal::inject_string(const StringView& str)
  820. {
  821. for (size_t i = 0; i < str.length(); ++i)
  822. on_input(str[i]);
  823. }
  824. void Terminal::emit_string(const StringView& string)
  825. {
  826. m_client.emit((const u8*)string.characters_without_null_termination(), string.length());
  827. }
  828. void Terminal::handle_key_press(KeyCode key, u32 code_point, u8 flags)
  829. {
  830. bool ctrl = flags & Mod_Ctrl;
  831. bool alt = flags & Mod_Alt;
  832. bool shift = flags & Mod_Shift;
  833. unsigned modifier_mask = int(shift) + (int(alt) << 1) + (int(ctrl) << 2);
  834. auto emit_final_with_modifier = [this, modifier_mask](char final) {
  835. if (modifier_mask)
  836. emit_string(String::formatted("\e[1;{}{:c}", modifier_mask + 1, final));
  837. else
  838. emit_string(String::formatted("\e[{:c}", final));
  839. };
  840. auto emit_tilde_with_modifier = [this, modifier_mask](unsigned num) {
  841. if (modifier_mask)
  842. emit_string(String::formatted("\e[{};{}~", num, modifier_mask + 1));
  843. else
  844. emit_string(String::formatted("\e[{}~", num));
  845. };
  846. switch (key) {
  847. case KeyCode::Key_Up:
  848. emit_final_with_modifier('A');
  849. return;
  850. case KeyCode::Key_Down:
  851. emit_final_with_modifier('B');
  852. return;
  853. case KeyCode::Key_Right:
  854. emit_final_with_modifier('C');
  855. return;
  856. case KeyCode::Key_Left:
  857. emit_final_with_modifier('D');
  858. return;
  859. case KeyCode::Key_Insert:
  860. emit_tilde_with_modifier(2);
  861. return;
  862. case KeyCode::Key_Delete:
  863. emit_tilde_with_modifier(3);
  864. return;
  865. case KeyCode::Key_Home:
  866. emit_final_with_modifier('H');
  867. return;
  868. case KeyCode::Key_End:
  869. emit_final_with_modifier('F');
  870. return;
  871. case KeyCode::Key_PageUp:
  872. emit_tilde_with_modifier(5);
  873. return;
  874. case KeyCode::Key_PageDown:
  875. emit_tilde_with_modifier(6);
  876. return;
  877. default:
  878. break;
  879. }
  880. if (!code_point) {
  881. // Probably a modifier being pressed.
  882. return;
  883. }
  884. if (shift && key == KeyCode::Key_Tab) {
  885. emit_string("\033[Z");
  886. return;
  887. }
  888. // Key event was not one of the above special cases,
  889. // attempt to treat it as a character...
  890. if (ctrl) {
  891. if (code_point >= 'a' && code_point <= 'z') {
  892. code_point = code_point - 'a' + 1;
  893. } else if (code_point == '\\') {
  894. code_point = 0x1c;
  895. }
  896. }
  897. // Alt modifier sends escape prefix.
  898. if (alt)
  899. emit_string("\033");
  900. StringBuilder sb;
  901. sb.append_code_point(code_point);
  902. emit_string(sb.to_string());
  903. }
  904. void Terminal::unimplemented_control_code(u8 code)
  905. {
  906. dbgln("Unimplemented control code {:02x}", code);
  907. }
  908. void Terminal::unimplemented_escape_sequence(Intermediates intermediates, u8 last_byte)
  909. {
  910. StringBuilder builder;
  911. builder.appendff("Unimplemented escape sequence {:c}", last_byte);
  912. if (!intermediates.is_empty()) {
  913. builder.append(", intermediates: ");
  914. for (size_t i = 0; i < intermediates.size(); ++i)
  915. builder.append((char)intermediates[i]);
  916. }
  917. dbgln("{}", builder.string_view());
  918. }
  919. void Terminal::unimplemented_csi_sequence(Parameters parameters, Intermediates intermediates, u8 last_byte)
  920. {
  921. StringBuilder builder;
  922. builder.appendff("Unimplemented CSI sequence: {:c}", last_byte);
  923. if (!parameters.is_empty()) {
  924. builder.append(", parameters: [");
  925. for (size_t i = 0; i < parameters.size(); ++i)
  926. builder.appendff("{}{}", (i == 0) ? "" : ", ", parameters[i]);
  927. builder.append("]");
  928. }
  929. if (!intermediates.is_empty()) {
  930. builder.append(", intermediates:");
  931. for (size_t i = 0; i < intermediates.size(); ++i)
  932. builder.append((char)intermediates[i]);
  933. }
  934. dbgln("{}", builder.string_view());
  935. }
  936. void Terminal::unimplemented_osc_sequence(OscParameters parameters, u8 last_byte)
  937. {
  938. StringBuilder builder;
  939. builder.appendff("Unimplemented OSC sequence parameters: (bel_terminated={}) [ ", last_byte == '\a');
  940. bool first = true;
  941. for (auto parameter : parameters) {
  942. if (!first)
  943. builder.append(", ");
  944. builder.append("[");
  945. for (auto character : parameter)
  946. builder.append((char)character);
  947. builder.append("]");
  948. first = false;
  949. }
  950. builder.append(" ]");
  951. dbgln("{}", builder.string_view());
  952. }
  953. void Terminal::set_size(u16 columns, u16 rows)
  954. {
  955. if (!columns)
  956. columns = 1;
  957. if (!rows)
  958. rows = 1;
  959. if (columns == m_columns && rows == m_rows)
  960. return;
  961. if (rows > m_rows) {
  962. while (m_lines.size() < rows)
  963. m_lines.append(make<Line>(columns));
  964. } else {
  965. m_lines.shrink(rows);
  966. }
  967. for (int i = 0; i < rows; ++i)
  968. m_lines[i].set_length(columns);
  969. m_columns = columns;
  970. m_rows = rows;
  971. m_scroll_region_top = 0;
  972. m_scroll_region_bottom = rows - 1;
  973. m_cursor_row = min((int)m_cursor_row, m_rows - 1);
  974. m_cursor_column = min((int)m_cursor_column, m_columns - 1);
  975. m_saved_cursor_row = min((int)m_saved_cursor_row, m_rows - 1);
  976. m_saved_cursor_column = min((int)m_saved_cursor_column, m_columns - 1);
  977. m_horizontal_tabs.resize(columns);
  978. for (unsigned i = 0; i < columns; ++i)
  979. m_horizontal_tabs[i] = (i % 8) == 0;
  980. // Rightmost column is always last tab on line.
  981. m_horizontal_tabs[columns - 1] = 1;
  982. m_client.terminal_did_resize(m_columns, m_rows);
  983. }
  984. void Terminal::invalidate_cursor()
  985. {
  986. m_lines[m_cursor_row].set_dirty(true);
  987. }
  988. Attribute Terminal::attribute_at(const Position& position) const
  989. {
  990. if (!position.is_valid())
  991. return {};
  992. if (position.row() >= static_cast<int>(line_count()))
  993. return {};
  994. auto& line = this->line(position.row());
  995. if (static_cast<size_t>(position.column()) >= line.length())
  996. return {};
  997. return line.attribute_at(position.column());
  998. }
  999. }