VirtualConsole.cpp 13 KB

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