2
0

console-vc.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. /*
  2. * SPDX-License-Identifier: MIT
  3. * QEMU VC
  4. */
  5. #include "qemu/osdep.h"
  6. #include "chardev/char.h"
  7. #include "qapi/error.h"
  8. #include "qemu/fifo8.h"
  9. #include "qemu/option.h"
  10. #include "ui/console.h"
  11. #include "trace.h"
  12. #include "console-priv.h"
  13. #define DEFAULT_BACKSCROLL 512
  14. #define CONSOLE_CURSOR_PERIOD 500
  15. typedef struct TextAttributes {
  16. uint8_t fgcol:4;
  17. uint8_t bgcol:4;
  18. uint8_t bold:1;
  19. uint8_t uline:1;
  20. uint8_t blink:1;
  21. uint8_t invers:1;
  22. uint8_t unvisible:1;
  23. } TextAttributes;
  24. #define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
  25. .fgcol = QEMU_COLOR_WHITE, \
  26. .bgcol = QEMU_COLOR_BLACK \
  27. })
  28. typedef struct TextCell {
  29. uint8_t ch;
  30. TextAttributes t_attrib;
  31. } TextCell;
  32. #define MAX_ESC_PARAMS 3
  33. enum TTYState {
  34. TTY_STATE_NORM,
  35. TTY_STATE_ESC,
  36. TTY_STATE_CSI,
  37. };
  38. typedef struct QemuTextConsole {
  39. QemuConsole parent;
  40. int width;
  41. int height;
  42. int total_height;
  43. int backscroll_height;
  44. int x, y;
  45. int y_displayed;
  46. int y_base;
  47. TextCell *cells;
  48. int text_x[2], text_y[2], cursor_invalidate;
  49. int echo;
  50. int update_x0;
  51. int update_y0;
  52. int update_x1;
  53. int update_y1;
  54. Chardev *chr;
  55. /* fifo for key pressed */
  56. Fifo8 out_fifo;
  57. } QemuTextConsole;
  58. typedef QemuConsoleClass QemuTextConsoleClass;
  59. OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
  60. typedef struct QemuFixedTextConsole {
  61. QemuTextConsole parent;
  62. } QemuFixedTextConsole;
  63. typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
  64. OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
  65. struct VCChardev {
  66. Chardev parent;
  67. QemuTextConsole *console;
  68. enum TTYState state;
  69. int esc_params[MAX_ESC_PARAMS];
  70. int nb_esc_params;
  71. TextAttributes t_attrib; /* currently active text attributes */
  72. int x_saved, y_saved;
  73. };
  74. typedef struct VCChardev VCChardev;
  75. static const pixman_color_t color_table_rgb[2][8] = {
  76. { /* dark */
  77. [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
  78. [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
  79. [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
  80. [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
  81. [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
  82. [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
  83. [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
  84. [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY,
  85. },
  86. { /* bright */
  87. [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
  88. [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
  89. [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
  90. [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
  91. [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
  92. [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
  93. [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
  94. [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
  95. }
  96. };
  97. static bool cursor_visible_phase;
  98. static QEMUTimer *cursor_timer;
  99. const char *
  100. qemu_text_console_get_label(QemuTextConsole *c)
  101. {
  102. return c->chr ? c->chr->label : NULL;
  103. }
  104. static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy,
  105. int width, int height, pixman_color_t color)
  106. {
  107. DisplaySurface *surface = qemu_console_surface(con);
  108. pixman_rectangle16_t rect = {
  109. .x = posx, .y = posy, .width = width, .height = height
  110. };
  111. assert(surface);
  112. pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
  113. &color, 1, &rect);
  114. }
  115. /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
  116. static void qemu_console_bitblt(QemuConsole *con,
  117. int xs, int ys, int xd, int yd, int w, int h)
  118. {
  119. DisplaySurface *surface = qemu_console_surface(con);
  120. assert(surface);
  121. pixman_image_composite(PIXMAN_OP_SRC,
  122. surface->image, NULL, surface->image,
  123. xs, ys, 0, 0, xd, yd, w, h);
  124. }
  125. static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
  126. TextAttributes *t_attrib)
  127. {
  128. static pixman_image_t *glyphs[256];
  129. DisplaySurface *surface = qemu_console_surface(s);
  130. pixman_color_t fgcol, bgcol;
  131. assert(surface);
  132. if (t_attrib->invers) {
  133. bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
  134. fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
  135. } else {
  136. fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
  137. bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
  138. }
  139. if (!glyphs[ch]) {
  140. glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
  141. }
  142. qemu_pixman_glyph_render(glyphs[ch], surface->image,
  143. &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
  144. }
  145. static void invalidate_xy(QemuTextConsole *s, int x, int y)
  146. {
  147. if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
  148. return;
  149. }
  150. if (s->update_x0 > x * FONT_WIDTH)
  151. s->update_x0 = x * FONT_WIDTH;
  152. if (s->update_y0 > y * FONT_HEIGHT)
  153. s->update_y0 = y * FONT_HEIGHT;
  154. if (s->update_x1 < (x + 1) * FONT_WIDTH)
  155. s->update_x1 = (x + 1) * FONT_WIDTH;
  156. if (s->update_y1 < (y + 1) * FONT_HEIGHT)
  157. s->update_y1 = (y + 1) * FONT_HEIGHT;
  158. }
  159. static void console_show_cursor(QemuTextConsole *s, int show)
  160. {
  161. TextCell *c;
  162. int y, y1;
  163. int x = s->x;
  164. s->cursor_invalidate = 1;
  165. if (x >= s->width) {
  166. x = s->width - 1;
  167. }
  168. y1 = (s->y_base + s->y) % s->total_height;
  169. y = y1 - s->y_displayed;
  170. if (y < 0) {
  171. y += s->total_height;
  172. }
  173. if (y < s->height) {
  174. c = &s->cells[y1 * s->width + x];
  175. if (show && cursor_visible_phase) {
  176. TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  177. t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
  178. vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib);
  179. } else {
  180. vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib));
  181. }
  182. invalidate_xy(s, x, y);
  183. }
  184. }
  185. static void console_refresh(QemuTextConsole *s)
  186. {
  187. DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s));
  188. TextCell *c;
  189. int x, y, y1;
  190. assert(surface);
  191. s->text_x[0] = 0;
  192. s->text_y[0] = 0;
  193. s->text_x[1] = s->width - 1;
  194. s->text_y[1] = s->height - 1;
  195. s->cursor_invalidate = 1;
  196. qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
  197. color_table_rgb[0][QEMU_COLOR_BLACK]);
  198. y1 = s->y_displayed;
  199. for (y = 0; y < s->height; y++) {
  200. c = s->cells + y1 * s->width;
  201. for (x = 0; x < s->width; x++) {
  202. vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
  203. &(c->t_attrib));
  204. c++;
  205. }
  206. if (++y1 == s->total_height) {
  207. y1 = 0;
  208. }
  209. }
  210. console_show_cursor(s, 1);
  211. dpy_gfx_update(QEMU_CONSOLE(s), 0, 0,
  212. surface_width(surface), surface_height(surface));
  213. }
  214. static void console_scroll(QemuTextConsole *s, int ydelta)
  215. {
  216. int i, y1;
  217. if (ydelta > 0) {
  218. for(i = 0; i < ydelta; i++) {
  219. if (s->y_displayed == s->y_base)
  220. break;
  221. if (++s->y_displayed == s->total_height)
  222. s->y_displayed = 0;
  223. }
  224. } else {
  225. ydelta = -ydelta;
  226. i = s->backscroll_height;
  227. if (i > s->total_height - s->height)
  228. i = s->total_height - s->height;
  229. y1 = s->y_base - i;
  230. if (y1 < 0)
  231. y1 += s->total_height;
  232. for(i = 0; i < ydelta; i++) {
  233. if (s->y_displayed == y1)
  234. break;
  235. if (--s->y_displayed < 0)
  236. s->y_displayed = s->total_height - 1;
  237. }
  238. }
  239. console_refresh(s);
  240. }
  241. static void kbd_send_chars(QemuTextConsole *s)
  242. {
  243. uint32_t len, avail;
  244. len = qemu_chr_be_can_write(s->chr);
  245. avail = fifo8_num_used(&s->out_fifo);
  246. while (len > 0 && avail > 0) {
  247. const uint8_t *buf;
  248. uint32_t size;
  249. buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
  250. qemu_chr_be_write(s->chr, buf, size);
  251. len = qemu_chr_be_can_write(s->chr);
  252. avail -= size;
  253. }
  254. }
  255. /* called when an ascii key is pressed */
  256. void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
  257. {
  258. uint8_t buf[16], *q;
  259. int c;
  260. uint32_t num_free;
  261. switch(keysym) {
  262. case QEMU_KEY_CTRL_UP:
  263. console_scroll(s, -1);
  264. break;
  265. case QEMU_KEY_CTRL_DOWN:
  266. console_scroll(s, 1);
  267. break;
  268. case QEMU_KEY_CTRL_PAGEUP:
  269. console_scroll(s, -10);
  270. break;
  271. case QEMU_KEY_CTRL_PAGEDOWN:
  272. console_scroll(s, 10);
  273. break;
  274. default:
  275. /* convert the QEMU keysym to VT100 key string */
  276. q = buf;
  277. if (keysym >= 0xe100 && keysym <= 0xe11f) {
  278. *q++ = '\033';
  279. *q++ = '[';
  280. c = keysym - 0xe100;
  281. if (c >= 10)
  282. *q++ = '0' + (c / 10);
  283. *q++ = '0' + (c % 10);
  284. *q++ = '~';
  285. } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
  286. *q++ = '\033';
  287. *q++ = '[';
  288. *q++ = keysym & 0xff;
  289. } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
  290. qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
  291. *q++ = '\n';
  292. } else {
  293. *q++ = keysym;
  294. }
  295. if (s->echo) {
  296. qemu_chr_write(s->chr, buf, q - buf, true);
  297. }
  298. num_free = fifo8_num_free(&s->out_fifo);
  299. fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
  300. kbd_send_chars(s);
  301. break;
  302. }
  303. }
  304. static void text_console_update(void *opaque, console_ch_t *chardata)
  305. {
  306. QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
  307. int i, j, src;
  308. if (s->text_x[0] <= s->text_x[1]) {
  309. src = (s->y_base + s->text_y[0]) * s->width;
  310. chardata += s->text_y[0] * s->width;
  311. for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
  312. for (j = 0; j < s->width; j++, src++) {
  313. console_write_ch(chardata ++,
  314. ATTR2CHTYPE(s->cells[src].ch,
  315. s->cells[src].t_attrib.fgcol,
  316. s->cells[src].t_attrib.bgcol,
  317. s->cells[src].t_attrib.bold));
  318. }
  319. dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
  320. s->text_x[1] - s->text_x[0], i - s->text_y[0]);
  321. s->text_x[0] = s->width;
  322. s->text_y[0] = s->height;
  323. s->text_x[1] = 0;
  324. s->text_y[1] = 0;
  325. }
  326. if (s->cursor_invalidate) {
  327. dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
  328. s->cursor_invalidate = 0;
  329. }
  330. }
  331. static void text_console_resize(QemuTextConsole *t)
  332. {
  333. QemuConsole *s = QEMU_CONSOLE(t);
  334. TextCell *cells, *c, *c1;
  335. int w1, x, y, last_width, w, h;
  336. assert(s->scanout.kind == SCANOUT_SURFACE);
  337. w = surface_width(s->surface) / FONT_WIDTH;
  338. h = surface_height(s->surface) / FONT_HEIGHT;
  339. if (w == t->width && h == t->height) {
  340. return;
  341. }
  342. last_width = t->width;
  343. t->width = w;
  344. t->height = h;
  345. w1 = MIN(t->width, last_width);
  346. cells = g_new(TextCell, t->width * t->total_height + 1);
  347. for (y = 0; y < t->total_height; y++) {
  348. c = &cells[y * t->width];
  349. if (w1 > 0) {
  350. c1 = &t->cells[y * last_width];
  351. for (x = 0; x < w1; x++) {
  352. *c++ = *c1++;
  353. }
  354. }
  355. for (x = w1; x < t->width; x++) {
  356. c->ch = ' ';
  357. c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  358. c++;
  359. }
  360. }
  361. g_free(t->cells);
  362. t->cells = cells;
  363. }
  364. static void vc_put_lf(VCChardev *vc)
  365. {
  366. QemuTextConsole *s = vc->console;
  367. TextCell *c;
  368. int x, y1;
  369. s->y++;
  370. if (s->y >= s->height) {
  371. s->y = s->height - 1;
  372. if (s->y_displayed == s->y_base) {
  373. if (++s->y_displayed == s->total_height)
  374. s->y_displayed = 0;
  375. }
  376. if (++s->y_base == s->total_height)
  377. s->y_base = 0;
  378. if (s->backscroll_height < s->total_height)
  379. s->backscroll_height++;
  380. y1 = (s->y_base + s->height - 1) % s->total_height;
  381. c = &s->cells[y1 * s->width];
  382. for(x = 0; x < s->width; x++) {
  383. c->ch = ' ';
  384. c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  385. c++;
  386. }
  387. if (s->y_displayed == s->y_base) {
  388. s->text_x[0] = 0;
  389. s->text_y[0] = 0;
  390. s->text_x[1] = s->width - 1;
  391. s->text_y[1] = s->height - 1;
  392. qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
  393. s->width * FONT_WIDTH,
  394. (s->height - 1) * FONT_HEIGHT);
  395. qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT,
  396. s->width * FONT_WIDTH, FONT_HEIGHT,
  397. color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
  398. s->update_x0 = 0;
  399. s->update_y0 = 0;
  400. s->update_x1 = s->width * FONT_WIDTH;
  401. s->update_y1 = s->height * FONT_HEIGHT;
  402. }
  403. }
  404. }
  405. /* Set console attributes depending on the current escape codes.
  406. * NOTE: I know this code is not very efficient (checking every color for it
  407. * self) but it is more readable and better maintainable.
  408. */
  409. static void vc_handle_escape(VCChardev *vc)
  410. {
  411. int i;
  412. for (i = 0; i < vc->nb_esc_params; i++) {
  413. switch (vc->esc_params[i]) {
  414. case 0: /* reset all console attributes to default */
  415. vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  416. break;
  417. case 1:
  418. vc->t_attrib.bold = 1;
  419. break;
  420. case 4:
  421. vc->t_attrib.uline = 1;
  422. break;
  423. case 5:
  424. vc->t_attrib.blink = 1;
  425. break;
  426. case 7:
  427. vc->t_attrib.invers = 1;
  428. break;
  429. case 8:
  430. vc->t_attrib.unvisible = 1;
  431. break;
  432. case 22:
  433. vc->t_attrib.bold = 0;
  434. break;
  435. case 24:
  436. vc->t_attrib.uline = 0;
  437. break;
  438. case 25:
  439. vc->t_attrib.blink = 0;
  440. break;
  441. case 27:
  442. vc->t_attrib.invers = 0;
  443. break;
  444. case 28:
  445. vc->t_attrib.unvisible = 0;
  446. break;
  447. /* set foreground color */
  448. case 30:
  449. vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
  450. break;
  451. case 31:
  452. vc->t_attrib.fgcol = QEMU_COLOR_RED;
  453. break;
  454. case 32:
  455. vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
  456. break;
  457. case 33:
  458. vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
  459. break;
  460. case 34:
  461. vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
  462. break;
  463. case 35:
  464. vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
  465. break;
  466. case 36:
  467. vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
  468. break;
  469. case 37:
  470. vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
  471. break;
  472. /* set background color */
  473. case 40:
  474. vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
  475. break;
  476. case 41:
  477. vc->t_attrib.bgcol = QEMU_COLOR_RED;
  478. break;
  479. case 42:
  480. vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
  481. break;
  482. case 43:
  483. vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
  484. break;
  485. case 44:
  486. vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
  487. break;
  488. case 45:
  489. vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
  490. break;
  491. case 46:
  492. vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
  493. break;
  494. case 47:
  495. vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
  496. break;
  497. }
  498. }
  499. }
  500. static void vc_update_xy(VCChardev *vc, int x, int y)
  501. {
  502. QemuTextConsole *s = vc->console;
  503. TextCell *c;
  504. int y1, y2;
  505. s->text_x[0] = MIN(s->text_x[0], x);
  506. s->text_x[1] = MAX(s->text_x[1], x);
  507. s->text_y[0] = MIN(s->text_y[0], y);
  508. s->text_y[1] = MAX(s->text_y[1], y);
  509. y1 = (s->y_base + y) % s->total_height;
  510. y2 = y1 - s->y_displayed;
  511. if (y2 < 0) {
  512. y2 += s->total_height;
  513. }
  514. if (y2 < s->height) {
  515. if (x >= s->width) {
  516. x = s->width - 1;
  517. }
  518. c = &s->cells[y1 * s->width + x];
  519. vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
  520. &(c->t_attrib));
  521. invalidate_xy(s, x, y2);
  522. }
  523. }
  524. static void vc_clear_xy(VCChardev *vc, int x, int y)
  525. {
  526. QemuTextConsole *s = vc->console;
  527. int y1 = (s->y_base + y) % s->total_height;
  528. if (x >= s->width) {
  529. x = s->width - 1;
  530. }
  531. TextCell *c = &s->cells[y1 * s->width + x];
  532. c->ch = ' ';
  533. c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  534. vc_update_xy(vc, x, y);
  535. }
  536. static void vc_put_one(VCChardev *vc, int ch)
  537. {
  538. QemuTextConsole *s = vc->console;
  539. TextCell *c;
  540. int y1;
  541. if (s->x >= s->width) {
  542. /* line wrap */
  543. s->x = 0;
  544. vc_put_lf(vc);
  545. }
  546. y1 = (s->y_base + s->y) % s->total_height;
  547. c = &s->cells[y1 * s->width + s->x];
  548. c->ch = ch;
  549. c->t_attrib = vc->t_attrib;
  550. vc_update_xy(vc, s->x, s->y);
  551. s->x++;
  552. }
  553. static void vc_respond_str(VCChardev *vc, const char *buf)
  554. {
  555. while (*buf) {
  556. vc_put_one(vc, *buf);
  557. buf++;
  558. }
  559. }
  560. /* set cursor, checking bounds */
  561. static void vc_set_cursor(VCChardev *vc, int x, int y)
  562. {
  563. QemuTextConsole *s = vc->console;
  564. if (x < 0) {
  565. x = 0;
  566. }
  567. if (y < 0) {
  568. y = 0;
  569. }
  570. if (y >= s->height) {
  571. y = s->height - 1;
  572. }
  573. if (x >= s->width) {
  574. x = s->width - 1;
  575. }
  576. s->x = x;
  577. s->y = y;
  578. }
  579. static void vc_putchar(VCChardev *vc, int ch)
  580. {
  581. QemuTextConsole *s = vc->console;
  582. int i;
  583. int x, y;
  584. char response[40];
  585. switch(vc->state) {
  586. case TTY_STATE_NORM:
  587. switch(ch) {
  588. case '\r': /* carriage return */
  589. s->x = 0;
  590. break;
  591. case '\n': /* newline */
  592. vc_put_lf(vc);
  593. break;
  594. case '\b': /* backspace */
  595. if (s->x > 0)
  596. s->x--;
  597. break;
  598. case '\t': /* tabspace */
  599. if (s->x + (8 - (s->x % 8)) > s->width) {
  600. s->x = 0;
  601. vc_put_lf(vc);
  602. } else {
  603. s->x = s->x + (8 - (s->x % 8));
  604. }
  605. break;
  606. case '\a': /* alert aka. bell */
  607. /* TODO: has to be implemented */
  608. break;
  609. case 14:
  610. /* SI (shift in), character set 0 (ignored) */
  611. break;
  612. case 15:
  613. /* SO (shift out), character set 1 (ignored) */
  614. break;
  615. case 27: /* esc (introducing an escape sequence) */
  616. vc->state = TTY_STATE_ESC;
  617. break;
  618. default:
  619. vc_put_one(vc, ch);
  620. break;
  621. }
  622. break;
  623. case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
  624. if (ch == '[') {
  625. for(i=0;i<MAX_ESC_PARAMS;i++)
  626. vc->esc_params[i] = 0;
  627. vc->nb_esc_params = 0;
  628. vc->state = TTY_STATE_CSI;
  629. } else {
  630. vc->state = TTY_STATE_NORM;
  631. }
  632. break;
  633. case TTY_STATE_CSI: /* handle escape sequence parameters */
  634. if (ch >= '0' && ch <= '9') {
  635. if (vc->nb_esc_params < MAX_ESC_PARAMS) {
  636. int *param = &vc->esc_params[vc->nb_esc_params];
  637. int digit = (ch - '0');
  638. *param = (*param <= (INT_MAX - digit) / 10) ?
  639. *param * 10 + digit : INT_MAX;
  640. }
  641. } else {
  642. if (vc->nb_esc_params < MAX_ESC_PARAMS)
  643. vc->nb_esc_params++;
  644. if (ch == ';' || ch == '?') {
  645. break;
  646. }
  647. trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
  648. ch, vc->nb_esc_params);
  649. vc->state = TTY_STATE_NORM;
  650. switch(ch) {
  651. case 'A':
  652. /* move cursor up */
  653. if (vc->esc_params[0] == 0) {
  654. vc->esc_params[0] = 1;
  655. }
  656. vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
  657. break;
  658. case 'B':
  659. /* move cursor down */
  660. if (vc->esc_params[0] == 0) {
  661. vc->esc_params[0] = 1;
  662. }
  663. vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
  664. break;
  665. case 'C':
  666. /* move cursor right */
  667. if (vc->esc_params[0] == 0) {
  668. vc->esc_params[0] = 1;
  669. }
  670. vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
  671. break;
  672. case 'D':
  673. /* move cursor left */
  674. if (vc->esc_params[0] == 0) {
  675. vc->esc_params[0] = 1;
  676. }
  677. vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
  678. break;
  679. case 'G':
  680. /* move cursor to column */
  681. vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
  682. break;
  683. case 'f':
  684. case 'H':
  685. /* move cursor to row, column */
  686. vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
  687. break;
  688. case 'J':
  689. switch (vc->esc_params[0]) {
  690. case 0:
  691. /* clear to end of screen */
  692. for (y = s->y; y < s->height; y++) {
  693. for (x = 0; x < s->width; x++) {
  694. if (y == s->y && x < s->x) {
  695. continue;
  696. }
  697. vc_clear_xy(vc, x, y);
  698. }
  699. }
  700. break;
  701. case 1:
  702. /* clear from beginning of screen */
  703. for (y = 0; y <= s->y; y++) {
  704. for (x = 0; x < s->width; x++) {
  705. if (y == s->y && x > s->x) {
  706. break;
  707. }
  708. vc_clear_xy(vc, x, y);
  709. }
  710. }
  711. break;
  712. case 2:
  713. /* clear entire screen */
  714. for (y = 0; y <= s->height; y++) {
  715. for (x = 0; x < s->width; x++) {
  716. vc_clear_xy(vc, x, y);
  717. }
  718. }
  719. break;
  720. }
  721. break;
  722. case 'K':
  723. switch (vc->esc_params[0]) {
  724. case 0:
  725. /* clear to eol */
  726. for(x = s->x; x < s->width; x++) {
  727. vc_clear_xy(vc, x, s->y);
  728. }
  729. break;
  730. case 1:
  731. /* clear from beginning of line */
  732. for (x = 0; x <= s->x && x < s->width; x++) {
  733. vc_clear_xy(vc, x, s->y);
  734. }
  735. break;
  736. case 2:
  737. /* clear entire line */
  738. for(x = 0; x < s->width; x++) {
  739. vc_clear_xy(vc, x, s->y);
  740. }
  741. break;
  742. }
  743. break;
  744. case 'm':
  745. vc_handle_escape(vc);
  746. break;
  747. case 'n':
  748. switch (vc->esc_params[0]) {
  749. case 5:
  750. /* report console status (always succeed)*/
  751. vc_respond_str(vc, "\033[0n");
  752. break;
  753. case 6:
  754. /* report cursor position */
  755. sprintf(response, "\033[%d;%dR",
  756. (s->y_base + s->y) % s->total_height + 1,
  757. s->x + 1);
  758. vc_respond_str(vc, response);
  759. break;
  760. }
  761. break;
  762. case 's':
  763. /* save cursor position */
  764. vc->x_saved = s->x;
  765. vc->y_saved = s->y;
  766. break;
  767. case 'u':
  768. /* restore cursor position */
  769. s->x = vc->x_saved;
  770. s->y = vc->y_saved;
  771. break;
  772. default:
  773. trace_console_putchar_unhandled(ch);
  774. break;
  775. }
  776. break;
  777. }
  778. }
  779. }
  780. #define TYPE_CHARDEV_VC "chardev-vc"
  781. DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
  782. TYPE_CHARDEV_VC)
  783. static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
  784. {
  785. VCChardev *drv = VC_CHARDEV(chr);
  786. QemuTextConsole *s = drv->console;
  787. int i;
  788. s->update_x0 = s->width * FONT_WIDTH;
  789. s->update_y0 = s->height * FONT_HEIGHT;
  790. s->update_x1 = 0;
  791. s->update_y1 = 0;
  792. console_show_cursor(s, 0);
  793. for(i = 0; i < len; i++) {
  794. vc_putchar(drv, buf[i]);
  795. }
  796. console_show_cursor(s, 1);
  797. if (s->update_x0 < s->update_x1) {
  798. dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0,
  799. s->update_x1 - s->update_x0,
  800. s->update_y1 - s->update_y0);
  801. }
  802. return len;
  803. }
  804. void qemu_text_console_update_cursor(void)
  805. {
  806. cursor_visible_phase = !cursor_visible_phase;
  807. if (qemu_invalidate_text_consoles()) {
  808. timer_mod(cursor_timer,
  809. qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
  810. }
  811. }
  812. static void
  813. cursor_timer_cb(void *opaque)
  814. {
  815. qemu_text_console_update_cursor();
  816. }
  817. static void text_console_invalidate(void *opaque)
  818. {
  819. QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
  820. if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
  821. text_console_resize(QEMU_TEXT_CONSOLE(s));
  822. }
  823. console_refresh(s);
  824. }
  825. static void
  826. qemu_text_console_finalize(Object *obj)
  827. {
  828. }
  829. static void
  830. qemu_text_console_class_init(ObjectClass *oc, void *data)
  831. {
  832. if (!cursor_timer) {
  833. cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
  834. }
  835. }
  836. static const GraphicHwOps text_console_ops = {
  837. .invalidate = text_console_invalidate,
  838. .text_update = text_console_update,
  839. };
  840. static void
  841. qemu_text_console_init(Object *obj)
  842. {
  843. QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
  844. fifo8_create(&c->out_fifo, 16);
  845. c->total_height = DEFAULT_BACKSCROLL;
  846. QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
  847. QEMU_CONSOLE(c)->hw = c;
  848. }
  849. static void
  850. qemu_fixed_text_console_finalize(Object *obj)
  851. {
  852. }
  853. static void
  854. qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
  855. {
  856. }
  857. static void
  858. qemu_fixed_text_console_init(Object *obj)
  859. {
  860. }
  861. static void vc_chr_accept_input(Chardev *chr)
  862. {
  863. VCChardev *drv = VC_CHARDEV(chr);
  864. kbd_send_chars(drv->console);
  865. }
  866. static void vc_chr_set_echo(Chardev *chr, bool echo)
  867. {
  868. VCChardev *drv = VC_CHARDEV(chr);
  869. drv->console->echo = echo;
  870. }
  871. void qemu_text_console_update_size(QemuTextConsole *c)
  872. {
  873. dpy_text_resize(QEMU_CONSOLE(c), c->width, c->height);
  874. }
  875. static void vc_chr_open(Chardev *chr,
  876. ChardevBackend *backend,
  877. bool *be_opened,
  878. Error **errp)
  879. {
  880. ChardevVC *vc = backend->u.vc.data;
  881. VCChardev *drv = VC_CHARDEV(chr);
  882. QemuTextConsole *s;
  883. unsigned width = 0;
  884. unsigned height = 0;
  885. if (vc->has_width) {
  886. width = vc->width;
  887. } else if (vc->has_cols) {
  888. width = vc->cols * FONT_WIDTH;
  889. }
  890. if (vc->has_height) {
  891. height = vc->height;
  892. } else if (vc->has_rows) {
  893. height = vc->rows * FONT_HEIGHT;
  894. }
  895. trace_console_txt_new(width, height);
  896. if (width == 0 || height == 0) {
  897. s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
  898. width = 80 * FONT_WIDTH;
  899. height = 24 * FONT_HEIGHT;
  900. } else {
  901. s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
  902. }
  903. dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
  904. s->chr = chr;
  905. drv->console = s;
  906. /* set current text attributes to default */
  907. drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  908. text_console_resize(s);
  909. if (chr->label) {
  910. char *msg;
  911. drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
  912. msg = g_strdup_printf("%s console\r\n", chr->label);
  913. qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
  914. g_free(msg);
  915. drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
  916. }
  917. *be_opened = true;
  918. }
  919. static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
  920. {
  921. int val;
  922. ChardevVC *vc;
  923. backend->type = CHARDEV_BACKEND_KIND_VC;
  924. vc = backend->u.vc.data = g_new0(ChardevVC, 1);
  925. qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
  926. val = qemu_opt_get_number(opts, "width", 0);
  927. if (val != 0) {
  928. vc->has_width = true;
  929. vc->width = val;
  930. }
  931. val = qemu_opt_get_number(opts, "height", 0);
  932. if (val != 0) {
  933. vc->has_height = true;
  934. vc->height = val;
  935. }
  936. val = qemu_opt_get_number(opts, "cols", 0);
  937. if (val != 0) {
  938. vc->has_cols = true;
  939. vc->cols = val;
  940. }
  941. val = qemu_opt_get_number(opts, "rows", 0);
  942. if (val != 0) {
  943. vc->has_rows = true;
  944. vc->rows = val;
  945. }
  946. }
  947. static void char_vc_class_init(ObjectClass *oc, void *data)
  948. {
  949. ChardevClass *cc = CHARDEV_CLASS(oc);
  950. cc->parse = vc_chr_parse;
  951. cc->open = vc_chr_open;
  952. cc->chr_write = vc_chr_write;
  953. cc->chr_accept_input = vc_chr_accept_input;
  954. cc->chr_set_echo = vc_chr_set_echo;
  955. }
  956. static const TypeInfo char_vc_type_info = {
  957. .name = TYPE_CHARDEV_VC,
  958. .parent = TYPE_CHARDEV,
  959. .instance_size = sizeof(VCChardev),
  960. .class_init = char_vc_class_init,
  961. };
  962. void qemu_console_early_init(void)
  963. {
  964. /* set the default vc driver */
  965. if (!object_class_by_name(TYPE_CHARDEV_VC)) {
  966. type_register(&char_vc_type_info);
  967. }
  968. }