VirtualConsole.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  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 "VirtualConsole.h"
  27. #include <AK/String.h>
  28. #include <Kernel/Arch/i386/CPU.h>
  29. #include <Kernel/Devices/KeyboardDevice.h>
  30. #include <Kernel/Heap/kmalloc.h>
  31. #include <LibBareMetal/IO.h>
  32. #include <LibBareMetal/StdLib.h>
  33. namespace Kernel {
  34. static u8* s_vga_buffer;
  35. static VirtualConsole* s_consoles[6];
  36. static int s_active_console;
  37. void VirtualConsole::get_vga_cursor(u8& row, u8& column)
  38. {
  39. u16 value;
  40. IO::out8(0x3d4, 0x0e);
  41. value = IO::in8(0x3d5) << 8;
  42. IO::out8(0x3d4, 0x0f);
  43. value |= IO::in8(0x3d5);
  44. row = value / columns();
  45. column = value % columns();
  46. }
  47. void VirtualConsole::flush_vga_cursor()
  48. {
  49. u16 value = m_current_vga_start_address + (m_cursor_row * columns() + m_cursor_column);
  50. IO::out8(0x3d4, 0x0e);
  51. IO::out8(0x3d5, MSB(value));
  52. IO::out8(0x3d4, 0x0f);
  53. IO::out8(0x3d5, LSB(value));
  54. }
  55. void VirtualConsole::initialize()
  56. {
  57. s_vga_buffer = (u8*)0xc00b8000;
  58. memset(s_consoles, 0, sizeof(s_consoles));
  59. s_active_console = -1;
  60. }
  61. void VirtualConsole::set_graphical(bool graphical)
  62. {
  63. if (graphical)
  64. set_vga_start_row(0);
  65. m_graphical = graphical;
  66. }
  67. VirtualConsole::VirtualConsole(unsigned index, InitialContents initial_contents)
  68. : TTY(4, index)
  69. , m_index(index)
  70. {
  71. sprintf(m_tty_name, "/dev/tty%u", m_index);
  72. set_size(80, 25);
  73. m_horizontal_tabs = static_cast<u8*>(kmalloc_eternal(columns()));
  74. for (unsigned i = 0; i < columns(); ++i)
  75. m_horizontal_tabs[i] = (i % 8) == 0;
  76. // Rightmost column is always last tab on line.
  77. m_horizontal_tabs[columns() - 1] = 1;
  78. s_consoles[index] = this;
  79. m_buffer = (u8*)kmalloc_eternal(rows() * columns() * 2);
  80. if (initial_contents == AdoptCurrentVGABuffer) {
  81. memcpy(m_buffer, s_vga_buffer, rows() * columns() * 2);
  82. get_vga_cursor(m_cursor_row, m_cursor_column);
  83. } else {
  84. u16* line_mem = reinterpret_cast<u16*>(m_buffer);
  85. for (u16 i = 0; i < rows() * columns(); ++i)
  86. line_mem[i] = 0x0720;
  87. }
  88. }
  89. VirtualConsole::~VirtualConsole()
  90. {
  91. ASSERT_NOT_REACHED();
  92. }
  93. void VirtualConsole::clear()
  94. {
  95. u16* linemem = m_active ? (u16*)s_vga_buffer : (u16*)m_buffer;
  96. for (u16 i = 0; i < rows() * columns(); ++i)
  97. linemem[i] = 0x0720;
  98. if (m_active)
  99. set_vga_start_row(0);
  100. set_cursor(0, 0);
  101. }
  102. void VirtualConsole::switch_to(unsigned index)
  103. {
  104. if ((int)index == s_active_console)
  105. return;
  106. ASSERT(index < 6);
  107. ASSERT(s_consoles[index]);
  108. InterruptDisabler disabler;
  109. if (s_active_console != -1) {
  110. auto* active_console = s_consoles[s_active_console];
  111. // We won't know how to switch away from a graphical console until we
  112. // can set the video mode on our own. Just stop anyone from trying for
  113. // now.
  114. if (active_console->is_graphical())
  115. return;
  116. active_console->set_active(false);
  117. }
  118. dbgprintf("VC: Switch to %u (%p)\n", index, s_consoles[index]);
  119. s_active_console = index;
  120. s_consoles[s_active_console]->set_active(true);
  121. Console::the().set_implementation(s_consoles[s_active_console]);
  122. }
  123. void VirtualConsole::set_active(bool b)
  124. {
  125. if (b == m_active)
  126. return;
  127. InterruptDisabler disabler;
  128. m_active = b;
  129. if (!m_active) {
  130. memcpy(m_buffer, m_current_vga_window, rows() * columns() * 2);
  131. KeyboardDevice::the().set_client(nullptr);
  132. return;
  133. }
  134. memcpy(s_vga_buffer, m_buffer, rows() * columns() * 2);
  135. set_vga_start_row(0);
  136. flush_vga_cursor();
  137. KeyboardDevice::the().set_client(this);
  138. }
  139. inline bool is_valid_parameter_character(u8 ch)
  140. {
  141. return ch >= 0x30 && ch <= 0x3f;
  142. }
  143. inline bool is_valid_intermediate_character(u8 ch)
  144. {
  145. return ch >= 0x20 && ch <= 0x2f;
  146. }
  147. inline bool is_valid_final_character(u8 ch)
  148. {
  149. return ch >= 0x40 && ch <= 0x7e;
  150. }
  151. enum class VGAColor : u8 {
  152. Black = 0,
  153. Blue,
  154. Green,
  155. Cyan,
  156. Red,
  157. Magenta,
  158. Brown,
  159. LightGray,
  160. DarkGray,
  161. BrightBlue,
  162. BrightGreen,
  163. BrightCyan,
  164. BrightRed,
  165. BrightMagenta,
  166. Yellow,
  167. White,
  168. };
  169. enum class ANSIColor : u8 {
  170. Black = 0,
  171. Red,
  172. Green,
  173. Brown,
  174. Blue,
  175. Magenta,
  176. Cyan,
  177. LightGray,
  178. DarkGray,
  179. BrightRed,
  180. BrightGreen,
  181. Yellow,
  182. BrightBlue,
  183. BrightMagenta,
  184. BrightCyan,
  185. White,
  186. };
  187. static inline VGAColor ansi_color_to_vga(ANSIColor color)
  188. {
  189. switch (color) {
  190. case ANSIColor::Black:
  191. return VGAColor::Black;
  192. case ANSIColor::Red:
  193. return VGAColor::Red;
  194. case ANSIColor::Brown:
  195. return VGAColor::Brown;
  196. case ANSIColor::Blue:
  197. return VGAColor::Blue;
  198. case ANSIColor::Magenta:
  199. return VGAColor::Magenta;
  200. case ANSIColor::Green:
  201. return VGAColor::Green;
  202. case ANSIColor::Cyan:
  203. return VGAColor::Cyan;
  204. case ANSIColor::LightGray:
  205. return VGAColor::LightGray;
  206. case ANSIColor::DarkGray:
  207. return VGAColor::DarkGray;
  208. case ANSIColor::BrightRed:
  209. return VGAColor::BrightRed;
  210. case ANSIColor::BrightGreen:
  211. return VGAColor::BrightGreen;
  212. case ANSIColor::Yellow:
  213. return VGAColor::Yellow;
  214. case ANSIColor::BrightBlue:
  215. return VGAColor::BrightBlue;
  216. case ANSIColor::BrightMagenta:
  217. return VGAColor::BrightMagenta;
  218. case ANSIColor::BrightCyan:
  219. return VGAColor::BrightCyan;
  220. case ANSIColor::White:
  221. return VGAColor::White;
  222. }
  223. ASSERT_NOT_REACHED();
  224. return VGAColor::LightGray;
  225. }
  226. static inline u8 ansi_color_to_vga(u8 color)
  227. {
  228. return (u8)ansi_color_to_vga((ANSIColor)color);
  229. }
  230. void VirtualConsole::escape$m(const Vector<unsigned>& params)
  231. {
  232. for (auto param : params) {
  233. switch (param) {
  234. case 0:
  235. // Reset
  236. m_current_attribute = 0x07;
  237. break;
  238. case 1:
  239. // Bold
  240. m_current_attribute |= 8;
  241. break;
  242. case 30:
  243. case 31:
  244. case 32:
  245. case 33:
  246. case 34:
  247. case 35:
  248. case 36:
  249. case 37:
  250. // Foreground color
  251. m_current_attribute &= ~0x7;
  252. m_current_attribute |= ansi_color_to_vga(param - 30);
  253. break;
  254. case 40:
  255. case 41:
  256. case 42:
  257. case 43:
  258. case 44:
  259. case 45:
  260. case 46:
  261. case 47:
  262. // Background color
  263. m_current_attribute &= ~0x70;
  264. m_current_attribute |= ansi_color_to_vga(param - 30) << 8;
  265. break;
  266. }
  267. }
  268. }
  269. void VirtualConsole::escape$s(const Vector<unsigned>&)
  270. {
  271. m_saved_cursor_row = m_cursor_row;
  272. m_saved_cursor_column = m_cursor_column;
  273. }
  274. void VirtualConsole::escape$u(const Vector<unsigned>&)
  275. {
  276. set_cursor(m_saved_cursor_row, m_saved_cursor_column);
  277. }
  278. void VirtualConsole::escape$H(const Vector<unsigned>& params)
  279. {
  280. unsigned row = 1;
  281. unsigned col = 1;
  282. if (params.size() >= 1)
  283. row = params[0];
  284. if (params.size() >= 2)
  285. col = params[1];
  286. set_cursor(row - 1, col - 1);
  287. }
  288. void VirtualConsole::escape$A(const Vector<unsigned>& params)
  289. {
  290. int num = 1;
  291. if (params.size() >= 1)
  292. num = params[0];
  293. int new_row = (int)m_cursor_row - num;
  294. if (new_row < 0)
  295. new_row = 0;
  296. set_cursor(new_row, m_cursor_column);
  297. }
  298. void VirtualConsole::escape$D(const Vector<unsigned>& params)
  299. {
  300. int num = 1;
  301. if (params.size() >= 1)
  302. num = params[0];
  303. int new_column = (int)m_cursor_column - num;
  304. if (new_column < 0)
  305. new_column = 0;
  306. set_cursor(m_cursor_row, new_column);
  307. }
  308. void VirtualConsole::escape$J(const Vector<unsigned>& params)
  309. {
  310. int mode = 0;
  311. if (params.size() >= 1)
  312. mode = params[0];
  313. switch (mode) {
  314. case 0:
  315. // FIXME: Clear from cursor to end of screen.
  316. ASSERT_NOT_REACHED();
  317. break;
  318. case 1:
  319. // FIXME: Clear from cursor to beginning of screen.
  320. ASSERT_NOT_REACHED();
  321. break;
  322. case 2:
  323. clear();
  324. break;
  325. case 3:
  326. // FIXME: <esc>[3J should also clear the scrollback buffer.
  327. clear();
  328. break;
  329. }
  330. }
  331. void VirtualConsole::execute_escape_sequence(u8 final)
  332. {
  333. auto paramparts = String::copy(m_parameters).split(';');
  334. Vector<unsigned> params;
  335. for (auto& parampart : paramparts) {
  336. bool ok;
  337. unsigned value = parampart.to_uint(ok);
  338. if (!ok) {
  339. // FIXME: Should we do something else?
  340. return;
  341. }
  342. params.append(value);
  343. }
  344. switch (final) {
  345. case 'A':
  346. escape$A(params);
  347. break;
  348. case 'D':
  349. escape$D(params);
  350. break;
  351. case 'H':
  352. escape$H(params);
  353. break;
  354. case 'J':
  355. escape$J(params);
  356. break;
  357. case 'm':
  358. escape$m(params);
  359. break;
  360. case 's':
  361. escape$s(params);
  362. break;
  363. case 'u':
  364. escape$u(params);
  365. break;
  366. default:
  367. break;
  368. }
  369. m_parameters.clear();
  370. m_intermediates.clear();
  371. }
  372. void VirtualConsole::clear_vga_row(u16 row)
  373. {
  374. u16* linemem = (u16*)&m_current_vga_window[row * 160];
  375. for (u16 i = 0; i < columns(); ++i)
  376. linemem[i] = 0x0720;
  377. }
  378. void VirtualConsole::scroll_up()
  379. {
  380. if (m_cursor_row == (rows() - 1)) {
  381. if (m_active) {
  382. if (m_vga_start_row >= 160) {
  383. memcpy(s_vga_buffer, m_current_vga_window + 160, (rows() - 1) * columns() * 2);
  384. set_vga_start_row(0);
  385. clear_vga_row(24);
  386. } else {
  387. set_vga_start_row(m_vga_start_row + 1);
  388. clear_vga_row(24);
  389. }
  390. } else {
  391. memmove(m_buffer, m_buffer + 160, 160 * 24);
  392. u16* linemem = (u16*)&m_buffer[24 * 160];
  393. for (u16 i = 0; i < columns(); ++i)
  394. linemem[i] = 0x0720;
  395. }
  396. } else {
  397. ++m_cursor_row;
  398. }
  399. m_cursor_column = 0;
  400. }
  401. void VirtualConsole::set_cursor(unsigned row, unsigned column)
  402. {
  403. ASSERT(row < rows());
  404. ASSERT(column < columns());
  405. m_cursor_row = row;
  406. m_cursor_column = column;
  407. if (m_active)
  408. flush_vga_cursor();
  409. }
  410. void VirtualConsole::put_character_at(unsigned row, unsigned column, u8 ch)
  411. {
  412. ASSERT(row < rows());
  413. ASSERT(column < columns());
  414. u16 cur = (row * 160) + (column * 2);
  415. if (m_active) {
  416. u16 cur = (row * 160) + (column * 2);
  417. m_current_vga_window[cur] = ch;
  418. m_current_vga_window[cur + 1] = m_current_attribute;
  419. } else {
  420. m_buffer[cur] = ch;
  421. m_buffer[cur + 1] = m_current_attribute;
  422. }
  423. }
  424. void VirtualConsole::on_char(u8 ch)
  425. {
  426. // ignore writes in graphical mode
  427. if (m_graphical)
  428. return;
  429. switch (m_escape_state) {
  430. case ExpectBracket:
  431. if (ch == '[')
  432. m_escape_state = ExpectParameter;
  433. else
  434. m_escape_state = Normal;
  435. return;
  436. case ExpectParameter:
  437. if (is_valid_parameter_character(ch)) {
  438. m_parameters.append(ch);
  439. return;
  440. }
  441. m_escape_state = ExpectIntermediate;
  442. [[fallthrough]];
  443. case ExpectIntermediate:
  444. if (is_valid_intermediate_character(ch)) {
  445. m_intermediates.append(ch);
  446. return;
  447. }
  448. m_escape_state = ExpectFinal;
  449. [[fallthrough]];
  450. case ExpectFinal:
  451. if (is_valid_final_character(ch)) {
  452. m_escape_state = Normal;
  453. execute_escape_sequence(ch);
  454. return;
  455. }
  456. m_escape_state = Normal;
  457. return;
  458. case Normal:
  459. break;
  460. }
  461. switch (ch) {
  462. case '\0':
  463. return;
  464. case '\033':
  465. m_escape_state = ExpectBracket;
  466. return;
  467. case 8: // Backspace
  468. if (m_cursor_column) {
  469. set_cursor(m_cursor_row, m_cursor_column - 1);
  470. put_character_at(m_cursor_row, m_cursor_column, ' ');
  471. return;
  472. }
  473. break;
  474. case '\a':
  475. // FIXME: Bell!
  476. return;
  477. case '\t': {
  478. for (unsigned i = m_cursor_column; i < columns(); ++i) {
  479. if (m_horizontal_tabs[i]) {
  480. set_cursor(m_cursor_row, i);
  481. return;
  482. }
  483. }
  484. return;
  485. }
  486. case '\n':
  487. scroll_up();
  488. set_cursor(m_cursor_row, m_cursor_column);
  489. return;
  490. }
  491. put_character_at(m_cursor_row, m_cursor_column, ch);
  492. ++m_cursor_column;
  493. if (m_cursor_column >= columns())
  494. scroll_up();
  495. set_cursor(m_cursor_row, m_cursor_column);
  496. }
  497. void VirtualConsole::on_key_pressed(KeyboardDevice::Event key)
  498. {
  499. // ignore keyboard in graphical mode
  500. if (m_graphical)
  501. return;
  502. if (!key.is_press())
  503. return;
  504. if (key.ctrl()) {
  505. if (key.character >= 'a' && key.character <= 'z') {
  506. emit(key.character - 'a' + 1);
  507. return;
  508. } else if (key.character == '\\') {
  509. emit(0x1c);
  510. return;
  511. }
  512. }
  513. emit(key.character);
  514. }
  515. void VirtualConsole::on_sysconsole_receive(u8 ch)
  516. {
  517. InterruptDisabler disabler;
  518. auto old_attribute = m_current_attribute;
  519. m_current_attribute = 0x03;
  520. on_char(ch);
  521. m_current_attribute = old_attribute;
  522. }
  523. ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size)
  524. {
  525. InterruptDisabler disabler;
  526. for (ssize_t i = 0; i < size; ++i)
  527. on_char(data[i]);
  528. return size;
  529. }
  530. StringView VirtualConsole::tty_name() const
  531. {
  532. return m_tty_name;
  533. }
  534. void VirtualConsole::echo(u8 ch)
  535. {
  536. if (should_echo_input()) {
  537. on_tty_write(&ch, 1);
  538. }
  539. }
  540. void VirtualConsole::set_vga_start_row(u16 row)
  541. {
  542. m_vga_start_row = row;
  543. m_current_vga_start_address = row * columns();
  544. m_current_vga_window = s_vga_buffer + row * 160;
  545. IO::out8(0x3d4, 0x0c);
  546. IO::out8(0x3d5, MSB(m_current_vga_start_address));
  547. IO::out8(0x3d4, 0x0d);
  548. IO::out8(0x3d5, LSB(m_current_vga_start_address));
  549. }
  550. }