]>
Commit | Line | Data |
---|---|---|
bd0d7896 | 1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
d6b392bd | 3 | * Copyright (C) 2006,2007 Free Software Foundation, Inc. |
bd0d7896 | 4 | * |
5a79f472 | 5 | * GRUB is free software: you can redistribute it and/or modify |
bd0d7896 | 6 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 7 | * the Free Software Foundation, either version 3 of the License, or |
bd0d7896 | 8 | * (at your option) any later version. |
9 | * | |
5a79f472 | 10 | * GRUB is distributed in the hope that it will be useful, |
bd0d7896 | 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 | |
5a79f472 | 16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
bd0d7896 | 17 | */ |
18 | ||
19 | #include <grub/machine/memory.h> | |
20 | #include <grub/machine/console.h> | |
21 | #include <grub/term.h> | |
22 | #include <grub/types.h> | |
23 | #include <grub/dl.h> | |
24 | #include <grub/misc.h> | |
25 | #include <grub/normal.h> | |
26 | #include <grub/font.h> | |
27 | #include <grub/arg.h> | |
28 | #include <grub/mm.h> | |
29 | #include <grub/env.h> | |
30 | #include <grub/video.h> | |
31 | ||
32 | #define DEFAULT_VIDEO_WIDTH 640 | |
33 | #define DEFAULT_VIDEO_HEIGHT 480 | |
34 | #define DEFAULT_VIDEO_FLAGS 0 | |
35 | ||
36 | #define DEFAULT_CHAR_WIDTH 8 | |
37 | #define DEFAULT_CHAR_HEIGHT 16 | |
38 | ||
39 | #define DEFAULT_BORDER_WIDTH 10 | |
40 | ||
41 | #define DEFAULT_FG_COLOR 0x0a | |
42 | #define DEFAULT_BG_COLOR 0x00 | |
43 | #define DEFAULT_CURSOR_COLOR 0x0f | |
44 | ||
45 | struct grub_dirty_region | |
46 | { | |
47 | int top_left_x; | |
48 | int top_left_y; | |
49 | int bottom_right_x; | |
50 | int bottom_right_y; | |
51 | }; | |
52 | ||
53 | struct grub_colored_char | |
54 | { | |
55 | /* An Unicode codepoint. */ | |
56 | grub_uint32_t code; | |
57 | ||
58 | /* Color values. */ | |
59 | grub_video_color_t fg_color; | |
60 | grub_video_color_t bg_color; | |
61 | ||
62 | /* The width of this character minus one. */ | |
63 | unsigned char width; | |
64 | ||
65 | /* The column index of this character. */ | |
66 | unsigned char index; | |
67 | }; | |
68 | ||
69 | struct grub_virtual_screen | |
70 | { | |
71 | /* Dimensions of the virtual screen. */ | |
72 | unsigned int width; | |
73 | unsigned int height; | |
74 | ||
75 | /* Offset in the display. */ | |
76 | unsigned int offset_x; | |
77 | unsigned int offset_y; | |
78 | ||
79 | /* TTY Character sizes. */ | |
80 | unsigned int char_width; | |
81 | unsigned int char_height; | |
82 | ||
83 | /* Virtual screen TTY size. */ | |
84 | unsigned int columns; | |
85 | unsigned int rows; | |
86 | ||
87 | /* Current cursor details. */ | |
88 | unsigned int cursor_x; | |
89 | unsigned int cursor_y; | |
90 | int cursor_state; | |
ad2a06ed | 91 | |
bd0d7896 | 92 | /* Color settings. */ |
93 | grub_video_color_t fg_color_setting; | |
94 | grub_video_color_t bg_color_setting; | |
95 | grub_video_color_t fg_color; | |
96 | grub_video_color_t bg_color; | |
97 | grub_video_color_t cursor_color; | |
98 | ||
99 | /* Text buffer for virtual screen. Contains (columns * rows) number | |
100 | of entries. */ | |
101 | struct grub_colored_char *text_buffer; | |
102 | }; | |
103 | ||
104 | static struct grub_virtual_screen virtual_screen; | |
105 | ||
106 | static grub_dl_t my_mod; | |
107 | static struct grub_video_mode_info mode_info; | |
108 | ||
109 | static struct grub_video_render_target *text_layer; | |
110 | ||
111 | static struct grub_dirty_region dirty_region; | |
112 | ||
113 | static void dirty_region_reset (void); | |
114 | ||
115 | static int dirty_region_is_empty (void); | |
116 | ||
117 | static void dirty_region_add (int x, int y, | |
118 | unsigned int width, unsigned int height); | |
119 | ||
120 | static void | |
121 | grub_virtual_screen_free (void) | |
122 | { | |
123 | /* If virtual screen has been allocated, free it. */ | |
124 | if (virtual_screen.text_buffer != 0) | |
125 | grub_free (virtual_screen.text_buffer); | |
126 | ||
127 | /* Reset virtual screen data. */ | |
128 | grub_memset (&virtual_screen, 0, sizeof (virtual_screen)); | |
ad2a06ed | 129 | |
bd0d7896 | 130 | /* Free render targets. */ |
131 | grub_video_delete_render_target (text_layer); | |
132 | text_layer = 0; | |
133 | } | |
134 | ||
135 | static grub_err_t | |
136 | grub_virtual_screen_setup (unsigned int x, unsigned int y, | |
137 | unsigned int width, unsigned int height) | |
138 | { | |
139 | /* Free old virtual screen. */ | |
140 | grub_virtual_screen_free (); | |
141 | ||
142 | /* Initialize with default data. */ | |
143 | virtual_screen.width = width; | |
144 | virtual_screen.height = height; | |
145 | virtual_screen.offset_x = x; | |
146 | virtual_screen.offset_y = y; | |
147 | virtual_screen.char_width = DEFAULT_CHAR_WIDTH; | |
148 | virtual_screen.char_height = DEFAULT_CHAR_HEIGHT; | |
149 | virtual_screen.cursor_x = 0; | |
150 | virtual_screen.cursor_y = 0; | |
151 | virtual_screen.cursor_state = 1; | |
152 | ||
153 | /* Calculate size of text buffer. */ | |
154 | virtual_screen.columns = virtual_screen.width / virtual_screen.char_width; | |
155 | virtual_screen.rows = virtual_screen.height / virtual_screen.char_height; | |
156 | ||
157 | /* Allocate memory for text buffer. */ | |
158 | virtual_screen.text_buffer = | |
159 | (struct grub_colored_char *) grub_malloc (virtual_screen.columns | |
ad2a06ed | 160 | * virtual_screen.rows |
161 | * sizeof (*virtual_screen.text_buffer)); | |
bd0d7896 | 162 | if (grub_errno != GRUB_ERR_NONE) |
163 | return grub_errno; | |
164 | ||
165 | /* Create new render target for text layer. */ | |
166 | grub_video_create_render_target (&text_layer, | |
167 | virtual_screen.width, | |
168 | virtual_screen.height, | |
169 | GRUB_VIDEO_MODE_TYPE_RGB | |
170 | | GRUB_VIDEO_MODE_TYPE_ALPHA); | |
171 | if (grub_errno != GRUB_ERR_NONE) | |
172 | return grub_errno; | |
ad2a06ed | 173 | |
bd0d7896 | 174 | /* As we want to have colors compatible with rendering target, |
ad2a06ed | 175 | we can only have those after mode is initialized. */ |
bd0d7896 | 176 | grub_video_set_active_render_target (text_layer); |
ad2a06ed | 177 | |
bd0d7896 | 178 | virtual_screen.fg_color_setting = grub_video_map_color (DEFAULT_FG_COLOR); |
179 | virtual_screen.bg_color_setting = grub_video_map_color (DEFAULT_BG_COLOR); | |
180 | virtual_screen.fg_color = virtual_screen.fg_color_setting; | |
181 | virtual_screen.bg_color = virtual_screen.bg_color_setting; | |
182 | virtual_screen.cursor_color = grub_video_map_color (DEFAULT_CURSOR_COLOR); | |
ad2a06ed | 183 | |
bd0d7896 | 184 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); |
ad2a06ed | 185 | |
bd0d7896 | 186 | return grub_errno; |
187 | } | |
188 | ||
189 | static grub_err_t | |
190 | grub_gfxterm_init (void) | |
191 | { | |
192 | char *modevar; | |
193 | int width = DEFAULT_VIDEO_WIDTH; | |
194 | int height = DEFAULT_VIDEO_HEIGHT; | |
195 | int depth = -1; | |
196 | int flags = DEFAULT_VIDEO_FLAGS; | |
197 | grub_video_color_t color; | |
ad2a06ed | 198 | |
bd0d7896 | 199 | /* Parse gfxmode environment variable if set. */ |
200 | modevar = grub_env_get ("gfxmode"); | |
201 | if (modevar) | |
202 | { | |
203 | char *tmp; | |
29b0ed46 | 204 | char *next_mode; |
205 | char *current_mode; | |
bd0d7896 | 206 | char *param; |
207 | char *value; | |
29b0ed46 | 208 | int mode_found = 0; |
ad2a06ed | 209 | |
bd0d7896 | 210 | /* Take copy of env.var. as we don't want to modify that. */ |
211 | tmp = grub_strdup (modevar); | |
212 | modevar = tmp; | |
ad2a06ed | 213 | |
bd0d7896 | 214 | if (grub_errno != GRUB_ERR_NONE) |
ad2a06ed | 215 | return grub_errno; |
29b0ed46 | 216 | |
217 | /* Initialize next mode. */ | |
218 | next_mode = modevar; | |
219 | ||
220 | /* Loop until all modes has been tested out. */ | |
221 | while (next_mode != NULL) | |
bd0d7896 | 222 | { |
29b0ed46 | 223 | /* Use last next_mode as current mode. */ |
224 | tmp = next_mode; | |
225 | ||
226 | /* Reset video mode settings. */ | |
227 | width = DEFAULT_VIDEO_WIDTH; | |
228 | height = DEFAULT_VIDEO_HEIGHT; | |
229 | depth = -1; | |
230 | flags = DEFAULT_VIDEO_FLAGS; | |
231 | ||
232 | /* Save position of next mode and separate modes. */ | |
233 | next_mode = grub_strchr(next_mode, ';'); | |
234 | if (next_mode) | |
235 | { | |
236 | *next_mode = 0; | |
237 | next_mode++; | |
238 | } | |
ad2a06ed | 239 | |
29b0ed46 | 240 | /* Skip whitespace. */ |
241 | while (grub_isspace (*tmp)) | |
242 | tmp++; | |
ad2a06ed | 243 | |
29b0ed46 | 244 | /* Initialize token holders. */ |
245 | current_mode = tmp; | |
246 | param = tmp; | |
247 | value = NULL; | |
bd0d7896 | 248 | |
29b0ed46 | 249 | /* Parse <width>x<height>[x<depth>]*/ |
250 | ||
251 | /* Find width value. */ | |
252 | value = param; | |
253 | param = grub_strchr(param, 'x'); | |
254 | if (param == NULL) | |
bd0d7896 | 255 | { |
29b0ed46 | 256 | grub_err_t rc; |
257 | ||
258 | /* First setup error message. */ | |
259 | rc = grub_error (GRUB_ERR_BAD_ARGUMENT, | |
d6b392bd | 260 | "Invalid mode: %s\n", |
261 | current_mode); | |
29b0ed46 | 262 | |
bd0d7896 | 263 | /* Free memory before returning. */ |
264 | grub_free (modevar); | |
29b0ed46 | 265 | |
266 | return rc; | |
bd0d7896 | 267 | } |
29b0ed46 | 268 | |
bd0d7896 | 269 | *param = 0; |
270 | param++; | |
ad2a06ed | 271 | |
29b0ed46 | 272 | width = grub_strtoul (value, 0, 0); |
bd0d7896 | 273 | if (grub_errno != GRUB_ERR_NONE) |
274 | { | |
29b0ed46 | 275 | grub_err_t rc; |
276 | ||
277 | /* First setup error message. */ | |
278 | rc = grub_error (GRUB_ERR_BAD_ARGUMENT, | |
279 | "Invalid mode: %s\n", | |
280 | current_mode); | |
281 | ||
bd0d7896 | 282 | /* Free memory before returning. */ |
283 | grub_free (modevar); | |
29b0ed46 | 284 | |
285 | return rc; | |
bd0d7896 | 286 | } |
ad2a06ed | 287 | |
29b0ed46 | 288 | /* Find height value. */ |
bd0d7896 | 289 | value = param; |
29b0ed46 | 290 | param = grub_strchr(param, 'x'); |
291 | if (param == NULL) | |
bd0d7896 | 292 | { |
29b0ed46 | 293 | height = grub_strtoul (value, 0, 0); |
294 | if (grub_errno != GRUB_ERR_NONE) | |
295 | { | |
296 | grub_err_t rc; | |
297 | ||
298 | /* First setup error message. */ | |
299 | rc = grub_error (GRUB_ERR_BAD_ARGUMENT, | |
300 | "Invalid mode: %s\n", | |
301 | current_mode); | |
302 | ||
303 | /* Free memory before returning. */ | |
304 | grub_free (modevar); | |
305 | ||
306 | return rc; | |
307 | } | |
308 | } | |
309 | else | |
310 | { | |
311 | /* We have optional color depth value. */ | |
312 | *param = 0; | |
313 | param++; | |
314 | ||
315 | height = grub_strtoul (value, 0, 0); | |
316 | if (grub_errno != GRUB_ERR_NONE) | |
317 | { | |
318 | grub_err_t rc; | |
319 | ||
320 | /* First setup error message. */ | |
321 | rc = grub_error (GRUB_ERR_BAD_ARGUMENT, | |
322 | "Invalid mode: %s\n", | |
323 | current_mode); | |
324 | ||
325 | /* Free memory before returning. */ | |
326 | grub_free (modevar); | |
327 | ||
328 | return rc; | |
329 | } | |
330 | ||
331 | /* Convert color depth value. */ | |
332 | value = param; | |
333 | depth = grub_strtoul (value, 0, 0); | |
334 | if (grub_errno != GRUB_ERR_NONE) | |
335 | { | |
336 | grub_err_t rc; | |
337 | ||
338 | /* First setup error message. */ | |
339 | rc = grub_error (GRUB_ERR_BAD_ARGUMENT, | |
340 | "Invalid mode: %s\n", | |
341 | current_mode); | |
342 | ||
343 | /* Free memory before returning. */ | |
344 | grub_free (modevar); | |
345 | ||
346 | return rc; | |
347 | } | |
348 | } | |
349 | ||
350 | /* Try out video mode. */ | |
351 | ||
352 | /* If we have 8 or less bits, then assuem that it is indexed color mode. */ | |
353 | if ((depth <= 8) && (depth != -1)) | |
354 | flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; | |
355 | ||
356 | /* We have more than 8 bits, then assume that it is RGB color mode. */ | |
357 | if (depth > 8) | |
358 | flags |= GRUB_VIDEO_MODE_TYPE_RGB; | |
359 | ||
360 | /* If user requested specific depth, forward that information to driver. */ | |
361 | if (depth != -1) | |
362 | flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | |
363 | & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; | |
364 | ||
365 | /* Try to initialize requested mode. Ignore any errors. */ | |
366 | grub_error_push (); | |
367 | if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE) | |
368 | { | |
369 | grub_error_pop (); | |
370 | continue; | |
bd0d7896 | 371 | } |
29b0ed46 | 372 | |
373 | /* Figure out what mode we ended up. */ | |
374 | if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) | |
375 | { | |
376 | /* Couldn't get video mode info, restore old mode and continue to next one. */ | |
377 | grub_error_pop (); | |
378 | ||
379 | grub_video_restore (); | |
380 | continue; | |
381 | } | |
382 | ||
383 | /* Restore state of error stack. */ | |
384 | grub_error_pop (); | |
385 | ||
386 | /* Mode found! Exit loop. */ | |
387 | mode_found = 1; | |
388 | break; | |
bd0d7896 | 389 | } |
ad2a06ed | 390 | |
bd0d7896 | 391 | /* Free memory. */ |
392 | grub_free (modevar); | |
29b0ed46 | 393 | |
394 | if (!mode_found) | |
395 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
396 | "No suitable mode found."); | |
bd0d7896 | 397 | } |
29b0ed46 | 398 | else |
399 | { | |
400 | /* No gfxmode variable set, use defaults. */ | |
401 | ||
402 | /* If we have 8 or less bits, then assuem that it is indexed color mode. */ | |
403 | if ((depth <= 8) && (depth != -1)) | |
404 | flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; | |
405 | ||
406 | /* We have more than 8 bits, then assume that it is RGB color mode. */ | |
407 | if (depth > 8) | |
408 | flags |= GRUB_VIDEO_MODE_TYPE_RGB; | |
409 | ||
410 | /* If user requested specific depth, forward that information to driver. */ | |
411 | if (depth != -1) | |
412 | flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | |
413 | & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; | |
414 | ||
415 | /* Initialize user requested mode. */ | |
416 | if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE) | |
417 | return grub_errno; | |
bd0d7896 | 418 | |
29b0ed46 | 419 | /* Figure out what mode we ended up. */ |
420 | if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) | |
421 | { | |
422 | grub_video_restore (); | |
423 | return grub_errno; | |
424 | } | |
425 | } | |
bd0d7896 | 426 | |
427 | /* Make sure screen is black. */ | |
428 | color = grub_video_map_rgb (0, 0, 0); | |
429 | grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); | |
430 | ||
431 | /* Leave borders for virtual screen. */ | |
432 | width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH); | |
433 | height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH); | |
ad2a06ed | 434 | |
bd0d7896 | 435 | /* Create virtual screen. */ |
436 | if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH, | |
437 | width, height) != GRUB_ERR_NONE) | |
438 | { | |
439 | grub_video_restore (); | |
440 | return grub_errno; | |
441 | } | |
442 | ||
443 | /* Mark whole screen as dirty. */ | |
ad2a06ed | 444 | dirty_region_reset (); |
bd0d7896 | 445 | dirty_region_add (0, 0, mode_info.width, mode_info.height); |
446 | ||
447 | return (grub_errno = GRUB_ERR_NONE); | |
448 | } | |
449 | ||
450 | static grub_err_t | |
451 | grub_gfxterm_fini (void) | |
452 | { | |
453 | grub_virtual_screen_free (); | |
454 | ||
455 | grub_video_restore (); | |
456 | ||
457 | return GRUB_ERR_NONE; | |
458 | } | |
459 | ||
460 | static void | |
461 | redraw_screen_rect (unsigned int x, unsigned int y, | |
462 | unsigned int width, unsigned int height) | |
463 | { | |
464 | grub_video_color_t color; | |
ad2a06ed | 465 | |
bd0d7896 | 466 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); |
467 | ||
468 | /* Render background layer. */ | |
469 | color = virtual_screen.bg_color; | |
470 | grub_video_fill_rect (color, x, y, width, height); | |
ad2a06ed | 471 | |
bd0d7896 | 472 | /* Render text layer. */ |
ad2a06ed | 473 | grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y, |
bd0d7896 | 474 | x - virtual_screen.offset_x, |
475 | y - virtual_screen.offset_y, | |
476 | width, height); | |
477 | } | |
478 | ||
479 | static void | |
480 | dirty_region_reset (void) | |
481 | { | |
482 | dirty_region.top_left_x = -1; | |
483 | dirty_region.top_left_y = -1; | |
484 | dirty_region.bottom_right_x = -1; | |
485 | dirty_region.bottom_right_y = -1; | |
486 | } | |
487 | ||
488 | static int | |
489 | dirty_region_is_empty (void) | |
490 | { | |
491 | if ((dirty_region.top_left_x == -1) | |
492 | || (dirty_region.top_left_y == -1) | |
493 | || (dirty_region.bottom_right_x == -1) | |
494 | || (dirty_region.bottom_right_y == -1)) | |
495 | return 1; | |
496 | return 0; | |
497 | } | |
498 | ||
499 | static void | |
500 | dirty_region_add (int x, int y, unsigned int width, unsigned int height) | |
501 | { | |
502 | if ((width == 0) || (height == 0)) | |
503 | return; | |
ad2a06ed | 504 | |
bd0d7896 | 505 | if (dirty_region_is_empty ()) |
506 | { | |
507 | dirty_region.top_left_x = x; | |
508 | dirty_region.top_left_y = y; | |
509 | dirty_region.bottom_right_x = x + width - 1; | |
510 | dirty_region.bottom_right_y = y + height - 1; | |
511 | } | |
512 | else | |
513 | { | |
514 | if (x < dirty_region.top_left_x) | |
515 | dirty_region.top_left_x = x; | |
516 | if (y < dirty_region.top_left_y) | |
517 | dirty_region.top_left_y = y; | |
518 | if ((x + (int)width - 1) > dirty_region.bottom_right_x) | |
519 | dirty_region.bottom_right_x = x + width - 1; | |
520 | if ((y + (int)height - 1) > dirty_region.bottom_right_y) | |
521 | dirty_region.bottom_right_y = y + height - 1; | |
522 | } | |
523 | } | |
524 | ||
525 | static void | |
526 | dirty_region_add_virtualscreen (void) | |
527 | { | |
528 | /* Mark virtual screen as dirty. */ | |
529 | dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y, | |
530 | virtual_screen.width, virtual_screen.height); | |
531 | } | |
532 | ||
533 | ||
534 | static void | |
535 | dirty_region_redraw (void) | |
536 | { | |
537 | int x; | |
538 | int y; | |
539 | int width; | |
540 | int height; | |
ad2a06ed | 541 | |
bd0d7896 | 542 | if (dirty_region_is_empty ()) |
543 | return; | |
ad2a06ed | 544 | |
bd0d7896 | 545 | x = dirty_region.top_left_x; |
546 | y = dirty_region.top_left_y; | |
ad2a06ed | 547 | |
bd0d7896 | 548 | width = dirty_region.bottom_right_x - x + 1; |
549 | height = dirty_region.bottom_right_y - y + 1; | |
ad2a06ed | 550 | |
bd0d7896 | 551 | redraw_screen_rect (x, y, width, height); |
ad2a06ed | 552 | |
bd0d7896 | 553 | dirty_region_reset (); |
554 | } | |
555 | ||
556 | static void | |
557 | write_char (void) | |
558 | { | |
559 | struct grub_colored_char *p; | |
560 | struct grub_font_glyph glyph; | |
561 | grub_video_color_t color; | |
ad2a06ed | 562 | grub_video_color_t bgcolor; |
bd0d7896 | 563 | unsigned int x; |
564 | unsigned int y; | |
565 | ||
566 | /* Find out active character. */ | |
567 | p = (virtual_screen.text_buffer | |
568 | + virtual_screen.cursor_x | |
569 | + (virtual_screen.cursor_y * virtual_screen.columns)); | |
570 | ||
571 | p -= p->index; | |
572 | ||
573 | /* Get glyph for character. */ | |
574 | grub_font_get_glyph (p->code, &glyph); | |
575 | ||
576 | color = p->fg_color; | |
577 | bgcolor = p->bg_color; | |
578 | ||
579 | x = virtual_screen.cursor_x * virtual_screen.char_width; | |
580 | y = virtual_screen.cursor_y * virtual_screen.char_height; | |
581 | ||
582 | /* Render glyph to text layer. */ | |
583 | grub_video_set_active_render_target (text_layer); | |
584 | grub_video_fill_rect (bgcolor, x, y, glyph.width, glyph.height); | |
ad2a06ed | 585 | grub_video_blit_glyph (&glyph, color, x, y); |
bd0d7896 | 586 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); |
587 | ||
588 | /* Mark character to be drawn. */ | |
589 | dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, | |
590 | glyph.width, glyph.height); | |
591 | } | |
592 | ||
593 | static void | |
594 | write_cursor (void) | |
595 | { | |
596 | unsigned int x; | |
597 | unsigned int y; | |
598 | unsigned int width; | |
599 | unsigned int height; | |
600 | grub_video_color_t color; | |
601 | ||
602 | /* Determine cursor properties and position on text layer. */ | |
603 | x = virtual_screen.cursor_x * virtual_screen.char_width; | |
604 | y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3; | |
605 | width = virtual_screen.char_width; | |
606 | height = 2; | |
607 | ||
608 | color = virtual_screen.cursor_color; | |
609 | ||
610 | /* Render cursor to text layer. */ | |
611 | grub_video_set_active_render_target (text_layer); | |
612 | grub_video_fill_rect (color, x, y, width, height); | |
613 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); | |
614 | ||
615 | /* Mark cursor to be redrawn. */ | |
616 | dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, | |
617 | width, height); | |
618 | } | |
619 | ||
620 | static void | |
621 | scroll_up (void) | |
622 | { | |
623 | unsigned int i; | |
624 | grub_video_color_t color; | |
625 | ||
626 | /* Scroll text buffer with one line to up. */ | |
627 | grub_memmove (virtual_screen.text_buffer, | |
ad2a06ed | 628 | virtual_screen.text_buffer + virtual_screen.columns, |
bd0d7896 | 629 | sizeof (*virtual_screen.text_buffer) |
630 | * virtual_screen.columns | |
631 | * (virtual_screen.rows - 1)); | |
632 | ||
633 | /* Clear last line in text buffer. */ | |
634 | for (i = virtual_screen.columns * (virtual_screen.rows - 1); | |
635 | i < virtual_screen.columns * virtual_screen.rows; | |
636 | i++) | |
637 | { | |
638 | virtual_screen.text_buffer[i].code = ' '; | |
639 | virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color; | |
640 | virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color; | |
641 | virtual_screen.text_buffer[i].width = 0; | |
642 | virtual_screen.text_buffer[i].index = 0; | |
643 | } | |
ad2a06ed | 644 | |
bd0d7896 | 645 | /* Scroll physical screen. */ |
646 | grub_video_set_active_render_target (text_layer); | |
647 | color = virtual_screen.bg_color; | |
648 | grub_video_scroll (color, 0, -virtual_screen.char_height); | |
649 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); | |
ad2a06ed | 650 | |
bd0d7896 | 651 | /* Mark virtual screen to be redrawn. */ |
652 | dirty_region_add_virtualscreen (); | |
653 | } | |
654 | ||
655 | static void | |
656 | grub_gfxterm_putchar (grub_uint32_t c) | |
657 | { | |
658 | if (c == '\a') | |
659 | /* FIXME */ | |
660 | return; | |
661 | ||
662 | if (c == '\b' || c == '\n' || c == '\r') | |
663 | { | |
664 | /* Erase current cursor, if any. */ | |
665 | if (virtual_screen.cursor_state) | |
ad2a06ed | 666 | write_char (); |
bd0d7896 | 667 | |
668 | switch (c) | |
ad2a06ed | 669 | { |
670 | case '\b': | |
671 | if (virtual_screen.cursor_x > 0) | |
672 | virtual_screen.cursor_x--; | |
673 | break; | |
674 | ||
675 | case '\n': | |
676 | if (virtual_screen.cursor_y >= virtual_screen.rows - 1) | |
677 | scroll_up (); | |
678 | else | |
679 | virtual_screen.cursor_y++; | |
680 | break; | |
681 | ||
682 | case '\r': | |
683 | virtual_screen.cursor_x = 0; | |
684 | break; | |
685 | } | |
bd0d7896 | 686 | |
687 | /* Redraw cursor if visible. */ | |
688 | if (virtual_screen.cursor_state) | |
ad2a06ed | 689 | write_cursor (); |
bd0d7896 | 690 | } |
691 | else | |
692 | { | |
693 | struct grub_font_glyph glyph; | |
694 | struct grub_colored_char *p; | |
ad2a06ed | 695 | |
bd0d7896 | 696 | /* Get properties of the character. */ |
697 | grub_font_get_glyph (c, &glyph); | |
698 | ||
699 | /* If we are about to exceed line length, wrap to next line. */ | |
700 | if (virtual_screen.cursor_x + glyph.char_width > virtual_screen.columns) | |
ad2a06ed | 701 | grub_putchar ('\n'); |
bd0d7896 | 702 | |
703 | /* Find position on virtual screen, and fill information. */ | |
704 | p = (virtual_screen.text_buffer + | |
ad2a06ed | 705 | virtual_screen.cursor_x + |
706 | virtual_screen.cursor_y * virtual_screen.columns); | |
bd0d7896 | 707 | p->code = c; |
708 | p->fg_color = virtual_screen.fg_color; | |
709 | p->bg_color = virtual_screen.bg_color; | |
710 | p->width = glyph.char_width - 1; | |
711 | p->index = 0; | |
712 | ||
713 | /* If we have large glyph, add fixup info. */ | |
714 | if (glyph.char_width > 1) | |
ad2a06ed | 715 | { |
716 | unsigned i; | |
717 | ||
718 | for (i = 1; i < glyph.char_width; i++) | |
719 | { | |
720 | p[i].code = ' '; | |
721 | p[i].width = glyph.char_width - 1; | |
722 | p[i].index = i; | |
723 | } | |
724 | } | |
725 | ||
bd0d7896 | 726 | /* Draw glyph. */ |
727 | write_char (); | |
ad2a06ed | 728 | |
bd0d7896 | 729 | /* Make sure we scroll screen when needed and wrap line correctly. */ |
730 | virtual_screen.cursor_x += glyph.char_width; | |
731 | if (virtual_screen.cursor_x >= virtual_screen.columns) | |
ad2a06ed | 732 | { |
733 | virtual_screen.cursor_x = 0; | |
734 | ||
735 | if (virtual_screen.cursor_y >= virtual_screen.rows - 1) | |
736 | scroll_up (); | |
737 | else | |
738 | virtual_screen.cursor_y++; | |
739 | } | |
bd0d7896 | 740 | |
741 | /* Draw cursor if visible. */ | |
742 | if (virtual_screen.cursor_state) | |
ad2a06ed | 743 | write_cursor (); |
bd0d7896 | 744 | } |
745 | } | |
746 | ||
747 | static grub_ssize_t | |
748 | grub_gfxterm_getcharwidth (grub_uint32_t c) | |
749 | { | |
750 | struct grub_font_glyph glyph; | |
ad2a06ed | 751 | |
37e5e1a4 | 752 | grub_font_get_glyph (c, &glyph); |
bd0d7896 | 753 | |
754 | return glyph.char_width; | |
755 | } | |
756 | ||
757 | static grub_uint16_t | |
758 | grub_virtual_screen_getwh (void) | |
759 | { | |
760 | return (virtual_screen.columns << 8) | virtual_screen.rows; | |
761 | } | |
762 | ||
763 | static grub_uint16_t | |
764 | grub_virtual_screen_getxy (void) | |
765 | { | |
766 | return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y); | |
767 | } | |
768 | ||
769 | static void | |
770 | grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y) | |
771 | { | |
772 | if (x >= virtual_screen.columns) | |
773 | x = virtual_screen.columns - 1; | |
ad2a06ed | 774 | |
bd0d7896 | 775 | if (y >= virtual_screen.rows) |
776 | y = virtual_screen.rows - 1; | |
777 | ||
778 | if (virtual_screen.cursor_state) | |
779 | write_char (); | |
780 | ||
781 | virtual_screen.cursor_x = x; | |
782 | virtual_screen.cursor_y = y; | |
783 | ||
784 | if (virtual_screen.cursor_state) | |
785 | write_cursor (); | |
786 | } | |
787 | ||
788 | static void | |
789 | grub_virtual_screen_cls (void) | |
790 | { | |
791 | grub_uint32_t i; | |
792 | ||
793 | for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) | |
794 | { | |
795 | virtual_screen.text_buffer[i].code = ' '; | |
796 | virtual_screen.text_buffer[i].fg_color = virtual_screen.fg_color; | |
797 | virtual_screen.text_buffer[i].bg_color = virtual_screen.bg_color; | |
798 | virtual_screen.text_buffer[i].width = 0; | |
799 | virtual_screen.text_buffer[i].index = 0; | |
800 | } | |
801 | ||
802 | virtual_screen.cursor_x = virtual_screen.cursor_y = 0; | |
803 | } | |
804 | ||
805 | static void | |
806 | grub_gfxterm_cls (void) | |
807 | { | |
808 | grub_video_color_t color; | |
809 | ||
810 | /* Clear virtual screen. */ | |
811 | grub_virtual_screen_cls (); | |
ad2a06ed | 812 | |
bd0d7896 | 813 | /* Clear text layer. */ |
814 | grub_video_set_active_render_target (text_layer); | |
815 | color = virtual_screen.bg_color_setting; | |
816 | grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); | |
817 | grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); | |
ad2a06ed | 818 | |
bd0d7896 | 819 | /* Mark virtual screen to be redrawn. */ |
820 | dirty_region_add_virtualscreen (); | |
821 | } | |
822 | ||
823 | static void | |
824 | grub_virtual_screen_setcolorstate (grub_term_color_state state) | |
825 | { | |
826 | switch (state) | |
827 | { | |
828 | case GRUB_TERM_COLOR_STANDARD: | |
829 | case GRUB_TERM_COLOR_NORMAL: | |
830 | virtual_screen.fg_color = virtual_screen.fg_color_setting; | |
831 | virtual_screen.bg_color = virtual_screen.bg_color_setting; | |
832 | break; | |
833 | case GRUB_TERM_COLOR_HIGHLIGHT: | |
834 | virtual_screen.fg_color = virtual_screen.bg_color_setting; | |
835 | virtual_screen.bg_color = virtual_screen.fg_color_setting; | |
836 | break; | |
837 | default: | |
838 | break; | |
839 | } | |
840 | } | |
841 | ||
842 | static void | |
843 | grub_virtual_screen_setcolor (grub_uint8_t normal_color, | |
844 | grub_uint8_t highlight_color) | |
845 | { | |
846 | virtual_screen.fg_color_setting = grub_video_map_color (normal_color); | |
847 | virtual_screen.bg_color_setting = grub_video_map_color (highlight_color); | |
848 | } | |
849 | ||
850 | static void | |
851 | grub_gfxterm_setcursor (int on) | |
852 | { | |
853 | if (virtual_screen.cursor_state != on) | |
854 | { | |
855 | if (virtual_screen.cursor_state) | |
ad2a06ed | 856 | write_char (); |
bd0d7896 | 857 | else |
ad2a06ed | 858 | write_cursor (); |
bd0d7896 | 859 | |
860 | virtual_screen.cursor_state = on; | |
861 | } | |
862 | } | |
863 | ||
864 | static void | |
865 | grub_gfxterm_refresh (void) | |
866 | { | |
867 | /* Redraw only changed regions. */ | |
868 | dirty_region_redraw (); | |
869 | } | |
870 | ||
871 | static struct grub_term grub_video_term = | |
872 | { | |
873 | .name = "gfxterm", | |
874 | .init = grub_gfxterm_init, | |
875 | .fini = grub_gfxterm_fini, | |
876 | .putchar = grub_gfxterm_putchar, | |
877 | .getcharwidth = grub_gfxterm_getcharwidth, | |
878 | .checkkey = grub_console_checkkey, | |
879 | .getkey = grub_console_getkey, | |
880 | .getwh = grub_virtual_screen_getwh, | |
881 | .getxy = grub_virtual_screen_getxy, | |
882 | .gotoxy = grub_gfxterm_gotoxy, | |
883 | .cls = grub_gfxterm_cls, | |
884 | .setcolorstate = grub_virtual_screen_setcolorstate, | |
885 | .setcolor = grub_virtual_screen_setcolor, | |
886 | .setcursor = grub_gfxterm_setcursor, | |
ad2a06ed | 887 | .refresh = grub_gfxterm_refresh, |
bd0d7896 | 888 | .flags = 0, |
889 | .next = 0 | |
890 | }; | |
891 | ||
892 | GRUB_MOD_INIT(term_gfxterm) | |
893 | { | |
894 | my_mod = mod; | |
895 | grub_term_register (&grub_video_term); | |
896 | } | |
897 | ||
898 | GRUB_MOD_FINI(term_gfxterm) | |
899 | { | |
900 | grub_term_unregister (&grub_video_term); | |
901 | } |