~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to grub-core/term/gfxterm.c

Tags: upstream-1.99~20101122
ImportĀ upstreamĀ versionĀ 1.99~20101122

Show diffs side-by-side

added added

removed removed

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