gfxterm.c 32 KB

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