Terminal.cpp 30 KB

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