gfxterm.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/term.h>
  19. #include <grub/types.h>
  20. #include <grub/dl.h>
  21. #include <grub/misc.h>
  22. #include <grub/font.h>
  23. #include <grub/mm.h>
  24. #include <grub/env.h>
  25. #include <grub/video.h>
  26. #include <grub/gfxterm.h>
  27. #include <grub/bitmap.h>
  28. #include <grub/command.h>
  29. #include <grub/extcmd.h>
  30. #include <grub/i18n.h>
  31. GRUB_MOD_LICENSE ("GPLv3+");
  32. #define DEFAULT_VIDEO_MODE "auto"
  33. #define DEFAULT_BORDER_WIDTH 10
  34. #define DEFAULT_STANDARD_COLOR 0x07
  35. struct grub_dirty_region
  36. {
  37. int top_left_x;
  38. int top_left_y;
  39. int bottom_right_x;
  40. int bottom_right_y;
  41. };
  42. struct grub_colored_char
  43. {
  44. /* An Unicode codepoint. */
  45. struct grub_unicode_glyph code;
  46. /* Color values. */
  47. grub_video_color_t fg_color;
  48. grub_video_color_t bg_color;
  49. };
  50. struct grub_virtual_screen
  51. {
  52. /* Dimensions of the virtual screen in pixels. */
  53. unsigned int width;
  54. unsigned int height;
  55. /* Offset in the display in pixels. */
  56. unsigned int offset_x;
  57. unsigned int offset_y;
  58. /* TTY Character sizes in pixes. */
  59. unsigned int normal_char_width;
  60. unsigned int normal_char_height;
  61. /* Virtual screen TTY size in characters. */
  62. unsigned int columns;
  63. unsigned int rows;
  64. /* Current cursor location in characters. */
  65. unsigned int cursor_x;
  66. unsigned int cursor_y;
  67. /* Current cursor state. */
  68. int cursor_state;
  69. /* Font settings. */
  70. grub_font_t font;
  71. /* Terminal color settings. */
  72. grub_uint8_t standard_color_setting;
  73. grub_uint8_t term_color;
  74. /* Color settings. */
  75. grub_video_color_t fg_color;
  76. grub_video_color_t bg_color;
  77. grub_video_color_t bg_color_display;
  78. /* Text buffer for virtual screen. Contains (columns * rows) number
  79. of entries. */
  80. struct grub_colored_char *text_buffer;
  81. int total_scroll;
  82. int functional;
  83. };
  84. struct grub_gfxterm_window
  85. {
  86. unsigned x;
  87. unsigned y;
  88. unsigned width;
  89. unsigned height;
  90. int double_repaint;
  91. };
  92. static struct grub_video_render_target *render_target;
  93. void (*grub_gfxterm_decorator_hook) (void) = NULL;
  94. static struct grub_gfxterm_window window;
  95. static struct grub_virtual_screen virtual_screen;
  96. static int repaint_scheduled = 0;
  97. static int repaint_was_scheduled = 0;
  98. static void destroy_window (void);
  99. static struct grub_video_render_target *text_layer;
  100. struct grub_gfxterm_background grub_gfxterm_background;
  101. static struct grub_dirty_region dirty_region;
  102. static void dirty_region_reset (void);
  103. static int dirty_region_is_empty (void);
  104. static void dirty_region_add (int x, int y,
  105. unsigned int width, unsigned int height);
  106. static unsigned int calculate_normal_character_width (grub_font_t font);
  107. static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
  108. static void grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)));
  109. static grub_size_t
  110. grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
  111. const struct grub_unicode_glyph *c);
  112. static void
  113. set_term_color (grub_uint8_t term_color)
  114. {
  115. struct grub_video_render_target *old_target;
  116. /* Save previous target and switch to text layer. */
  117. grub_video_get_active_render_target (&old_target);
  118. grub_video_set_active_render_target (text_layer);
  119. /* Map terminal color to text layer compatible video colors. */
  120. virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f);
  121. /* Special case: use black as transparent color. */
  122. if (((term_color >> 4) & 0x0f) == 0)
  123. {
  124. virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0);
  125. }
  126. else
  127. {
  128. virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f);
  129. }
  130. /* Restore previous target. */
  131. grub_video_set_active_render_target (old_target);
  132. }
  133. static void
  134. clear_char (struct grub_colored_char *c)
  135. {
  136. grub_unicode_destroy_glyph (&c->code);
  137. grub_unicode_set_glyph_from_code (&c->code, ' ');
  138. c->fg_color = virtual_screen.fg_color;
  139. c->bg_color = virtual_screen.bg_color;
  140. }
  141. static void
  142. grub_virtual_screen_free (void)
  143. {
  144. virtual_screen.functional = 0;
  145. /* If virtual screen has been allocated, free it. */
  146. if (virtual_screen.text_buffer != 0)
  147. {
  148. unsigned i;
  149. for (i = 0;
  150. i < virtual_screen.columns * virtual_screen.rows;
  151. i++)
  152. grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
  153. grub_free (virtual_screen.text_buffer);
  154. }
  155. /* Reset virtual screen data. */
  156. grub_memset (&virtual_screen, 0, sizeof (virtual_screen));
  157. /* Free render targets. */
  158. grub_video_delete_render_target (text_layer);
  159. text_layer = 0;
  160. }
  161. static grub_err_t
  162. grub_virtual_screen_setup (unsigned int x, unsigned int y,
  163. unsigned int width, unsigned int height,
  164. grub_font_t font)
  165. {
  166. unsigned int i;
  167. /* Free old virtual screen. */
  168. grub_virtual_screen_free ();
  169. /* Initialize with default data. */
  170. virtual_screen.font = font;
  171. virtual_screen.width = width;
  172. virtual_screen.height = height;
  173. virtual_screen.offset_x = x;
  174. virtual_screen.offset_y = y;
  175. virtual_screen.normal_char_width =
  176. calculate_normal_character_width (virtual_screen.font);
  177. virtual_screen.normal_char_height =
  178. grub_font_get_max_char_height (virtual_screen.font);
  179. if (virtual_screen.normal_char_height == 0)
  180. virtual_screen.normal_char_height = 16;
  181. virtual_screen.cursor_x = 0;
  182. virtual_screen.cursor_y = 0;
  183. virtual_screen.cursor_state = 1;
  184. virtual_screen.total_scroll = 0;
  185. /* Calculate size of text buffer. */
  186. virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width;
  187. virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height;
  188. /*
  189. * There must be a minimum number of rows and columns for the screen to
  190. * make sense. Arbitrarily pick half of 80x24. If either dimensions is 0
  191. * we would allocate 0 bytes for the text_buffer.
  192. */
  193. if (virtual_screen.columns < 40 || virtual_screen.rows < 12)
  194. return grub_error (GRUB_ERR_BAD_FONT,
  195. "font: glyphs too large to fit on screen");
  196. /* Allocate memory for text buffer. */
  197. virtual_screen.text_buffer =
  198. (struct grub_colored_char *) grub_malloc (virtual_screen.columns
  199. * virtual_screen.rows
  200. * sizeof (*virtual_screen.text_buffer));
  201. if (grub_errno != GRUB_ERR_NONE)
  202. return grub_errno;
  203. /* Create new render target for text layer. */
  204. grub_video_create_render_target (&text_layer,
  205. virtual_screen.width,
  206. virtual_screen.height,
  207. GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
  208. | GRUB_VIDEO_MODE_TYPE_ALPHA);
  209. if (grub_errno != GRUB_ERR_NONE)
  210. return grub_errno;
  211. /* As we want to have colors compatible with rendering target,
  212. we can only have those after mode is initialized. */
  213. grub_video_set_active_render_target (text_layer);
  214. virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR;
  215. virtual_screen.term_color = virtual_screen.standard_color_setting;
  216. set_term_color (virtual_screen.term_color);
  217. grub_video_set_active_render_target (render_target);
  218. virtual_screen.bg_color_display =
  219. grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
  220. /* Clear out text buffer. */
  221. for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
  222. {
  223. virtual_screen.text_buffer[i].code.ncomb = 0;
  224. clear_char (&(virtual_screen.text_buffer[i]));
  225. }
  226. if (grub_errno)
  227. return grub_errno;
  228. virtual_screen.functional = 1;
  229. return GRUB_ERR_NONE;
  230. }
  231. void
  232. grub_gfxterm_schedule_repaint (void)
  233. {
  234. repaint_scheduled = 1;
  235. }
  236. grub_err_t
  237. grub_gfxterm_set_window (struct grub_video_render_target *target,
  238. int x, int y, int width, int height,
  239. int double_repaint,
  240. grub_font_t font, int border_width)
  241. {
  242. /* Clean up any prior instance. */
  243. destroy_window ();
  244. /* Set the render target. */
  245. render_target = target;
  246. /* Create virtual screen. */
  247. if (grub_virtual_screen_setup (border_width, border_width,
  248. width - 2 * border_width,
  249. height - 2 * border_width,
  250. font)
  251. != GRUB_ERR_NONE)
  252. {
  253. return grub_errno;
  254. }
  255. /* Set window bounds. */
  256. window.x = x;
  257. window.y = y;
  258. window.width = width;
  259. window.height = height;
  260. window.double_repaint = double_repaint;
  261. dirty_region_reset ();
  262. grub_gfxterm_schedule_repaint ();
  263. return grub_errno;
  264. }
  265. static grub_err_t
  266. grub_gfxterm_fullscreen (void)
  267. {
  268. const char *font_name;
  269. struct grub_video_mode_info mode_info;
  270. grub_video_color_t color;
  271. grub_err_t err;
  272. int double_redraw;
  273. grub_font_t font;
  274. err = grub_video_get_info (&mode_info);
  275. /* Figure out what mode we ended up. */
  276. if (err)
  277. return err;
  278. grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
  279. double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
  280. && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
  281. /* Make sure screen is set to the default background color. */
  282. color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
  283. grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
  284. if (double_redraw)
  285. {
  286. grub_video_swap_buffers ();
  287. grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
  288. }
  289. /* Select the font to use. */
  290. font_name = grub_env_get ("gfxterm_font");
  291. if (! font_name)
  292. font_name = ""; /* Allow fallback to any font. */
  293. font = grub_font_get (font_name);
  294. if (!font)
  295. return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
  296. grub_gfxterm_decorator_hook = NULL;
  297. return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
  298. 0, 0, mode_info.width, mode_info.height,
  299. double_redraw,
  300. font, DEFAULT_BORDER_WIDTH);
  301. }
  302. static grub_err_t
  303. grub_gfxterm_term_init (struct grub_term_output *term __attribute__ ((unused)))
  304. {
  305. char *tmp;
  306. grub_err_t err;
  307. const char *modevar;
  308. /* Parse gfxmode environment variable if set. */
  309. modevar = grub_env_get ("gfxmode");
  310. if (! modevar || *modevar == 0)
  311. err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
  312. GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
  313. else
  314. {
  315. tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
  316. if (!tmp)
  317. return grub_errno;
  318. err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
  319. grub_free (tmp);
  320. }
  321. if (err)
  322. return err;
  323. err = grub_gfxterm_fullscreen ();
  324. if (err)
  325. grub_video_restore ();
  326. return err;
  327. }
  328. static void
  329. destroy_window (void)
  330. {
  331. grub_virtual_screen_free ();
  332. }
  333. static grub_err_t
  334. grub_gfxterm_term_fini (struct grub_term_output *term __attribute__ ((unused)))
  335. {
  336. unsigned i;
  337. destroy_window ();
  338. grub_video_restore ();
  339. for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
  340. {
  341. grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
  342. virtual_screen.text_buffer[i].code.ncomb = 0;
  343. virtual_screen.text_buffer[i].code.base = 0;
  344. }
  345. /* Clear error state. */
  346. grub_errno = GRUB_ERR_NONE;
  347. return GRUB_ERR_NONE;
  348. }
  349. static void
  350. redraw_screen_rect (unsigned int x, unsigned int y,
  351. unsigned int width, unsigned int height)
  352. {
  353. grub_video_color_t color;
  354. grub_video_rect_t saved_view;
  355. grub_video_set_active_render_target (render_target);
  356. /* Save viewport and set it to our window. */
  357. grub_video_get_viewport ((unsigned *) &saved_view.x,
  358. (unsigned *) &saved_view.y,
  359. (unsigned *) &saved_view.width,
  360. (unsigned *) &saved_view.height);
  361. grub_video_set_viewport (window.x, window.y, window.width, window.height);
  362. if (grub_gfxterm_background.bitmap)
  363. {
  364. /* Render bitmap as background. */
  365. grub_video_blit_bitmap (grub_gfxterm_background.bitmap,
  366. GRUB_VIDEO_BLIT_REPLACE, x, y,
  367. x, y,
  368. width, height);
  369. /* If bitmap is smaller than requested blit area, use background
  370. color. */
  371. color = virtual_screen.bg_color_display;
  372. /* Fill right side of the bitmap if needed. */
  373. if ((x + width >= grub_gfxterm_background.bitmap->mode_info.width)
  374. && (y < grub_gfxterm_background.bitmap->mode_info.height))
  375. {
  376. int w = (x + width) - grub_gfxterm_background.bitmap->mode_info.width;
  377. int h = height;
  378. unsigned int tx = x;
  379. if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
  380. {
  381. h = grub_gfxterm_background.bitmap->mode_info.height - y;
  382. }
  383. if (grub_gfxterm_background.bitmap->mode_info.width > tx)
  384. {
  385. tx = grub_gfxterm_background.bitmap->mode_info.width;
  386. }
  387. /* Render background layer. */
  388. grub_video_fill_rect (color, tx, y, w, h);
  389. }
  390. /* Fill bottom side of the bitmap if needed. */
  391. if (y + height >= grub_gfxterm_background.bitmap->mode_info.height)
  392. {
  393. int h = (y + height) - grub_gfxterm_background.bitmap->mode_info.height;
  394. unsigned int ty = y;
  395. if (grub_gfxterm_background.bitmap->mode_info.height > ty)
  396. {
  397. ty = grub_gfxterm_background.bitmap->mode_info.height;
  398. }
  399. /* Render background layer. */
  400. grub_video_fill_rect (color, x, ty, width, h);
  401. }
  402. }
  403. else
  404. {
  405. /* Render background layer. */
  406. color = virtual_screen.bg_color_display;
  407. grub_video_fill_rect (color, x, y, width, height);
  408. }
  409. if (grub_gfxterm_background.blend_text_bg)
  410. /* Render text layer as blended. */
  411. grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y,
  412. x - virtual_screen.offset_x,
  413. y - virtual_screen.offset_y,
  414. width, height);
  415. else
  416. /* Render text layer as replaced (to get texts background color). */
  417. grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y,
  418. x - virtual_screen.offset_x,
  419. y - virtual_screen.offset_y,
  420. width, height);
  421. /* Restore saved viewport. */
  422. grub_video_set_viewport (saved_view.x, saved_view.y,
  423. saved_view.width, saved_view.height);
  424. grub_video_set_active_render_target (render_target);
  425. }
  426. static void
  427. dirty_region_reset (void)
  428. {
  429. dirty_region.top_left_x = -1;
  430. dirty_region.top_left_y = -1;
  431. dirty_region.bottom_right_x = -1;
  432. dirty_region.bottom_right_y = -1;
  433. repaint_was_scheduled = 0;
  434. }
  435. static int
  436. dirty_region_is_empty (void)
  437. {
  438. if ((dirty_region.top_left_x == -1)
  439. || (dirty_region.top_left_y == -1)
  440. || (dirty_region.bottom_right_x == -1)
  441. || (dirty_region.bottom_right_y == -1))
  442. return 1;
  443. return 0;
  444. }
  445. static void
  446. dirty_region_add_real (int x, int y, unsigned int width, unsigned int height)
  447. {
  448. if (dirty_region_is_empty ())
  449. {
  450. dirty_region.top_left_x = x;
  451. dirty_region.top_left_y = y;
  452. dirty_region.bottom_right_x = x + width - 1;
  453. dirty_region.bottom_right_y = y + height - 1;
  454. }
  455. else
  456. {
  457. if (x < dirty_region.top_left_x)
  458. dirty_region.top_left_x = x;
  459. if (y < dirty_region.top_left_y)
  460. dirty_region.top_left_y = y;
  461. if ((x + (int)width - 1) > dirty_region.bottom_right_x)
  462. dirty_region.bottom_right_x = x + width - 1;
  463. if ((y + (int)height - 1) > dirty_region.bottom_right_y)
  464. dirty_region.bottom_right_y = y + height - 1;
  465. }
  466. }
  467. static void
  468. dirty_region_add (int x, int y, unsigned int width, unsigned int height)
  469. {
  470. if ((width == 0) || (height == 0))
  471. return;
  472. if (repaint_scheduled)
  473. {
  474. dirty_region_add_real (0, 0,
  475. window.width, window.height);
  476. repaint_scheduled = 0;
  477. repaint_was_scheduled = 1;
  478. }
  479. dirty_region_add_real (x, y, width, height);
  480. }
  481. static void
  482. dirty_region_add_virtualscreen (void)
  483. {
  484. /* Mark virtual screen as dirty. */
  485. dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y,
  486. virtual_screen.width, virtual_screen.height);
  487. }
  488. static void
  489. dirty_region_redraw (void)
  490. {
  491. int x;
  492. int y;
  493. int width;
  494. int height;
  495. if (dirty_region_is_empty ())
  496. return;
  497. x = dirty_region.top_left_x;
  498. y = dirty_region.top_left_y;
  499. width = dirty_region.bottom_right_x - x + 1;
  500. height = dirty_region.bottom_right_y - y + 1;
  501. if (repaint_was_scheduled && grub_gfxterm_decorator_hook)
  502. grub_gfxterm_decorator_hook ();
  503. redraw_screen_rect (x, y, width, height);
  504. }
  505. static inline void
  506. paint_char (unsigned cx, unsigned cy)
  507. {
  508. struct grub_colored_char *p;
  509. struct grub_font_glyph *glyph;
  510. grub_video_color_t color;
  511. grub_video_color_t bgcolor;
  512. unsigned int x;
  513. unsigned int y;
  514. int ascent;
  515. unsigned int height;
  516. unsigned int width;
  517. if (cy + virtual_screen.total_scroll >= virtual_screen.rows)
  518. return;
  519. /* Find out active character. */
  520. p = (virtual_screen.text_buffer
  521. + cx + (cy * virtual_screen.columns));
  522. if (!p->code.base)
  523. return;
  524. /* Get glyph for character. */
  525. glyph = grub_font_construct_glyph (virtual_screen.font, &p->code);
  526. if (!glyph)
  527. {
  528. grub_errno = GRUB_ERR_NONE;
  529. return;
  530. }
  531. ascent = grub_font_get_ascent (virtual_screen.font);
  532. width = virtual_screen.normal_char_width * calculate_character_width(glyph);
  533. height = virtual_screen.normal_char_height;
  534. color = p->fg_color;
  535. bgcolor = p->bg_color;
  536. x = cx * virtual_screen.normal_char_width;
  537. y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height;
  538. /* Render glyph to text layer. */
  539. grub_video_set_active_render_target (text_layer);
  540. grub_video_fill_rect (bgcolor, x, y, width, height);
  541. grub_font_draw_glyph (glyph, color, x, y + ascent);
  542. grub_video_set_active_render_target (render_target);
  543. /* Mark character to be drawn. */
  544. dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
  545. width, height);
  546. }
  547. static inline void
  548. write_char (void)
  549. {
  550. paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y);
  551. }
  552. static inline void
  553. draw_cursor (int show)
  554. {
  555. unsigned int x;
  556. unsigned int y;
  557. unsigned int width;
  558. unsigned int height;
  559. unsigned int ascent;
  560. grub_video_color_t color;
  561. write_char ();
  562. if (!show)
  563. return;
  564. if (virtual_screen.cursor_y + virtual_screen.total_scroll
  565. >= virtual_screen.rows)
  566. return;
  567. /* Ensure that cursor doesn't go outside of character box. */
  568. ascent = grub_font_get_ascent(virtual_screen.font);
  569. if (ascent > virtual_screen.normal_char_height - 2)
  570. ascent = virtual_screen.normal_char_height - 2;
  571. /* Determine cursor properties and position on text layer. */
  572. x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
  573. width = virtual_screen.normal_char_width;
  574. color = virtual_screen.fg_color;
  575. y = ((virtual_screen.cursor_y + virtual_screen.total_scroll)
  576. * virtual_screen.normal_char_height
  577. + ascent);
  578. height = 2;
  579. /* Render cursor to text layer. */
  580. grub_video_set_active_render_target (text_layer);
  581. grub_video_fill_rect (color, x, y, width, height);
  582. grub_video_set_active_render_target (render_target);
  583. /* Mark cursor to be redrawn. */
  584. dirty_region_add (virtual_screen.offset_x + x,
  585. virtual_screen.offset_y + y,
  586. width, height);
  587. }
  588. static void
  589. real_scroll (void)
  590. {
  591. unsigned int i, j, was_scroll;
  592. grub_video_color_t color;
  593. if (!virtual_screen.total_scroll)
  594. return;
  595. /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */
  596. if (grub_gfxterm_background.bitmap)
  597. {
  598. /* Scroll physical screen. */
  599. grub_video_set_active_render_target (text_layer);
  600. color = virtual_screen.bg_color;
  601. grub_video_scroll (color, 0, -virtual_screen.normal_char_height
  602. * virtual_screen.total_scroll);
  603. /* Mark virtual screen to be redrawn. */
  604. dirty_region_add_virtualscreen ();
  605. }
  606. else
  607. {
  608. grub_video_rect_t saved_view;
  609. /* Remove cursor. */
  610. draw_cursor (0);
  611. grub_video_set_active_render_target (render_target);
  612. i = window.double_repaint ? 2 : 1;
  613. color = virtual_screen.bg_color_display;
  614. while (i--)
  615. {
  616. /* Save viewport and set it to our window. */
  617. grub_video_get_viewport ((unsigned *) &saved_view.x,
  618. (unsigned *) &saved_view.y,
  619. (unsigned *) &saved_view.width,
  620. (unsigned *) &saved_view.height);
  621. grub_video_set_viewport (window.x, window.y, window.width,
  622. window.height);
  623. /* Clear new border area. */
  624. grub_video_fill_rect (color,
  625. virtual_screen.offset_x,
  626. virtual_screen.offset_y,
  627. virtual_screen.width,
  628. virtual_screen.normal_char_height
  629. * virtual_screen.total_scroll);
  630. grub_video_set_active_render_target (render_target);
  631. dirty_region_redraw ();
  632. /* Scroll physical screen. */
  633. grub_video_scroll (color, 0, -virtual_screen.normal_char_height
  634. * virtual_screen.total_scroll);
  635. /* Restore saved viewport. */
  636. grub_video_set_viewport (saved_view.x, saved_view.y,
  637. saved_view.width, saved_view.height);
  638. if (i)
  639. grub_video_swap_buffers ();
  640. }
  641. dirty_region_reset ();
  642. /* Scroll physical screen. */
  643. grub_video_set_active_render_target (text_layer);
  644. color = virtual_screen.bg_color;
  645. grub_video_scroll (color, 0, -virtual_screen.normal_char_height
  646. * virtual_screen.total_scroll);
  647. grub_video_set_active_render_target (render_target);
  648. }
  649. was_scroll = virtual_screen.total_scroll;
  650. virtual_screen.total_scroll = 0;
  651. if (was_scroll > virtual_screen.rows)
  652. was_scroll = virtual_screen.rows;
  653. /* Draw shadow part. */
  654. for (i = virtual_screen.rows - was_scroll;
  655. i < virtual_screen.rows; i++)
  656. for (j = 0; j < virtual_screen.columns; j++)
  657. paint_char (j, i);
  658. /* Draw cursor if visible. */
  659. if (virtual_screen.cursor_state)
  660. draw_cursor (1);
  661. }
  662. static void
  663. scroll_up (void)
  664. {
  665. unsigned int i;
  666. /* Clear first line in text buffer. */
  667. for (i = 0; i < virtual_screen.columns; i++)
  668. grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code);
  669. /* Scroll text buffer with one line to up. */
  670. grub_memmove (virtual_screen.text_buffer,
  671. virtual_screen.text_buffer + virtual_screen.columns,
  672. sizeof (*virtual_screen.text_buffer)
  673. * virtual_screen.columns
  674. * (virtual_screen.rows - 1));
  675. /* Clear last line in text buffer. */
  676. for (i = virtual_screen.columns * (virtual_screen.rows - 1);
  677. i < virtual_screen.columns * virtual_screen.rows;
  678. i++)
  679. clear_char (&(virtual_screen.text_buffer[i]));
  680. virtual_screen.total_scroll++;
  681. }
  682. static void
  683. grub_gfxterm_putchar (struct grub_term_output *term,
  684. const struct grub_unicode_glyph *c)
  685. {
  686. if (!virtual_screen.functional)
  687. return;
  688. if (c->base == '\a')
  689. /* FIXME */
  690. return;
  691. /* Erase current cursor, if any. */
  692. if (virtual_screen.cursor_state)
  693. draw_cursor (0);
  694. if (c->base == '\b' || c->base == '\n' || c->base == '\r')
  695. {
  696. switch (c->base)
  697. {
  698. case '\b':
  699. if (virtual_screen.cursor_x > 0)
  700. virtual_screen.cursor_x--;
  701. break;
  702. case '\n':
  703. if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
  704. scroll_up ();
  705. else
  706. virtual_screen.cursor_y++;
  707. break;
  708. case '\r':
  709. virtual_screen.cursor_x = 0;
  710. break;
  711. }
  712. }
  713. else
  714. {
  715. struct grub_colored_char *p;
  716. unsigned char char_width;
  717. /* Calculate actual character width for glyph. This is number of
  718. times of normal_font_width. */
  719. char_width = grub_gfxterm_getcharwidth (term, c);
  720. /* If we are about to exceed line length, wrap to next line. */
  721. if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
  722. {
  723. if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
  724. scroll_up ();
  725. else
  726. virtual_screen.cursor_y++;
  727. }
  728. /* Find position on virtual screen, and fill information. */
  729. p = (virtual_screen.text_buffer +
  730. virtual_screen.cursor_x +
  731. virtual_screen.cursor_y * virtual_screen.columns);
  732. grub_unicode_destroy_glyph (&p->code);
  733. grub_unicode_set_glyph (&p->code, c);
  734. grub_errno = GRUB_ERR_NONE;
  735. p->fg_color = virtual_screen.fg_color;
  736. p->bg_color = virtual_screen.bg_color;
  737. /* If we have large glyph, add fixup info. */
  738. if (char_width > 1)
  739. {
  740. unsigned i;
  741. for (i = 1; i < char_width && p + i <
  742. virtual_screen.text_buffer + virtual_screen.columns
  743. * virtual_screen.rows; i++)
  744. {
  745. grub_unicode_destroy_glyph (&p[i].code);
  746. p[i].code.base = 0;
  747. }
  748. }
  749. /* Draw glyph. */
  750. write_char ();
  751. /* Make sure we scroll screen when needed and wrap line correctly. */
  752. virtual_screen.cursor_x += char_width;
  753. if (virtual_screen.cursor_x >= virtual_screen.columns)
  754. {
  755. virtual_screen.cursor_x = 0;
  756. if (virtual_screen.cursor_y >= virtual_screen.rows - 1)
  757. scroll_up ();
  758. else
  759. virtual_screen.cursor_y++;
  760. }
  761. }
  762. /* Redraw cursor if it should be visible. */
  763. /* Note: This will redraw the character as well, which means that the
  764. above call to write_char is redundant when the cursor is showing. */
  765. if (virtual_screen.cursor_state)
  766. draw_cursor (1);
  767. }
  768. /* Use ASCII characters to determine normal character width. */
  769. static unsigned int
  770. calculate_normal_character_width (grub_font_t font)
  771. {
  772. struct grub_font_glyph *glyph;
  773. unsigned int width = 0;
  774. unsigned int i;
  775. /* Get properties of every printable ASCII character. */
  776. for (i = 32; i < 127; i++)
  777. {
  778. glyph = grub_font_get_glyph (font, i);
  779. /* Skip unknown characters. Should never happen on normal conditions. */
  780. if (! glyph)
  781. continue;
  782. if (glyph->device_width > width)
  783. width = glyph->device_width;
  784. }
  785. if (!width)
  786. return 8;
  787. return width;
  788. }
  789. static unsigned char
  790. calculate_character_width (struct grub_font_glyph *glyph)
  791. {
  792. if (! glyph || glyph->device_width == 0)
  793. return 1;
  794. return (glyph->device_width
  795. + (virtual_screen.normal_char_width - 1))
  796. / virtual_screen.normal_char_width;
  797. }
  798. static grub_size_t
  799. grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)),
  800. const struct grub_unicode_glyph *c)
  801. {
  802. int dev_width;
  803. dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c);
  804. if (dev_width == 0)
  805. return 1;
  806. return (dev_width + (virtual_screen.normal_char_width - 1))
  807. / virtual_screen.normal_char_width;
  808. }
  809. static struct grub_term_coordinate
  810. grub_virtual_screen_getwh (struct grub_term_output *term __attribute__ ((unused)))
  811. {
  812. return (struct grub_term_coordinate) { virtual_screen.columns, virtual_screen.rows };
  813. }
  814. static struct grub_term_coordinate
  815. grub_virtual_screen_getxy (struct grub_term_output *term __attribute__ ((unused)))
  816. {
  817. return (struct grub_term_coordinate) { virtual_screen.cursor_x, virtual_screen.cursor_y };
  818. }
  819. static void
  820. grub_gfxterm_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
  821. struct grub_term_coordinate pos)
  822. {
  823. if (pos.x >= virtual_screen.columns)
  824. pos.x = virtual_screen.columns - 1;
  825. if (pos.y >= virtual_screen.rows)
  826. pos.y = virtual_screen.rows - 1;
  827. /* Erase current cursor, if any. */
  828. if (virtual_screen.cursor_state)
  829. draw_cursor (0);
  830. virtual_screen.cursor_x = pos.x;
  831. virtual_screen.cursor_y = pos.y;
  832. /* Draw cursor if visible. */
  833. if (virtual_screen.cursor_state)
  834. draw_cursor (1);
  835. }
  836. static void
  837. grub_virtual_screen_cls (struct grub_term_output *term __attribute__ ((unused)))
  838. {
  839. grub_uint32_t i;
  840. for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++)
  841. clear_char (&(virtual_screen.text_buffer[i]));
  842. virtual_screen.cursor_x = virtual_screen.cursor_y = 0;
  843. }
  844. static void
  845. grub_gfxterm_cls (struct grub_term_output *term)
  846. {
  847. grub_video_color_t color;
  848. /* Clear virtual screen. */
  849. grub_virtual_screen_cls (term);
  850. /* Clear text layer. */
  851. grub_video_set_active_render_target (text_layer);
  852. color = virtual_screen.bg_color;
  853. grub_video_fill_rect (color, 0, 0,
  854. virtual_screen.width, virtual_screen.height);
  855. grub_video_set_active_render_target (render_target);
  856. /* Mark virtual screen to be redrawn. */
  857. dirty_region_add_virtualscreen ();
  858. grub_gfxterm_refresh (term);
  859. }
  860. static void
  861. grub_virtual_screen_setcolorstate (struct grub_term_output *term __attribute__ ((unused)),
  862. grub_term_color_state state)
  863. {
  864. switch (state)
  865. {
  866. case GRUB_TERM_COLOR_STANDARD:
  867. virtual_screen.term_color = virtual_screen.standard_color_setting;
  868. break;
  869. case GRUB_TERM_COLOR_NORMAL:
  870. virtual_screen.term_color = grub_term_normal_color;
  871. break;
  872. case GRUB_TERM_COLOR_HIGHLIGHT:
  873. virtual_screen.term_color = grub_term_highlight_color;
  874. break;
  875. default:
  876. break;
  877. }
  878. /* Change color to virtual terminal. */
  879. set_term_color (virtual_screen.term_color);
  880. }
  881. static void
  882. grub_gfxterm_setcursor (struct grub_term_output *term __attribute__ ((unused)),
  883. int on)
  884. {
  885. if (virtual_screen.cursor_state != on)
  886. {
  887. if (virtual_screen.cursor_state)
  888. draw_cursor (0);
  889. else
  890. draw_cursor (1);
  891. virtual_screen.cursor_state = on;
  892. }
  893. }
  894. static void
  895. grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused)))
  896. {
  897. real_scroll ();
  898. /* Redraw only changed regions. */
  899. dirty_region_redraw ();
  900. grub_video_swap_buffers ();
  901. if (window.double_repaint)
  902. dirty_region_redraw ();
  903. dirty_region_reset ();
  904. }
  905. static struct grub_term_output grub_video_term =
  906. {
  907. .name = "gfxterm",
  908. .init = grub_gfxterm_term_init,
  909. .fini = grub_gfxterm_term_fini,
  910. .putchar = grub_gfxterm_putchar,
  911. .getcharwidth = grub_gfxterm_getcharwidth,
  912. .getwh = grub_virtual_screen_getwh,
  913. .getxy = grub_virtual_screen_getxy,
  914. .gotoxy = grub_gfxterm_gotoxy,
  915. .cls = grub_gfxterm_cls,
  916. .setcolorstate = grub_virtual_screen_setcolorstate,
  917. .setcursor = grub_gfxterm_setcursor,
  918. .refresh = grub_gfxterm_refresh,
  919. .fullscreen = grub_gfxterm_fullscreen,
  920. .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS,
  921. .progress_update_divisor = GRUB_PROGRESS_SLOW,
  922. .next = 0
  923. };
  924. void
  925. grub_gfxterm_video_update_color (void)
  926. {
  927. struct grub_video_render_target *old_target;
  928. grub_video_get_active_render_target (&old_target);
  929. grub_video_set_active_render_target (text_layer);
  930. virtual_screen.bg_color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
  931. grub_video_set_active_render_target (old_target);
  932. virtual_screen.bg_color_display =
  933. grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color);
  934. }
  935. void
  936. grub_gfxterm_get_dimensions (unsigned *width, unsigned *height)
  937. {
  938. *width = window.width;
  939. *height = window.height;
  940. }
  941. GRUB_MOD_INIT(gfxterm)
  942. {
  943. grub_term_register_output ("gfxterm", &grub_video_term);
  944. }
  945. GRUB_MOD_FINI(gfxterm)
  946. {
  947. grub_term_unregister_output (&grub_video_term);
  948. }