# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: colin@gibibit.com-20090615143625-5mw3hbalmw1vbbxl # target_branch: http://grub.gibibit.com/bzr/trunk-clean # testament_sha1: 54ff4c41df941b80a7d0190feb00b93953d814c9 # timestamp: 2009-06-15 07:41:27 -0700 # source_branch: http://grub.gibibit.com/bzr/gfxmenu # base_revision_id: colin@gibibit.com-20090615143527-aecd7xrtc3v0vcfs # # Begin patch === modified file 'commands/videotest.c' --- commands/videotest.c 2009-06-04 17:22:45 +0000 +++ commands/videotest.c 2009-06-08 06:46:58 +0000 @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,38 +16,67 @@ * along with GRUB. If not, see . */ +#include #include #include #include +#include #include #include #include #include -#include - -static grub_err_t -grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - if (grub_video_set_mode ("1024x768;800x600;640x480", 0) != GRUB_ERR_NONE) - return grub_errno; - +#include +#include +#include /* to test grub_vbe_bios_set_display_start */ + +/* Option array indices. */ +#define ARGINDEX_TEST_TIME 0 +#define ARGINDEX_DOUBLE_BUF 1 + +static const struct grub_arg_option arg_options[] = { + {"time", 't', 0, "Time to run each test, in seconds.", 0, ARG_TYPE_INT}, + {"dbuf", 'd', 0, "Use double buffered graphics.", 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +#define DEFAULT_TEST_TIME 5 + +/* Command options -- populated base on command line arguments. */ +struct videotest_options +{ + int test_time; + int double_buffering; +}; + +/* List of video modes to try when an exact resolution is not required. */ +static const char preferred_mode_list[] = "1024x768;800x600;640x480"; + +static void +basic_video_test (struct videotest_options *vt_opts) +{ grub_video_color_t color; + grub_video_color_t bg; unsigned int x; unsigned int y; unsigned int width; unsigned int height; int i; - grub_font_t sansbig; - grub_font_t sans; - grub_font_t sanssmall; - grub_font_t fixed; struct grub_font_glyph *glyph; struct grub_video_render_target *text_layer; + grub_font_t fixed; grub_video_color_t palette[16]; - const char *str; - int texty; + int info_width; + int info_height; + + if (grub_video_set_mode (preferred_mode_list, 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + if (! (fixed = grub_font_get ("Fixed Regular 20"))) + { + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } grub_video_get_viewport (&x, &y, &width, &height); @@ -57,7 +86,11 @@ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + info_width = 500; + info_height = 100; + color = grub_video_map_rgb (0, 0, 0); + bg = color; grub_video_fill_rect (color, 0, 0, width, height); color = grub_video_map_rgb (255, 0, 0); @@ -66,13 +99,6 @@ color = grub_video_map_rgb (0, 255, 255); grub_video_fill_rect (color, 100, 100, 100, 100); - sansbig = grub_font_get ("Helvetica Bold 24"); - sans = grub_font_get ("Helvetica Bold 14"); - sanssmall = grub_font_get ("Helvetica 8"); - fixed = grub_font_get ("Fixed 20"); - if (! sansbig || ! sans || ! sanssmall || ! fixed) - return grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); - glyph = grub_font_get_glyph (fixed, '*'); grub_font_draw_glyph (glyph, color, 200 ,0); @@ -83,17 +109,1028 @@ grub_video_set_active_render_target (text_layer); - color = grub_video_map_rgb (255, 255, 255); - - texty = 32; - grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", - sans, color, 16, texty); - texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); - - texty += grub_font_get_ascent (fixed); - grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", - fixed, color, 16, texty); - texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); + color = grub_video_map_rgb (255, 255, 0); + grub_font_draw_string ("ABCDEFG", fixed, color, 16, 100); + color = grub_video_map_rgb (128, 128, 255); + grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", + fixed, color, 16, 150); + + color = grub_video_map_rgb (255, 255, 255); + glyph = grub_font_get_glyph (fixed, '*'); + + for (i = 0; i < 16; i++) + { + color = grub_video_map_color (i); + palette[i] = color; + grub_font_draw_glyph (glyph, color, 16 + i * 16, 220); + } + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + for (i = 0; i < 255; i++) + { + color = grub_video_map_rgb (i, 33, 77); + grub_video_fill_rect (color, 0, 0, width, height); + grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, 0, 0, + 0, 0, width, height); + } + + color = grub_video_map_rgb (255, 255, 0); + grub_font_draw_string ("Press a key to continue.", fixed, color, 10, 60); + grub_video_swap_buffers (); + grub_getkey (); + grub_video_fill_rect (bg, 0, 0, info_width, info_height); /* Clear info. */ + + /* Test VBE set display start address. */ + /* This should scroll the screen, first vertically, + * then horizontally. The horizontal scrolling seems to + * only have a resolution of about 16 pixels on my VIA Mini-ITX. */ + grub_font_draw_string ("Testing VBE 'Set display start' operation " + "for horizontal scrolling...", + fixed, color, 10, 20); + grub_video_swap_buffers (); + int vbestatus; + int vbeok = 0; + int vbeerr = 0; + for (i = 0; i < 50; i++) + { + vbestatus = grub_vbe_bios_set_display_start (0, i); + if (vbestatus == GRUB_VBE_STATUS_OK) + vbeok++; + else + vbeerr++; + } + + grub_font_draw_string ("Press a key to continue.", fixed, color, 10, 60); + grub_video_swap_buffers (); + grub_getkey (); + grub_video_fill_rect (bg, 0, 0, info_width, info_height); /* Clear info. */ + grub_video_swap_buffers (); + + grub_font_draw_string ("Testing VBE 'Set display start' operation " + "for vertical scrolling...", + fixed, color, 10, 40); + grub_video_swap_buffers (); + for (i = 0; i < 50; i++) + { + vbestatus = grub_vbe_bios_set_display_start (i, 50); + if (vbestatus == GRUB_VBE_STATUS_OK) + vbeok++; + else + vbeerr++; + } + + grub_font_draw_string ("Press a key to continue.", fixed, color, 10, 60); + grub_video_swap_buffers (); + grub_getkey (); + grub_video_fill_rect (bg, 0, 0, info_width, info_height); /* Clear info. */ + grub_video_swap_buffers (); + + grub_video_delete_render_target (text_layer); + grub_video_restore (); + + grub_printf ("VBE set_display_start status: %d\n", vbestatus); + grub_printf ("ok: %d\n", vbeok); + grub_printf ("errors: %d\n", vbeerr); + + grub_errno = GRUB_ERR_NONE; +} + + + +/* Simple opaque image blit test. + Returns the error status in grub_errno. */ +static void +bitmap_demo (struct videotest_options *vt_opts) +{ + if (grub_video_set_mode (preferred_mode_list, 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + const int bm1width = 500, bm1height = 400; + struct grub_video_bitmap *bitmap1; + + if (grub_video_bitmap_create (&bitmap1, bm1width, bm1height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888) + != GRUB_ERR_NONE) + return; + + int offset = 0; + int x; + int y; + grub_uint8_t *data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bm1height; y++) + { + for (x = 0; x < bm1width; x++) + { + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + } + } + + /* Blit the entire bitmap in the center of the screen. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + (view.width - bm1width) / 2, + view.y + (view.height - bm1height) / 2, + 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + grub_getkey (); + + /* Blit more copies of the bitmap. */ + /* Upper left. */ + grub_video_blit_bitmap (bitmap1, GRUB_VIDEO_BLIT_REPLACE, + view.x, view.y, 0, 0, bm1width, bm1height); + /* Upper right. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + view.width - bm1width, + view.y, 0, 0, bm1width, bm1height); + /* Lower left. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x, + view.y + view.height - bm1height, + 0, 0, bm1width, bm1height); + /* Lower right. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + view.width - bm1width, + view.y + view.height - bm1height, + 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + grub_getkey (); + + int clearbg = 0; /* Boolean flag: whether to fill background. */ + grub_video_color_t bgcolor = grub_video_map_rgb (16, 16, 96); + + /* Animate the image sliding in. End when a key is pressed. */ + int vscale = 1000; + int velocityx = -5000; + int velocityy = -8000; + int positionx = 100; + int positiony = 300; + + grub_uint32_t frame_count = 0; + grub_uint32_t start_time = grub_get_time_ms (); + + while (grub_checkkey () == -1) + { + /* If the time limit option is set, then check if it's exceeded. */ + if (vt_opts->test_time != 0 + && grub_get_time_ms () >= start_time + vt_opts->test_time * 1000) + break; + + int newx = positionx + velocityx / vscale; + int newy = positiony + velocityy / vscale; + + /* Check collision w/ left */ + if (newx < view.x && velocityx < 0) + { + velocityx = -velocityx; + newx = positionx + velocityx / vscale; + } + /* Check collision w/ right */ + if (newx + bm1width > view.x + view.width && velocityx > 0) + { + velocityx = -velocityx; + newx = positionx + velocityx / vscale; + } + /* Check collision w/ top */ + if (newy < 0 && velocityy < 0) + { + velocityy = -velocityy; + newy = positiony + velocityy / vscale; + } + /* Check collision w/ bottom */ + if (newy + bm1height > view.y + view.height && velocityy > 0) + { + velocityy = -velocityy; + newy = positiony + velocityy / vscale; + } + + positionx = newx; + positiony = newy; + + if (clearbg) + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, + view.height); + + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + positionx, + view.y + positiony, 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + frame_count++; + + /* Acceleration due to gravity... + * note that the y coordinate is increasing downward. */ + velocityy += vscale / 7; + } + + /* Calculate average frame rate. */ + grub_uint32_t duration = grub_get_time_ms () - start_time; + grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration; + + /* Eat the keystroke. */ + if (grub_checkkey () != -1) + grub_getkey (); + + grub_video_bitmap_destroy (bitmap1); + grub_video_restore (); + + grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10); + + grub_errno = GRUB_ERR_NONE; +} + + + +/* Configuration settings for a benchmark run. */ +struct benchmark_config +{ + const char *modespec; + int use_rgba_bitmaps; +}; + +/* The video configurations to use for the benchmark. */ +static struct benchmark_config benchmark_configs[] = { + {"320x200", 0}, + {"640x480", 0}, + {"1024x768", 0}, + {"1024x768", 1}, +}; + +#define NUM_BENCHMARK_CONFIGS (sizeof(benchmark_configs) \ + / sizeof(benchmark_configs[0])) + +struct benchmark_result +{ + /* If set to 1, the test was able to run successfully; + 0 means there was an error. */ + int test_passed; + + /* Dimensions of the video mode used. */ + int width; + int height; + + /* Bits per pixel for the video mode used. */ + int bpp; + + /* All fps are in fps * 10 to achieve one decimal place. */ + /* Set to 0 to indicate the test could not be run. */ + grub_int32_t fill_fps; + grub_int32_t blit_fps; + grub_int32_t blend_fps; + grub_int32_t text_ascii_cps; + grub_int32_t text_unicode_cps; +}; + +#define BENCHMARK_RESULT_FPS_SCALE 10 +#define BENCHMARK_RESULT_CPS_SCALE 1 + +static void +move_rectangle_one_step (int *x, int *y, + int width, int height, + int *vx, int *vy, const grub_video_rect_t * bounds) +{ + int newx = *x + *vx; + int newy = *y + *vy; + + /* Check collision w/ left */ + if (newx < bounds->x && *vx < 0) + { + *vx = -*vx; + newx = *x + *vx; + } + /* Check collision w/ right */ + if (newx + width > bounds->x + bounds->width && *vx > 0) + { + *vx = -*vx; + newx = *x + *vx; + } + /* Check collision w/ top */ + if (newy < 0 && *vy < 0) + { + *vy = -*vy; + newy = *y + *vy; + } + /* Check collision w/ bottom */ + if (newy + height > bounds->y + bounds->height && *vy > 0) + { + *vy = -*vy; + newy = *y + *vy; + } + + *x = newx; + *y = newy; +} + +/* Get the length of a UTF-8 encoded character string. */ +static int +strlen_utf8 (const char *utf8str) +{ + int len; + const grub_uint8_t *ptr; + grub_uint32_t code; + + for (len = 0, ptr = (const grub_uint8_t *) utf8str; + grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; ) + len++; + + return len; +} + +/* Test text rendering performance. */ +static void +benchmark_text (const char *test_str, + grub_int32_t *cps_result, + struct videotest_options *vt_opts, + grub_video_rect_t *view) +{ + grub_uint32_t char_count; + grub_uint64_t start_time; + grub_uint64_t desired_stop_time; + grub_uint32_t duration; + grub_video_color_t black; + grub_video_color_t text_colors[4]; + grub_font_t font; + int phase; + int test_str_len; + + test_str_len = strlen_utf8 (test_str); + black = grub_video_map_rgb (0, 0, 0); + text_colors[0] = grub_video_map_rgb (44, 44, 54); + text_colors[1] = grub_video_map_rgb (64, 64, 96); + text_colors[2] = grub_video_map_rgb (80, 80, 140); + text_colors[3] = grub_video_map_rgb (255, 255, 255); + font = grub_font_get ("unifont Regular 10"); + + char_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + phase = 0; + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + int color_index; + + grub_video_fill_rect (black, 0, 0, view->width, view->height); + + /* Draw a series of lines in different colors. */ + for (color_index = 0; color_index < 4; color_index++) + { + int line_number; + + for (line_number = 0; line_number < 20; line_number++) + { + int x; + int y; + + x = 10 - color_index; + y = (10 - color_index + + (line_number + 1) * phase + + grub_font_get_height (font) * line_number + + grub_font_get_ascent (font)); + grub_font_draw_string (test_str, font, + text_colors[color_index], x, y); + /* Note: Using grub_strlen is not UTF-8-safe. */ + char_count += test_str_len; + } + } + + phase += 2; + if (phase > 15) + phase = 0; + + grub_video_swap_buffers (); + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was one. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + *cps_result = 0; + } + else + { + *cps_result = (BENCHMARK_RESULT_CPS_SCALE * 1000 + * char_count / duration); + } +} + +/* Run the benchmark test for a particular video mode, which is specified + by `*config'. The results of the test are stored in `*result'. */ +static void +do_benchmark (const struct benchmark_config *config, + struct benchmark_result *result, + struct videotest_options *vt_opts) +{ + struct grub_video_mode_info modeinfo; + + result->test_passed = 0; + result->width = 0; + result->height = 0; + result->bpp = 0; + result->fill_fps = 0; + result->blit_fps = 0; + result->blend_fps = 0; + + if (grub_video_set_mode (config->modespec, 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + if (grub_video_get_info (&modeinfo) == GRUB_ERR_NONE) + { + result->width = modeinfo.width; + result->height = modeinfo.height; + result->bpp = modeinfo.bpp; + } + + /* Full screen bitmap blit test. */ + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + + /* For measuring timing. */ + grub_uint32_t frame_count; + grub_uint64_t start_time; + grub_uint64_t desired_stop_time; + grub_uint32_t duration; + + + /*** FILL TEST ***/ + + /* Alternates between 0 and 1 to change the color. */ + int color_flag = 0; + grub_video_color_t fillcolors[2]; + fillcolors[0] = grub_video_map_rgb (0, 0, 255); + fillcolors[1] = grub_video_map_rgb (255, 255, 0); + + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + grub_video_fill_rect (fillcolors[color_flag], + view.x, view.y, view.width, view.height); + grub_video_swap_buffers (); + color_flag ^= 1; + frame_count++; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was one. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->fill_fps = 0; + } + else + { + result->fill_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + + /*** BLIT TEST ***/ + + /* Generate two bitmaps, the same size as the screen. */ + const int bitmapwidth = view.width, bitmapheight = view.height; + struct grub_video_bitmap *bitmap1; + struct grub_video_bitmap *bitmap2; + enum grub_video_blit_format bitmap_format = config->use_rgba_bitmaps + ? GRUB_VIDEO_BLIT_FORMAT_RGBA_8888 : GRUB_VIDEO_BLIT_FORMAT_RGB_888; + + if (grub_video_bitmap_create (&bitmap1, bitmapwidth, bitmapheight, + bitmap_format) != GRUB_ERR_NONE) + return; + + if (grub_video_bitmap_create (&bitmap2, bitmapwidth, bitmapheight, + bitmap_format) != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (bitmap1); + return; + } + + int offset; + int x; + int y; + grub_uint8_t *data; + + offset = 0; + data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bitmapheight; y++) + { + for (x = 0; x < bitmapwidth; x++) + { + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + if (config->use_rgba_bitmaps) + data[offset++] = 255; + } + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap2); + for (y = 0; y < bitmapheight; y++) + { + for (x = 0; x < bitmapwidth; x++) + { + data[offset++] = x + y; /* red */ + data[offset++] = x * x + y * y; /* green */ + data[offset++] = x * x / 4 + y * y / 4; /* blue */ + if (config->use_rgba_bitmaps) + data[offset++] = 255; + } + } + + + /* Now do the blit test, alternating between the two bitmaps. */ + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + int cur = 0; /* Which bitmap to draw this frame. */ + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + struct grub_video_bitmap *current_bitmap = cur == 0 ? bitmap1 : bitmap2; + grub_video_blit_bitmap (current_bitmap, + GRUB_VIDEO_BLIT_REPLACE, + view.x, view.y, 0, 0, bitmapwidth, + bitmapheight); + grub_video_swap_buffers (); + frame_count++; + cur ^= 1; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was once. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->blit_fps = 0; + } + else + { + result->blit_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + grub_video_bitmap_destroy (bitmap2); + grub_video_bitmap_destroy (bitmap1); + + + + /*** BLEND TEST ***/ + + /* Generate two bitmaps, with alpha translucency. */ + const int bbw = view.width * 2 / 3; + const int bbh = view.height * 2 / 3; + + if (grub_video_bitmap_create (&bitmap1, bbw, bbh, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) != + GRUB_ERR_NONE) + return; + + if (grub_video_bitmap_create (&bitmap2, bbw, bbh, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) != + GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (bitmap1); + return; + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bbh; y++) + { + for (x = 0; x < bbw; x++) + { + /* Calculate a to be increasing away from the center. */ + int dx = 256 * (x - bbw / 2) / (bbw / 2); + int dy = 256 * (y - bbh / 2) / (bbh / 2); + int a = dx * dx + dy * dy; + /* range for a = 0 .. 2*(256^2) = 2*2^16 = 2^17 */ + a >>= 17 - 8; /* Make range 0..256. */ + if (a > 255) + a = 255; + + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + data[offset++] = 255 - a; + } + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap2); + for (y = 0; y < bbh; y++) + { + for (x = 0; x < bbw; x++) + { + data[offset++] = x + y; /* red */ + data[offset++] = x * x + y * y; /* green */ + data[offset++] = x * x / 4 + y * y / 4; /* blue */ + data[offset++] = 255; + } + } + + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + + grub_video_color_t bgcolor = grub_video_map_rgb (80, 80, 80); + + /* Bitmap locations. */ + int b1x = 0; + int b1y = 0; + int b2x = view.width - bbw; + int b2y = 0; + /* Bitmap velocities. */ + int b1vx = 8; + int b1vy = 12; + int b2vx = -10; + int b2vy = 9; + + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + move_rectangle_one_step (&b1x, &b1y, bbw, bbh, &b1vx, &b1vy, &view); + move_rectangle_one_step (&b2x, &b2y, bbw, bbh, &b2vx, &b2vy, &view); + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height); + grub_video_blit_bitmap (bitmap2, + GRUB_VIDEO_BLIT_BLEND, + b2x, b2y, 0, 0, bbw, bbh); + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_BLEND, + b1x, b1y, 0, 0, bbw, bbh); + grub_video_swap_buffers (); + frame_count++; + cur ^= 1; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was once. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->blend_fps = 0; + } + else + { + result->blend_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + grub_video_bitmap_destroy (bitmap2); + grub_video_bitmap_destroy (bitmap1); + + /* Text benchmark with 104 characters of English ASCII text. */ + benchmark_text ("The quick brown fox jumped over the lazy dog. " + "(1234567890). " + "THE RAIN IN SPAIN FALLS MAINLY ON THE PLAIN.", + &result->text_ascii_cps, vt_opts, &view); + /* Text benchmark with 104 Unicode characters pseudorandomly uniformly + distributed over the characters provided by GNU unifont. The string is + UTF-8 encoded. */ + benchmark_text ("\xE4\x94\x93\xEB\x83\xA6\xE2\x94\x85\xEB\xA8\xB1\xEE\x81" + "\xA3\xDD\x9D\xEA\xBC\xA4\xE1\xA3\x86\xEE\x8C\xA5\xE5\x8A" + "\x81\xEC\x99\x8A\xE0\xAE\xA1\xE8\xB4\xAC\xE4\x9A\xBD\xE6" + "\xB5\xAD\xED\xA4\x98\xEA\x88\x92\xE7\xA2\x9C\xEE\xAC\x8C" + "\xE9\x95\x9C\xE5\xB0\x89\xEB\x82\xA2\xE3\x9E\x89\xE8\xA4" + "\x87\xE4\xAC\xA0\xE8\x89\xA4\xE7\x8E\xAC\xE6\xA2\x95\xEE" + "\xAC\xB9\xE5\x9C\x8D\xEC\x8A\xA5\xE6\xB5\xA2\xE1\xA5\xBA" + "\xE2\x93\x80\xE6\x92\xA9\xE3\x9B\xB3\xE2\xB0\x9D\xE2\x95" + "\x94\xE7\xAD\x89\xE2\x83\x88\xEA\x8D\xA5\xE5\x8C\x99\xE5" + "\x9F\xB9\xE4\x88\x97\xEC\x95\xA6\xC9\xBD\xE5\xA1\x86\xE7" + "\xA3\xBE\xEA\x9A\xA9\xE5\x93\x98\xE4\xAD\xB1\xE1\x90\xB8" + "\xE4\x8A\x91\xE8\x8B\xBA\xE9\xB5\x80\xE8\xB6\xB1\xE4\x89" + "\xB4\xE4\xB8\x82\xDF\x8C\xE6\xAB\x83\xEA\x94\x8F\xEC\xA9" + "\xB1\xED\xA0\xA6\xEB\xBA\x89\xE2\xB1\x88\xE7\xA7\xA5\xE3" + "\x8A\x92\xE5\xA1\xA5\xEC\xAB\x89\xEA\xB7\x9B\xEA\x92\xBE" + "\xEA\xAD\x85\xE3\xB8\x8A\xE0\xB8\xBD\xEE\xB5\x9C\xE1\x93" + "\xB7\xE3\xB1\x8A\xE8\x8A\xB8\xEB\xA6\x85\xEE\x8B\xB4\xE1" + "\x92\xA7\xE4\x88\x8C\xE0\xA2\xB2\xE5\x9C\xB8\xEC\x94\x86" + "\xED\x86\x82\xE2\x87\xBF\xE1\xA4\x81\xE3\x84\x8A\xE2\xA7" + "\x8B\xE8\x8F\x84\xED\x98\x9A\xE3\x85\x92\xE9\xA4\x80\xED" + "\x86\xB9\xE5\xB6\x9A\xE4\xBF\xBC\xE1\x97\x92\xEE\x86\x90" + "\xE2\xB1\x8B\xC3\x83\xEC\x8D\xA4\xED\x9E\x90\xE3\xBB\x8E", + &result->text_unicode_cps, vt_opts, &view); + + grub_video_restore (); + result->test_passed = 1; +} + +/* Run a benchmark test in a series of video modes. + The results are reported in tabular form. This will be helpful to + determine how effective various optimizations are. */ +static void +benchmark_test (struct videotest_options *vt_opts) +{ + unsigned int i; + struct benchmark_result results[NUM_BENCHMARK_CONFIGS]; + + /* Set option default values. */ + if (vt_opts->test_time == 0) + vt_opts->test_time = DEFAULT_TEST_TIME; + + /* Run benchmarks. */ + for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++) + { + grub_error_push (); + do_benchmark (&benchmark_configs[i], &results[i], vt_opts); + } + + grub_print_error (); + + /* Display results. */ + grub_printf ("Benchmark results (in frames/s):\n"); + grub_printf ("(W=Width, H=Height, B=Bits per pixel, A=Bitmap Alpha)\n"); + grub_printf (" TEXT chars/s\n"); + grub_printf (" W H B A FILL BLIT BLEND ASCII UNICODE\n"); + for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++) + { + struct benchmark_config *c = &benchmark_configs[i]; + struct benchmark_result *r = &results[i]; + + if (r->test_passed) + { + grub_printf ("%4dx%4d %2d %d: %4d.%d %4d.%d %4d.%d %5d %5d\n", + r->width, r->height, r->bpp, + c->use_rgba_bitmaps, + r->fill_fps / BENCHMARK_RESULT_FPS_SCALE, + r->fill_fps % BENCHMARK_RESULT_FPS_SCALE, + r->blit_fps / BENCHMARK_RESULT_FPS_SCALE, + r->blit_fps % BENCHMARK_RESULT_FPS_SCALE, + r->blend_fps / BENCHMARK_RESULT_FPS_SCALE, + r->blend_fps % BENCHMARK_RESULT_FPS_SCALE, + r->text_ascii_cps / BENCHMARK_RESULT_CPS_SCALE, + r->text_unicode_cps / BENCHMARK_RESULT_CPS_SCALE); + } + else + { + grub_printf ("%s Not supported.\n", c->modespec); + } + } + + grub_errno = GRUB_ERR_NONE; +} + + +/* Test time functions. */ +static void +clock_test (struct videotest_options *vt_opts) +{ + if (grub_video_set_mode ("640x480", 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + /* Draw a progress bar that animates in sync with time. */ + + grub_video_rect_t bar_frame; + bar_frame.width = view.width - 2 * view.width / 10; + bar_frame.height = view.height / 20; + bar_frame.x = view.x + view.width / 10; + bar_frame.y = view.y + view.height - bar_frame.height - view.height / 10; + + grub_video_color_t bgcolor = grub_video_map_rgb (50, 50, 50); + grub_video_color_t framecolor = grub_video_map_rgb (255, 255, 255); + grub_video_color_t barbgcolor = grub_video_map_rgb (0, 0, 128); + grub_video_color_t barcolor = grub_video_map_rgb (100, 100, 255); + + grub_uint32_t frame_count; + grub_uint64_t start_time; + grub_uint64_t barstart; + grub_uint32_t barlength = 1000; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + frame_count = 0; + start_time = grub_get_time_ms (); + barstart = grub_get_time_ms (); + + while (grub_checkkey () == -1) + { + grub_uint64_t now; + grub_uint32_t bartime; + now = grub_get_time_ms (); + /* If the time limit option is set, then check if it's exceeded. */ + if (vt_opts->test_time != 0 + && now >= start_time + vt_opts->test_time * 1000) + break; + bartime = now - barstart; + if (bartime > barlength) + { + barstart = grub_get_time_ms (); /* Start over. */ + bartime = barlength; + } + + /* Clear screen. */ + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height); + + /* Border. */ + grub_video_fill_rect (framecolor, + bar_frame.x - 1, bar_frame.y - 1, + bar_frame.width + 2, bar_frame.height + 2); + + /* Bar background. */ + int barwidth = bar_frame.width * bartime / barlength; + grub_video_fill_rect (barbgcolor, bar_frame.x + barwidth, + bar_frame.y, bar_frame.width - barwidth, + bar_frame.height); + /* Bar foreground. */ + grub_video_fill_rect (barcolor, bar_frame.x, bar_frame.y, + barwidth, bar_frame.height); + grub_video_swap_buffers (); + frame_count++; + } + + grub_uint32_t duration = grub_get_time_ms () - start_time; + grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration; + + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was one. */ + grub_video_restore (); + grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10); +} + + +/* Test double buffering. */ +static void +doublebuf_test (struct videotest_options *vt_opts) +{ + if (grub_video_set_mode ("640x480", 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + grub_video_color_t red = grub_video_map_rgb (255, 50, 50); + grub_video_color_t yellow = grub_video_map_rgb (255, 255, 0); + grub_video_color_t green = grub_video_map_rgb (20, 255, 20); + grub_video_color_t blue = grub_video_map_rgb (50, 50, 255); + grub_video_color_t black = grub_video_map_rgb (0, 0, 0); + grub_video_color_t bgcolor = grub_video_map_rgb (255, 255, 255); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (red, 100, 100, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (yellow, 120, 120, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (green, 140, 140, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (blue, 160, 160, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (black, 180, 180, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_restore (); +} + + +/* Test text rendering. */ +static void +text_test (struct videotest_options *vt_opts) +{ + grub_video_color_t color; + const char *s; + int view_x; + int view_y; + int view_width; + int view_height; + int xpos; + int ypos; + int i; + grub_font_t font1; + grub_font_t font2; + grub_font_t font3; + grub_font_t sansbig; + grub_font_t sans; + grub_font_t sanssmall; + grub_font_t fixed; + + if (grub_video_set_mode (preferred_mode_list, 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + grub_video_get_viewport ((unsigned int *) &view_x, + (unsigned int *) &view_y, + (unsigned int *) &view_width, + (unsigned int *) &view_height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + if (!(font1 = grub_font_get ("New Century Schoolbook Regular 24")) + || !(font2 = grub_font_get ("Helvetica Bold 14")) + || !(font3 = grub_font_get ("Helvetica Regular 10")) + || !(sansbig = grub_font_get ("Helvetica Bold 24")) + || !(sans = grub_font_get ("Helvetica Bold 14")) + || !(sanssmall = grub_font_get ("Helvetica Regular 8")) + || !(fixed = grub_font_get ("Fixed Regular 20"))) + { + grub_video_restore (); + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } + + color = grub_video_map_rgb (0, 0, 0); + grub_video_fill_rect (color, 0, 0, view_width, view_height); + + color = grub_video_map_rgb (255, 0, 0); + grub_video_fill_rect (color, 0, 0, 100, 100); + + color = grub_video_map_rgb (0, 255, 255); + grub_video_fill_rect (color, 100, 100, 100, 100); + + color = grub_video_map_rgb (255, 255, 255); + + xpos = 10; + ypos = 30; + s = "Hello, World!"; + for (i = 0; i < 24; i++) + { + if (xpos + grub_font_get_string_width (font1, s) >= view_width) + { + /* The string will wrap; go to the beginning of the next line. */ + xpos = 10; + ypos += (grub_font_get_descent (font1) + + grub_font_get_ascent (font1)); + } + grub_font_draw_string ("Hello, World!", + font1, grub_video_map_rgb (255, 255, 0), + xpos, ypos); + + xpos += grub_font_get_string_width (font1, s); + } + + xpos = 240; + ypos = 280; + grub_font_draw_string (grub_font_get_name (font1), + font1, grub_video_map_rgb (255, 255, 255), + xpos, ypos); + ypos += grub_font_get_descent (font1) + grub_font_get_ascent (font2) + 2; + grub_font_draw_string (grub_font_get_name (font2), + font2, grub_video_map_rgb (255, 255, 255), + xpos, ypos); + ypos += grub_font_get_descent (font2) + grub_font_get_ascent (font3) + 2; + grub_font_draw_string (grub_font_get_name (font3), + font3, grub_video_map_rgb (255, 255, 255), + xpos, ypos); + + ypos += 40; /* Spacing. */ + + grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", + sans, color, view_x + 16, ypos); + ypos += grub_font_get_descent (sans) + grub_font_get_leading (sans); + + ypos += grub_font_get_ascent (fixed); + grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", + fixed, color, 16, ypos); + ypos += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); + + ypos += 40; /* Spacing. */ /* To convert Unicode characters into UTF-8 for this test, the following command is useful: @@ -112,76 +1149,252 @@ U+2287 subset symbol E2 8A 87 U+211D set 'R' symbol (real numbers) E2 84 9D */ - str = + s = "Unicode test: happy\xE2\x98\xBA \xC2\xA3 5.00" " \xC2\xA1\xCF\x84\xC3\xA4u! " " \xE2\x84\xA4\xE2\x8A\x87\xE2\x84\x9D"; color = grub_video_map_rgb (128, 128, 255); /* All characters in the string exist in the 'Fixed 20' (10x20) font. */ - texty += grub_font_get_ascent(fixed); - grub_font_draw_string (str, fixed, color, 16, texty); - texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); - - /* Some character don't exist in the Helvetica font, so the font engine - will fall back to using glyphs from another font that does contain them. - TODO The font engine should be smart about selecting a replacement font - and prioritize fonts with similar sizes. */ - - texty += grub_font_get_ascent(sansbig); - grub_font_draw_string (str, sansbig, color, 16, texty); - texty += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig); - - texty += grub_font_get_ascent(sans); - grub_font_draw_string (str, sans, color, 16, texty); - texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); - - texty += grub_font_get_ascent(sanssmall); - grub_font_draw_string (str, sanssmall, color, 16, texty); - texty += (grub_font_get_descent (sanssmall) + ypos += grub_font_get_ascent (fixed); + grub_font_draw_string (s, fixed, color, 16, ypos); + ypos += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); + + /* Some characters don't exist in the Helvetica font, so the font engine + may fall back to using glyphs from another font that has them. */ + + ypos += grub_font_get_ascent (sansbig); + grub_font_draw_string (s, sansbig, color, 16, ypos); + ypos += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig); + + ypos += grub_font_get_ascent (sans); + grub_font_draw_string (s, sans, color, 16, ypos); + ypos += grub_font_get_descent (sans) + grub_font_get_leading (sans); + + ypos += grub_font_get_ascent (sanssmall); + grub_font_draw_string (s, sanssmall, color, 16, ypos); + ypos += (grub_font_get_descent (sanssmall) + grub_font_get_leading (sanssmall)); - glyph = grub_font_get_glyph (fixed, '*'); - - for (i = 0; i < 16; i++) - { - color = grub_video_map_color (i); - palette[i] = color; - grub_font_draw_glyph (glyph, color, 16 + i * 16, 220); - } + + grub_video_swap_buffers (); + grub_getkey (); + grub_video_restore (); + grub_errno = GRUB_ERR_NONE; +} + + +/* Test bitmap scaling. */ +static void +scale_test (struct videotest_options *vt_opts) +{ + if (grub_video_set_mode (preferred_mode_list, 0) != GRUB_ERR_NONE) + return; + grub_video_enable_double_buffering (vt_opts->double_buffering); + + grub_errno = GRUB_ERR_NONE; + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - for (i = 0; i < 255; i++) - { - color = grub_video_map_rgb (i, 33, 77); - grub_video_fill_rect (color, 0, 0, width, height); - grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, 0, 0, - 0, 0, width, height); - } - + grub_font_t font; + if (!(font = grub_font_get ("Helvetica Regular 10"))) + { + grub_video_restore (); + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } + + grub_video_color_t color; + + int text_y = 0; + int text_height = 25; + + color = grub_video_map_rgb (44, 44, 200); + grub_video_fill_rect (color, view.x, view.y, view.width, view.height); + color = grub_video_map_rgb (255, 255, 255); + + grub_font_draw_string ("Loading image", + font, color, 10, text_y += text_height); + + enum grub_video_bitmap_scale_method scale_method = + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST; + const char *bitmap_name = "/boot/grub/themes/winter/without-leaves.png"; + struct grub_video_bitmap *bitmap; + grub_video_bitmap_load (&bitmap, bitmap_name); + if (grub_errno != GRUB_ERR_NONE) + { + grub_font_draw_string ("Error loading bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_font_draw_string ("Original image", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND, + 400, text_y - 10, + 0, 0, grub_video_bitmap_get_width (bitmap), + grub_video_bitmap_get_height (bitmap)); + + struct grub_video_bitmap *bitmap2; + if (grub_video_bitmap_create_scaled (&bitmap2, 40, 40, + bitmap, + scale_method) + != GRUB_ERR_NONE) + { + grub_font_draw_string ("Error scaling down bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_font_draw_string ("Scaled down version", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap2, GRUB_VIDEO_BLIT_BLEND, + 400, text_y + 100, + 0, 0, grub_video_bitmap_get_width (bitmap2), + grub_video_bitmap_get_height (bitmap2)); + grub_video_bitmap_destroy (bitmap2); + } + + struct grub_video_bitmap *bitmap3; + if (grub_video_bitmap_create_scaled (&bitmap3, 500, 300, bitmap, + scale_method) + != GRUB_ERR_NONE) + { + grub_font_draw_string ("Error scaling up bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_font_draw_string ("Scaled up version", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap3, GRUB_VIDEO_BLIT_BLEND, + 400, text_y + 50, + 0, 0, grub_video_bitmap_get_width (bitmap3), + grub_video_bitmap_get_height (bitmap3)); + grub_video_bitmap_destroy (bitmap3); + } + } + + grub_video_swap_buffers (); grub_getkey (); - - grub_video_delete_render_target (text_layer); - + grub_video_bitmap_destroy (bitmap); grub_video_restore (); - - for (i = 0; i < 16; i++) - grub_printf("color %d: %08x\n", i, palette[i]); - - grub_errno = GRUB_ERR_NONE; +} + + +/* Print a list of the available tests. */ +static void list_tests (void); + +/* + Video test command. Takes an argument specifying the test to run. + */ +static grub_err_t +grub_cmd_videotest (grub_extcmd_t cmd, int argc, char **args) +{ + struct grub_arg_list *state = cmd->state; + int i; + struct videotest_options vt_opts; + /* Pointer to the test function. */ + void (*test_func) (struct videotest_options *) = NULL; + + vt_opts.test_time = + state[ARGINDEX_TEST_TIME].set + ? grub_strtoul (state[ARGINDEX_TEST_TIME].arg, 0, 0) : 0; + vt_opts.double_buffering = state[ARGINDEX_DOUBLE_BUF].set; + + /* Parse command line arguments to determine the test to run. */ + for (i = 0; i < argc; i++) + { + char *arg = args[i]; + if (grub_strcmp (arg, "list") == 0) + { + list_tests (); + return GRUB_ERR_NONE; + } + else if (grub_strcmp (arg, "basic") == 0) + { + test_func = basic_video_test; + } + else if (grub_strcmp (arg, "bench") == 0) + { + test_func = benchmark_test; + } + else if (grub_strcmp (arg, "bitmaps") == 0) + { + test_func = bitmap_demo; + } + else if (grub_strcmp (arg, "clock") == 0) + { + test_func = clock_test; + } + else if (grub_strcmp (arg, "doublebuf") == 0) + { + test_func = doublebuf_test; + } + else if (grub_strcmp (arg, "text") == 0) + { + test_func = text_test; + } + else if (grub_strcmp (arg, "scale") == 0) + { + test_func = scale_test; + } + else + { + grub_printf ("Error: Unknown test `%s'\n", arg); + grub_errno = GRUB_ERR_BAD_ARGUMENT; + return grub_errno; + } + } + + if (test_func == NULL) + { + grub_printf ("Usage: videotest TESTNAME Run a test.\n"); + grub_printf (" videotest list List available tests.\n"); + grub_printf ("\n"); + list_tests (); + } + else + { + test_func (&vt_opts); + } + return grub_errno; } -static grub_command_t cmd; - -GRUB_MOD_INIT(videotest) -{ - cmd = grub_register_command ("videotest", grub_cmd_videotest, - 0, "Test video subsystem"); -} - -GRUB_MOD_FINI(videotest) -{ - grub_unregister_command (cmd); +static void +list_tests (void) +{ + grub_printf ("Available tests\n"); + grub_printf ("===============\n"); + grub_printf ("basic Basic video test with filled rectangles,\n"); + grub_printf (" offscreen rendering targets, some text.\n"); + grub_printf ("bench Run a performance benchmark.\n"); + grub_printf ("bitmaps Test generating and blitting bitmaps.\n"); + grub_printf ("clock Test time functions w/ animated progress bar.\n"); + grub_printf ("doublebuf Test double buffering.\n"); + grub_printf ("text Test text rendering.\n"); + grub_printf ("scale Test image scaling.\n"); + grub_printf ("\n"); +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT (videotest) +{ + cmd = grub_register_extcmd ("videotest", + grub_cmd_videotest, + GRUB_COMMAND_FLAG_BOTH, + "videotest TEST", + "Run the specified video subsystem test.", + arg_options); +} + +GRUB_MOD_FINI (videotest) +{ + grub_unregister_extcmd (cmd); } === modified file 'conf/common.rmk' --- conf/common.rmk 2009-06-12 01:10:21 +0000 +++ conf/common.rmk 2009-06-13 13:42:27 +0000 @@ -354,7 +354,7 @@ terminfo.mod test.mod blocklist.mod hexdump.mod \ read.mod sleep.mod loadenv.mod crc.mod parttool.mod \ pcpart.mod memrw.mod normal.mod sh.mod lua.mod \ - gptsync.mod true.mod + gptsync.mod true.mod gfxmenu.mod # For gptsync.mod. gptsync_mod_SOURCES = commands/gptsync.c @@ -376,6 +376,27 @@ hello_mod_CFLAGS = $(COMMON_CFLAGS) hello_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For gfxmenu.mod. +gfxmenu_mod_SOURCES = \ + gfxmenu/gfxmenu.c \ + gfxmenu/model.c \ + gfxmenu/view.c \ + gfxmenu/icon_manager.c \ + gfxmenu/theme_loader.c \ + gfxmenu/widget-box.c \ + gfxmenu/gui_canvas.c \ + gfxmenu/gui_circular_progress.c \ + gfxmenu/gui_box.c \ + gfxmenu/gui_label.c \ + gfxmenu/gui_list.c \ + gfxmenu/gui_image.c \ + gfxmenu/gui_progress_bar.c \ + gfxmenu/gui_util.c \ + gfxmenu/gui_string_util.c \ + gfxmenu/named_colors.c +gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS) +gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For parttool.mod. parttool_mod_SOURCES = commands/parttool.c parttool_mod_CFLAGS = $(COMMON_CFLAGS) @@ -534,7 +555,7 @@ videotest_mod_LDFLAGS = $(COMMON_LDFLAGS) # For bitmap.mod -bitmap_mod_SOURCES = video/bitmap.c +bitmap_mod_SOURCES = video/bitmap.c video/bitmap_scale.c bitmap_mod_CFLAGS = $(COMMON_CFLAGS) bitmap_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -564,7 +585,7 @@ gfxterm_mod_LDFLAGS = $(COMMON_LDFLAGS) # Misc. -pkglib_MODULES += gzio.mod bufio.mod elf.mod +pkglib_MODULES += gzio.mod bufio.mod elf.mod trig.mod # For elf.mod. elf_mod_SOURCES = kern/elf.c @@ -580,3 +601,8 @@ bufio_mod_SOURCES = io/bufio.c bufio_mod_CFLAGS = $(COMMON_CFLAGS) bufio_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For trig.mod. +trig_mod_SOURCES = lib/trig.c +trig_mod_CFLAGS = $(COMMON_CFLAGS) +trig_mod_LDFLAGS = $(COMMON_LDFLAGS) === added file 'docs/gfxmenu-theme-example.txt' --- docs/gfxmenu-theme-example.txt 1970-01-01 00:00:00 +0000 +++ docs/gfxmenu-theme-example.txt 2009-01-19 17:29:32 +0000 @@ -0,0 +1,128 @@ +# GRUB gfxmenu theme "winter". +# Uses background image from: +# http://www.cyberpunkcafe.com/e107_plugins/autogallery/autogallery.php?show=1.Open%20Source%20Wallpaper +# "without-leaves.png" was called "Without Leafs in Winter.png" + +lua-script: "winter.lua" +title-text: "" +title-font: "Helvetica Bold 18" +status-font: "Helvetica 8" +terminal-font: "Fixed 9" +title-color: "40, 40, 40" +status-color: "#FFF" +status-bg-color: "0, 166, 183, 128" +desktop-image: "without-leaves.png" +desktop-color: "0, 154, 183" +terminal-box: "terminal_*.png" + ++ boot_menu { + position = (120, 60) + preferred_size = (400, -1) + item_font = "Helvetica Bold 14" + selected_item_font = "Helvetica Bold 14" + item_color = "0, 0, 0" + selected_item_color = "203, 251, 255" + menu_pixmap_style = "menu_*.png" + selected_item_pixmap_style = "select_*.png" + icon_width = 44 + icon_height = 44 + item_height = 32 + item_padding = 0 + item_icon_space = 3 + item_spacing = 11 +} + +# You can add text at arbitrary locations on the screen. +# The specification within the "+label {...}" block is free-form, +# so you can use as much or as little white space as you like. + ++ label { + position = (170, 50) + font = "smoothansi 13" + color = "0,0,128" + text = "This is the Winter theme ... brought to you by GRUB!" +} + +# Show the text alignment supported by labels. ++ vbox { + position = (220, 347) + preferred_size = (200, -1) # A preferred size of -1 means automatic. + + label { text="Text alignment demo" align="center" font="aqui 11" } + + label { text="Left" align="left" font="cure 11" } + + label { text="Center" align="center" font="cure 11" } + + label { text="Right" align="right" font="cure 11" } +} + ++ vbox { + position = (580, 10) + + label { text="GNU" font="gelly 11" color="0, 0, 0" } + + label { text="GRUB" font="aqui 11" color="0, 0, 0" } + + label { text="boot loader" font="cure 11" color="0, 0, 0" } +} + ++ hbox { + position = (80, 10) + + label { text="GNU" font="gelly 11" color="0, 0, 0" } + + label { text="GRUB" font="aqui 11" color="0, 0, 0" } + + label { text="boot loader" font="cure 11" color="0, 0, 0" } +} + +# Demonstration of a compound layout: boxes within boxes. ++ hbox +{ + position = (480, 3) + + + vbox + { + # Note: We can't just use 'size' to set the image's size, + # since the vbox will resize the component according to its + # preferred size, which for images is the native image size. + + + image { file="/boot/grub/themes/icons/ubuntu.png" + preferred_size = (20, 20) } + + image { file="/boot/grub/themes/icons/gentoo.png" + preferred_size = (20, 20) } + } + + + vbox + { + + label { text="GRand" font="cure 11" color=#99F } + + label { text="Unified" font="cure 11" color=#BBF } + + label { text="Bootloader" font="cure 11" color=#DDF } + } +} + +# By defining a 'progress_bar' type component with an ID of '__timeout__', +# the progress bar will be used to display the time remaining before an +# the default entry is automatically booted. ++ progress_bar +{ + id = "__timeout__" + position = (80, 393) + preferred_size = (500, 24) + font = "cure 11" + text_color = #000 + fg_color = #CCF + bg_color = #66B + border_color = #006 + show_text = false +} + +# Although the progress_bar component is normally used to indicate the +# time remaining, it's also possible to create other components with an ID +# of '__timeout__'. All components with and ID of 'timeout_bar' will have +# the following properties set based on the timeout value: +# text, value, start, end, visible. +# In this case, we have set 'show_text=false' on the progress bar, and use +# the following label's 'text' property to display the message. ++ label +{ + id = "__timeout__" + position = (80, 420) + preferred_size = (500, 24) + font = "lime 11" + color = #117 + align = "center" +} + + === added file 'gentrigtables.py' --- gentrigtables.py 1970-01-01 00:00:00 +0000 +++ gentrigtables.py 2009-01-31 17:06:21 +0000 @@ -0,0 +1,58 @@ +#!/usr/bin/python +# Script to generate trigonometric function tables. +# +# GRUB -- GRand Unified Bootloader +# Copyright (C) 2008 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +from math import * +from sys import stdout + +def write(x): + stdout.write(x) + +def writeTable(arr, name): + indent = ' ' * 4 + write("short ") + write(name) + write("[] =\n{\n") + write(indent) + for i in range(len(arr)): + if i != 0: + write(",") + if i % 10 == 0: + write("\n") + write(indent) + write("%d" % arr[i]) + write("\n};\n") + +def main(): + sintab = [] + costab = [] + for i in range(256): + # Convert to an angle in 1/256 of a circle. + x = i * 2 * pi / 256 + sintab.append(int(round(sin(x) * 16384))) + costab.append(int(round(cos(x) * 16384))) + + write("#define TRIG_ANGLE_MAX 256\n") + write("#define TRIG_FRACTION_SCALE 16384\n") + writeTable(sintab, "sintab") + writeTable(costab, "costab") + +if __name__ == "__main__": + main() + +# vim:ai et sw=4 ts=4 === added directory 'gfxmenu' === added file 'gfxmenu/gfxmenu.c' --- gfxmenu/gfxmenu.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gfxmenu.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,235 @@ +/* gfxmenu.c - Graphical menu interface controller. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void switch_to_text_menu (void) +{ + grub_env_set ("menuviewer", "text"); +} + +static void +process_key_press (int c, + grub_gfxmenu_model_t model, + grub_gfxmenu_view_t view, + int nested, + int *should_exit) +{ + /* When a key is pressed, stop the timeout. */ + grub_gfxmenu_model_clear_timeout (model); + + if (c == 'j' || c == GRUB_TERM_DOWN) + { + int i = grub_gfxmenu_model_get_selected_index (model); + int num_items = grub_gfxmenu_model_get_num_entries (model); + if (i < num_items - 1) + { + i++; + grub_gfxmenu_model_set_selected_index (model, i); + } + } + else if (c == 'k' || c == GRUB_TERM_UP) + { + int i = grub_gfxmenu_model_get_selected_index (model); + if (i > 0) + { + i--; + grub_gfxmenu_model_set_selected_index (model, i); + } + } + else if (c == '\r' || c == '\n' || c == GRUB_TERM_RIGHT) + { + int selected = grub_gfxmenu_model_get_selected_index (model); + int num_entries = grub_gfxmenu_model_get_num_entries (model); + if (selected >= 0 && selected < num_entries) + { + grub_menu_entry_t entry = + grub_gfxmenu_model_get_entry (model, selected); + grub_gfxmenu_view_execute_entry (view, entry); + } + } + else if (c == 'c') + { + grub_gfxmenu_view_run_terminal (view); + } + else if (c == 't') + { + /* The write hook for 'menuviewer' will cause + * grub_menu_viewer_should_return to return nonzero. */ + switch_to_text_menu (); + *should_exit = 1; + } + else if (c == '1') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/proto/theme.txt"); + } + else if (c == '2') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/winter/theme.txt"); + } + else if (c == '3') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/ubuntu1/theme.txt"); + } + else if (c == '4') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/ubuntu2/theme.txt"); + } + else if (nested && c == GRUB_TERM_ESC) + { + *should_exit = 1; + } + + if (grub_errno != GRUB_ERR_NONE) + *should_exit = 1; +} + +static void +handle_key_events (grub_gfxmenu_model_t model, + grub_gfxmenu_view_t view, + int nested, + int *should_exit) +{ + while ((! *should_exit) && (grub_checkkey () != -1)) + { + int key = grub_getkey (); + int c = GRUB_TERM_ASCII_CHAR (key); + process_key_press (c, model, view, nested, should_exit); + } +} + +static grub_err_t +show_menu (grub_menu_t menu, int nested) +{ + grub_gfxmenu_model_t model; + + model = grub_gfxmenu_model_new (menu); + if (! model) + { + grub_print_error (); + grub_printf ("Initializing menu data for graphical menu failed;\n" + "falling back to text based menu.\n"); + grub_wait_after_message (); + switch_to_text_menu (); + return grub_errno; + } + + grub_gfxmenu_view_t view; + + /* Create the view. */ + const char *theme_path = grub_env_get ("theme"); + if (! theme_path) + theme_path = "/boot/grub/themes/proto/theme.txt"; + + view = grub_gfxmenu_view_new (theme_path, model); + if (! view) + { + grub_print_error (); + grub_printf ("Starting graphical menu failed;\n" + "falling back to text based menu.\n"); + grub_wait_after_message (); + grub_gfxmenu_model_destroy (model); + switch_to_text_menu (); + return grub_errno; + } + + /* Initially select the default menu entry. */ + int default_index = grub_menu_get_default_entry_index (menu); + grub_gfxmenu_model_set_selected_index (model, default_index); + + /* Start the timer to execute the default entry. */ + grub_gfxmenu_model_set_timeout (model); + + /* Main event loop. */ + int exit_requested = 0; + while ((! exit_requested) && (! grub_menu_viewer_should_return ())) + { + if (grub_gfxmenu_model_timeout_expired (model)) + { + grub_gfxmenu_model_clear_timeout (model); + int i = grub_gfxmenu_model_get_selected_index (model); + grub_menu_entry_t e = grub_gfxmenu_model_get_entry (model, i); + grub_gfxmenu_view_execute_with_fallback (view, e); + continue; + } + + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); + handle_key_events (model, view, nested, &exit_requested); + } + + grub_gfxmenu_view_destroy (view); + grub_gfxmenu_model_destroy (model); + + return grub_errno; +} + +static grub_err_t +grub_cmd_gfxmenu (grub_command_t cmd UNUSED, + int argc UNUSED, char **args UNUSED) +{ + grub_menu_t menu = grub_env_get_data_slot ("menu"); + if (! menu) + return grub_error (GRUB_ERR_MENU, "no menu context"); + + return show_menu (menu, 1); +} + +static struct grub_menu_viewer menu_viewer = +{ + .name = "gfxmenu", + .show_menu = show_menu +}; + +static grub_command_t cmd; + +GRUB_MOD_INIT (gfxmenu) +{ + (void) mod; /* To stop warning. */ + grub_menu_viewer_register (&menu_viewer); + cmd = grub_register_command ("gfxmenu", grub_cmd_gfxmenu, + "gfxmenu", + "Show graphical menu interface"); +} + +GRUB_MOD_FINI (gfxmenu) +{ + grub_unregister_command (cmd); +} === added file 'gfxmenu/gui_box.c' --- gfxmenu/gui_box.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_box.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,372 @@ +/* gui_box.c - GUI container that stack components. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct component_node +{ + grub_gui_component_t component; + struct component_node *next; + struct component_node *prev; +}; + +typedef struct grub_gui_box *grub_gui_box_t; + +typedef void (*layout_func_t) (grub_gui_box_t self, int modify_layout, + int *width, int *height); + +struct grub_gui_box +{ + struct grub_gui_container_ops *container; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + + /* Doubly linked list of components with dummy head & tail nodes. */ + struct component_node chead; + struct component_node ctail; + + /* The layout function: differs for vertical and horizontal boxes. */ + layout_func_t layout_func; +}; + +static void +box_destroy (void *vself) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + struct component_node *next; + for (cur = self->chead.next; cur != &self->ctail; cur = next) + { + /* Copy the 'next' pointer, since we need it for the next iteration, + and we're going to free the memory it is stored in. */ + next = cur->next; + /* Destroy the child component. */ + cur->component->ops->destroy (cur->component); + /* Free the linked list node. */ + grub_free (cur); + } + grub_free (self); +} + +static const char * +box_get_id (void *vself) +{ + grub_gui_box_t self = vself; + return self->id; +} + +static int +box_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "container") == 0); +} + +static void +layout_horizontally (grub_gui_box_t self, int modify_layout, + int *width, int *height) +{ + /* Start at the left (chead) and set the x coordinates as we go right. */ + /* All components have their width set to the box's width. */ + + struct component_node *cur; + int x = 0; + if (height) + *height = 0; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t c = cur->component; + grub_video_rect_t r; + + c->ops->get_preferred_size (c, &r.width, &r.height); + + /* Check and possibly update the maximum width, if non-null. */ + if (height && r.height > *height) + *height = r.height; + + /* Set the component's bounds, if the flag is set. */ + if (modify_layout) + { + r.x = x; + r.y = 0; + /* Width comes from the component's preferred size. */ + r.height = self->bounds.height; + c->ops->set_bounds (c, &r); + } + + x += r.width; + } + + /* Return the sum of the children's preferred widths. */ + if (width) + *width = x; +} + +static void +layout_vertically (grub_gui_box_t self, int modify_layout, + int *width, int *height) +{ + /* Start at the top (chead) and set the y coordinates as we go down. */ + /* All components have their width set to the vbox's width. */ + + struct component_node *cur; + int y = 0; + if (width) + *width = 0; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t c = cur->component; + grub_video_rect_t r; + + c->ops->get_preferred_size (c, &r.width, &r.height); + + /* Check and possibly update the maximum width, if non-null. */ + if (width && r.width > *width) + *width = r.width; + + /* Set the component's bounds, if the flag is set. */ + if (modify_layout) + { + r.x = 0; + r.y = y; + r.width = self->bounds.width; + /* Height comes from the component's preferred size. */ + c->ops->set_bounds (c, &r); + } + + y += r.height; + } + + /* Return the sum of the children's preferred heights. */ + if (height) + *height = y; +} + +static void +box_paint (void *vself) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + grub_video_rect_t vpsave; + + grub_gui_set_viewport (&self->bounds, &vpsave); + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t comp = cur->component; + comp->ops->paint (comp); + } + grub_gui_restore_viewport (&vpsave); +} + +static void +box_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_box_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +box_get_parent (void *vself) +{ + grub_gui_box_t self = vself; + return self->parent; +} + +static void +box_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_box_t self = vself; + self->bounds = *bounds; + self->layout_func (self, 1, 0, 0); /* Relayout the children. */ +} + +static void +box_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_box_t self = vself; + *bounds = self->bounds; +} + +/* The box's preferred size is based on the preferred sizes + of its children. */ +static void +box_get_preferred_size (void *vself, int *width, int *height) +{ + grub_gui_box_t self = vself; + self->layout_func (self, 0, width, height); /* Just calculate the size. */ + + /* Allow preferred dimensions to override the computed dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +box_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_box_t self = vself; + if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + { + self->id = grub_strdup (value); + if (! self->id) + return grub_errno; + } + else + self->id = 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + + return grub_errno; +} + +static void +box_add (void *vself, grub_gui_component_t comp) +{ + grub_gui_box_t self = vself; + struct component_node *node; + node = grub_malloc (sizeof (*node)); + if (! node) + return; /* Note: probably should handle the error. */ + node->component = comp; + /* Insert the node before the tail. */ + node->prev = self->ctail.prev; + node->prev->next = node; + node->next = &self->ctail; + node->next->prev = node; + + comp->ops->set_parent (comp, (grub_gui_container_t) self); + self->layout_func (self, 1, 0, 0); /* Relayout the children. */ +} + +static void +box_remove (void *vself, grub_gui_component_t comp) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + if (cur->component == comp) + { + /* Unlink 'cur' from the list. */ + cur->prev->next = cur->next; + cur->next->prev = cur->prev; + /* Free the node's memory (but don't destroy the component). */ + grub_free (cur); + /* Must not loop again, since 'cur' would be dereferenced! */ + return; + } + } +} + +static void +box_iterate_children (void *vself, + grub_gui_component_callback cb, void *userdata) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + cb (cur->component, userdata); +} + +static struct grub_gui_container_ops box_ops = +{ + .component = + { + .destroy = box_destroy, + .get_id = box_get_id, + .is_instance = box_is_instance, + .paint = box_paint, + .set_parent = box_set_parent, + .get_parent = box_get_parent, + .set_bounds = box_set_bounds, + .get_bounds = box_get_bounds, + .get_preferred_size = box_get_preferred_size, + .set_property = box_set_property + }, + .add = box_add, + .remove = box_remove, + .iterate_children = box_iterate_children +}; + +/* Box constructor. Specify the appropriate layout function to create + a horizontal or vertical stacking box. */ +static grub_gui_box_t +box_new (layout_func_t layout_func) +{ + grub_gui_box_t box; + box = grub_malloc (sizeof (*box)); + if (! box) + return 0; + box->container = &box_ops; + box->parent = 0; + box->bounds.x = 0; + box->bounds.y = 0; + box->bounds.width = 0; + box->bounds.height = 0; + box->id = 0; + box->preferred_width = -1; + box->preferred_height = -1; + box->chead.component = 0; + box->chead.prev = 0; + box->chead.next = &box->ctail; + box->ctail.component = 0; + box->ctail.prev = &box->chead; + box->ctail.next = 0; + box->layout_func = layout_func; + return box; +} + +/* Create a new container that stacks its child components horizontally, + from left to right. Each child get a width corresponding to its + preferred width. The height of each child is set the maximum of the + preferred heights of all children. */ +grub_gui_container_t +grub_gui_hbox_new (void) +{ + return (grub_gui_container_t) box_new (layout_horizontally); +} + +/* Create a new container that stacks its child components verticallyj, + from top to bottom. Each child get a height corresponding to its + preferred height. The width of each child is set the maximum of the + preferred widths of all children. */ +grub_gui_container_t +grub_gui_vbox_new (void) +{ + return (grub_gui_container_t) box_new (layout_vertically); +} === added file 'gfxmenu/gui_canvas.c' --- gfxmenu/gui_canvas.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_canvas.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,268 @@ +/* gui_canvas.c - GUI container allowing manually placed components. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* TODO Add layering so that components can be properly overlaid. */ + +struct component_node +{ + grub_gui_component_t component; + struct component_node *next; +}; + +struct grub_gui_canvas +{ + struct grub_gui_container_ops *container; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + /* Component list (dummy head node). */ + struct component_node components; +}; + +typedef struct grub_gui_canvas *grub_gui_canvas_t; + +static void +canvas_destroy (void *vself) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + struct component_node *next; + for (cur = self->components.next; cur; cur = next) + { + /* Copy the 'next' pointer, since we need it for the next iteration, + and we're going to free the memory it is stored in. */ + next = cur->next; + /* Destroy the child component. */ + cur->component->ops->destroy (cur->component); + /* Free the linked list node. */ + grub_free (cur); + } + grub_free (self); +} + +static const char * +canvas_get_id (void *vself) +{ + grub_gui_canvas_t self = vself; + return self->id; +} + +static int +canvas_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "container") == 0); +} + +static void +canvas_paint (void *vself) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + grub_video_rect_t vpsave; + + grub_gui_set_viewport (&self->bounds, &vpsave); + for (cur = self->components.next; cur; cur = cur->next) + { + int pw; + int ph; + grub_video_rect_t r; + grub_gui_component_t comp; + + comp = cur->component; + + /* Give the child its preferred size. */ + comp->ops->get_preferred_size (comp, &pw, &ph); + comp->ops->get_bounds (comp, &r); + if (r.width != pw || r.height != ph) + { + r.width = pw; + r.height = ph; + comp->ops->set_bounds (comp, &r); + } + + /* Paint the child. */ + comp->ops->paint (comp); + } + grub_gui_restore_viewport (&vpsave); +} + +static void +canvas_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_canvas_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +canvas_get_parent (void *vself) +{ + grub_gui_canvas_t self = vself; + return self->parent; +} + +static void +canvas_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_canvas_t self = vself; + self->bounds = *bounds; +} + +static void +canvas_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_canvas_t self = vself; + *bounds = self->bounds; +} + +static void +canvas_get_preferred_size (void *vself, int *width, int *height) +{ + grub_gui_canvas_t self = vself; + *width = 0; + *height = 0; + + /* Allow preferred dimensions to override the empty dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +canvas_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_canvas_t self = vself; + if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + { + self->id = grub_strdup (value); + if (! self->id) + return grub_errno; + } + else + self->id = 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + return grub_errno; +} + +static void +canvas_add (void *vself, grub_gui_component_t comp) +{ + grub_gui_canvas_t self = vself; + struct component_node *node; + node = grub_malloc (sizeof (*node)); + if (! node) + return; /* Note: probably should handle the error. */ + node->component = comp; + node->next = self->components.next; + self->components.next = node; + comp->ops->set_parent (comp, (grub_gui_container_t) self); +} + +static void +canvas_remove (void *vself, grub_gui_component_t comp) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + struct component_node *prev; + prev = &self->components; + for (cur = self->components.next; cur; prev = cur, cur = cur->next) + { + if (cur->component == comp) + { + /* Unlink 'cur' from the list. */ + prev->next = cur->next; + /* Free the node's memory (but don't destroy the component). */ + grub_free (cur); + /* Must not loop again, since 'cur' would be dereferenced! */ + return; + } + } +} + +static void +canvas_iterate_children (void *vself, + grub_gui_component_callback cb, void *userdata) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + for (cur = self->components.next; cur; cur = cur->next) + cb (cur->component, userdata); +} + +static struct grub_gui_container_ops canvas_ops = +{ + .component = + { + .destroy = canvas_destroy, + .get_id = canvas_get_id, + .is_instance = canvas_is_instance, + .paint = canvas_paint, + .set_parent = canvas_set_parent, + .get_parent = canvas_get_parent, + .set_bounds = canvas_set_bounds, + .get_bounds = canvas_get_bounds, + .get_preferred_size = canvas_get_preferred_size, + .set_property = canvas_set_property + }, + .add = canvas_add, + .remove = canvas_remove, + .iterate_children = canvas_iterate_children +}; + +grub_gui_container_t +grub_gui_canvas_new (void) +{ + grub_gui_canvas_t canvas; + canvas = grub_malloc (sizeof (*canvas)); + if (! canvas) + return 0; + canvas->container = &canvas_ops; + canvas->parent = 0; + canvas->bounds.x = 0; + canvas->bounds.y = 0; + canvas->bounds.width = 0; + canvas->bounds.height = 0; + canvas->id = 0; + canvas->preferred_width = -1; + canvas->preferred_height = -1; + canvas->components.component = 0; + canvas->components.next = 0; + return (grub_gui_container_t) canvas; +} === added file 'gfxmenu/gui_circular_progress.c' --- gfxmenu/gui_circular_progress.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_circular_progress.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,339 @@ +/* gui_circular_process.c - GUI circular progress indicator component. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct grub_gui_circular_progress +{ + struct grub_gui_component_ops *circprog_ops; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + int visible; + int start; + int end; + int value; + int num_ticks; + int start_angle; + int ticks_disappear; + char *theme_dir; + int need_to_load_pixmaps; + char *center_file; + char *tick_file; + struct grub_video_bitmap *center_bitmap; + struct grub_video_bitmap *tick_bitmap; +}; + +typedef struct grub_gui_circular_progress *circular_progress_t; + +static void +circprog_destroy (void *vself) +{ + circular_progress_t self = vself; + grub_free (self); +} + +static const char * +circprog_get_id (void *vself) +{ + circular_progress_t self = vself; + return self->id; +} + +static int +circprog_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static struct grub_video_bitmap * +load_bitmap (const char *dir, const char *file) +{ + struct grub_video_bitmap *bitmap; + char *abspath; + + /* Check arguments. */ + if (! dir || ! file) + return 0; + + /* Resolve to an absolute path. */ + abspath = grub_resolve_relative_path (dir, file); + if (! abspath) + return 0; + + /* Load the image. */ + grub_errno = GRUB_ERR_NONE; + grub_video_bitmap_load (&bitmap, abspath); + grub_errno = GRUB_ERR_NONE; + + grub_free (abspath); + return bitmap; +} + +static int +check_pixmaps (circular_progress_t self) +{ + if (self->need_to_load_pixmaps) + { + if (self->center_bitmap) + grub_video_bitmap_destroy (self->center_bitmap); + self->center_bitmap = load_bitmap (self->theme_dir, self->center_file); + self->tick_bitmap = load_bitmap (self->theme_dir, self->tick_file); + self->need_to_load_pixmaps = 0; + } + + return (self->center_bitmap != 0 && self->tick_bitmap != 0); +} + +static void +circprog_paint (void *vself) +{ + circular_progress_t self = vself; + + if (! self->visible) + return; + if (! check_pixmaps (self)) + return; + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + + int width = self->bounds.width; + int height = self->bounds.height; + int center_width = grub_video_bitmap_get_width (self->center_bitmap); + int center_height = grub_video_bitmap_get_height (self->center_bitmap); + int tick_width = grub_video_bitmap_get_width (self->tick_bitmap); + int tick_height = grub_video_bitmap_get_height (self->tick_bitmap); + grub_video_blit_bitmap (self->center_bitmap, GRUB_VIDEO_BLIT_BLEND, + (width - center_width) / 2, + (height - center_height) / 2, 0, 0, + center_width, center_height); + + int radius = width / 2 - tick_width / 2 - 1; + int nticks = (self->num_ticks + * (self->value - self->start) + / (self->end - self->start)); + int tick_begin; + int tick_end; + /* Do ticks appear or disappear as the value approached the end? */ + if (self->ticks_disappear) + { + tick_begin = nticks; + tick_end = self->num_ticks - 1; + } + else + { + tick_begin = 0; + tick_end = nticks - 1; + } + + int i; + for (i = tick_begin; i < tick_end; i++) + { + int x; + int y; + int angle; + + /* Calculate the location of the tick. */ + angle = self->start_angle + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks; + x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + + /* Adjust (x,y) so the tick is centered. */ + x -= tick_width / 2; + y -= tick_height / 2; + + /* Draw the tick. */ + grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, tick_width, tick_height); + } + + grub_gui_restore_viewport (&vpsave); +} + +static void +circprog_set_parent (void *vself, grub_gui_container_t parent) +{ + circular_progress_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +circprog_get_parent (void *vself) +{ + circular_progress_t self = vself; + return self->parent; +} + +static void +circprog_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + circular_progress_t self = vself; + self->bounds = *bounds; +} + +static void +circprog_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + circular_progress_t self = vself; + *bounds = self->bounds; +} + +static void +circprog_get_preferred_size (void *vself, int *width, int *height) +{ + circular_progress_t self = vself; + + *width = 0; + *height = 0; + + /* Allow preferred dimensions to override the circprog dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +circprog_set_property (void *vself, const char *name, const char *value) +{ + circular_progress_t self = vself; + if (grub_strcmp (name, "value") == 0) + { + self->value = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "start") == 0) + { + self->start = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "end") == 0) + { + self->end = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "num_ticks") == 0) + { + self->num_ticks = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "start_angle") == 0) + { + self->start_angle = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "ticks_disappear") == 0) + { + self->ticks_disappear = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "center_bitmap") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->center_file); + self->center_file = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "tick_bitmap") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->tick_file); + self->tick_file = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static struct grub_gui_component_ops circprog_ops = +{ + .destroy = circprog_destroy, + .get_id = circprog_get_id, + .is_instance = circprog_is_instance, + .paint = circprog_paint, + .set_parent = circprog_set_parent, + .get_parent = circprog_get_parent, + .set_bounds = circprog_set_bounds, + .get_bounds = circprog_get_bounds, + .get_preferred_size = circprog_get_preferred_size, + .set_property = circprog_set_property +}; + +grub_gui_component_t +grub_gui_circular_progress_new (void) +{ + circular_progress_t self; + self = grub_malloc (sizeof (*self)); + if (! self) + return 0; + self->circprog_ops = &circprog_ops; + self->parent = 0; + self->bounds.x = 0; + self->bounds.y = 0; + self->bounds.width = 0; + self->bounds.height = 0; + self->id = 0; + self->preferred_width = -1; + self->preferred_height = -1; + self->visible = 1; + self->start = 0; + self->end = 0; + self->value = 0; + self->num_ticks = 64; + self->start_angle = -64; + self->ticks_disappear = 0; + + self->theme_dir = 0; + self->need_to_load_pixmaps = 0; + self->center_file = 0; + self->tick_file = 0; + self->center_bitmap = 0; + self->tick_bitmap = 0; + + return (grub_gui_component_t) self; +} === added file 'gfxmenu/gui_image.c' --- gfxmenu/gui_image.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_image.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,270 @@ +/* gui_image.c - GUI component to display an image. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_gui_image +{ + struct grub_gui_component_ops *image; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + struct grub_video_bitmap *raw_bitmap; + struct grub_video_bitmap *bitmap; +}; + +typedef struct grub_gui_image *grub_gui_image_t; + +static void +image_destroy (void *vself) +{ + grub_gui_image_t self = vself; + + /* Free the scaled bitmap, unless it's a reference to the raw bitmap. */ + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + if (self->raw_bitmap) + grub_video_bitmap_destroy (self->raw_bitmap); + + grub_free (self); +} + +static const char * +image_get_id (void *vself) +{ + grub_gui_image_t self = vself; + return self->id; +} + +static int +image_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static void +image_paint (void *vself) +{ + grub_gui_image_t self = vself; + if (! self->bitmap) + return; + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + grub_video_blit_bitmap (self->bitmap, GRUB_VIDEO_BLIT_BLEND, + 0, 0, 0, 0, + grub_video_bitmap_get_width (self->bitmap), + grub_video_bitmap_get_height (self->bitmap)); + grub_gui_restore_viewport (&vpsave); +} + +static void +image_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_image_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +image_get_parent (void *vself) +{ + grub_gui_image_t self = vself; + return self->parent; +} + +static grub_err_t +rescale_image (grub_gui_image_t self) +{ + if (! self->raw_bitmap) + { + if (self->bitmap) + { + grub_video_bitmap_destroy (self->bitmap); + self->bitmap = 0; + } + return grub_errno; + } + + unsigned width = self->bounds.width; + unsigned height = self->bounds.height; + + if (self->bitmap + && (grub_video_bitmap_get_width (self->bitmap) == width) + && (grub_video_bitmap_get_height (self->bitmap) == height)) + { + /* Nothing to do; already the right size. */ + return grub_errno; + } + + /* Free any old scaled bitmap, + *unless* it's a reference to the raw bitmap. */ + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + + self->bitmap = 0; + + /* Create a scaled bitmap, unless the requested size is the same + as the raw size -- in that case a reference is made. */ + if (grub_video_bitmap_get_width (self->raw_bitmap) == width + && grub_video_bitmap_get_height (self->raw_bitmap) == height) + { + self->bitmap = self->raw_bitmap; + return grub_errno; + } + + /* Don't scale to an invalid size. */ + if (width == 0 || height == 0) + return grub_errno; + + /* Create the scaled bitmap. */ + grub_video_bitmap_create_scaled (&self->bitmap, + width, + height, + self->raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + { + grub_error_push (); + grub_error (grub_errno, "failed to scale bitmap for image component"); + } + return grub_errno; +} + +static void +image_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_image_t self = vself; + self->bounds = *bounds; + rescale_image (self); +} + +static void +image_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_image_t self = vself; + *bounds = self->bounds; +} + +static void +image_get_preferred_size (void *vself, int *width, int *height) +{ + grub_gui_image_t self = vself; + + if (self->raw_bitmap) + { + *width = grub_video_bitmap_get_width (self->raw_bitmap); + *height = grub_video_bitmap_get_height (self->raw_bitmap); + } + else + { + *width = 0; + *height = 0; + } + + /* Allow preferred dimensions to override the image dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +load_image (grub_gui_image_t self, const char *path) +{ + struct grub_video_bitmap *bitmap; + if (grub_video_bitmap_load (&bitmap, path) != GRUB_ERR_NONE) + return grub_errno; + + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + if (self->raw_bitmap) + grub_video_bitmap_destroy (self->raw_bitmap); + + self->raw_bitmap = bitmap; + return rescale_image (self); +} + +static grub_err_t +image_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_image_t self = vself; + if (grub_strcmp (name, "file") == 0) + return load_image (self, value); + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static struct grub_gui_component_ops image_ops = +{ + .destroy = image_destroy, + .get_id = image_get_id, + .is_instance = image_is_instance, + .paint = image_paint, + .set_parent = image_set_parent, + .get_parent = image_get_parent, + .set_bounds = image_set_bounds, + .get_bounds = image_get_bounds, + .get_preferred_size = image_get_preferred_size, + .set_property = image_set_property +}; + +grub_gui_component_t +grub_gui_image_new (void) +{ + grub_gui_image_t image; + image = grub_malloc (sizeof (*image)); + if (! image) + return 0; + image->image = &image_ops; + image->parent = 0; + image->bounds.x = 0; + image->bounds.y = 0; + image->bounds.width = 0; + image->bounds.height = 0; + image->id = 0; + image->preferred_width = -1; + image->preferred_height = -1; + image->raw_bitmap = 0; + image->bitmap = 0; + return (grub_gui_component_t) image; +} + === added file 'gfxmenu/gui_label.c' --- gfxmenu/gui_label.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_label.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,248 @@ +/* gui_label.c - GUI component to display a line of text. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +static const char *align_options[] = +{ + "left", + "center", + "right", + 0 +}; + +enum align_mode { + align_left, + align_center, + align_right +}; + +struct grub_gui_label +{ + struct grub_gui_component_ops *label; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + int visible; + char *text; + grub_font_t font; + grub_gui_color_t color; + enum align_mode align; +}; + +typedef struct grub_gui_label *grub_gui_label_t; + +static void +label_destroy (void *vself) +{ + grub_gui_label_t self = vself; + grub_free (self->text); + grub_free (self); +} + +static const char * +label_get_id (void *vself) +{ + grub_gui_label_t self = vself; + return self->id; +} + +static int +label_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static void +label_paint (void *vself) +{ + grub_gui_label_t self = vself; + + if (! self->visible) + return; + + /* Calculate the starting x coordinate. */ + int left_x; + if (self->align == align_left) + left_x = 0; + else if (self->align == align_center) + left_x = ((self->bounds.width + - grub_font_get_string_width (self->font, self->text)) + ) / 2; + else if (self->align == align_right) + left_x = (self->bounds.width + - grub_font_get_string_width (self->font, self->text)); + else + return; /* Invalid alignment. */ + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + grub_font_draw_string (self->text, + self->font, + grub_gui_map_color (self->color), + left_x, + grub_font_get_ascent (self->font)); + grub_gui_restore_viewport (&vpsave); +} + +static void +label_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_label_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +label_get_parent (void *vself) +{ + grub_gui_label_t self = vself; + return self->parent; +} + +static void +label_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_label_t self = vself; + self->bounds = *bounds; +} + +static void +label_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_label_t self = vself; + *bounds = self->bounds; +} + +static void +label_get_preferred_size (void *vself, int *width, int *height) +{ + grub_gui_label_t self = vself; + *width = grub_font_get_string_width (self->font, self->text); + *height = (grub_font_get_ascent (self->font) + + grub_font_get_descent (self->font)); + + /* Allow preferred dimensions to override the computed dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static void +label_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_label_t self = vself; + if (grub_strcmp (name, "text") == 0) + { + grub_free (self->text); + if (! value) + value = ""; + self->text = grub_strdup (value); + } + else if (grub_strcmp (name, "font") == 0) + { + self->font = grub_font_get (value); + } + else if (grub_strcmp (name, "color") == 0) + { + grub_gui_parse_color (value, &self->color); + } + else if (grub_strcmp (name, "align") == 0) + { + int i; + for (i = 0; align_options[i]; i++) + { + if (grub_strcmp (align_options[i], value) == 0) + { + self->align = i; /* Set the alignment mode. */ + break; + } + } + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) == GRUB_ERR_NONE) + { + self->preferred_width = w; + self->preferred_height = h; + } + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } +} + +static struct grub_gui_component_ops label_ops = +{ + .destroy = label_destroy, + .get_id = label_get_id, + .is_instance = label_is_instance, + .paint = label_paint, + .set_parent = label_set_parent, + .get_parent = label_get_parent, + .set_bounds = label_set_bounds, + .get_bounds = label_get_bounds, + .get_preferred_size = label_get_preferred_size, + .set_property = label_set_property +}; + +grub_gui_component_t +grub_gui_label_new (void) +{ + grub_gui_label_t label; + label = grub_malloc (sizeof (*label)); + if (! label) + return 0; + label->label = &label_ops; + label->parent = 0; + label->bounds.x = 0; + label->bounds.y = 0; + label->bounds.width = 0; + label->bounds.height = 0; + label->id = 0; + label->preferred_width = -1; + label->preferred_height = -1; + label->visible = 1; + label->text = grub_strdup (""); + label->font = grub_font_get ("Helvetica 10"); + label->color.red = 0; + label->color.green = 0; + label->color.blue = 0; + label->color.alpha = 255; + label->align = align_left; + return (grub_gui_component_t) label; +} === added file 'gfxmenu/gui_list.c' --- gfxmenu/gui_list.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_list.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,625 @@ +/* gui_list.c - GUI component to display a selectable list of items. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_gui_list_impl +{ + struct grub_gui_list_ops *list_ops; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + int visible; + + int icon_width; + int icon_height; + int item_height; + int item_padding; + int item_icon_space; + int item_spacing; + grub_font_t item_font; + grub_font_t selected_item_font; + grub_gui_color_t item_color; + int selected_item_color_set; + grub_gui_color_t selected_item_color; + + int draw_scrollbar; + int need_to_recreate_scrollbar; + char *scrollbar_frame_pattern; + char *scrollbar_thumb_pattern; + grub_gfxmenu_box_t scrollbar_frame; + grub_gfxmenu_box_t scrollbar_thumb; + int scrollbar_width; + + int min_items_shown; + int max_items_shown; + int first_shown_index; + + int need_to_recreate_boxes; + char *theme_dir; + char *menu_box_pattern; + char *selected_item_box_pattern; + grub_gfxmenu_box_t menu_box; + grub_gfxmenu_box_t selected_item_box; + + grub_gfxmenu_icon_manager_t icon_manager; + grub_gfxmenu_model_t menu; +}; + +typedef struct grub_gui_list_impl *list_impl_t; + +static void +list_destroy (void *vself) +{ + list_impl_t self = vself; + + grub_free (self->theme_dir); + grub_free (self->menu_box_pattern); + grub_free (self->selected_item_box_pattern); + if (self->menu_box) + self->menu_box->destroy (self->menu_box); + if (self->selected_item_box) + self->selected_item_box->destroy (self->selected_item_box); + if (self->icon_manager) + grub_gfxmenu_icon_manager_destroy (self->icon_manager); + + grub_free (self); +} + +static int +get_num_shown_items (list_impl_t self) +{ + int n = grub_gfxmenu_model_get_num_entries (self->menu); + if (self->min_items_shown != -1 && n < self->min_items_shown) + n = self->min_items_shown; + if (self->max_items_shown != -1 && n > self->max_items_shown) + n = self->max_items_shown; + return n; +} + +static int +check_boxes (list_impl_t self) +{ + if (self->need_to_recreate_boxes) + { + grub_gui_recreate_box (&self->menu_box, + self->menu_box_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->selected_item_box, + self->selected_item_box_pattern, + self->theme_dir); + + self->need_to_recreate_boxes = 0; + } + + return (self->menu_box != 0 && self->selected_item_box != 0); +} + +static int +check_scrollbar (list_impl_t self) +{ + if (self->need_to_recreate_scrollbar) + { + grub_gui_recreate_box (&self->scrollbar_frame, + self->scrollbar_frame_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->scrollbar_thumb, + self->scrollbar_thumb_pattern, + self->theme_dir); + + self->need_to_recreate_scrollbar = 0; + } + + return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 0); +} + +static const char * +list_get_id (void *vself) +{ + list_impl_t self = vself; + return self->id; +} + +static int +list_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "list") == 0); +} + +static struct grub_video_bitmap * +get_item_icon (list_impl_t self, int item_index) +{ + grub_menu_entry_t entry; + entry = grub_gfxmenu_model_get_entry (self->menu, item_index); + if (! entry) + return 0; + + return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry); +} + +static void +make_selected_item_visible (list_impl_t self) +{ + int selected_index = grub_gfxmenu_model_get_selected_index (self->menu); + if (selected_index < 0) + return; /* No item is selected. */ + int num_shown_items = get_num_shown_items (self); + int last_shown_index = self->first_shown_index + (num_shown_items - 1); + if (selected_index < self->first_shown_index) + self->first_shown_index = selected_index; + else if (selected_index > last_shown_index) + self->first_shown_index = selected_index - (num_shown_items - 1); +} + +/* Draw a scrollbar on the menu. */ +static void +draw_scrollbar (list_impl_t self, + int value, int extent, int min, int max, + int rightx, int topy, int height) +{ + grub_gfxmenu_box_t frame = self->scrollbar_frame; + grub_gfxmenu_box_t thumb = self->scrollbar_thumb; + int frame_vertical_pad = (frame->get_top_pad (frame) + + frame->get_bottom_pad (frame)); + int frame_horizontal_pad = (frame->get_left_pad (frame) + + frame->get_right_pad (frame)); + int tracktop = topy + frame->get_top_pad (frame); + int tracklen = height - frame_vertical_pad; + frame->set_content_size (frame, self->scrollbar_width, tracklen); + int thumby = tracktop + tracklen * (value - min) / (max - min); + int thumbheight = tracklen * extent / (max - min) + 1; + thumb->set_content_size (thumb, + self->scrollbar_width - frame_horizontal_pad, + thumbheight - (thumb->get_top_pad (thumb) + + thumb->get_bottom_pad (thumb))); + frame->draw (frame, + rightx - (self->scrollbar_width + frame_horizontal_pad), + topy); + thumb->draw (thumb, + rightx - (self->scrollbar_width - frame->get_right_pad (frame)), + thumby); +} + +/* Draw the list of items. */ +static void +draw_menu (list_impl_t self) +{ + if (! self->menu_box || ! self->selected_item_box) + return; + + int boxpad = self->item_padding; + int icon_text_space = self->item_icon_space; + int item_vspace = self->item_spacing; + + int ascent = grub_font_get_ascent (self->item_font); + int descent = grub_font_get_descent (self->item_font); + int item_height = self->item_height; + + int total_num_items = grub_gfxmenu_model_get_num_entries (self->menu); + int num_shown_items = get_num_shown_items (self); + grub_gfxmenu_box_t box = self->menu_box; + int width = self->bounds.width; + int height = self->bounds.height; + + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + + box->set_content_size (box, + width - box_left_pad - box_right_pad, + height - box_top_pad - box_bottom_pad); + + box->draw (box, 0, 0); + + make_selected_item_visible (self); + + int drawing_scrollbar = (self->draw_scrollbar + && (num_shown_items < total_num_items) + && check_scrollbar (self)); + + int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0; + + int item_top = box_top_pad + boxpad; + int item_left = box_left_pad + boxpad; + int menu_index; + int visible_index; + + for (visible_index = 0, menu_index = self->first_shown_index; + visible_index < num_shown_items && menu_index < total_num_items; + visible_index++, menu_index++) + { + int is_selected = + (menu_index == grub_gfxmenu_model_get_selected_index (self->menu)); + + if (is_selected) + { + grub_gfxmenu_box_t selbox = self->selected_item_box; + int sel_leftpad = selbox->get_left_pad (selbox); + int sel_toppad = selbox->get_top_pad (selbox); + selbox->set_content_size (selbox, + (width - 2 * boxpad + - box_left_pad - box_right_pad + - scrollbar_h_space), + item_height); + selbox->draw (selbox, + item_left - sel_leftpad, + item_top - sel_toppad); + } + + struct grub_video_bitmap *icon; + if ((icon = get_item_icon (self, menu_index)) != 0) + grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND, + item_left, + item_top + (item_height - self->icon_height) / 2, + 0, 0, self->icon_width, self->icon_height); + + const char *item_title = + grub_gfxmenu_model_get_entry_title (self->menu, menu_index); + grub_font_t font = + (is_selected && self->selected_item_font + ? self->selected_item_font + : self->item_font); + grub_gui_color_t text_color = + ((is_selected && self->selected_item_color_set) + ? self->selected_item_color + : self->item_color); + grub_font_draw_string (item_title, + font, + grub_gui_map_color (text_color), + item_left + self->icon_width + icon_text_space, + (item_top + (item_height - (ascent + descent)) + / 2 + ascent)); + + item_top += item_height + item_vspace; + } + + if (drawing_scrollbar) + draw_scrollbar (self, + self->first_shown_index, num_shown_items, + 0, total_num_items, + width - box_right_pad + self->scrollbar_width, + box_top_pad + boxpad, + height - box_top_pad - box_bottom_pad); +} + +static void +list_paint (void *vself) +{ + list_impl_t self = vself; + + if (! self->visible) + return; + + check_boxes (self); + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + draw_menu (self); + grub_gui_restore_viewport (&vpsave); +} + +static void +list_set_parent (void *vself, grub_gui_container_t parent) +{ + list_impl_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +list_get_parent (void *vself) +{ + list_impl_t self = vself; + return self->parent; +} + +static void +list_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + self->bounds = *bounds; +} + +static void +list_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + *bounds = self->bounds; +} + +static void +list_get_preferred_size (void *vself, int *width, int *height) +{ + list_impl_t self = vself; + + if (check_boxes (self)) + { + int boxpad = self->item_padding; + int item_vspace = self->item_spacing; + int item_height = self->item_height; + int num_items = get_num_shown_items (self); + + grub_gfxmenu_box_t box = self->menu_box; + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + + *width = 400 + 2 * boxpad + box_left_pad + box_right_pad; + + /* Set the menu box height to fit the items. */ + *height = (item_height * num_items + + item_vspace * (num_items - 1) + + 2 * boxpad + + box_top_pad + box_bottom_pad); + } + else + { + *width = 0; + *height = 0; + } + + /* Allow preferred dimensions to override the computed dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +list_set_property (void *vself, const char *name, const char *value) +{ + list_impl_t self = vself; + if (grub_strcmp (name, "item_font") == 0) + { + self->item_font = grub_font_get (value); + } + else if (grub_strcmp (name, "selected_item_font") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + self->selected_item_font = 0; + else + self->selected_item_font = grub_font_get (value); + } + else if (grub_strcmp (name, "item_color") == 0) + { + grub_gui_parse_color (value, &self->item_color); + } + else if (grub_strcmp (name, "selected_item_color") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + { + self->selected_item_color_set = 0; + } + else + { + if (grub_gui_parse_color (value, &self->selected_item_color) + == GRUB_ERR_NONE) + self->selected_item_color_set = 1; + } + } + else if (grub_strcmp (name, "icon_width") == 0) + { + self->icon_width = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "icon_height") == 0) + { + self->icon_height = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "item_height") == 0) + { + self->item_height = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_padding") == 0) + { + self->item_padding = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_icon_space") == 0) + { + self->item_icon_space = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_spacing") == 0) + { + self->item_spacing = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "menu_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->menu_box_pattern); + self->menu_box_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "selected_item_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->selected_item_box_pattern); + self->selected_item_box_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_frame") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_frame_pattern); + self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_thumb") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_thumb_pattern); + self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_width") == 0) + { + self->scrollbar_width = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar") == 0) + { + self->draw_scrollbar = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "min_items_shown") == 0) + { + self->min_items_shown = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "max_items_shown") == 0) + { + self->max_items_shown = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +/* Set necessary information that the gfxmenu view provides. */ +static void +list_set_view_info (void *vself, + const char *theme_path, + grub_gfxmenu_model_t menu) +{ + list_impl_t self = vself; + grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, theme_path); + self->menu = menu; +} + +static struct grub_gui_list_ops list_ops = +{ + .component_ops = + { + .destroy = list_destroy, + .get_id = list_get_id, + .is_instance = list_is_instance, + .paint = list_paint, + .set_parent = list_set_parent, + .get_parent = list_get_parent, + .set_bounds = list_set_bounds, + .get_bounds = list_get_bounds, + .get_preferred_size = list_get_preferred_size, + .set_property = list_set_property + }, + .set_view_info = list_set_view_info +}; + +grub_gui_component_t +grub_gui_list_new (void) +{ + list_impl_t self; + grub_font_t default_font; + grub_gui_color_t default_fg_color; + grub_gui_color_t default_bg_color; + + self = grub_malloc (sizeof (*self)); + if (! self) + return 0; + + self->list_ops = &list_ops; + self->parent = 0; + self->bounds.x = 0; + self->bounds.y = 0; + self->bounds.width = 0; + self->bounds.height = 0; + self->id = 0; + self->preferred_width = -1; + self->preferred_height = -1; + self->visible = 1; + + default_font = grub_font_get ("Helvetica 12"); + default_fg_color = grub_gui_color_rgb (0, 0, 0); + default_bg_color = grub_gui_color_rgb (255, 255, 255); + + self->icon_width = 32; + self->icon_height = 32; + self->item_height = 42; + self->item_padding = 14; + self->item_icon_space = 4; + self->item_spacing = 16; + self->item_font = default_font; + self->selected_item_font = 0; /* Default to using the item_font. */ + self->item_color = default_fg_color; + self->selected_item_color_set = 0; /* Default to using the item_color. */ + self->selected_item_color = default_fg_color; + + self->draw_scrollbar = 1; + self->need_to_recreate_scrollbar = 1; + self->scrollbar_frame = 0; + self->scrollbar_thumb = 0; + self->scrollbar_frame_pattern = 0; + self->scrollbar_thumb_pattern = 0; + self->scrollbar_width = 16; + + self->min_items_shown = -1; + self->max_items_shown = -1; + self->first_shown_index = 0; + + self->need_to_recreate_boxes = 0; + self->theme_dir = 0; + self->menu_box_pattern = 0; + self->selected_item_box_pattern = 0; + self->menu_box = grub_gfxmenu_create_box (0, 0); + self->selected_item_box = grub_gfxmenu_create_box (0, 0); + + self->icon_manager = grub_gfxmenu_icon_manager_new (); + if (! self->icon_manager) + { + self->list_ops->component_ops.destroy (self); + return 0; + } + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + return (grub_gui_component_t) self; +} === added file 'gfxmenu/gui_progress_bar.c' --- gfxmenu/gui_progress_bar.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_progress_bar.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,378 @@ +/* gui_progress_bar.c - GUI progress bar component. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +struct grub_gui_progress_bar +{ + struct grub_gui_component_ops *progress_bar; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int preferred_width; + int preferred_height; + int visible; + int start; + int end; + int value; + int show_text; + char *text; + grub_font_t font; + grub_gui_color_t text_color; + grub_gui_color_t border_color; + grub_gui_color_t bg_color; + grub_gui_color_t fg_color; + + char *theme_dir; + int need_to_recreate_pixmaps; + char *bar_pattern; + char *highlight_pattern; + grub_gfxmenu_box_t bar_box; + grub_gfxmenu_box_t highlight_box; +}; + +typedef struct grub_gui_progress_bar *grub_gui_progress_bar_t; + +static void +progress_bar_destroy (void *vself) +{ + grub_gui_progress_bar_t self = vself; + grub_free (self); +} + +static const char * +progress_bar_get_id (void *vself) +{ + grub_gui_progress_bar_t self = vself; + return self->id; +} + +static int +progress_bar_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static int +check_pixmaps (grub_gui_progress_bar_t self) +{ + if (self->need_to_recreate_pixmaps) + { + grub_gui_recreate_box (&self->bar_box, + self->bar_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->highlight_box, + self->highlight_pattern, + self->theme_dir); + + self->need_to_recreate_pixmaps = 0; + } + + return (self->bar_box != 0 && self->highlight_box != 0); +} + +static void +draw_filled_rect_bar (grub_gui_progress_bar_t self) +{ + /* Set the progress bar's frame. */ + grub_video_rect_t f; + f.x = 1; + f.y = 1; + f.width = self->bounds.width - 2; + f.height = self->bounds.height - 2; + + /* Border. */ + grub_video_fill_rect (grub_gui_map_color (self->border_color), + f.x - 1, f.y - 1, + f.width + 2, f.height + 2); + + /* Bar background. */ + int barwidth = (f.width + * (self->value - self->start) + / (self->end - self->start)); + grub_video_fill_rect (grub_gui_map_color (self->bg_color), + f.x + barwidth, f.y, + f.width - barwidth, f.height); + + /* Bar foreground. */ + grub_video_fill_rect (grub_gui_map_color (self->fg_color), + f.x, f.y, + barwidth, f.height); +} + +static void +draw_pixmap_bar (grub_gui_progress_bar_t self) +{ + grub_gfxmenu_box_t bar = self->bar_box; + grub_gfxmenu_box_t hl = self->highlight_box; + int w = self->bounds.width; + int h = self->bounds.height; + int bar_l_pad = bar->get_left_pad (bar); + int bar_r_pad = bar->get_right_pad (bar); + int bar_t_pad = bar->get_top_pad (bar); + int bar_b_pad = bar->get_bottom_pad (bar); + int bar_h_pad = bar_l_pad + bar_r_pad; + int bar_v_pad = bar_t_pad + bar_b_pad; + int tracklen = w - bar_h_pad; + int trackheight = h - bar_v_pad; + bar->set_content_size (bar, tracklen, trackheight); + + int barwidth = (tracklen + * (self->value - self->start) + / (self->end - self->start)); + hl->set_content_size (hl, barwidth, h - bar_v_pad); + + bar->draw (bar, 0, 0); + hl->draw (hl, bar_l_pad, bar_t_pad); +} + +static void +draw_text (grub_gui_progress_bar_t self) +{ + const char *text = self->text; + if (text && self->show_text) + { + grub_font_t font = self->font; + grub_video_color_t text_color = grub_gui_map_color (self->text_color); + int width = self->bounds.width; + int height = self->bounds.height; + + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = (width - text_width) / 2; + int y = ((height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_font_draw_string (text, font, text_color, x, y); + } +} + +static void +progress_bar_paint (void *vself) +{ + grub_gui_progress_bar_t self = vself; + if (! self->visible) + return; + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + + if (check_pixmaps (self)) + draw_pixmap_bar (self); + else + draw_filled_rect_bar (self); + + draw_text (self); + + grub_gui_restore_viewport (&vpsave); +} + +static void +progress_bar_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_progress_bar_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +progress_bar_get_parent (void *vself) +{ + grub_gui_progress_bar_t self = vself; + return self->parent; +} + +static void +progress_bar_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_progress_bar_t self = vself; + self->bounds = *bounds; +} + +static void +progress_bar_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_progress_bar_t self = vself; + *bounds = self->bounds; +} + +static void +progress_bar_get_preferred_size (void *vself, int *width, int *height) +{ + grub_gui_progress_bar_t self = vself; + + *width = 200; + *height = 28; + + /* Allow preferred dimensions to override the progress_bar dimensions. */ + if (self->preferred_width >= 0) + *width = self->preferred_width; + if (self->preferred_height >= 0) + *height = self->preferred_height; +} + +static grub_err_t +progress_bar_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_progress_bar_t self = vself; + if (grub_strcmp (name, "value") == 0) + { + self->value = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "start") == 0) + { + self->start = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "end") == 0) + { + self->end = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "text") == 0) + { + grub_free (self->text); + if (! value) + value = ""; + self->text = grub_strdup (value); + } + else if (grub_strcmp (name, "font") == 0) + { + self->font = grub_font_get (value); + } + else if (grub_strcmp (name, "text_color") == 0) + { + grub_gui_parse_color (value, &self->text_color); + } + else if (grub_strcmp (name, "border_color") == 0) + { + grub_gui_parse_color (value, &self->border_color); + } + else if (grub_strcmp (name, "bg_color") == 0) + { + grub_gui_parse_color (value, &self->bg_color); + } + else if (grub_strcmp (name, "fg_color") == 0) + { + grub_gui_parse_color (value, &self->fg_color); + } + else if (grub_strcmp (name, "bar_style") == 0) + { + self->need_to_recreate_pixmaps = 1; + grub_free (self->bar_pattern); + self->bar_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "highlight_style") == 0) + { + self->need_to_recreate_pixmaps = 1; + grub_free (self->highlight_pattern); + self->highlight_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_recreate_pixmaps = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "preferred_size") == 0) + { + int w; + int h; + if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE) + return grub_errno; + self->preferred_width = w; + self->preferred_height = h; + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "show_text") == 0) + { + self->show_text = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static struct grub_gui_component_ops progress_bar_ops = +{ + .destroy = progress_bar_destroy, + .get_id = progress_bar_get_id, + .is_instance = progress_bar_is_instance, + .paint = progress_bar_paint, + .set_parent = progress_bar_set_parent, + .get_parent = progress_bar_get_parent, + .set_bounds = progress_bar_set_bounds, + .get_bounds = progress_bar_get_bounds, + .get_preferred_size = progress_bar_get_preferred_size, + .set_property = progress_bar_set_property +}; + +grub_gui_component_t +grub_gui_progress_bar_new (void) +{ + grub_gui_progress_bar_t self; + self = grub_malloc (sizeof (*self)); + if (! self) + return 0; + self->progress_bar = &progress_bar_ops; + self->parent = 0; + self->bounds.x = 0; + self->bounds.y = 0; + self->bounds.width = 0; + self->bounds.height = 0; + self->id = 0; + self->preferred_width = -1; + self->preferred_height = -1; + self->visible = 1; + self->start = 0; + self->end = 0; + self->value = 0; + self->show_text = 1; + self->text = grub_strdup (""); + self->font = grub_font_get ("Helvetica 10"); + grub_gui_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 }; + grub_gui_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 }; + grub_gui_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 }; + self->text_color = black; + self->border_color = black; + self->bg_color = gray; + self->fg_color = lightgray; + + self->theme_dir = 0; + self->need_to_recreate_pixmaps = 0; + self->bar_pattern = 0; + self->highlight_pattern = 0; + self->bar_box = 0; + self->highlight_box = 0; + + return (grub_gui_component_t) self; +} === added file 'gfxmenu/gui_string_util.c' --- gfxmenu/gui_string_util.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_string_util.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,358 @@ +/* gui_string_util.c - String utilities used by the GUI system. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* Create a new NUL-terminated string on the heap as a substring of BUF. + The range of buf included is the half-open interval [START,END). + The index START is inclusive, END is exclusive. */ +char * +grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end) +{ + if (end < start) + return 0; + grub_size_t len = end - start; + char *s = grub_malloc (len + 1); + if (! s) + return 0; + grub_memcpy (s, buf + start, len); + s[len] = '\0'; + return s; +} + +/* Eliminate "." and ".." path elements from PATH. A new heap-allocated + string is returned. */ +static char * +canonicalize_path (const char *path) +{ + int i; + const char *p; + char *newpath = 0; + + /* Count the path components in path. */ + int components = 1; + for (p = path; *p; p++) + if (*p == '/') + components++; + + char **path_array = grub_malloc (components * sizeof (*path_array)); + if (! path_array) + return 0; + + /* Initialize array elements to NULL pointers; in case once of the + allocations fails, the cleanup code can just call grub_free() for all + pointers in the array. */ + for (i = 0; i < components; i++) + path_array[i] = 0; + + /* Parse the path into path_array. */ + p = path; + for (i = 0; i < components && p; i++) + { + /* Find the end of the path element. */ + const char *end = grub_strchr (p, '/'); + if (!end) + end = p + grub_strlen (p); + + /* Copy the element. */ + path_array[i] = grub_new_substring (p, 0, end - p); + if (! path_array[i]) + goto cleanup; + + /* Advance p to point to the start of the next element, or NULL. */ + if (*end) + p = end + 1; + else + p = 0; + } + + /* Eliminate '.' and '..' elements from the path array. */ + int newpath_length = 0; + for (i = components - 1; i >= 0; --i) + { + if (! grub_strcmp (path_array[i], ".")) + { + grub_free (path_array[i]); + path_array[i] = 0; + } + else if (! grub_strcmp (path_array[i], "..") + && i > 0) + { + /* Delete the '..' and the prior path element. */ + grub_free (path_array[i]); + path_array[i] = 0; + --i; + grub_free (path_array[i]); + path_array[i] = 0; + } + else + { + newpath_length += grub_strlen (path_array[i]) + 1; + } + } + + /* Construct a new path string. */ + newpath = grub_malloc (newpath_length + 1); + if (! newpath) + goto cleanup; + + newpath[0] = '\0'; + char *newpath_end = newpath; + int first = 1; + for (i = 0; i < components; i++) + { + char *element = path_array[i]; + if (element) + { + /* For all components but the first, prefix with a slash. */ + if (! first) + newpath_end = grub_stpcpy (newpath_end, "/"); + newpath_end = grub_stpcpy (newpath_end, element); + first = 0; + } + } + +cleanup: + for (i = 0; i < components; i++) + grub_free (path_array[i]); + grub_free (path_array); + + return newpath; +} + +/* Return a new heap-allocated string representing to absolute path + to the file referred to by PATH. If PATH is an absolute path, then + the returned path is a copy of PATH. If PATH is a relative path, then + BASE is with PATH used to construct the absolute path. */ +char * +grub_resolve_relative_path (const char *base, const char *path) +{ + char *abspath; + char *canonpath; + char *p; + + /* If PATH is an absolute path, then just use it as is. */ + if (path[0] == '/' || path[0] == '(') + return canonicalize_path (path); + + abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 1); + if (! abspath) + return 0; + + /* Concatenate BASE and PATH. + Note that BASE is expected to have a trailing slash. */ + p = grub_stpcpy (abspath, base); + grub_stpcpy (p, path); + + canonpath = canonicalize_path (abspath); + if (! canonpath) + return abspath; + + grub_free (abspath); + return canonpath; +} + +/* Get the path of the directory where the file at FILE_PATH is located. + FILE_PATH should refer to a file, not a directory. The returned path + includes a trailing slash. + This does not handle GRUB "(hd0,0)" paths properly yet since it only + looks at slashes. */ +char * +grub_get_dirname (const char *file_path) +{ + int i; + int last_slash; + + last_slash = -1; + for (i = grub_strlen (file_path) - 1; i >= 0; --i) + { + if (file_path[i] == '/') + { + last_slash = i; + break; + } + } + if (last_slash == -1) + return grub_strdup ("/"); + + return grub_new_substring (file_path, 0, last_slash + 1); +} + +static __inline int +isxdigit (char c) +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +static int +parse_hex_color_component (const char *s, unsigned start, unsigned end) +{ + unsigned len; + char buf[3]; + + len = end - start; + /* Check the limits so we don't overrun the buffer. */ + if (len < 1 || len > 2) + return 0; + + if (len == 1) + { + buf[0] = s[start]; /* Get the first and only hex digit. */ + buf[1] = buf[0]; /* Duplicate the hex digit. */ + } + else if (len == 2) + { + buf[0] = s[start]; + buf[1] = s[start + 1]; + } + + buf[2] = '\0'; + + return grub_strtoul (buf, 0, 16); +} + +/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA", + "#RRGGBB", or "#RRGGBBAA". */ +grub_err_t +grub_gui_parse_color (const char *s, grub_gui_color_t *color) +{ + grub_gui_color_t c; + + /* Skip whitespace. */ + while (*s && grub_isspace (*s)) + s++; + + if (*s == '#') + { + /* HTML-style. Number if hex digits: + [6] #RRGGBB [3] #RGB + [8] #RRGGBBAA [4] #RGBA */ + + s++; /* Skip the '#'. */ + /* Count the hexits to determine the format. */ + int hexits = 0; + const char *end = s; + while (isxdigit (*end)) + { + end++; + hexits++; + } + + /* Parse the color components based on the format. */ + if (hexits == 3 || hexits == 4) + { + c.red = parse_hex_color_component (s, 0, 1); + c.green = parse_hex_color_component (s, 1, 2); + c.blue = parse_hex_color_component (s, 2, 3); + if (hexits == 4) + c.alpha = parse_hex_color_component (s, 3, 4); + else + c.alpha = 255; + } + else if (hexits == 6 || hexits == 8) + { + c.red = parse_hex_color_component (s, 0, 2); + c.green = parse_hex_color_component (s, 2, 4); + c.blue = parse_hex_color_component (s, 4, 6); + if (hexits == 8) + c.alpha = parse_hex_color_component (s, 6, 8); + else + c.alpha = 255; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid HTML-type color string `%s'", s); + } + else if (grub_isdigit (*s)) + { + /* Comma separated decimal values. */ + c.red = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing 1st comma separator in color `%s'", s); + s++; + c.green = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing 2nd comma separator in color `%s'", s); + s++; + c.blue = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + c.alpha = 255; + else + { + s++; + c.alpha = grub_strtoul (s, 0, 0); + } + } + else + { + if (! grub_gui_get_named_color (s, &c)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid named color `%s'", s); + } + + if (grub_errno == GRUB_ERR_NONE) + *color = c; + return grub_errno; +} + +/* Parse a value in the form "(x, y)", storing the first element (x) into + *PX and the second element (y) into *PY. + Returns GRUB_ERR_NONE if successfully parsed. */ +grub_err_t +grub_gui_parse_2_tuple (const char *s, int *px, int *py) +{ + int x; + int y; + + while (*s && grub_isspace (*s)) + s++; + if (*s != '(') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing `(' in 2-tuple `%s'", s); + + /* Skip the opening parentheses. */ + s++; + if (*s == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "unexpected end of 2-tuple after `(' in `%s'", s); + + /* Parse the first element. */ + x = grub_strtol (s, 0, 10); + if ((s = grub_strchr (s, ',')) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing comma in 2-tuple `%s'", s); + + /* Skip the element separator (the comma). */ + s++; + /* Parse the second element. */ + y = grub_strtol (s, 0, 10); + + *px = x; + *py = y; + + return grub_errno; +} === added file 'gfxmenu/gui_util.c' --- gfxmenu/gui_util.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gui_util.c 2008-08-16 17:35:05 +0000 @@ -0,0 +1,101 @@ +/* gui_util.c - GUI utility functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + + +struct find_by_id_state +{ + const char *match_id; + grub_gui_component_callback match_callback; + void *match_userdata; +}; + +static void +find_by_id_recursively (grub_gui_component_t component, void *userdata) +{ + struct find_by_id_state *state; + const char *id; + + state = (struct find_by_id_state *) userdata; + id = component->ops->get_id (component); + if (id && grub_strcmp (id, state->match_id) == 0) + state->match_callback (component, state->match_userdata); + + if (component->ops->is_instance (component, "container")) + { + grub_gui_container_t container; + container = (grub_gui_container_t) component; + container->ops->iterate_children (container, + find_by_id_recursively, + state); + } +} + +void +grub_gui_find_by_id (grub_gui_component_t root, + const char *id, + grub_gui_component_callback cb, + void *userdata) +{ + struct find_by_id_state state; + state.match_id = id; + state.match_callback = cb; + state.match_userdata = userdata; + find_by_id_recursively (root, &state); +} + + +struct iterate_recursively_state +{ + grub_gui_component_callback callback; + void *userdata; +}; + +static +void iterate_recursively_cb (grub_gui_component_t component, void *userdata) +{ + struct iterate_recursively_state *state; + + state = (struct iterate_recursively_state *) userdata; + state->callback (component, state->userdata); + + if (component->ops->is_instance (component, "container")) + { + grub_gui_container_t container; + container = (grub_gui_container_t) component; + container->ops->iterate_children (container, + iterate_recursively_cb, + state); + } +} + +void +grub_gui_iterate_recursively (grub_gui_component_t root, + grub_gui_component_callback cb, + void *userdata) +{ + struct iterate_recursively_state state; + state.callback = cb; + state.userdata = userdata; + iterate_recursively_cb (root, &state); +} === added file 'gfxmenu/icon_manager.c' --- gfxmenu/icon_manager.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/icon_manager.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,258 @@ +/* icon_manager.c - gfxmenu icon manager. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Currently hard coded to '.png' extension. */ +static const char icon_extension[] = ".png"; + +typedef struct icon_entry +{ + char *class_name; + struct grub_video_bitmap *bitmap; + struct icon_entry *next; +} *icon_entry_t; + +struct grub_gfxmenu_icon_manager +{ + char *theme_path; + int icon_width; + int icon_height; + + /* Icon cache: linked list w/ dummy head node. */ + struct icon_entry cache; +}; + + +/* Create a new icon manager and return a point to it. */ +grub_gfxmenu_icon_manager_t +grub_gfxmenu_icon_manager_new (void) +{ + grub_gfxmenu_icon_manager_t mgr; + mgr = grub_malloc (sizeof (*mgr)); + if (! mgr) + return 0; + + mgr->theme_path = 0; + mgr->icon_width = 0; + mgr->icon_height = 0; + + /* Initialize the dummy head node. */ + mgr->cache.class_name = 0; + mgr->cache.bitmap = 0; + mgr->cache.next = 0; + + return mgr; +} + +/* Destroy the icon manager MGR, freeing all resources used by it. + +Note: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon() +are destroyed and must not be used by the caller after this function +is called. */ +void +grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr) +{ + grub_gfxmenu_icon_manager_clear_cache (mgr); + grub_free (mgr->theme_path); + grub_free (mgr); +} + +/* Clear the icon cache. */ +void +grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr) +{ + icon_entry_t cur; + icon_entry_t next; + for (cur = mgr->cache.next; cur; cur = next) + { + next = cur->next; + grub_free (cur->class_name); + grub_video_bitmap_destroy (cur->bitmap); + grub_free (cur); + } + mgr->cache.next = 0; +} + +/* Set the theme path. If the theme path is changed, the icon cache + is cleared. */ +void +grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr, + const char *path) +{ + /* Clear the cache if the theme path has changed. */ + if (((mgr->theme_path == 0) != (path == 0)) + || (grub_strcmp (mgr->theme_path, path) != 0)) + grub_gfxmenu_icon_manager_clear_cache (mgr); + + grub_free (mgr->theme_path); + mgr->theme_path = path ? grub_strdup (path) : 0; +} + +/* Set the icon size. When icons are requested from the icon manager, + they are scaled to this size before being returned. If the size is + changed, the icon cache is cleared. */ +void +grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr, + int width, int height) +{ + /* If the width or height is changed, we must clear the cache, since the + scaled bitmaps are stored in the cache. */ + if (width != mgr->icon_width || height != mgr->icon_height) + grub_gfxmenu_icon_manager_clear_cache (mgr); + + mgr->icon_width = width; + mgr->icon_height = height; +} + +/* Try to load an icon for the specified CLASS_NAME in the directory DIR. + Returns 0 if the icon could not be loaded, or returns a pointer to a new + bitmap if it was successful. */ +static struct grub_video_bitmap * +try_loading_icon (grub_gfxmenu_icon_manager_t mgr, + const char *dir, const char *class_name) +{ + char *path = grub_malloc (grub_strlen (dir) + + grub_strlen (class_name) + + grub_strlen (icon_extension) + + 1); + if (! path) + return 0; + + grub_strcpy (path, dir); + grub_strcat (path, class_name); + grub_strcat (path, icon_extension); + + struct grub_video_bitmap *raw_bitmap; + grub_video_bitmap_load (&raw_bitmap, path); + grub_free (path); + grub_errno = GRUB_ERR_NONE; /* Critical to clear the error!! */ + if (! raw_bitmap) + return 0; + + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + mgr->icon_width, mgr->icon_height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (! scaled_bitmap) + { + grub_error_push (); + grub_error (grub_errno, "failed to scale icon"); + return 0; + } + + return scaled_bitmap; +} + +/* Get the icon for the specified class CLASS_NAME. If an icon for + CLASS_NAME already exists in the cache, then a reference to the cached + bitmap is returned. If it is not cached, then it is loaded and cached. + If no icon could be could for CLASS_NAME, then 0 is returned. */ +static struct grub_video_bitmap * +get_icon_by_class (grub_gfxmenu_icon_manager_t mgr, const char *class_name) +{ + /* First check the icon cache. */ + icon_entry_t entry; + for (entry = mgr->cache.next; entry; entry = entry->next) + { + if (grub_strcmp (entry->class_name, class_name) == 0) + return entry->bitmap; + } + + if (! mgr->theme_path) + return 0; + + /* Otherwise, we search for an icon to load. */ + char *theme_dir = grub_get_dirname (mgr->theme_path); + char *icons_dir; + struct grub_video_bitmap *icon; + icon = 0; + /* First try the theme's own icons, from "grub/themes/NAME/icons/" */ + icons_dir = grub_resolve_relative_path (theme_dir, "icons/"); + if (icons_dir) + { + icon = try_loading_icon (mgr, icons_dir, class_name); + grub_free (icons_dir); + } + if (! icon) + { + /* If the theme doesn't have an appropriate icon, check in + "grub/themes/icons". */ + /* TODO use GRUB prefix "/icons" */ + icons_dir = grub_resolve_relative_path (theme_dir, "../icons/"); + if (icons_dir) + { + icon = try_loading_icon (mgr, icons_dir, class_name); + grub_free (icons_dir); + } + } + grub_free (theme_dir); + + /* No icon was found. */ + /* This should probably be noted in the cache, so that a search is not + performed each time an icon for CLASS_NAME is requested. */ + if (! icon) + return 0; + + /* Insert a new cache entry for this icon. */ + entry = grub_malloc (sizeof (*entry)); + if (! entry) + { + grub_video_bitmap_destroy (icon); + return 0; + } + entry->class_name = grub_strdup (class_name); + entry->bitmap = icon; + entry->next = mgr->cache.next; + mgr->cache.next = entry; /* Link it into the cache. */ + return entry->bitmap; +} + +/* Get the best available icon for ENTRY. Beginning with the first class + listed in the menu entry and proceeding forward, an icon for each class + is searched for. The first icon found is returned. The returned icon + is scaled to the size specified by + grub_gfxmenu_icon_manager_set_icon_size(). + + Note: Bitmaps returned by this function are destroyed when the + icon manager is destroyed. + */ +struct grub_video_bitmap * +grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr, + grub_menu_entry_t entry) +{ + struct grub_menu_entry_class *c; + struct grub_video_bitmap *icon; + + /* Try each class in succession. */ + icon = 0; + for (c = entry->classes->next; c && ! icon; c = c->next) + icon = get_icon_by_class (mgr, c->name); + return icon; +} === added file 'gfxmenu/model.c' --- gfxmenu/model.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/model.c 2008-08-12 02:55:06 +0000 @@ -0,0 +1,191 @@ +/* model.c - Graphical menu interface MVC model. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Model type definition. */ +struct grub_gfxmenu_model +{ + grub_menu_t menu; + int num_entries; + grub_menu_entry_t *entries; + int selected_entry_index; + int timeout_set; + grub_uint64_t timeout_start; + grub_uint64_t timeout_at; +}; + + +grub_gfxmenu_model_t +grub_gfxmenu_model_new (grub_menu_t menu) +{ + grub_gfxmenu_model_t model; + + model = grub_malloc (sizeof (*model)); + if (! model) + return 0; + + model->menu = menu; + model->num_entries = menu->size; + model->entries = 0; + model->selected_entry_index = 0; + model->timeout_set = 0; + model->timeout_at = 0; + if (model->num_entries > 0) + { + model->entries = grub_malloc (model->num_entries + * sizeof (*model->entries)); + if (! model->entries) + goto fail_and_free; + + int i; + grub_menu_entry_t cur; + for (i = 0, cur = menu->entry_list; + i < model->num_entries; + i++, cur = cur->next) + { + model->entries[i] = cur; + } + } + + return model; + +fail_and_free: + grub_free (model->entries); + grub_free (model); + return 0; +} + +void +grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model) +{ + if (! model) + return; + + grub_free (model->entries); + model->entries = 0; + + grub_free (model); +} + +grub_menu_t +grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model) +{ + return model->menu; +} + +void +grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model) +{ + int timeout_sec = grub_menu_get_timeout (); + if (timeout_sec >= 0) + { + model->timeout_start = grub_get_time_ms (); + model->timeout_at = model->timeout_start + timeout_sec * 1000; + model->timeout_set = 1; + } + else + { + model->timeout_set = 0; + } +} + +void +grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model) +{ + model->timeout_set = 0; + grub_menu_set_timeout (-1); +} + +int +grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model) +{ + if (!model->timeout_set) + return -1; + + return model->timeout_at - model->timeout_start; +} + +int +grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model) +{ + if (!model->timeout_set) + return -1; + + return model->timeout_at - grub_get_time_ms (); +} + +int +grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model) +{ + if (model->timeout_set + && grub_get_time_ms () >= model->timeout_at) + return 1; + + return 0; +} + +int +grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model) +{ + return model->num_entries; +} + +int +grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model) +{ + return model->selected_entry_index; +} + +void +grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, int index) +{ + model->selected_entry_index = index; +} + +const char * +grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, int index) +{ + if (index < 0 || index >= model->num_entries) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index"); + return 0; + } + + return model->entries[index]->title; +} + +grub_menu_entry_t +grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, int index) +{ + if (index < 0 || index >= model->num_entries) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index"); + return 0; + } + + return model->entries[index]; +} === added file 'gfxmenu/named_colors.c' --- gfxmenu/named_colors.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/named_colors.c 2008-08-16 17:35:05 +0000 @@ -0,0 +1,209 @@ +/* named_colors.c - Named color values. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct named_color +{ + const char *name; + grub_gui_color_t color; +}; + +/* + Named color list generated from the list of SVG color keywords from + , + processed through the following Perl command: + perl -ne 'chomp;split;print "{ \"$_[0]\", RGB_COLOR($_[2]) },\n"' + */ + +#define RGB_COLOR(r,g,b) {.red = r, .green = g, .blue = b, .alpha = 255} + +static struct named_color named_colors[] = +{ + { "aliceblue", RGB_COLOR(240,248,255) }, + { "antiquewhite", RGB_COLOR(250,235,215) }, + { "aqua", RGB_COLOR(0,255,255) }, + { "aquamarine", RGB_COLOR(127,255,212) }, + { "azure", RGB_COLOR(240,255,255) }, + { "beige", RGB_COLOR(245,245,220) }, + { "bisque", RGB_COLOR(255,228,196) }, + { "black", RGB_COLOR(0,0,0) }, + { "blanchedalmond", RGB_COLOR(255,235,205) }, + { "blue", RGB_COLOR(0,0,255) }, + { "blueviolet", RGB_COLOR(138,43,226) }, + { "brown", RGB_COLOR(165,42,42) }, + { "burlywood", RGB_COLOR(222,184,135) }, + { "cadetblue", RGB_COLOR(95,158,160) }, + { "chartreuse", RGB_COLOR(127,255,0) }, + { "chocolate", RGB_COLOR(210,105,30) }, + { "coral", RGB_COLOR(255,127,80) }, + { "cornflowerblue", RGB_COLOR(100,149,237) }, + { "cornsilk", RGB_COLOR(255,248,220) }, + { "crimson", RGB_COLOR(220,20,60) }, + { "cyan", RGB_COLOR(0,255,255) }, + { "darkblue", RGB_COLOR(0,0,139) }, + { "darkcyan", RGB_COLOR(0,139,139) }, + { "darkgoldenrod", RGB_COLOR(184,134,11) }, + { "darkgray", RGB_COLOR(169,169,169) }, + { "darkgreen", RGB_COLOR(0,100,0) }, + { "darkgrey", RGB_COLOR(169,169,169) }, + { "darkkhaki", RGB_COLOR(189,183,107) }, + { "darkmagenta", RGB_COLOR(139,0,139) }, + { "darkolivegreen", RGB_COLOR(85,107,47) }, + { "darkorange", RGB_COLOR(255,140,0) }, + { "darkorchid", RGB_COLOR(153,50,204) }, + { "darkred", RGB_COLOR(139,0,0) }, + { "darksalmon", RGB_COLOR(233,150,122) }, + { "darkseagreen", RGB_COLOR(143,188,143) }, + { "darkslateblue", RGB_COLOR(72,61,139) }, + { "darkslategray", RGB_COLOR(47,79,79) }, + { "darkslategrey", RGB_COLOR(47,79,79) }, + { "darkturquoise", RGB_COLOR(0,206,209) }, + { "darkviolet", RGB_COLOR(148,0,211) }, + { "deeppink", RGB_COLOR(255,20,147) }, + { "deepskyblue", RGB_COLOR(0,191,255) }, + { "dimgray", RGB_COLOR(105,105,105) }, + { "dimgrey", RGB_COLOR(105,105,105) }, + { "dodgerblue", RGB_COLOR(30,144,255) }, + { "firebrick", RGB_COLOR(178,34,34) }, + { "floralwhite", RGB_COLOR(255,250,240) }, + { "forestgreen", RGB_COLOR(34,139,34) }, + { "fuchsia", RGB_COLOR(255,0,255) }, + { "gainsboro", RGB_COLOR(220,220,220) }, + { "ghostwhite", RGB_COLOR(248,248,255) }, + { "gold", RGB_COLOR(255,215,0) }, + { "goldenrod", RGB_COLOR(218,165,32) }, + { "gray", RGB_COLOR(128,128,128) }, + { "green", RGB_COLOR(0,128,0) }, + { "greenyellow", RGB_COLOR(173,255,47) }, + { "grey", RGB_COLOR(128,128,128) }, + { "honeydew", RGB_COLOR(240,255,240) }, + { "hotpink", RGB_COLOR(255,105,180) }, + { "indianred", RGB_COLOR(205,92,92) }, + { "indigo", RGB_COLOR(75,0,130) }, + { "ivory", RGB_COLOR(255,255,240) }, + { "khaki", RGB_COLOR(240,230,140) }, + { "lavender", RGB_COLOR(230,230,250) }, + { "lavenderblush", RGB_COLOR(255,240,245) }, + { "lawngreen", RGB_COLOR(124,252,0) }, + { "lemonchiffon", RGB_COLOR(255,250,205) }, + { "lightblue", RGB_COLOR(173,216,230) }, + { "lightcoral", RGB_COLOR(240,128,128) }, + { "lightcyan", RGB_COLOR(224,255,255) }, + { "lightgoldenrodyellow", RGB_COLOR(250,250,210) }, + { "lightgray", RGB_COLOR(211,211,211) }, + { "lightgreen", RGB_COLOR(144,238,144) }, + { "lightgrey", RGB_COLOR(211,211,211) }, + { "lightpink", RGB_COLOR(255,182,193) }, + { "lightsalmon", RGB_COLOR(255,160,122) }, + { "lightseagreen", RGB_COLOR(32,178,170) }, + { "lightskyblue", RGB_COLOR(135,206,250) }, + { "lightslategray", RGB_COLOR(119,136,153) }, + { "lightslategrey", RGB_COLOR(119,136,153) }, + { "lightsteelblue", RGB_COLOR(176,196,222) }, + { "lightyellow", RGB_COLOR(255,255,224) }, + { "lime", RGB_COLOR(0,255,0) }, + { "limegreen", RGB_COLOR(50,205,50) }, + { "linen", RGB_COLOR(250,240,230) }, + { "magenta", RGB_COLOR(255,0,255) }, + { "maroon", RGB_COLOR(128,0,0) }, + { "mediumaquamarine", RGB_COLOR(102,205,170) }, + { "mediumblue", RGB_COLOR(0,0,205) }, + { "mediumorchid", RGB_COLOR(186,85,211) }, + { "mediumpurple", RGB_COLOR(147,112,219) }, + { "mediumseagreen", RGB_COLOR(60,179,113) }, + { "mediumslateblue", RGB_COLOR(123,104,238) }, + { "mediumspringgreen", RGB_COLOR(0,250,154) }, + { "mediumturquoise", RGB_COLOR(72,209,204) }, + { "mediumvioletred", RGB_COLOR(199,21,133) }, + { "midnightblue", RGB_COLOR(25,25,112) }, + { "mintcream", RGB_COLOR(245,255,250) }, + { "mistyrose", RGB_COLOR(255,228,225) }, + { "moccasin", RGB_COLOR(255,228,181) }, + { "navajowhite", RGB_COLOR(255,222,173) }, + { "navy", RGB_COLOR(0,0,128) }, + { "oldlace", RGB_COLOR(253,245,230) }, + { "olive", RGB_COLOR(128,128,0) }, + { "olivedrab", RGB_COLOR(107,142,35) }, + { "orange", RGB_COLOR(255,165,0) }, + { "orangered", RGB_COLOR(255,69,0) }, + { "orchid", RGB_COLOR(218,112,214) }, + { "palegoldenrod", RGB_COLOR(238,232,170) }, + { "palegreen", RGB_COLOR(152,251,152) }, + { "paleturquoise", RGB_COLOR(175,238,238) }, + { "palevioletred", RGB_COLOR(219,112,147) }, + { "papayawhip", RGB_COLOR(255,239,213) }, + { "peachpuff", RGB_COLOR(255,218,185) }, + { "peru", RGB_COLOR(205,133,63) }, + { "pink", RGB_COLOR(255,192,203) }, + { "plum", RGB_COLOR(221,160,221) }, + { "powderblue", RGB_COLOR(176,224,230) }, + { "purple", RGB_COLOR(128,0,128) }, + { "red", RGB_COLOR(255,0,0) }, + { "rosybrown", RGB_COLOR(188,143,143) }, + { "royalblue", RGB_COLOR(65,105,225) }, + { "saddlebrown", RGB_COLOR(139,69,19) }, + { "salmon", RGB_COLOR(250,128,114) }, + { "sandybrown", RGB_COLOR(244,164,96) }, + { "seagreen", RGB_COLOR(46,139,87) }, + { "seashell", RGB_COLOR(255,245,238) }, + { "sienna", RGB_COLOR(160,82,45) }, + { "silver", RGB_COLOR(192,192,192) }, + { "skyblue", RGB_COLOR(135,206,235) }, + { "slateblue", RGB_COLOR(106,90,205) }, + { "slategray", RGB_COLOR(112,128,144) }, + { "slategrey", RGB_COLOR(112,128,144) }, + { "snow", RGB_COLOR(255,250,250) }, + { "springgreen", RGB_COLOR(0,255,127) }, + { "steelblue", RGB_COLOR(70,130,180) }, + { "tan", RGB_COLOR(210,180,140) }, + { "teal", RGB_COLOR(0,128,128) }, + { "thistle", RGB_COLOR(216,191,216) }, + { "tomato", RGB_COLOR(255,99,71) }, + { "turquoise", RGB_COLOR(64,224,208) }, + { "violet", RGB_COLOR(238,130,238) }, + { "wheat", RGB_COLOR(245,222,179) }, + { "white", RGB_COLOR(255,255,255) }, + { "whitesmoke", RGB_COLOR(245,245,245) }, + { "yellow", RGB_COLOR(255,255,0) }, + { "yellowgreen", RGB_COLOR(154,205,50) }, + { 0, { 0, 0, 0, 0 } } /* Terminator. */ +}; + +/* Get the color named NAME. If the color was found, returns 1 and + stores the color into *COLOR. If the color was not found, returns 0 and + does not modify *COLOR. */ +int +grub_gui_get_named_color (const char *name, + grub_gui_color_t *color) +{ + int i; + for (i = 0; named_colors[i].name; i++) + { + if (grub_strcmp (named_colors[i].name, name) == 0) + { + *color = named_colors[i].color; + return 1; + } + } + return 0; +} === added file 'gfxmenu/theme_loader.c' --- gfxmenu/theme_loader.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/theme_loader.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,720 @@ +/* theme_loader.c - Theme file loader for gfxmenu. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Construct a new box widget using ABSPATTERN to find the pixmap files for + it, storing the new box instance at *BOXPTR. + PATTERN should be of the form: "(hd0,0)/somewhere/style*.png". + The '*' then gets substituted with the various pixmap names that the + box uses. */ +static grub_err_t +recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern) +{ + char *prefix; + char *suffix; + char *star; + grub_gfxmenu_box_t box; + + star = grub_strchr (abspattern, '*'); + if (! star) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing `*' in box pixmap pattern `%s'", abspattern); + + /* Prefix: Get the part before the '*'. */ + prefix = grub_malloc (star - abspattern + 1); + if (! prefix) + return grub_errno; + + grub_memcpy (prefix, abspattern, star - abspattern); + prefix[star - abspattern] = '\0'; + + /* Suffix: Everything after the '*' is the suffix. */ + suffix = star + 1; + + box = grub_gfxmenu_create_box (prefix, suffix); + grub_free (prefix); + if (! box) + return grub_errno; + + if (*boxptr) + (*boxptr)->destroy (*boxptr); + *boxptr = box; + return grub_errno; +} + + +/* Construct a new box widget using PATTERN to find the pixmap files for it, + storing the new widget at *BOXPTR. PATTERN should be of the form: + "somewhere/style*.png". The '*' then gets substituted with the various + pixmap names that the widget uses. + + Important! The value of *BOXPTR must be initialized! It must either + (1) Be 0 (a NULL pointer), or + (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance. + In this case, the previous instance is destroyed. */ +grub_err_t +grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, + const char *pattern, const char *theme_dir) +{ + char *abspattern; + + /* Check arguments. */ + if (! pattern) + { + /* If no pixmap pattern is given, then just create an empty box. */ + if (*boxptr) + (*boxptr)->destroy (*boxptr); + *boxptr = grub_gfxmenu_create_box (0, 0); + return grub_errno; + } + + if (! theme_dir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "styled box missing theme directory"); + + /* Resolve to an absolute path. */ + abspattern = grub_resolve_relative_path (theme_dir, pattern); + if (! abspattern) + return grub_errno; + + /* Create the box. */ + recreate_box_absolute (boxptr, abspattern); + grub_free (abspattern); + return grub_errno; +} + +/* Set the specified property NAME on the view to the given string VALUE. + The caller is responsible for the lifetimes of NAME and VALUE. */ +static grub_err_t +theme_set_string (grub_gfxmenu_view_t view, + const char *name, + const char *value, + const char *theme_dir, + const char *filename, + int line_num, + int col_num) +{ + if (! grub_strcmp ("title-font", name)) + view->title_font = grub_font_get (value); + else if (! grub_strcmp ("message-font", name)) + view->message_font = grub_font_get (value); + else if (! grub_strcmp ("terminal-font", name)) + { + grub_free (view->terminal_font_name); + view->terminal_font_name = grub_strdup (value); + if (! view->terminal_font_name) + return grub_errno; + } + else if (! grub_strcmp ("title-color", name)) + grub_gui_parse_color (value, &view->title_color); + else if (! grub_strcmp ("message-color", name)) + grub_gui_parse_color (value, &view->message_color); + else if (! grub_strcmp ("message-bg-color", name)) + grub_gui_parse_color (value, &view->message_bg_color); + else if (! grub_strcmp ("desktop-image", name)) + { + struct grub_video_bitmap *raw_bitmap; + struct grub_video_bitmap *scaled_bitmap; + char *path; + path = grub_resolve_relative_path (theme_dir, value); + if (! path) + return grub_errno; + if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE) + { + grub_free (path); + return grub_errno; + } + grub_free(path); + grub_video_bitmap_create_scaled (&scaled_bitmap, + view->screen.width, + view->screen.height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (! scaled_bitmap) + { + grub_error_push (); + return grub_error (grub_errno, "error scaling desktop image"); + } + + grub_video_bitmap_destroy (view->desktop_image); + view->desktop_image = scaled_bitmap; + } + else if (! grub_strcmp ("desktop-color", name)) + grub_gui_parse_color (value, &view->desktop_color); + else if (! grub_strcmp ("terminal-box", name)) + { + grub_err_t err; + err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir); + if (err != GRUB_ERR_NONE) + return err; + } + else if (! grub_strcmp ("title-text", name)) + { + grub_free (view->title_text); + view->title_text = grub_strdup (value); + if (! view->title_text) + return grub_errno; + } + else + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "%s:%d:%d unknown property `%s'", + filename, line_num, col_num, name); + } + return grub_errno; +} + +struct parsebuf +{ + char *buf; + int pos; + int len; + int line_num; + int col_num; + const char *filename; + char *theme_dir; + grub_gfxmenu_view_t view; +}; + +static int +has_more (struct parsebuf *p) +{ + return p->pos < p->len; +} + +static int +read_char (struct parsebuf *p) +{ + if (has_more (p)) + { + char c; + c = p->buf[p->pos++]; + if (c == '\n') + { + p->line_num++; + p->col_num = 1; + } + else + { + p->col_num++; + } + return c; + } + else + return -1; +} + +static int +peek_char (struct parsebuf *p) +{ + if (has_more (p)) + return p->buf[p->pos]; + else + return -1; +} + +static int +is_whitespace (char c) +{ + return (c == ' ' + || c == '\t' + || c == '\r' + || c == '\n' + || c == '\f'); +} + +static void +skip_whitespace (struct parsebuf *p) +{ + while (has_more (p) && is_whitespace(peek_char (p))) + read_char (p); +} + +static void +advance_to_next_line (struct parsebuf *p) +{ + int c; + + /* Eat characters up to the newline. */ + do + { + c = read_char (p); + } + while (c != -1 && c != '\n'); +} + +static int +is_identifier_char (int c) +{ + return (c != -1 + && (grub_isalpha(c) + || grub_isdigit(c) + || c == '_' + || c == '-')); +} + +static char * +read_identifier (struct parsebuf *p) +{ + /* Index of the first character of the identifier in p->buf. */ + int start; + /* Next index after the last character of the identifer in p->buf. */ + int end; + + skip_whitespace (p); + + /* Capture the start of the identifier. */ + start = p->pos; + + /* Scan for the end. */ + while (is_identifier_char (peek_char (p))) + read_char (p); + end = p->pos; + + if (end - start < 1) + return 0; + + return grub_new_substring (p->buf, start, end); +} + +static char * +read_expression (struct parsebuf *p) +{ + int start; + int end; + + skip_whitespace (p); + if (peek_char (p) == '"') + { + /* Read as a quoted string. + The quotation marks are not included in the expression value. */ + /* Skip opening quotation mark. */ + read_char (p); + start = p->pos; + while (has_more (p) && peek_char (p) != '"') + read_char (p); + end = p->pos; + /* Skip the terminating quotation mark. */ + read_char (p); + } + else if (peek_char (p) == '(') + { + /* Read as a parenthesized string -- for tuples/coordinates. */ + /* The parentheses are included in the expression value. */ + int c; + + start = p->pos; + do + { + c = read_char (p); + } + while (c != -1 && c != ')'); + end = p->pos; + } + else if (has_more (p)) + { + /* Read as a single word -- for numeric values or words without + whitespace. */ + start = p->pos; + while (has_more (p) && ! is_whitespace (peek_char (p))) + read_char (p); + end = p->pos; + } + else + { + /* The end of the theme file has been reached. */ + grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file", + p->filename, p->line_num, p->col_num); + return 0; + } + + return grub_new_substring (p->buf, start, end); +} + +/* Read a GUI object specification from the theme file. + Any components created will be added to the GUI container PARENT. */ +static grub_err_t +read_object (struct parsebuf *p, grub_gui_container_t parent) +{ + grub_video_rect_t bounds; + + char *name; + name = read_identifier (p); + if (! name) + goto cleanup; + + grub_gui_component_t component = 0; + if (grub_strcmp (name, "label") == 0) + { + component = grub_gui_label_new (); + } + else if (grub_strcmp (name, "image") == 0) + { + component = grub_gui_image_new (); + } + else if (grub_strcmp (name, "vbox") == 0) + { + component = (grub_gui_component_t) grub_gui_vbox_new (); + } + else if (grub_strcmp (name, "hbox") == 0) + { + component = (grub_gui_component_t) grub_gui_hbox_new (); + } + else if (grub_strcmp (name, "canvas") == 0) + { + component = (grub_gui_component_t) grub_gui_canvas_new (); + } + else if (grub_strcmp (name, "progress_bar") == 0) + { + component = grub_gui_progress_bar_new (); + } + else if (grub_strcmp (name, "circular_progress") == 0) + { + component = grub_gui_circular_progress_new (); + } + else if (grub_strcmp (name, "boot_menu") == 0) + { + component = grub_gui_list_new (); + } + else + { + /* Unknown type. */ + grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'", + p->filename, p->line_num, p->col_num, name); + goto cleanup; + } + + if (! component) + goto cleanup; + + /* Inform the component about the theme so it can find its resources. */ + component->ops->set_property (component, "theme_dir", p->theme_dir); + component->ops->set_property (component, "theme_path", p->filename); + + /* Add the component as a child of PARENT. */ + bounds.x = 0; + bounds.y = 0; + bounds.width = -1; + bounds.height = -1; + component->ops->set_bounds (component, &bounds); + parent->ops->add (parent, component); + + skip_whitespace (p); + if (read_char (p) != '{') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d expected `{' after object type name `%s'", + p->filename, p->line_num, p->col_num, name); + goto cleanup; + } + + while (has_more (p)) + { + skip_whitespace (p); + + /* Check whether the end has been encountered. */ + if (peek_char (p) == '}') + { + /* Skip the closing brace. */ + read_char (p); + break; + } + + if (peek_char (p) == '#') + { + /* Skip comments. */ + advance_to_next_line (p); + continue; + } + + if (peek_char (p) == '+') + { + /* Skip the '+'. */ + read_char (p); + + /* Check whether this component is a container. */ + if (component->ops->is_instance (component, "container")) + { + /* Read the sub-object recursively and add it as a child. */ + if (read_object (p, (grub_gui_container_t) component) != 0) + goto cleanup; + /* After reading the sub-object, resume parsing, expecting + another property assignment or sub-object definition. */ + continue; + } + else + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d attempted to add object to non-container", + p->filename, p->line_num, p->col_num); + goto cleanup; + } + } + + char *property; + property = read_identifier (p); + if (! property) + { + grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file", + p->filename, p->line_num, p->col_num); + goto cleanup; + } + + skip_whitespace (p); + if (read_char (p) != '=') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d expected `=' after property name `%s'", + p->filename, p->line_num, p->col_num, property); + grub_free (property); + goto cleanup; + } + skip_whitespace (p); + + char *value; + value = read_expression (p); + if (! value) + { + grub_free (property); + goto cleanup; + } + + /* Handle the property value. */ + if (grub_strcmp (property, "position") == 0) + { + /* Special case for position value. */ + int x; + int y; + + if (grub_gui_parse_2_tuple (value, &x, &y) == GRUB_ERR_NONE) + { + grub_video_rect_t r; + component->ops->get_bounds (component, &r); + r.x = x; + r.y = y; + component->ops->set_bounds (component, &r); + } + } + else if (grub_strcmp (property, "size") == 0) + { + /* Special case for size value. */ + int w; + int h; + + if (grub_gui_parse_2_tuple (value, &w, &h) == GRUB_ERR_NONE) + { + grub_video_rect_t r; + component->ops->get_bounds (component, &r); + r.width = w; + r.height = h; + component->ops->set_bounds (component, &r); + } + } + else + { + /* General property handling. */ + component->ops->set_property (component, property, value); + } + + grub_free (value); + grub_free (property); + if (grub_errno != GRUB_ERR_NONE) + goto cleanup; + } + + /* Set the object's size to its preferred size unless the user has + explicitly specified the size. */ + component->ops->get_bounds (component, &bounds); + if (bounds.width == -1 || bounds.height == -1) + { + component->ops->get_preferred_size (component, + &bounds.width, &bounds.height); + component->ops->set_bounds (component, &bounds); + } + +cleanup: + grub_free (name); + return grub_errno; +} + +static grub_err_t +read_property (struct parsebuf *p) +{ + char *name; + + /* Read the property name. */ + name = read_identifier (p); + if (! name) + { + advance_to_next_line (p); + return grub_errno; + } + + /* Skip whitespace before separator. */ + skip_whitespace (p); + + /* Read separator. */ + if (read_char (p) != ':') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d missing separator after property name `%s'", + p->filename, p->line_num, p->col_num, name); + goto done; + } + + /* Skip whitespace after separator. */ + skip_whitespace (p); + + /* Get the value based on its type. */ + if (peek_char (p) == '"') + { + /* String value (e.g., '"My string"'). */ + char *value = read_expression (p); + if (! value) + { + grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value", + p->filename, p->line_num, p->col_num); + goto done; + } + /* If theme_set_string results in an error, grub_errno will be returned + below. */ + theme_set_string (p->view, name, value, p->theme_dir, + p->filename, p->line_num, p->col_num); + grub_free (value); + } + else + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d property value invalid; " + "enclose literal values in quotes (\")", + p->filename, p->line_num, p->col_num); + goto done; + } + +done: + grub_free (name); + return grub_errno; +} + +/* Set properties on the view based on settings from the specified + theme file. */ +grub_err_t +grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) +{ + grub_file_t file; + struct parsebuf p; + + p.view = view; + p.theme_dir = grub_get_dirname (theme_path); + + file = grub_file_open (theme_path); + if (! file) + { + grub_free (p.theme_dir); + return grub_errno; + } + + p.len = grub_file_size (file); + p.buf = grub_malloc (p.len); + p.pos = 0; + p.line_num = 1; + p.col_num = 1; + p.filename = theme_path; + if (! p.buf) + { + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; + } + if (grub_file_read (file, p.buf, p.len) != p.len) + { + grub_free (p.buf); + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; + } + + if (view->canvas) + view->canvas->ops->component.destroy (view->canvas); + + view->canvas = grub_gui_canvas_new (); + ((grub_gui_component_t) view->canvas) + ->ops->set_bounds ((grub_gui_component_t) view->canvas, + &view->screen); + + while (has_more (&p)) + { + /* Skip comments (lines beginning with #). */ + if (peek_char (&p) == '#') + { + advance_to_next_line (&p); + continue; + } + + /* Find the first non-whitespace character. */ + skip_whitespace (&p); + + /* Handle the content. */ + if (peek_char (&p) == '+') + { + /* Skip the '+'. */ + read_char (&p); + read_object (&p, view->canvas); + } + else + { + read_property (&p); + } + + if (grub_errno != GRUB_ERR_NONE) + goto fail; + } + + /* Set the new theme path. */ + grub_free (view->theme_path); + view->theme_path = grub_strdup (theme_path); + goto cleanup; + +fail: + if (view->canvas) + { + view->canvas->ops->component.destroy (view->canvas); + view->canvas = 0; + } + +cleanup: + grub_free (p.buf); + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; +} === added file 'gfxmenu/view.c' --- gfxmenu/view.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/view.c 2009-06-08 16:39:59 +0000 @@ -0,0 +1,503 @@ +/* view.c - Graphical menu interface MVC view. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The component ID identifying GUI components to be updated as the timeout + status changes. */ +#define TIMEOUT_COMPONENT_ID "__timeout__" + +static void init_terminal (grub_gfxmenu_view_t view); +static void destroy_terminal (void); +static grub_err_t set_graphics_mode (void); +static grub_err_t set_text_mode (void); + +/* Create a new view object, loading the theme specified by THEME_PATH and + associating MODEL with the view. */ +grub_gfxmenu_view_t +grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model) +{ + grub_gfxmenu_view_t view; + + view = grub_malloc (sizeof (*view)); + if (! view) + return 0; + + set_graphics_mode (); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_get_viewport ((unsigned *) &view->screen.x, + (unsigned *) &view->screen.y, + (unsigned *) &view->screen.width, + (unsigned *) &view->screen.height); + + /* Clear the screen; there may be garbage left over in video memory, and + loading the menu style (particularly the background) can take a while. */ + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + grub_video_swap_buffers (); + + grub_font_t default_font; + grub_gui_color_t default_fg_color; + grub_gui_color_t default_bg_color; + + default_font = grub_font_get ("Helvetica 12"); + default_fg_color = grub_gui_color_rgb (0, 0, 0); + default_bg_color = grub_gui_color_rgb (255, 255, 255); + + view->model = model; + view->canvas = 0; + + view->title_font = default_font; + view->message_font = default_font; + view->terminal_font_name = grub_strdup ("Fixed 10"); + view->title_color = default_fg_color; + view->message_color = default_bg_color; + view->message_bg_color = default_fg_color; + view->desktop_image = 0; + view->desktop_color = default_bg_color; + view->terminal_box = grub_gfxmenu_create_box (0, 0); + view->title_text = grub_strdup ("GRUB Boot Menu"); + view->progress_message_text = 0; + view->theme_path = 0; + + if (grub_gfxmenu_view_load_theme (view, theme_path) != 0) + { + grub_gfxmenu_view_destroy (view); + return 0; + } + + init_terminal (view); + + return view; +} + +/* Destroy the view object. All used memory is freed. */ +void +grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) +{ + grub_video_bitmap_destroy (view->desktop_image); + if (view->terminal_box) + view->terminal_box->destroy (view->terminal_box); + grub_free (view->terminal_font_name); + grub_free (view->title_text); + grub_free (view->progress_message_text); + grub_free (view->theme_path); + if (view->canvas) + view->canvas->ops->component.destroy (view->canvas); + grub_free (view); + + set_text_mode (); + destroy_terminal (); +} + +/* Sets MESSAGE as the progress message for the view. + MESSAGE can be 0, in which case no message is displayed. */ +static void +set_progress_message (grub_gfxmenu_view_t view, const char *message) +{ + grub_free (view->progress_message_text); + if (message) + view->progress_message_text = grub_strdup (message); + else + view->progress_message_text = 0; +} + +static void +draw_background (grub_gfxmenu_view_t view) +{ + if (view->desktop_image) + { + struct grub_video_bitmap *img = view->desktop_image; + grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, + view->screen.x, view->screen.y, 0, 0, + grub_video_bitmap_get_width (img), + grub_video_bitmap_get_height (img)); + } + else + { + grub_video_fill_rect (grub_gui_map_color (view->desktop_color), + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + } +} + +static void +draw_title (grub_gfxmenu_view_t view) +{ + if (! view->title_text) + return; + + /* Center the title. */ + int title_width = grub_font_get_string_width (view->title_font, + view->title_text); + int x = (view->screen.width - title_width) / 2; + int y = 40 + grub_font_get_ascent (view->title_font); + grub_font_draw_string (view->title_text, + view->title_font, + grub_gui_map_color (view->title_color), + x, y); +} + +struct progress_value_data +{ + const char *visible; + const char *start; + const char *end; + const char *value; + const char *text; +}; + +static void +update_timeout_visit (grub_gui_component_t component, + void *userdata) +{ + struct progress_value_data *pv; + pv = (struct progress_value_data *) userdata; + component->ops->set_property (component, "visible", pv->visible); + component->ops->set_property (component, "start", pv->start); + component->ops->set_property (component, "end", pv->end); + component->ops->set_property (component, "value", pv->value); + component->ops->set_property (component, "text", pv->text); +} + +static void +update_timeout (grub_gfxmenu_view_t view) +{ + char startbuf[20]; + char valuebuf[20]; + char msgbuf[120]; + + int timeout = grub_gfxmenu_model_get_timeout_ms (view->model); + int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model); + struct progress_value_data pv; + + pv.visible = timeout > 0 ? "true" : "false"; + grub_sprintf (startbuf, "%d", -timeout); + pv.start = startbuf; + pv.end = "0"; + grub_sprintf (valuebuf, "%d", remaining > 0 ? -remaining : 0); + pv.value = valuebuf; + + int seconds_remaining_rounded_up = (remaining + 999) / 1000; + grub_sprintf (msgbuf, + "The highlighted entry will be booted automatically in %d s.", + seconds_remaining_rounded_up); + pv.text = msgbuf; + + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); +} + +static void +update_menu_visit (grub_gui_component_t component, + void *userdata) +{ + grub_gfxmenu_view_t view; + view = userdata; + if (component->ops->is_instance (component, "list")) + { + grub_gui_list_t list = (grub_gui_list_t) component; + list->ops->set_view_info (list, view->theme_path, view->model); + } +} + +/* Update any boot menu components with the current menu model and + theme path. */ +static void +update_menu_components (grub_gfxmenu_view_t view) +{ + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + update_menu_visit, view); +} + +static void +draw_message (grub_gfxmenu_view_t view) +{ + char *text = view->progress_message_text; + if (! text) + return; + + grub_font_t font = view->message_font; + grub_video_color_t color = grub_gui_map_color (view->message_color); + + /* Set the timeout bar's frame. */ + grub_video_rect_t f; + f.width = view->screen.width * 4 / 5; + f.height = 50; + f.x = view->screen.x + (view->screen.width - f.width) / 2; + f.y = view->screen.y + view->screen.height - 90 - 20 - f.height; + + /* Border. */ + grub_video_fill_rect (color, + f.x-1, f.y-1, f.width+2, f.height+2); + /* Fill. */ + grub_video_fill_rect (grub_gui_map_color (view->message_bg_color), + f.x, f.y, f.width, f.height); + + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = f.x + (f.width - text_width) / 2; + int y = (f.y + (f.height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_font_draw_string (text, font, color, x, y); +} + + +void +grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) +{ + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + update_timeout (view); + update_menu_components (view); + + draw_background (view); + if (view->canvas) + view->canvas->ops->component.paint (view->canvas); + draw_title (view); + draw_message (view); +} + +static grub_err_t +set_graphics_mode (void) +{ + const char *doublebuf_str = grub_env_get ("doublebuffering"); + int double_buffering = (doublebuf_str && doublebuf_str[0] == 'n') ? 0 : 1; + + const char *modestr = grub_env_get ("gfxmode"); + if (grub_video_set_mode (modestr, 0) != GRUB_ERR_NONE) + return grub_errno; + + grub_video_enable_double_buffering(double_buffering); + + return GRUB_ERR_NONE; +} + +static grub_err_t +set_text_mode (void) +{ + return grub_video_restore (); +} + +static int term_target_width; +static int term_target_height; +static struct grub_video_render_target *term_target; +static int term_initialized; +static grub_term_output_t term_original; +static grub_gfxmenu_view_t term_view; + +static void +repaint_terminal (int x __attribute ((unused)), + int y __attribute ((unused)), + int width __attribute ((unused)), + int height __attribute ((unused))) +{ + if (! term_view) + return; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_gfxmenu_view_draw (term_view); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + int termx = term_view->screen.x + + term_view->screen.width * (10 - 7) / 10 / 2; + int termy = term_view->screen.y + + term_view->screen.height * (10 - 7) / 10 / 2; + + grub_gfxmenu_box_t term_box = term_view->terminal_box; + if (term_box) + { + term_box->set_content_size (term_box, + term_target_width, term_target_height); + + term_box->draw (term_box, + termx - term_box->get_left_pad (term_box), + termy - term_box->get_top_pad (term_box)); + } + + grub_video_blit_render_target (term_target, GRUB_VIDEO_BLIT_REPLACE, + termx, termy, + 0, 0, term_target_width, term_target_height); + grub_video_swap_buffers (); +} + +static void +init_terminal (grub_gfxmenu_view_t view) +{ + term_original = grub_term_get_current_output (); + + term_target_width = view->screen.width * 7 / 10; + term_target_height = view->screen.height * 7 / 10; + + grub_video_create_render_target (&term_target, + term_target_width, + term_target_height, + GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA); + if (grub_errno != GRUB_ERR_NONE) + return; + + /* Note: currently there is no API for changing the gfxterm font + on the fly, so whatever font the initially loaded theme specifies + will be permanent. */ + grub_gfxterm_init_window (term_target, 0, 0, + term_target_width, term_target_height, + view->terminal_font_name, 3); + if (grub_errno != GRUB_ERR_NONE) + return; + term_initialized = 1; + + /* XXX: store static pointer to the 'view' object so the repaint callback can access it. */ + term_view = view; + grub_gfxterm_set_repaint_callback (repaint_terminal); + grub_term_set_current_output (grub_gfxterm_get_term ()); +} + +static void destroy_terminal (void) +{ + term_view = 0; + if (term_initialized) + grub_gfxterm_destroy_window (); + grub_gfxterm_set_repaint_callback (0); + if (term_target) + grub_video_delete_render_target (term_target); + if (term_original) + grub_term_set_current_output (term_original); +} + + +static void +notify_booting (grub_menu_entry_t entry, void *userdata) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Booting '%s'", entry->title); + set_progress_message (view, s); + grub_free (s); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); +} + +static void +notify_fallback (grub_menu_entry_t entry, void *userdata) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Falling back to '%s'", entry->title); + set_progress_message (view, s); + grub_free (s); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); +} + +static void +notify_execution_failure (void *userdata __attribute__ ((unused))) +{ +} + + +static struct grub_menu_execute_callback execute_callback = +{ + .notify_booting = notify_booting, + .notify_fallback = notify_fallback, + .notify_failure = notify_execution_failure +}; + +int +grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, + grub_menu_entry_t entry) +{ + grub_menu_execute_with_fallback (grub_gfxmenu_model_get_menu (view->model), + entry, &execute_callback, (void *) view); + + if (set_graphics_mode () != GRUB_ERR_NONE) + return 0; /* Failure. */ + + /* If we returned, there was a failure. */ + set_progress_message (view, + "Unable to automatically boot. " + "Press SPACE to continue."); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); + while (GRUB_TERM_ASCII_CHAR(grub_getkey ()) != ' ') + { + /* Wait for SPACE to be pressed. */ + } + + set_progress_message (view, 0); /* Clear the message. */ + + return 1; /* Ok. */ +} + +int +grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, + grub_menu_entry_t entry) +{ + /* Currently we switch back to text mode by restoring + the original terminal before executing the menu entry. + It is hard to make it work when executing a menu entry + that switches video modes -- it using gfxterm in a + window, the repaint callback seems to crash GRUB. */ + /* TODO: Determine if this works when 'gfxterm' was set as + the current terminal before invoking the gfxmenu. */ + destroy_terminal (); + + grub_menu_execute_entry (entry); + if (grub_errno != GRUB_ERR_NONE) + grub_wait_after_message (); + + if (set_graphics_mode () != GRUB_ERR_NONE) + return 0; /* Failure. */ + + init_terminal (view); + return 1; /* Ok. */ +} + +void +grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view __attribute__((unused))) +{ + grub_cmdline_run (1); +} === added file 'gfxmenu/widget-box.c' --- gfxmenu/widget-box.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/widget-box.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,313 @@ +/* widget_box.c - Pixmap-stylized box widget. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum box_pixmaps +{ + BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW, + BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W, + BOX_PIXMAP_CENTER +}; + +static const char *box_pixmap_names[] = { + /* Corners: */ + "nw", "ne", "se", "sw", + /* Sides: */ + "n", "e", "s", "w", + /* Center: */ + "c" +}; + +#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names)) + +static int +get_height (struct grub_video_bitmap *bitmap) +{ + if (bitmap) + return grub_video_bitmap_get_height (bitmap); + else + return 0; +} + +static int +get_width (struct grub_video_bitmap *bitmap) +{ + if (bitmap) + return grub_video_bitmap_get_width (bitmap); + else + return 0; +} + +static void +blit (grub_gfxmenu_box_t self, int pixmap_index, int x, int y) +{ + struct grub_video_bitmap *bitmap; + bitmap = self->scaled_pixmaps[pixmap_index]; + if (! bitmap) + return; + grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, + grub_video_bitmap_get_width (bitmap), + grub_video_bitmap_get_height (bitmap)); +} + +static void +draw (grub_gfxmenu_box_t self, int x, int y) +{ + int height_n; + int height_s; + int height_e; + int height_w; + int width_n; + int width_s; + int width_e; + int width_w; + + height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); + height_s = get_height (self->scaled_pixmaps[BOX_PIXMAP_S]); + height_e = get_height (self->scaled_pixmaps[BOX_PIXMAP_E]); + height_w = get_height (self->scaled_pixmaps[BOX_PIXMAP_W]); + width_n = get_width (self->scaled_pixmaps[BOX_PIXMAP_N]); + width_s = get_width (self->scaled_pixmaps[BOX_PIXMAP_S]); + width_e = get_width (self->scaled_pixmaps[BOX_PIXMAP_E]); + width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + + /* Draw sides. */ + blit (self, BOX_PIXMAP_N, x + width_w, y); + blit (self, BOX_PIXMAP_S, x + width_w, y + height_n + self->content_height); + blit (self, BOX_PIXMAP_E, x + width_w + self->content_width, y + height_n); + blit (self, BOX_PIXMAP_W, x, y + height_n); + + /* Draw corners. */ + blit (self, BOX_PIXMAP_NW, x, y); + blit (self, BOX_PIXMAP_NE, x + width_w + self->content_width, y); + blit (self, BOX_PIXMAP_SE, + x + width_w + self->content_width, + y + height_n + self->content_height); + blit (self, BOX_PIXMAP_SW, x, y + height_n + self->content_height); + + /* Draw center. */ + blit (self, BOX_PIXMAP_CENTER, x + width_w, y + height_n); +} + +static grub_err_t +scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h) +{ + struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i]; + struct grub_video_bitmap *raw = self->raw_pixmaps[i]; + + if (raw == 0) + return grub_errno; + + if (w == -1) + w = grub_video_bitmap_get_width (raw); + if (h == -1) + h = grub_video_bitmap_get_height (raw); + + if (*scaled == 0 + || ((int) grub_video_bitmap_get_width (*scaled) != w) + || ((int) grub_video_bitmap_get_height (*scaled) != h)) + { + if (*scaled) + { + grub_video_bitmap_destroy (*scaled); + *scaled = 0; + } + + /* Don't try to create a bitmap with a zero dimension. */ + if (w != 0 && h != 0) + grub_video_bitmap_create_scaled (scaled, w, h, raw, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + { + grub_error_push (); + grub_error (grub_errno, + "failed to scale bitmap for styled box pixmap #%d", i); + } + } + + return grub_errno; +} + +static void +set_content_size (grub_gfxmenu_box_t self, + int width, int height) +{ + self->content_width = width; + self->content_height = height; + + /* Resize sides to match the width and height. */ + /* It is assumed that the corners width/height match the adjacent sides. */ + + /* Resize N and S sides to match width. */ + if (scale_pixmap(self, BOX_PIXMAP_N, width, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_S, width, -1) != GRUB_ERR_NONE) + return; + + /* Resize E and W sides to match height. */ + if (scale_pixmap(self, BOX_PIXMAP_E, -1, height) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_W, -1, height) != GRUB_ERR_NONE) + return; + + /* Don't scale the corners--they are assumed to match the sides. */ + if (scale_pixmap(self, BOX_PIXMAP_NW, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_SW, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_NE, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_SE, -1, -1) != GRUB_ERR_NONE) + return; + + /* Scale the center area. */ + if (scale_pixmap(self, BOX_PIXMAP_CENTER, width, height) != GRUB_ERR_NONE) + return; +} + +static int +get_left_pad (grub_gfxmenu_box_t self) +{ + return get_width (self->raw_pixmaps[BOX_PIXMAP_W]); +} + +static int +get_top_pad (grub_gfxmenu_box_t self) +{ + return get_height (self->raw_pixmaps[BOX_PIXMAP_N]); +} + +static int +get_right_pad (grub_gfxmenu_box_t self) +{ + return get_width (self->raw_pixmaps[BOX_PIXMAP_E]); +} + +static int +get_bottom_pad (grub_gfxmenu_box_t self) +{ + return get_height (self->raw_pixmaps[BOX_PIXMAP_S]); +} + +static void +destroy (grub_gfxmenu_box_t self) +{ + int i; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (self->raw_pixmaps[i]) + grub_video_bitmap_destroy(self->raw_pixmaps[i]); + self->raw_pixmaps[i] = 0; + + if (self->scaled_pixmaps[i]) + grub_video_bitmap_destroy(self->scaled_pixmaps[i]); + self->scaled_pixmaps[i] = 0; + } + grub_free (self->raw_pixmaps); + self->raw_pixmaps = 0; + grub_free (self->scaled_pixmaps); + self->scaled_pixmaps = 0; + + /* Free self: must be the last step! */ + grub_free (self); +} + + +/* Create a new box. If PIXMAPS_PREFIX and PIXMAPS_SUFFIX are both non-null, + then an attempt is made to load the north, south, east, west, northwest, + northeast, southeast, southwest, and center pixmaps. + If either PIXMAPS_PREFIX or PIXMAPS_SUFFIX is 0, then no pixmaps are + loaded, and the box has zero-width borders and is drawn transparent. */ +grub_gfxmenu_box_t +grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix) +{ + int i; + grub_gfxmenu_box_t box; + + box = (grub_gfxmenu_box_t) grub_malloc (sizeof (*box)); + if (! box) + return 0; + + box->content_width = 0; + box->content_height = 0; + box->raw_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + box->scaled_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + + /* Initialize all pixmap pointers to NULL so that proper destruction can + be performed if an error is encountered partway through construction. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + box->raw_pixmaps[i] = 0; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + box->scaled_pixmaps[i] = 0; + + /* Load the pixmaps. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (pixmaps_prefix && pixmaps_suffix) + { + char *path; + char *path_end; + + path = grub_malloc (grub_strlen (pixmaps_prefix) + + grub_strlen (box_pixmap_names[i]) + + grub_strlen (pixmaps_suffix) + + 1); + if (! path) + goto fail_and_destroy; + + /* Construct the specific path for this pixmap. */ + path_end = grub_stpcpy (path, pixmaps_prefix); + path_end = grub_stpcpy (path_end, box_pixmap_names[i]); + path_end = grub_stpcpy (path_end, pixmaps_suffix); + + grub_video_bitmap_load (&box->raw_pixmaps[i], path); + grub_free (path); + + /* Ignore missing pixmaps. */ + grub_errno = GRUB_ERR_NONE; + } + } + + box->draw = draw; + box->set_content_size = set_content_size; + box->get_left_pad = get_left_pad; + box->get_top_pad = get_top_pad; + box->get_right_pad = get_right_pad; + box->get_bottom_pad = get_bottom_pad; + box->destroy = destroy; + return box; + +fail_and_destroy: + destroy (box); + return 0; +} === added file 'include/grub/bitmap_scale.h' --- include/grub/bitmap_scale.h 1970-01-01 00:00:00 +0000 +++ include/grub/bitmap_scale.h 2009-01-31 20:28:17 +0000 @@ -0,0 +1,48 @@ +/* bitmap_scale.h - Bitmap scaling functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_BITMAP_SCALE_HEADER +#define GRUB_BITMAP_SCALE_HEADER 1 + +#include +#include +#include + +enum grub_video_bitmap_scale_method +{ + /* Choose the fastest interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST, + /* Choose the highest quality interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST, + + /* Specific algorithms: */ + /* Nearest neighbor interpolation. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST, + /* Bilinear interpolation. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR +}; + +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum + grub_video_bitmap_scale_method scale_method); + +#endif /* ! GRUB_BITMAP_SCALE_HEADER */ === added file 'include/grub/gfxmenu_model.h' --- include/grub/gfxmenu_model.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxmenu_model.h 2008-07-26 23:37:04 +0000 @@ -0,0 +1,59 @@ +/* gfxmenu_model.h - gfxmenu model interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXMENU_MODEL_HEADER +#define GRUB_GFXMENU_MODEL_HEADER 1 + +#include + +struct grub_gfxmenu_model; /* Forward declaration of opaque type. */ +typedef struct grub_gfxmenu_model *grub_gfxmenu_model_t; + + +grub_gfxmenu_model_t grub_gfxmenu_model_new (grub_menu_t menu); + +void grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model); + +grub_menu_t grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, + int index); + +const char *grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, + int index); + +grub_menu_entry_t grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, + int index); + +#endif /* GRUB_GFXMENU_MODEL_HEADER */ + === added file 'include/grub/gfxmenu_view.h' --- include/grub/gfxmenu_view.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxmenu_view.h 2009-06-09 21:49:06 +0000 @@ -0,0 +1,91 @@ +/* gfxmenu_view.h - gfxmenu view interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXMENU_VIEW_HEADER +#define GRUB_GFXMENU_VIEW_HEADER 1 + +#include +#include +#include +#include +#include +#include + +struct grub_gfxmenu_view; /* Forward declaration of opaque type. */ +typedef struct grub_gfxmenu_view *grub_gfxmenu_view_t; + + +grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path, + grub_gfxmenu_model_t model); + +void grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view); + +/* Set properties on the view based on settings from the specified + theme file. */ +grub_err_t grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, + const char *theme_path); + +grub_err_t grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, + const char *pattern, const char *theme_dir); + +void grub_gfxmenu_view_draw (grub_gfxmenu_view_t view); + +int grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, + grub_menu_entry_t entry); + +int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, + grub_menu_entry_t entry); + +void grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view); + + + +/* Implementation details -- this should not be used outside of the + view itself. */ + +#include +#include +#include +#include +#include + +/* Definition of the private representation of the view. */ +struct grub_gfxmenu_view +{ + grub_video_rect_t screen; + + grub_font_t title_font; + grub_font_t message_font; + char *terminal_font_name; + grub_gui_color_t title_color; + grub_gui_color_t message_color; + grub_gui_color_t message_bg_color; + struct grub_video_bitmap *desktop_image; + grub_gui_color_t desktop_color; + grub_gfxmenu_box_t terminal_box; + char *title_text; + char *progress_message_text; + char *theme_path; + + grub_gui_container_t canvas; + + grub_gfxmenu_model_t model; +}; + +#endif /* ! GRUB_GFXMENU_VIEW_HEADER */ === added file 'include/grub/gfxterm.h' --- include/grub/gfxterm.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxterm.h 2009-01-19 17:29:32 +0000 @@ -0,0 +1,41 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXTERM_HEADER +#define GRUB_GFXTERM_HEADER 1 + +#include +#include +#include +#include + +grub_err_t +grub_gfxterm_init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width); + +void grub_gfxterm_destroy_window (void); + +grub_term_output_t grub_gfxterm_get_term (void); + +typedef void (*grub_gfxterm_repaint_callback_t)(int x, int y, + int width, int height); + +void grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func); + +#endif /* ! GRUB_GFXTERM_HEADER */ === added file 'include/grub/gfxwidgets.h' --- include/grub/gfxwidgets.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxwidgets.h 2008-08-15 04:57:08 +0000 @@ -0,0 +1,49 @@ +/* gfxwidgets.h - Widgets for the graphical menu (gfxmenu). */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXWIDGETS_HEADER +#define GRUB_GFXWIDGETS_HEADER 1 + +#include + +typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t; + +struct grub_gfxmenu_box +{ + /* The size of the content. */ + int content_width; + int content_height; + + struct grub_video_bitmap **raw_pixmaps; + struct grub_video_bitmap **scaled_pixmaps; + + void (*draw) (grub_gfxmenu_box_t self, int x, int y); + void (*set_content_size) (grub_gfxmenu_box_t self, + int width, int height); + int (*get_left_pad) (grub_gfxmenu_box_t self); + int (*get_top_pad) (grub_gfxmenu_box_t self); + int (*get_right_pad) (grub_gfxmenu_box_t self); + int (*get_bottom_pad) (grub_gfxmenu_box_t self); + void (*destroy) (grub_gfxmenu_box_t self); +}; + +grub_gfxmenu_box_t grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix); + +#endif /* ! GRUB_GFXWIDGETS_HEADER */ === added file 'include/grub/gui.h' --- include/grub/gui.h 1970-01-01 00:00:00 +0000 +++ include/grub/gui.h 2009-06-09 21:49:06 +0000 @@ -0,0 +1,165 @@ +/* gui.h - GUI components header file. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#ifndef GRUB_GUI_H +#define GRUB_GUI_H 1 + +/* A representation of a color. Unlike grub_video_color_t, this + representation is independent of any video mode specifics. */ +typedef struct grub_gui_color +{ + grub_uint8_t red; + grub_uint8_t green; + grub_uint8_t blue; + grub_uint8_t alpha; +} grub_gui_color_t; + +typedef struct grub_gui_component *grub_gui_component_t; +typedef struct grub_gui_container *grub_gui_container_t; +typedef struct grub_gui_list *grub_gui_list_t; + +typedef void (*grub_gui_component_callback) (grub_gui_component_t component, + void *userdata); + +/* Component interface. */ + +struct grub_gui_component_ops +{ + void (*destroy) (void *self); + const char * (*get_id) (void *self); + int (*is_instance) (void *self, const char *type); + void (*paint) (void *self); + void (*set_parent) (void *self, grub_gui_container_t parent); + grub_gui_container_t (*get_parent) (void *self); + void (*set_bounds) (void *self, const grub_video_rect_t *bounds); + void (*get_bounds) (void *self, grub_video_rect_t *bounds); + void (*get_preferred_size) (void *self, int *width, int *height); + grub_err_t (*set_property) (void *self, const char *name, const char *value); +}; + +struct grub_gui_container_ops +{ + struct grub_gui_component_ops component; + void (*add) (void *self, grub_gui_component_t comp); + void (*remove) (void *self, grub_gui_component_t comp); + void (*iterate_children) (void *self, + grub_gui_component_callback cb, void *userdata); +}; + +struct grub_gui_list_ops +{ + struct grub_gui_component_ops component_ops; + void (*set_view_info) (void *self, + const char *theme_path, + grub_gfxmenu_model_t menu); +}; + +struct grub_gui_component +{ + struct grub_gui_component_ops *ops; +}; + +struct grub_gui_container +{ + struct grub_gui_container_ops *ops; +}; + +struct grub_gui_list +{ + struct grub_gui_list_ops *ops; +}; + + +/* Interfaces to concrete component classes. */ + +grub_gui_container_t grub_gui_canvas_new (void); +grub_gui_container_t grub_gui_vbox_new (void); +grub_gui_container_t grub_gui_hbox_new (void); +grub_gui_component_t grub_gui_label_new (void); +grub_gui_component_t grub_gui_image_new (void); +grub_gui_component_t grub_gui_progress_bar_new (void); +grub_gui_component_t grub_gui_list_new (void); +grub_gui_component_t grub_gui_circular_progress_new (void); + +/* Manipulation functions. */ + +/* Visit all components with the specified ID. */ +void grub_gui_find_by_id (grub_gui_component_t root, + const char *id, + grub_gui_component_callback cb, + void *userdata); + +/* Visit all components. */ +void grub_gui_iterate_recursively (grub_gui_component_t root, + grub_gui_component_callback cb, + void *userdata); + +/* Helper functions. */ + +static __inline void +grub_gui_save_viewport (grub_video_rect_t *r) +{ + grub_video_get_viewport ((unsigned *) &r->x, + (unsigned *) &r->y, + (unsigned *) &r->width, + (unsigned *) &r->height); +} + +static __inline void +grub_gui_restore_viewport (const grub_video_rect_t *r) +{ + grub_video_set_viewport (r->x, r->y, r->width, r->height); +} + +/* Set a new viewport relative the the current one, saving the current + viewport in OLD so it can be later restored. */ +static __inline void +grub_gui_set_viewport (const grub_video_rect_t *r, grub_video_rect_t *old) +{ + grub_gui_save_viewport (old); + grub_video_set_viewport (old->x + r->x, + old->y + r->y, + r->width, + r->height); +} + +static __inline grub_gui_color_t +grub_gui_color_rgb (int r, int g, int b) +{ + grub_gui_color_t c; + c.red = r; + c.green = g; + c.blue = b; + c.alpha = 255; + return c; +} + +static __inline grub_video_color_t +grub_gui_map_color (grub_gui_color_t c) +{ + return grub_video_map_rgba (c.red, c.green, c.blue, c.alpha); +} + +#endif /* ! GRUB_GUI_H */ === added file 'include/grub/gui_string_util.h' --- include/grub/gui_string_util.h 1970-01-01 00:00:00 +0000 +++ include/grub/gui_string_util.h 2009-06-09 21:49:06 +0000 @@ -0,0 +1,39 @@ +/* gui_string_util.h - String utilities for the graphical menu interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GUI_STRING_UTIL_HEADER +#define GRUB_GUI_STRING_UTIL_HEADER 1 + +#include +#include + +char *grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end); + +char *grub_resolve_relative_path (const char *base, const char *path); + +char *grub_get_dirname (const char *file_path); + +int grub_gui_get_named_color (const char *name, grub_gui_color_t *color); + +grub_err_t grub_gui_parse_color (const char *s, grub_gui_color_t *color); + +grub_err_t grub_gui_parse_2_tuple (const char *s, int *px, int *py); + +#endif /* GRUB_GUI_STRING_UTIL_HEADER */ === added file 'include/grub/icon_manager.h' --- include/grub/icon_manager.h 1970-01-01 00:00:00 +0000 +++ include/grub/icon_manager.h 2008-08-14 16:32:41 +0000 @@ -0,0 +1,41 @@ +/* icon_manager.h - gfxmenu icon manager. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_ICON_MANAGER_HEADER +#define GRUB_ICON_MANAGER_HEADER 1 + +#include +#include + +/* Forward declaration of opaque structure handle type. */ +typedef struct grub_gfxmenu_icon_manager *grub_gfxmenu_icon_manager_t; + +grub_gfxmenu_icon_manager_t grub_gfxmenu_icon_manager_new (void); +void grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr); +void grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr); +void grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr, + const char *path); +void grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr, + int width, int height); +struct grub_video_bitmap * +grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr, + grub_menu_entry_t entry); + +#endif /* GRUB_ICON_MANAGER_HEADER */ + === modified file 'include/grub/menu.h' --- include/grub/menu.h 2009-05-23 17:07:17 +0000 +++ include/grub/menu.h 2009-05-24 08:59:48 +0000 @@ -81,6 +81,7 @@ grub_menu_entry_t grub_menu_get_entry (grub_menu_t menu, int no); int grub_menu_get_timeout (void); void grub_menu_set_timeout (int timeout); +int grub_menu_get_default_entry_index (grub_menu_t menu); void grub_menu_execute_entry (grub_menu_entry_t entry); void grub_menu_execute_with_fallback (grub_menu_t menu, grub_menu_entry_t entry, === modified file 'include/grub/menu_viewer.h' --- include/grub/menu_viewer.h 2009-01-31 09:15:43 +0000 +++ include/grub/menu_viewer.h 2009-01-31 17:03:28 +0000 @@ -36,8 +36,13 @@ }; typedef struct grub_menu_viewer *grub_menu_viewer_t; +void grub_menu_viewer_init (void); + void grub_menu_viewer_register (grub_menu_viewer_t viewer); grub_err_t grub_menu_viewer_show_menu (grub_menu_t menu, int nested); +/* Return nonzero iff the menu viewer should clean up and return ASAP. */ +int grub_menu_viewer_should_return (void); + #endif /* GRUB_MENU_VIEWER_HEADER */ === modified file 'include/grub/misc.h' --- include/grub/misc.h 2009-06-04 20:17:05 +0000 +++ include/grub/misc.h 2009-06-08 06:46:58 +0000 @@ -1,7 +1,7 @@ /* misc.h - prototypes for misc functions */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2008 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,11 +56,14 @@ char *EXPORT_FUNC(grub_strstr) (const char *haystack, const char *needle); int EXPORT_FUNC(grub_iswordseparator) (int c); int EXPORT_FUNC(grub_isspace) (int c); +int EXPORT_FUNC(grub_iscntrl) (int c); int EXPORT_FUNC(grub_isprint) (int c); int EXPORT_FUNC(grub_isalpha) (int c); +int EXPORT_FUNC(grub_isalnum) (int c); int EXPORT_FUNC(grub_isgraph) (int c); int EXPORT_FUNC(grub_isdigit) (int c); int EXPORT_FUNC(grub_tolower) (int c); +long EXPORT_FUNC(grub_strtol) (const char *str, char **end, int base); unsigned long EXPORT_FUNC(grub_strtoul) (const char *str, char **end, int base); unsigned long long EXPORT_FUNC(grub_strtoull) (const char *str, char **end, int base); char *EXPORT_FUNC(grub_strdup) (const char *s); === added file 'include/grub/trig.h' --- include/grub/trig.h 1970-01-01 00:00:00 +0000 +++ include/grub/trig.h 2008-08-15 23:59:38 +0000 @@ -0,0 +1,44 @@ +/* trig.h - Trigonometric function support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_TRIG_HEADER +#define GRUB_TRIG_HEADER 1 + +#define GRUB_TRIG_ANGLE_MAX 256 +#define GRUB_TRIG_ANGLE_MASK 255 +#define GRUB_TRIG_FRACTION_SCALE 16384 + +extern short grub_trig_sintab[]; +extern short grub_trig_costab[]; + +static __inline int +grub_sin (int x) +{ + x &= GRUB_TRIG_ANGLE_MASK; + return grub_trig_sintab[x]; +} + +static __inline int +grub_cos (int x) +{ + x &= GRUB_TRIG_ANGLE_MASK; + return grub_trig_costab[x]; +} + +#endif /* ! GRUB_TRIG_HEADER */ === modified file 'include/grub/video.h' --- include/grub/video.h 2009-06-10 20:04:23 +0000 +++ include/grub/video.h 2009-06-13 13:42:27 +0000 @@ -48,10 +48,10 @@ #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK 0x0000ff00 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS 8 -/* Defined predefined render targets. */ -#define GRUB_VIDEO_RENDER_TARGET_DISPLAY ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER ((struct grub_video_render_target *) 1) +/* The basic render target representing the whole display. This always + renders to the back buffer when double-buffering is in use. */ +#define GRUB_VIDEO_RENDER_TARGET_DISPLAY \ + ((struct grub_video_render_target *) 0) /* Defined blitting formats. */ enum grub_video_blit_format @@ -161,6 +161,16 @@ grub_uint8_t a; /* Reserved bits value (0-255). */ }; +/* A 2D rectangle type. */ +struct grub_video_rect +{ + int x; + int y; + int width; + int height; +}; +typedef struct grub_video_rect grub_video_rect_t; + struct grub_video_adapter { /* The video adapter name. */ @@ -218,6 +228,8 @@ grub_err_t (*swap_buffers) (void); + grub_err_t (*enable_double_buffering) (int enable); + grub_err_t (*create_render_target) (struct grub_video_render_target **result, unsigned int width, unsigned int height, unsigned int mode_type); @@ -286,6 +298,8 @@ grub_err_t grub_video_swap_buffers (void); +grub_err_t grub_video_enable_double_buffering (int enable); + grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, unsigned int width, unsigned int height, @@ -297,7 +311,7 @@ grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target); -grub_err_t grub_video_set_mode (char *modestring, +grub_err_t grub_video_set_mode (const char *modestring, int NESTED_FUNC_ATTR (*hook) (grub_video_adapter_t p, struct grub_video_mode_info *mode_info)); === modified file 'kern/misc.c' --- kern/misc.c 2009-06-11 20:39:03 +0000 +++ kern/misc.c 2009-06-13 13:42:27 +0000 @@ -407,6 +407,12 @@ } int +grub_iscntrl (int c) +{ + return (c >= 0x00 && c <= 0x1F) || c == 0x7F; +} + +int grub_isprint (int c) { return (c >= ' ' && c <= '~'); @@ -419,6 +425,12 @@ } int +grub_isalnum (int c) +{ + return grub_isalpha (c) || grub_isdigit (c); +} + +int grub_isdigit (int c) { return (c >= '0' && c <= '9'); @@ -439,6 +451,41 @@ return c; } +long +grub_strtol (const char *str, char **end, int base) +{ + int negative = 0; + + while (*str && grub_isspace (*str)) + str++; + + if (*str == '-') + { + negative = 1; + str++; + } + + unsigned long long magnitude; + magnitude = grub_strtoull (str, end, base); + if (negative) + { + if (magnitude > -((long long) GRUB_LONG_MIN)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "negative overflow"); + return GRUB_LONG_MIN; + } + return -((long long) magnitude); + } + else + { + if (magnitude > GRUB_LONG_MAX) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "positive overflow"); + return GRUB_LONG_MAX; + } + return (long) magnitude; + } +} unsigned long grub_strtoul (const char *str, char **end, int base) === added file 'lib/trig.c' --- lib/trig.c 1970-01-01 00:00:00 +0000 +++ lib/trig.c 2008-08-15 23:59:38 +0000 @@ -0,0 +1,83 @@ +/* trig.c - Trigonometric table definitions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +/* These tables were generated with `gentrigtables.py'. */ + +short grub_trig_sintab[] = +{ + 0,402,804,1205,1606,2006,2404,2801,3196,3590, + 3981,4370,4756,5139,5520,5897,6270,6639,7005,7366, + 7723,8076,8423,8765,9102,9434,9760,10080,10394,10702, + 11003,11297,11585,11866,12140,12406,12665,12916,13160,13395, + 13623,13842,14053,14256,14449,14635,14811,14978,15137,15286, + 15426,15557,15679,15791,15893,15986,16069,16143,16207,16261, + 16305,16340,16364,16379,16384,16379,16364,16340,16305,16261, + 16207,16143,16069,15986,15893,15791,15679,15557,15426,15286, + 15137,14978,14811,14635,14449,14256,14053,13842,13623,13395, + 13160,12916,12665,12406,12140,11866,11585,11297,11003,10702, + 10394,10080,9760,9434,9102,8765,8423,8076,7723,7366, + 7005,6639,6270,5897,5520,5139,4756,4370,3981,3590, + 3196,2801,2404,2006,1606,1205,804,402,0,-402, + -804,-1205,-1606,-2006,-2404,-2801,-3196,-3590,-3981,-4370, + -4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723,-8076, + -8423,-8765,-9102,-9434,-9760,-10080,-10394,-10702,-11003,-11297, + -11585,-11866,-12140,-12406,-12665,-12916,-13160,-13395,-13623,-13842, + -14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557, + -15679,-15791,-15893,-15986,-16069,-16143,-16207,-16261,-16305,-16340, + -16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143, + -16069,-15986,-15893,-15791,-15679,-15557,-15426,-15286,-15137,-14978, + -14811,-14635,-14449,-14256,-14053,-13842,-13623,-13395,-13160,-12916, + -12665,-12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080, + -9760,-9434,-9102,-8765,-8423,-8076,-7723,-7366,-7005,-6639, + -6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801, + -2404,-2006,-1606,-1205,-804,-402 +}; + +short grub_trig_costab[] = +{ + 16384,16379,16364,16340,16305,16261,16207,16143,16069,15986, + 15893,15791,15679,15557,15426,15286,15137,14978,14811,14635, + 14449,14256,14053,13842,13623,13395,13160,12916,12665,12406, + 12140,11866,11585,11297,11003,10702,10394,10080,9760,9434, + 9102,8765,8423,8076,7723,7366,7005,6639,6270,5897, + 5520,5139,4756,4370,3981,3590,3196,2801,2404,2006, + 1606,1205,804,402,0,-402,-804,-1205,-1606,-2006, + -2404,-2801,-3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897, + -6270,-6639,-7005,-7366,-7723,-8076,-8423,-8765,-9102,-9434, + -9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406, + -12665,-12916,-13160,-13395,-13623,-13842,-14053,-14256,-14449,-14635, + -14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893,-15986, + -16069,-16143,-16207,-16261,-16305,-16340,-16364,-16379,-16384,-16379, + -16364,-16340,-16305,-16261,-16207,-16143,-16069,-15986,-15893,-15791, + -15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256, + -14053,-13842,-13623,-13395,-13160,-12916,-12665,-12406,-12140,-11866, + -11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765, + -8423,-8076,-7723,-7366,-7005,-6639,-6270,-5897,-5520,-5139, + -4756,-4370,-3981,-3590,-3196,-2801,-2404,-2006,-1606,-1205, + -804,-402,0,402,804,1205,1606,2006,2404,2801, + 3196,3590,3981,4370,4756,5139,5520,5897,6270,6639, + 7005,7366,7723,8076,8423,8765,9102,9434,9760,10080, + 10394,10702,11003,11297,11585,11866,12140,12406,12665,12916, + 13160,13395,13623,13842,14053,14256,14449,14635,14811,14978, + 15137,15286,15426,15557,15679,15791,15893,15986,16069,16143, + 16207,16261,16305,16340,16364,16379 +}; + === modified file 'normal/main.c' --- normal/main.c 2009-06-10 20:04:23 +0000 +++ normal/main.c 2009-06-13 13:42:27 +0000 @@ -561,6 +561,8 @@ /* Preserve hooks after context changes. */ grub_env_export ("color_normal"); grub_env_export ("color_highlight"); + + grub_menu_viewer_init (); } GRUB_MOD_FINI(normal) === modified file 'normal/menu.c' --- normal/menu.c 2009-05-04 19:06:05 +0000 +++ normal/menu.c 2009-05-16 16:43:20 +0000 @@ -82,6 +82,48 @@ } } +/* Get the entry number from the environment variable NAME. */ +static int +get_entry_number (const char *name) +{ + char *val; + int entry; + + val = grub_env_get (name); + if (! val) + return -1; + + grub_error_push (); + + entry = (int) grub_strtoul (val, 0, 0); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_errno = GRUB_ERR_NONE; + entry = -1; + } + + grub_error_pop (); + + return entry; +} + +/* Get the default menu entry index. */ +int +grub_menu_get_default_entry_index (grub_menu_t menu) +{ + int i; + + i = get_entry_number ("default"); + + /* If DEFAULT_ENTRY is not within the menu entries, fall back to + the first entry. */ + if (i < 0 || i >= menu->size) + i = 0; + + return i; +} + /* Get the first entry number from the value of the environment variable NAME, which is a space-separated list of non-negative integers. The entry number which is returned is stripped from the value of NAME. If no entry number === modified file 'normal/menu_text.c' --- normal/menu_text.c 2009-05-02 18:49:34 +0000 +++ normal/menu_text.c 2009-05-16 16:43:20 +0000 @@ -124,7 +124,7 @@ return; len = grub_utf8_to_ucs4 (unicode_title, title_len, - (grub_uint8_t *) title, -1, 0); + (grub_uint8_t *) title, -1, 0); if (len < 0) { /* It is an invalid sequence. */ @@ -235,32 +235,6 @@ print_message (nested, edit); } -/* Get the entry number from the variable NAME. */ -static int -get_entry_number (const char *name) -{ - char *val; - int entry; - - val = grub_env_get (name); - if (! val) - return -1; - - grub_error_push (); - - entry = (int) grub_strtoul (val, 0, 0); - - if (grub_errno != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - entry = -1; - } - - grub_error_pop (); - - return entry; -} - static void print_timeout (int timeout, int offset, int second_stage) { @@ -291,12 +265,7 @@ first = 0; - default_entry = get_entry_number ("default"); - - /* If DEFAULT_ENTRY is not within the menu entries, fall back to - the first entry. */ - if (default_entry < 0 || default_entry >= menu->size) - default_entry = 0; + default_entry = grub_menu_get_default_entry_index (menu); /* If timeout is 0, drawing is pointless (and ugly). */ if (grub_menu_get_timeout () == 0) @@ -326,7 +295,7 @@ if (timeout > 0) print_timeout (timeout, offset, 0); - while (1) + while (! grub_menu_viewer_should_return ()) { int c; timeout = grub_menu_get_timeout (); @@ -348,7 +317,7 @@ if (timeout == 0) { grub_env_unset ("timeout"); - *auto_boot = 1; + *auto_boot = 1; return default_entry; } @@ -476,7 +445,7 @@ case '\r': case 6: grub_setcursor (1); - *auto_boot = 0; + *auto_boot = 0; return first + offset; case '\e': @@ -499,6 +468,10 @@ } goto refresh; + case 't': + grub_env_set ("menuviewer", "gfxmenu"); + goto refresh; + default: break; } @@ -507,7 +480,8 @@ } } - /* Never reach here. */ + /* Exit menu without activating an item. This occurs if the user presses + * 't', switching to the graphical menu viewer. */ return -1; } @@ -574,20 +548,20 @@ grub_setcursor (1); if (auto_boot) - { - grub_menu_execute_with_fallback (menu, e, &execution_callback, 0); - } + { + grub_menu_execute_with_fallback (menu, e, &execution_callback, 0); + } else - { - grub_errno = GRUB_ERR_NONE; - grub_menu_execute_entry (e); - if (grub_errno != GRUB_ERR_NONE) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - grub_wait_after_message (); - } - } + { + grub_errno = GRUB_ERR_NONE; + grub_menu_execute_entry (e); + if (grub_errno != GRUB_ERR_NONE) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + grub_wait_after_message (); + } + } } return GRUB_ERR_NONE; === modified file 'normal/menu_viewer.c' --- normal/menu_viewer.c 2009-06-10 20:04:23 +0000 +++ normal/menu_viewer.c 2009-06-13 13:42:27 +0000 @@ -25,6 +25,9 @@ /* The list of menu viewers. */ static grub_menu_viewer_t menu_viewer_list; +static int should_return; +static int menu_viewer_changed; + void grub_menu_viewer_register (grub_menu_viewer_t viewer) { @@ -36,7 +39,7 @@ { const char *selected_name = grub_env_get ("menuviewer"); - /* If none selected, pick the last registered one. */ + /* If none selected, pick the last registered one. */ if (selected_name == 0) return menu_viewer_list; @@ -54,10 +57,47 @@ grub_err_t grub_menu_viewer_show_menu (grub_menu_t menu, int nested) { - grub_menu_viewer_t cur = get_current_menu_viewer (); - if (!cur) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available."); - - return cur->show_menu (menu, nested); + int repeat = 0; + do + { + repeat = 0; + menu_viewer_changed = 0; + grub_menu_viewer_t cur = get_current_menu_viewer (); + if (! cur) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menu viewer available."); + + should_return = 0; + cur->show_menu (menu, nested); + if (grub_errno != GRUB_ERR_NONE) + { + grub_print_error (); + grub_wait_after_message (); + } + if (menu_viewer_changed) + repeat = 1; + } + while (repeat); + return grub_errno; +} + +int +grub_menu_viewer_should_return (void) +{ + return should_return; +} + +static char * +menuviewer_write_hook (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + menu_viewer_changed = 1; + should_return = 1; + return grub_strdup (val); +} + +void +grub_menu_viewer_init (void) +{ + grub_register_variable_hook ("menuviewer", 0, menuviewer_write_hook); } === modified file 'term/gfxterm.c' --- term/gfxterm.c 2009-06-10 20:04:23 +0000 +++ term/gfxterm.c 2009-06-13 13:42:27 +0000 @@ -24,8 +24,10 @@ #include #include #include +#include #include -#include +#include +#include #define DEFAULT_VIDEO_MODE "1024x768,800x600,640x480" #define DEFAULT_BORDER_WIDTH 10 @@ -80,10 +82,10 @@ unsigned int cursor_x; unsigned int cursor_y; - /* Current cursor state. */ + /* Current cursor state. */ int cursor_state; - /* Font settings. */ + /* Font settings. */ grub_font_t font; /* Terminal color settings. */ @@ -101,9 +103,18 @@ struct grub_colored_char *text_buffer; }; +static int refcount; +static struct grub_video_render_target *render_target; +static grub_video_rect_t window; static struct grub_virtual_screen virtual_screen; - -static struct grub_video_mode_info mode_info; +static grub_gfxterm_repaint_callback_t repaint_callback; + +static grub_err_t init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width); + +static void destroy_window (void); + static struct grub_video_render_target *text_layer; @@ -235,20 +246,67 @@ } static grub_err_t +init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width) +{ + /* Clean up any prior instance. */ + destroy_window (); + + /* Create virtual screen. */ + if (grub_virtual_screen_setup (border_width, border_width, + width - 2 * border_width, + height - 2 * border_width, + font_name) + != GRUB_ERR_NONE) + { + return grub_errno; + } + + /* Set the render target. */ + render_target = target; + + /* Set window bounds. */ + window.x = x; + window.y = y; + window.width = width; + window.height = height; + + /* Mark whole window as dirty. */ + dirty_region_reset (); + dirty_region_add (0, 0, width, height); + + return grub_errno; +} + +grub_err_t +grub_gfxterm_init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width) +{ + if (refcount++ == 0) + init_window (target, x, y, width, height, font_name, border_width); + return grub_errno; +} + +static grub_err_t grub_gfxterm_init (void) { - char *font_name; - char *modevar; + const char *font_name; + const char *modevar; + struct grub_video_mode_info mode_info; char *tmp; - grub_video_color_t color; - int width; - int height; grub_err_t err; - /* Select the font to use. */ + /* If gfxterm has already been initialized by calling the init_window + function, then leave it alone when it is set as the current terminal. */ + if (refcount++ != 0) + return GRUB_ERR_NONE; + + /* Select the font to use. */ font_name = grub_env_get ("gfxterm_font"); if (! font_name) - font_name = ""; /* Allow fallback to any font. */ + font_name = ""; /* Allow fallback to any font. */ /* Parse gfxmode environment variable if set. */ modevar = grub_env_get ("gfxmode"); @@ -258,6 +316,8 @@ { tmp = grub_malloc (grub_strlen (modevar) + sizeof (DEFAULT_VIDEO_MODE) + 1); + if (! tmp) + return grub_errno; grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar); err = grub_video_set_mode (tmp, video_hook); grub_free (tmp); @@ -272,53 +332,77 @@ return err; /* Make sure screen is black. */ - color = grub_video_map_rgb (0, 0, 0); - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + 0, 0, mode_info.width, mode_info.height); bitmap = 0; + /* Select the font to use. */ + font_name = grub_env_get ("gfxterm_font"); + if (! font_name) + font_name = ""; /* Allow fallback to any font. */ + /* Leave borders for virtual screen. */ - width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH); - height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH); - - /* Create virtual screen. */ - if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH, - width, height, font_name) != GRUB_ERR_NONE) + if (init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, + 0, 0, mode_info.width, mode_info.height, + font_name, + DEFAULT_BORDER_WIDTH) != GRUB_ERR_NONE) { grub_video_restore (); return grub_errno; } - /* Mark whole screen as dirty. */ - dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); - + return grub_errno; +} + +static void +destroy_window (void) +{ + if (bitmap) + { + grub_video_bitmap_destroy (bitmap); + bitmap = 0; + } + + repaint_callback = 0; + grub_virtual_screen_free (); +} + +void +grub_gfxterm_destroy_window (void) +{ + if (--refcount == 0) + destroy_window (); +} + +static grub_err_t +grub_gfxterm_fini (void) +{ + /* Don't destroy an explicitly initialized terminal instance when it is + unset as the current terminal. */ + if (--refcount == 0) + { + destroy_window (); + grub_video_restore (); + } + + /* Clear error state. */ return (grub_errno = GRUB_ERR_NONE); } -static grub_err_t -grub_gfxterm_fini (void) -{ - if (bitmap) - { - grub_video_bitmap_destroy (bitmap); - bitmap = 0; - } - - grub_virtual_screen_free (); - - grub_video_restore (); - - return GRUB_ERR_NONE; -} - static void redraw_screen_rect (unsigned int x, unsigned int y, unsigned int width, unsigned int height) { grub_video_color_t color; - - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - + grub_video_rect_t saved_view; + + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); if (bitmap) { @@ -385,6 +469,14 @@ y - virtual_screen.offset_y, width, height); } + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + if (repaint_callback) + repaint_callback (x, y, width, height); } static void @@ -521,7 +613,7 @@ unsigned int height; grub_video_color_t color; - /* Determine cursor properties and position on text layer. */ + /* Determine cursor properties and position on text layer. */ x = virtual_screen.cursor_x * virtual_screen.normal_char_width; width = virtual_screen.normal_char_width; color = virtual_screen.fg_color; @@ -547,7 +639,7 @@ unsigned int i; grub_video_color_t color; - /* If we don't have background bitmap, remove cursor. */ + /* If we don't have background bitmap, remove cursor. */ if (!bitmap) { /* Remove cursor. */ @@ -590,6 +682,15 @@ } else { + grub_video_rect_t saved_view; + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); + /* Clear new border area. */ grub_video_fill_rect (color, virtual_screen.offset_x, virtual_screen.offset_y, @@ -598,10 +699,18 @@ /* Scroll physical screen. */ grub_video_scroll (color, 0, -virtual_screen.normal_char_height); + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + /* Draw cursor if visible. */ if (virtual_screen.cursor_state) draw_cursor (1); } + + if (repaint_callback) + repaint_callback (window.x, window.y, window.width, window.height); } static void @@ -735,7 +844,7 @@ } static grub_ssize_t -grub_gfxterm_getcharwidth (grub_uint32_t c) +grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused))) { struct grub_font_glyph *glyph; unsigned char char_width; @@ -810,7 +919,8 @@ /* Clear text layer. */ grub_video_set_active_render_target (text_layer); color = virtual_screen.bg_color; - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (color, 0, 0, + virtual_screen.width, virtual_screen.height); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); /* Mark virtual screen to be redrawn. */ @@ -879,11 +989,29 @@ dirty_region_redraw (); } +void +grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func) +{ + repaint_callback = func; +} + +/* Option array indices. */ +#define BACKGROUND_CMD_ARGINDEX_MODE 0 + +static const struct grub_arg_option background_image_cmd_options[] = + { + {"mode", 'm', 0, "Background image mode (`stretch', `normal').", 0, + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + static grub_err_t -grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), +grub_gfxterm_background_image_cmd (grub_extcmd_t cmd, int argc, char **args) { + struct grub_arg_list *state = cmd->state; + /* Check that we have video adapter active. */ if (grub_video_get_info(NULL) != GRUB_ERR_NONE) return grub_errno; @@ -896,7 +1024,7 @@ /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } /* If filename was provided, try to load that. */ @@ -907,16 +1035,40 @@ if (grub_errno != GRUB_ERR_NONE) return grub_errno; + /* Determine if the bitmap should be scaled to fit the screen. */ + if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set + || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg, + "stretch") == 0) + { + if (window.width != (int) grub_video_bitmap_get_width (bitmap) + || window.height != (int) grub_video_bitmap_get_height (bitmap)) + { + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + window.width, + window.height, + bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno == GRUB_ERR_NONE) + { + /* Replace the original bitmap with the scaled one. */ + grub_video_bitmap_destroy (bitmap); + bitmap = scaled_bitmap; + } + } + } + + /* If bitmap was loaded correctly, display it. */ if (bitmap) { /* Determine bitmap dimensions. */ bitmap_width = grub_video_bitmap_get_width (bitmap); - bitmap_height = grub_video_bitmap_get_width (bitmap); + bitmap_height = grub_video_bitmap_get_height (bitmap); /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } } @@ -945,18 +1097,30 @@ .next = 0 }; -static grub_command_t cmd; +grub_term_output_t +grub_gfxterm_get_term (void) +{ + return &grub_video_term; +} + +static grub_extcmd_t background_image_cmd_handle; GRUB_MOD_INIT(term_gfxterm) { + refcount = 0; + grub_term_register_output ("gfxterm", &grub_video_term); - cmd = grub_register_command ("background_image", - grub_gfxterm_background_image_cmd, - 0, "Load background image for active terminal"); + background_image_cmd_handle = + grub_register_extcmd ("background_image", + grub_gfxterm_background_image_cmd, + GRUB_COMMAND_FLAG_BOTH, + "background_image [-m (stretch|normal)] FILE", + "Load background image for active terminal.", + background_image_cmd_options); } GRUB_MOD_FINI(term_gfxterm) { - grub_unregister_command (cmd); + grub_unregister_extcmd (background_image_cmd_handle); grub_term_unregister_output (&grub_video_term); } === added file 'video/bitmap_scale.c' --- video/bitmap_scale.c 1970-01-01 00:00:00 +0000 +++ video/bitmap_scale.c 2009-06-09 21:49:06 +0000 @@ -0,0 +1,308 @@ +/* bitmap_scale.c - Bitmap scaling. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +/* Prototypes for module-local functions. */ +static grub_err_t scale_nn (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); +static grub_err_t scale_bilinear (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); + +/* This function creates a new scaled version of the bitmap SRC. The new + bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm + is given by SCALE_METHOD. If an error is encountered, the return code is + not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or + it is destroyed before this function returns. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method + scale_method) +{ + *dst = 0; + + /* Verify the simplifying assumptions. */ + if (src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "null src bitmap in grub_video_bitmap_create_scaled"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "src format not supported for scale"); + if (src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "source bitmap has a zero dimension"); + if (dst_width <= 0 || dst_height <= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "requested to scale to a size w/ a zero dimension"); + if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "bitmap to scale has inconsistent Bpp and bpp"); + + /* Create the new bitmap. */ + grub_err_t ret; + ret = grub_video_bitmap_create (dst, dst_width, dst_height, + src->mode_info.blit_format); + if (ret != GRUB_ERR_NONE) + return ret; /* Error. */ + + switch (scale_method) + { + case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST: + ret = scale_nn (*dst, src); + break; + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR: + ret = scale_bilinear (*dst, src); + break; + default: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value"); + break; + } + + if (ret == GRUB_ERR_NONE) + { + /* Success: *dst is now a pointer to the scaled bitmap. */ + return GRUB_ERR_NONE; + } + else + { + /* Destroy the bitmap and return the error code. */ + grub_video_bitmap_destroy (*dst); + *dst = 0; + return ret; + } +} + +/* Nearest neighbor bitmap scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the nearest neighbor algorithm to + interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + return GRUB_ERR_NONE; +} + +/* Bilinear interpolation image scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the bilinear interpolation algorithm + to interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* If we have enough space to do so, use bilinear interpolation. + Otherwise, fall back to nearest neighbor for this pixel. */ + if (sx < sw - 1 && sy < sh - 1) + { + /* Do bilinear interpolation. */ + + /* Fixed-point .8 numbers representing the fraction of the + distance in the x (u) and y (v) direction within the + box of 4 pixels in the source. */ + int u = (256 * sw * dx / dw) - (sx * 256); + int v = (256 * sh * dy / dh) - (sy * 256); + + for (comp = 0; comp < bytes_per_pixel; comp++) + { + /* Get the component's values for the + four source corner pixels. */ + grub_uint8_t f00 = sptr[comp]; + grub_uint8_t f10 = sptr[comp + bytes_per_pixel]; + grub_uint8_t f01 = sptr[comp + sstride]; + grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel]; + + /* Do linear interpolations along the top and bottom + rows of the box. */ + grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256; + grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256; + + /* Interpolate vertically. */ + grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256; + + dptr[comp] = fxy; + } + } + else + { + /* Fall back to nearest neighbor interpolation. */ + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + } + return GRUB_ERR_NONE; +} === modified file 'video/i386/pc/vbe.c' --- video/i386/pc/vbe.c 2009-05-04 19:06:05 +0000 +++ video/i386/pc/vbe.c 2009-06-08 06:46:58 +0000 @@ -76,6 +76,24 @@ static grub_uint32_t mode_in_use = 0x55aa; static grub_uint16_t *mode_list; +static struct +{ + grub_size_t page_size; /* The size of a page in bytes. */ + + /* For page flipping strategy. */ + int displayed_page; /* The page # that is the front buffer. */ + int render_page; /* The page # that is the back buffer. */ + + /* For blit strategy. */ + grub_uint8_t *offscreen_buffer; + + /* Virtual functions. */ + int (*update_screen) (void); + int (*destroy) (void); +} doublebuf_state; + +static void double_buffering_init (int enable); + static void * real2pm (grub_vbe_farptr_t ptr) { @@ -388,6 +406,7 @@ /* Reset frame buffer and render target variables. */ grub_memset (&framebuffer, 0, sizeof(framebuffer)); render_target = &framebuffer.render_target; + grub_memset (&doublebuf_state, 0, sizeof(doublebuf_state)); return GRUB_ERR_NONE; } @@ -403,6 +422,9 @@ /* TODO: Decide, is this something we want to do. */ return grub_errno; + if (doublebuf_state.destroy) + doublebuf_state.destroy(); + /* TODO: Free any resources allocated by driver. */ grub_free (mode_list); mode_list = 0; @@ -545,9 +567,11 @@ render_target->viewport.width = active_mode_info.x_resolution; render_target->viewport.height = active_mode_info.y_resolution; - /* Set framebuffer pointer and mark it as non allocated. */ + /* Mark framebuffer memory as non allocated. */ render_target->is_allocated = 0; - render_target->data = framebuffer.ptr; + + /* Set up double buffering, initially disabled. */ + double_buffering_init (0); /* Copy default palette to initialize emulated palette. */ for (i = 0; @@ -568,6 +592,166 @@ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found."); } +/* + Set framebuffer render target page and display the proper page, based on + `doublebuf_state.render_page' and `doublebuf_state.displayed_page', + respectively. + + Returns 0 upon success, nonzero upon failure. + */ +static int +doublebuf_pageflipping_commit (void) +{ + /* Set the render target's data pointer to the start of the render_page. */ + framebuffer.render_target.data = + ((char *) framebuffer.ptr) + + doublebuf_state.page_size * doublebuf_state.render_page; + + /* Tell the video adapter to display the new front page. */ + int display_start_line = + framebuffer.render_target.mode_info.height + * doublebuf_state.displayed_page; + + grub_vbe_status_t vbe_err = + grub_vbe_bios_set_display_start (0, display_start_line); + if (vbe_err != GRUB_VBE_STATUS_OK) + return 1; + + return 0; +} + +static int +doublebuf_pageflipping_update_screen (void) +{ + /* Swap the page numbers in the framebuffer struct. */ + int new_displayed_page = doublebuf_state.render_page; + doublebuf_state.render_page = doublebuf_state.displayed_page; + doublebuf_state.displayed_page = new_displayed_page; + + return doublebuf_pageflipping_commit (); +} + +static int +doublebuf_pageflipping_destroy (void) +{ + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_pageflipping_init (void) +{ + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * render_target->mode_info.height; + + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; + + if (2 * doublebuf_state.page_size > vram_size) + return 1; /* Not enough video memory for 2 pages. */ + + doublebuf_state.displayed_page = 0; + doublebuf_state.render_page = 1; + + doublebuf_state.update_screen = doublebuf_pageflipping_update_screen; + doublebuf_state.destroy = doublebuf_pageflipping_destroy; + + /* Set the framebuffer memory data pointer and display the right page. */ + if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE) + return 1; /* Unable to set the display start. */ + + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +static int +doublebuf_blit_update_screen (void) +{ + grub_memcpy (framebuffer.ptr, + doublebuf_state.offscreen_buffer, + doublebuf_state.page_size); + return 0; +} + +static int +doublebuf_blit_destroy (void) +{ + grub_free (doublebuf_state.offscreen_buffer); + doublebuf_state.offscreen_buffer = 0; + + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_blit_init (void) +{ + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * render_target->mode_info.height; + + doublebuf_state.offscreen_buffer = (grub_uint8_t *) + grub_malloc (doublebuf_state.page_size); + if (doublebuf_state.offscreen_buffer == 0) + return 1; /* Error. */ + + framebuffer.render_target.data = doublebuf_state.offscreen_buffer; + doublebuf_state.update_screen = doublebuf_blit_update_screen; + doublebuf_state.destroy = doublebuf_blit_destroy; + + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +static int +doublebuf_null_update_screen (void) +{ + return 0; +} + +static int +doublebuf_null_destroy (void) +{ + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_null_init (void) +{ + framebuffer.render_target.data = framebuffer.ptr; + doublebuf_state.update_screen = doublebuf_null_update_screen; + doublebuf_state.destroy = doublebuf_null_destroy; + + framebuffer.render_target.mode_info.mode_type + &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +/* Select the best double buffering mode available. */ +static void +double_buffering_init (int enable) +{ + if (doublebuf_state.destroy) + doublebuf_state.destroy (); + + if (enable) + { + if (doublebuf_pageflipping_init () == 0) + return; + + if (doublebuf_blit_init () == 0) + return; + } + + /* Fall back to no double buffering. */ + doublebuf_null_init (); +} + static grub_err_t grub_video_vbe_get_info (struct grub_video_mode_info *mode_info) { @@ -1468,7 +1652,17 @@ static grub_err_t grub_video_vbe_swap_buffers (void) { - /* TODO: Implement buffer swapping. */ + if (doublebuf_state.update_screen () != 0) + return grub_error (GRUB_ERR_INVALID_COMMAND, + "Double buffer update failed"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vbe_enable_double_buffering (int enable) +{ + double_buffering_init (enable); return GRUB_ERR_NONE; } @@ -1567,17 +1761,13 @@ static grub_err_t grub_video_vbe_set_active_render_target (struct grub_video_render_target *target) { - if (target == GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER) + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) { render_target = &framebuffer.render_target; return GRUB_ERR_NONE; } - if (target == GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "double buffering not implemented yet."); - if (! target->data) return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid render target given."); @@ -1616,6 +1806,7 @@ .blit_render_target = grub_video_vbe_blit_render_target, .scroll = grub_video_vbe_scroll, .swap_buffers = grub_video_vbe_swap_buffers, + .enable_double_buffering = grub_video_vbe_enable_double_buffering, .create_render_target = grub_video_vbe_create_render_target, .delete_render_target = grub_video_vbe_delete_render_target, .set_active_render_target = grub_video_vbe_set_active_render_target, === modified file 'video/video.c' --- video/video.c 2009-06-10 20:04:23 +0000 +++ video/video.c 2009-06-13 13:42:27 +0000 @@ -335,6 +335,16 @@ return grub_video_adapter_active->swap_buffers (); } +/* Enable or disable double buffering. */ +grub_err_t +grub_video_enable_double_buffering (int enable) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "No video mode activated"); + + return grub_video_adapter_active->enable_double_buffering (enable); +} + /* Create new render target. */ grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, @@ -380,7 +390,7 @@ } grub_err_t -grub_video_set_mode (char *modestring, +grub_video_set_mode (const char *modestring, int NESTED_FUNC_ATTR (*hook) (grub_video_adapter_t p, struct grub_video_mode_info *mode_info)) { # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWUBttUkCukP/gH////////// //////////9iz3976Cnwa92F0HsOm7HAfAgqIAMmlT6Pvvvr4965WCcdr2zDRa3tu6FKXS97T3sz 00d7XYe7AqLKNuPtzkpsANa0BTQPtjrdd81Tbm7x80AbfeDpe53w8+9n2Q0AABu7uz7671649Vq+ LcSJlU21EZVmtrYGSpmuh5eI1z3unttvXcHctU+unvY9PIdbZdZ1Xtu7a6nJHxrVKrenkemCnTu1 ba6eV6BSgBDWFvfPurmO0KBLTffbk3tOH1pFyUFKGQA2YYsy3Dy9VXt5o1aNu4Pd6hy9zcbm9s9a qidtSqN5q66rNG+3c3du1qr31YB10pu7nbsdUhezNNfEMdfdl2Wz6aFSkkdN7s7t3daHdm2yW2c+ 803HQm73rtnq4x6B7nZrgKXZXrWinfbVT618+y7LldWcpA0NmdcqU7QduLY7mk3mA+vqtPtk6adu gAfR6PVOyjub2M82fEbNkAFG73eCm+d160+fWgVeSVs6crujvepZ3Ymz6Qh69HoGmpsJSS+8Po9H QAJxj0l7D0F5469AdHoGqGhJQHkGQcvh9AD7VofdztVPaAH0H17W9u2Ozs6AUKU9BjulS9Gvu9fO TmduQ3lnbk8WV7J7X1rewNBpQD0065boGoDE4UpcC1B73gqd5FGXu970vfZ27OijWVrvs6rStvPq vet3fdW9thhuveT58cN2td267as2tuu7FzdvO7e7veW5VVBue0O8D33X3r6Pk+oD0d73B4oF0Cu7 KAPcz166HkPR6+ezkioe+95xKe91tPfWD6Cj2wdFJ9gade9jdrjAZB3uyA69xx3rHoPXPbV0altk NAyQQ2iUVZQapLTSrZio1oPTVEmnd7MCaOFDpK2Fa73cqikKBD2KE9vce6+u4Z97e9qDTTY1p92E u2umAHdcdLWrbOmjp9cgQEEdB73N4vMCi7GrZke+znYwIfWAfdO6p2wKU6emnQqVAeum+fZ3oaKK M2aJrN8u9PdV1i6o0iiK1SuQGi7Npg7st8wTQtNtgGFOqyKp1RoHQG7DVA0HQaKC8snVtAr1QCjQ BhGqVQAAFHdg6oAMgA66qiujoB7sA9D0ABkCj6enQrbDbeg+7ad2K3TuVpV97dtgPQHJhGfdz7N7 NWR97vO7uvTe2O632bfb4fWPq6871cZM72NXeCjj7ji+fUIiV0Ts+zlfPXO24XuzxOjXTYCdd0Np uN3Hca7YrW0627sXQcnXn1QkQWi+spzZJDvDfOx6Ne9A29u5r2CxnXp1nwKqgRuKzpd894k+869Z Xrd7czadoArN1ijbRVdSbbPTvNT1t1rtduuqNbePgQFPeeTiL1VPi69un251eN66R3bvb0PND7tt 24lyO7feb0xtE3nXt3nqd2+bXY85s0fTzPB7JzoL0QWRVS7tfOA99qKZu5ua+x4Xx3sYq7p9miRs fCT1uKVndo6zbXQd3Wrqr5983aj3dr63266+y+fTz0+zJ92gHFtbDYlbtXcyfHyJVCmzk6K6d7my ahVJ8m7apJ2N2fT333NfPeHVxceweu9bvUnnsbN7dw3TIKlE25steuOmsWiZawyKVtPBVuxtZbPW 5hMqxLdaJzVbztbXW9HjwrVbud7e5TWuHRGilu53b3eXbaw2rDapXpnducLW1bvZ2Nq2yNVbXvKd L117bQpreDtuqtrNTGoELs1Upm3Tlmu729B1sZWem6XNMaFdnXJT23JZHrdnRrtt0ydPboOxNFeC 2Gi2xOlxjtPMNrrmoopRQVVKktHWJFQNmrsXo511joKLSatNYGb267TPNt3HKSbNqbdm2Ord313U fdkk77Hcwe69cdOQe7U62wHgnT1F4AW19dTi+XvbwBfHbSu6OdIa019D3N7tqp01TMPXOrJaLO9e 333Z7ubvat9vO9GK5Uc67j7NRRm2r429tmNc2q3aobnYzSq9Z73Xajt7jeJvGw0NXd3U4bQvdvPf bdgddF3O7tt20ded3NZUQ9K3WXp93l0LYz3d8t4+c0IAHyXsum9ep2My33zFHjzOvveT2GivapVK 7gOjuT3ecTM2bS9mnoxpp7V112yquz3YvWPW1pCqLY8V2692F7ux7Oj22vDLAGmnprOwCa2lt3ap 3OX1p7t3NmH2b6xW5UpqDCSQAIBAgCAJoE00AmCTNTCaaKntkJqZlTzU0yNRnlGo3pqjQJQAiAkS JoVPxFT8ZNJop6n6RBo0aaGmnqAaHqAAyZAAAACQSIQgQECNMgmmJPITKnmEmaaUbTZEmnlPU/Se qep6J6j1PU2ptQ8ptTRkBU1KSKZENE01PCMJlPKn5E9GpphMibVPZTPSUH7VQeyU/Kag8RMaTMIN T9U2kCFJE0gAQmARMTJpiDImFPTQ0mCAT0jaQ1PUzTSeo9GTSYRo0BUSQggEACABNGgAEaMgExMp mpkaniaaKn5NoFT8Uwm1NT0ny9U7H7JCfmlaNkZILJFFRSgf6eciZjeywelt5gjeFswVdP/EPzia X74FT8oyqkMqgYUqGZ7MmbBv4v3HBvSJZm8mbMZ4TDM9bMfDbf9DLMcKQ1q01GqWWa3/M/menm9J kmf+qM4VLa2stkmyVZtLSSVpYtk2VrIUKImkJSEkZj+XBHhCVE69tIH9Zh/lT+j9BP/hv86b474W 91tlA5S2fvw8h5N0Rpt2zjc2fXP4mlf4eHDTO3/l/hf/noWmL+Y0M/2tRfB/1/8uq88WjG+e7WsF mm4omzD/3vT7HY+bRtaGmepejQz/oUIdmGcttHIH83purh9hjfdMi/w+41mMIDDb8nzcviInSc0c VvbHoP9Yv+G8564RQsOb95shmnSwdZ3pfDNC1lqkT583h/QLkWAveo7TErwm8DkagNeOcpO/lhrP n/39HxdJRP/pufL/ls/36vx/5bM1xeH/xPzH/65+F/c7dfXBs9Se6ndgtGTOazs+0NDrF3jy3P+n /U1f3zCjeVnvDz8hmGZLxCD6vnFP1Tf1Rk9h49FaRKxj/kMivaYML9z+6xEY+ZGcSBBj/ZDBaIxp tL6WRsbGNtDjJYV213b3opohjb9GvK5RjPua8ZoqiNREkRGNjYRjpSg3+4whzPxN2f8OLT0wirab Yf1f1FufSbjn1OOyam/SWY5JKLWooi/6ZwgUiUGO3Dx1T6AnUUNZTX1u301seI5B1tWnccsy1r6O 7dZTeI5YkJq0irEnz22ciJLbTUhJLIkrWyESb5EOSs0nqw6yKvGhyzU2h5nHW0WJJVVSslh40Gjp d3/tXSxrfkrIp6C7y2ITYjHJsFmyHAgooGbkR2G3XowJXdko4Gy3JctM4ztYEGmeu2uUibvwyjnc wn6Yd5YIZj0WQPlHj8+KdhKpvNAwSFB0TlHUQRjaEyxKIcaRO8QFp4ohpqBO5mJqFkhMgabnBjJC HDGRjC5M22GDBRUGQeMLqViyTjTtaynL15I5NmKWWwpasWKdHnm8ysWQSkoT/y430J8L1DTuGLEJ koZIZdgfKeHt9G6+kdsOcPcTXQQExM6ZwgIZxHeE3SCGSgGCEo2PRyzlDsXDLwattLhlxbYHm+5y GUk9p5/oD9i/dWztyxmERXRjb8Eg2PkjpJT5nft8p/3dbPPtt58j7fH/WYcqobFGpHPanuMMPed9 ZmEoxUGev2ohIduKGfHiF8nITfKJ5fJm8mdyZxpZJpsZmsv0by7+Pz7871svVtdyMr/PLpjTjIxN scHjdI6m5aQ20qqSB3ZAY1W15H31FGv1ZP/Ti/u/3ff/h7z8nzJH5M/CfjsKa+j81ubmOfaaq51T ngYti0X+jNUjKQoKTU0umerpyXxcGVcM02jWmIqYKNMlabKdTIoeCMVy3JcNWjqi6KSChitYrZWE 2miNJpVZ4HglhYnJtldV1OtOLqGNCxDSzYS8Ko9uSRK0lLONSpqRtLjWDtUQ0xRpVnTLL4KkM5Fa bUxC3BpLqLIJZc1cC1moZqu81pqK0QtvLpNalahDW4cHR1u72dSZrzPj7mfG/Owh+4H1kpafKcb2 qMTGfm4VvEigQuUKZs0UwMBvh0oyZkUKSNjb7z9eff9vPG2NkmY51tTW+LunfIuz/S9mRZwt00/m anQ8bs9/E83r/UdmTTznDnHrR+sw/uPpvCGat8FKNj/mhqxs/Vx4MM+C+qCj87iiGGV34gbuUx2t qHRha22Sws2hbI18E1L11kGLxn3L6O/QZ0ceCn4+1zvTfOBLrh9sDW3SlFI09fyGdfyYxTkXEHGJ FU5G0KEMwyyOqjEp8V5aL4usqkLIoVR0oGUQ3B7PeaTBs/cKlf/zScvaul7cZ2IVh9EXg1NcNHfM rP5HD6nGbfI99tdIzwtZW4m3SwZ3nB7tUDSYaVnvnwznRH2Z0Uh3ZDXUoYwznRLPuiOOYnzHSJnv ZFs4h4NKGvrPzcfhOfoOODtPZLiekhia4d06WDbTwnp/rJjPR6Z8X6NvnT3Uns6KqY91ax6Wq7hw 961rIxjGxjCtd8lMUo4zzZXGcN4oeOPK9ammt9Re9zGTWn2MVxYfXhVrIn4IV4qfumsEnSBlkYo2 2Pw2yXZUr+MztmhuatsL9fa+TD6Hz5Tl9xuw6ca7ZlHxD0aIMOGBthzJWiVkbXxsODr3Wj0MzvDN w4bbpCtyjq7ljG+xrWphaQ/J94fS40OVmeqA922EeWeydZy+Z2qiL5j3PRo9Pb19/uk+T4hx/T6f yywTTMUBMNCzfqsj/9IZ+ST++aP+W4TFoTBsZcrrkFkPi46j1mPWTLavi9880NsbE3zPhX8o+3V+ j5fOc+Rrb297aWJhXao6khROempXMktYMp4gdiQZ8kmt9cawitVuHXIcva3u1R2VW3c4drUYtuMb bTb6pxxreUe9VRorGMTbJmpZpHFQeJdxSTqenXXX6NHeueFTgl1c5hCFej9MxeJE2HXFuuJWRLD0 IKB3O30HbBtDtywvHnk0HFsD1QwOUqRLu0MQM3hCzD2ySo5Wx9LMY7qouNvMzN5rAucqK1rNSTpl N8HuKuidPRu7qdlPqdwMKRKPcQRFkOBgeL9rCoqBEMqx8+u1Pez2T5X07wreVZlfRrVxems1Jk0O ZIU8axovKQwh1Fqsy1lK0q446kZogm60KYVqiaTHK18+/rsQTxED1RkQJfp0109e+pDit1uUY8fK aR5pY2skyh2qSIsJ9Xutvv2Xdt9CNyU1agyDIyXkS5CTNagMjY43uOd57gXj2ZA3hBsGLDrvfk1p dNBpiaQyxY3gRgkQFA1Az2EdPlhfP3G+HZGaBHh7EegL3AwKgxHUu3zPcch4eDe89hzMzsdBAkSE xg+Jy7LckHIY1sd66ZC1GO8YMkTTBgOnb4D83N+iBKbM1wWebwk3/jHSMEBcZBlZv4mMqTPbiOxY m6nf06ZpJ71HenZ4Tnzrudkjk5Ltz6d49Ry9ck5xj2lYYyTupG29wUefjEPoskY+fAdopOJXz2Sq W6YIHoGOsTxhGkQurAmE8iQHjowQPKTcgfKTxoeglvF4dfGdzrkeNkdl3derarZBoss+MNAdpdT2 2JolTwMwfohXJU5QfNYfevLCmb1bFKwPhdFHu7pJJIiTeW2zyDzWPpfd2Md/Y+c+8tskaSIMk2ls yW/SnJ263Y67TdWsictjllrNLEWc+7u2eJTyw6Vk7s4QpmYi0q1Tgz00WpKKA0ZiSyZhYpkhSXfi GRmjE0EKeiN/sP1HgdM06TWb/y5uqx/J3Pv+bj6bz6kzSPn/+XT9/7J+4h49Tmxz26y+Znp5bU08 9v/d+d/A0xyw/jY91Iq1uPWGUvp/8V7CeMnsDxHq2Xsez2zyT0ZokyVWQKa/EYVRg4p0VSvHRiq8 69ribX49NtFlsbMtwyGmCQOq+bRiSde+Zo5ajDlr+z7fsEU9Xl9pZ2OXWY69meJU1JU0tq2Lummt Cmn/antrbbozmWhe2Zgj9XMbQsEzOx/40HFE8ZMLlhvPJh0x+bDY5bOQGhSco3p3Nrh9/TjY4blU cWRl3st1hHnrSl0Yy2xs3waUbZZcLGy9MugwNIYMYNQkHjx1aLVQUYyCYM0EZDeKBXVUQTIooluC gmZvDAxtGxgxo0O6eVtaBlINLTqZEQWDLl3ix0WRLTSbzYVGXXO1g9BhuhppmTJdkOeDpjZi2MOq cNcMcYbVeiiY8ao6BifT2wq0Q1W1fiHAjgww0gw2hmeWYYzKD00rRSuAWkMlqhiNlVTLIkMZG2ak baZU01AbhrFqg7Y0ejOQRqXg4wgjioqKHROGpE0sRXSYcl0cMkndZoVTHLByEqnaaacGoYpIcNTL 7gUFBb29KDT6hDgIGhjGqapSpb1Is9Lw5zeOeteKVbJ3ic09l1n4B/MPsHBwcBwcHBkU//vzn1c/ 4EdkvJnkUH5mRMJG6DMyEa93uA9fU5Wmbi1MLExXg/Grx+AQ7zkjkfg42abPgjj5Uv7NePD0mnE5 xJWkKWDFTGNzRZW6tOwxuppTFDWmN1PFZNvVOHDGyeNaKnI92tKocnJu/D4bk4NjGK9lYozMKpVO FbWPNZ77NHi48LHIdkRz2yd0qOZ7p7nOTkn3+83n3bngiccdSGGr+SOY8eNnr5DSqo8aRs8Mhsrl V2xZqYY7+00ZkGGmylMGh5ntzPZnj04keIucTnEmt9iYVwxslFbq81c91xu7c5GmKYmNt3SNoxs6 8MSyV32YqllKVVKkmkWlppE3pIsXt7kelNnPEw39mKa6Y3DiSlJkQpYmLNpArVg/G+ie5pbYn1XO WaIVnujeLYeSWZW2ymcS6IdAREERC+kej9NVzOo4/CHU7PQt3C2GBdAjf7cBPoXCxZjdgyEMHAwA 4YqpCDCHvpBUaI/YaoOwIRkGNmZBGOYGGcpcfP5GGglqndk9ooqIwzFFbE0qKqJUG2FYUBhBhUhQ VGog4EYYGLFwXENozCMTi3Kw9lk4XXN1Yc6bal5c201S0lGmmpSPLPY3nTyF9vc9h45bQHIaIcTE yr/JVUjYYc4vsMNDO7NAzWzQNIgytITRJaoDhXFSjVYEFzsyGn56ZsrMwU4MRw2MRxeXDEsW0m6e /iGhWxMOXgxJNlKawgAzPQSZ/AiwHPn4fXzOwUVWGa4w/sPEsOjZ6yT8Ldu6Ob2mmJWxMJ72NmNJ SrWymxflfhdvA2FbnvadK9ebLp0qrUhtYQMBnECFILA8k0gfJTRojkr37TdqbNNNIxRWMjlowp2y jRyWTE4VN00nCzDZjFerc0VoFS9zTU57OFjrCi2IdjhHSkVJEkTIkkTLTU0kREJ5jVRW3VyPe6/l QuPic7ezz9R2Y6yHsxMVTnTLJvLreKTZYKqYN62azcekceTsbkMUGpiyiE4IYLbVmAYmDGBpGiKm ES0NI0OkNrURv59PYdj7ymuJzd74aGpSbqfPx7TuJ3mmGJSsmDD6FaNPHwuGzFdlmKmiqlOlMZwm HVsaHbsMVPTnmWRvD0K0p9Olw0VpWKlsJ22Ttsmj1bU8d8jRDc05HWbATTngjZYGR5XkcIOEyY7g 5rp1qoPSOBHRYeuQw8Z4EnhxesUfHjr/Uux7+xvPH/wSedPMOZ3niKqelUYoqK0mHws09imDxVFV O/TFVNlVQxRivNpWJtI7oYmxODHBStGFbqYl4q2PftyunRwkmp20k8vtnSxrxWSaqR4VzOtdVNSY 6tNmhpjHG+DZkxuSqmhjU4WelXIkdEEtl4yHMqCgaGhKEdVbLT9YLJDVjZehY+nwZI5dMmik3GHs J5sjyn8AbhiobGVuezz7h6CHmRNEieJJsSaO0H2ulOmGlJ0YuVRBR/FZDyg2IfceJvpBKQIPuGIT vqGG7uf/hyboPYp312XrXYkMkOGBMZse+MBC25e4lufow5d8PEZjYu86jI+jf+p9XU7bETIQZFz4 uknC9h19D47LrE3yg/4DJwcn0s9WyvYpubJ5WMl3lGqtavl/Z5Oxe1PGD+Uc46J55uHn64E/3eh/ W7v7SOiqaDJ5UtbugoZHul4QUe+ctNb7Tdbo5D16uH/MToMzFtXPKjipOTehnQZ5oUKQUc1U0dMp aQ48z617lyXie7dDJDax99QQREdAjyg3DeTE5S6g0MpONNscij5a50cOMaUcqj8ZZJl3Z2GMPguP 17mzd7zporQ2qytMaK12xUqZmFFmBOz7sDyGjTRwxdDOE0VPf1lKkwzsawrA29HEjBdvxwrFppUZ bCrNMahGBRZb9Bi4tZDfIxwqQKCoopbctOU0fhxs1tLVTEpmgdQyj8ZIMYaGjaK7M5FEymsMFiOq OicjUkNhOBMMasYPCDOvEPf2ba6eLBurbCDGDLC1oibbYxNMVsrMcdcGBimMR7Fxa0oyNTRqTM0U wZCs0wRx57/DoTfgwwh5VLlThCQQMOe25HPn2aT+K/V3OdIZQpEkjSBR3dvZ75oE2CgwjBLQeB/K T0eOtdMK4qHKBMSaDxvRBoCRK4fI69th2Zwh65NNGhwFiJmczHDCC+GZNOnKlSpfFXXubTS9Mwl4 pbcvo5nft5+HR0anlqh3IC7GoHAMG7vD1KY7GDYxrjnKGHBqQBjSk4OYYnpi2DJkTaUNxRh4ZG+7 akMhtq94NSRqTKaZlZAYxLlojBTUtjhNy0j7OIKf2WkFTaQNsLGIgCNkdUkK1W0+qI55DAZoUVGo mYYZmGW9iFKYWargiAYbgQpDDJ67PDW15Dr66OBsUaRtcoKJgql1sWykbJOz8tk202ctRrYdjAoq muWWOs0EGYQTTHDCDaGRjpt4FUgtDSKz08RUYuBNCUBpM4InFcYZu5W8mDGpbQmBbWL22reCwhao D1ERgxobbD92RLPLdwRSJFK0rjMFlhpmaYZVil7663dtGMBnENl83GYn4kSNDQDGhtG9ON2YIUDo Zzli7NW1tKUBlFqKEmpbEezA0NYmDMIBTsZUmh7nbcyJomMiZ6s46YsACjhNpph2ww0jTZUPRI0u O2LRsyGtr6lmsSwRJCvZQbCMMZRj3A1pSZdAYwbrqrHBigxQIbVWt6NWGY0bBoqPP087j57jhQXn e3qRLG9E7USpXH+TKLbXZ+OA4p0wo29mEGMAdjYNnEKViZCKapFjbFsWt8d4HQw7Ot9UAU8Tgb5Q 4hQwIvXigFGRgjVhubM3K41bNNxnFInSVwGC21q6NYayGMDBig0FdaZqqozfZvsTFSVW+btEqmLC oMafwIRC5kFkRoE9vNe/RQ2+zDY83zmPmLTctfDgNQIIaaiyRB8+f/4Q2a0+/MdMHUo0vZ92Bgxq QgwfYiZA1YvDU7tY3SNeTIMao9MUPsMRsTEY2mZ1KMDkQ2yJ9DFowgNYEIiOKUiqarqYQbEqSKVV byivnSisR72+h6Orb1u3OgfQscjUVTZRNrGKOeT26h5qc9g5ksZqGKKJo2MRAaEYDVTxnxphYEJA OocGyMwhGSTVBQhGUspDgqyZiTGIjGOJ5DKPMsG8jcs3YB5Mm2Qb7cGA6YAwZiFw7MRyChoDfYx7 jlkLqapBHQwwwjtayB5sVaGKsm9lbMuGy6cmR+Y5fDgodMd+jvPwQD7kZn4Bj4SO7dNJloZa+poH 3QhPfIFkDJA5yB9iQPqOYbnmBkwkcZZI/BOkNSYudRgL0h4yTiHyVCvyPsKOFIDyHh6JiSKCZSUr PCZAWD5FFQoxNMnuT09Q1KKYxIwfjdCE6yrSHrf0u8Y+VVVVVVYjm+HKeqgZEf711Z2X/Xsf/if2 1+eQN49sEYjMxSNsHu9W9ptP4/sn5vg/M/vV2vB+Gfh++8nh4nEpXirJmb3/Ms3X/EOT0nML6odn 1dTnWtq+aBzKhEmOIEGrDyeXUXMxmzkDMp4ni2J4aB9a/v/++bxCe7g4IKYySlyNzXgr2EBycPMf Zs9cpuyd0PfKaYUg/C38seak6rIWQ4YhYVRVVju23qbyZTLWlTpyeTyeEKNvLMikRry6HiHW63nb zxVDWZlEMwSlmZIU08lfl/XHwfceXX7jqz/P4E6CbG73L8rXNfuf0JhqmGSDdhHm0YJaQHyYuzQs fyzwU7nUSMfv0iNh8mu4+OOqktidHtnwOmcHXC4YtoZ5eLRsO8SbSPJhtmSJKAwPB3OswWh9uoka a0EjEqhbsFzg6J0sqkKPQt2Pw8EbBxjihaeOovkrCRYvGWaRiuCKWHjWnylNiJSogdAwQhKsRQLj iAxJfpP44q0q2DGWHrNh7nOk0L29s93di20sKDqmTmaiUW8diO4WNa1MUpkQ7GtvC7aseGW625Ny 4xTOqLC2rI8Xkyy0uNW9u1su73toi6l7YZpuZ2uVUxctqoGeszcPVI5qoixYjJlp1p3jerkqsm8x tE21XpZxspSId8yFnM1UjacVWHXbRd6fW43eGnopjBtXGrt8nK0xutyu1d20u7rWK+Yu8KhY3mTA 7m6dNtq6qaycUxyBy9bJiSJm9UuRi6nFyWzJpWgvJlsbTtuYinrLrF00REtCvuy8bVRFNmQNkvmP WP/Q/CaVGUJxuF4XTsbca269MDjnvUZqV8NPqcd7UV9nWY1bMYHNaDnMJByGO2r3lBkijI5DKebj nM1F7p5eljv8mtTu4yrH5zkczqiCdLKp3KQ75XxIEeotSeBdkvdG5JXfIvOcJU4jUPRvgvErS7w7 SjqQ5SJxIJxCGQeMdMinQSLzlCg4gN8MDouRInE6tQhtJQZKNCmSPEUFA5IlZIdEvFyhHeHiFCkO z6YjfaAl0+mLl9mkFYhY1zqLkaXYh3qoTOfToHXGIaliR2hdbYO8j0axTJF2lNoQOmyHnBxIY3OD eQ0xG2JIxMMGC4aXZrTMYCF1z265vVJ0PW9w9apbrdU81rd++J6on5p96SA8kp2VQ2N1mML9upNJ YIhSWX8DbCa00rfm03JTvqMOF+5Tk1OPlyZ5MRtu7Ue1IPGUggxyEcwyUyCwMZmt1TH0zzxPMdOS drdtJ43PDxed5lLbSdlvNPKq45E3dnNyTd4cm8Oamqqlni55zlTSlrzdO3FO2WOOO0cdunAAihYq 4hOQkoRA0ypMsFBJ11FsSoQtaiG47WAxpEfrKq3ko2NTzqC0EwqcsiaIAw6ewwzJpsdevWFBRWKK sjlHmKq9cZPGzXEMHnSni5A8JMTGHkGHRwGtJYPRSQ1kLj0NO4WDB6vYzhqUDG3rAbQ0xwajPduA 4KuX9Gh3h6eg/PW9szzUL8w0CqIaBhbvnzGqVza5dXrbt3DGMVWMYxTwVhpjGMVWOpox1XYypjk3 cOTx6dVqGN2MhKeMUSsq8NXI1RDY+S7DOrPWBNQulddPg11GKZLZpXjTq2M8M9MKNcsKaGlLdqtr KStFrhj3DTCiYKS8hgSQS8KaHVdMO2UFEFqymiGUGBa1UMmjKAUMUKa4NYMKYzY8bIbzhzLnRrZC 7HwjPlGUpqsqictruL1SZI8LEkI6SjMxRBQWZAqMPI5mQzOSphEahyrrT00UtPqhsgqZfMqsa6aG bMFd709FvprfVvWlFFUFCbFwrQ4QMglLdsqq6kySYMku1k6nGcnpCS5nGIphhyBltVRRw2WbFHSa mSGCpuxiipNsusvWit3pglWeiyjRFvYaHIMN7bSqO63Gxl3NTlAKm2Depch8qZIzbpg5kUjLlJqh piRk0G73MDbWCLmFNviqruj08TSNTSUbWzCbutFDApGWaZ5RR9DGmNFsYStSOPLs1S5qh4MnJUjU ZMVTkwxQ66dimNUjNDXWNelfKraNtWTB2EneVCKilKNtgYoLlHGMZGKXbMPG6uTQtZhUKRZcPAu1 XVkwkaYYsbWRBqySJJrBy6W5kKsMl71tMZshsHbc4PZTCxt1nRhLXbNOM0xMQ+jZkik1rStmJOWO OGplyEU2iqDrFsmpNyaF2F7pCBzeTGS1TdD3qlNKYpOaIHZbaxR5NaW2pjeY7YPpEuRk1CvKvA0s lKkPm1NC5aum90KNTU5EaH0Qm02UTux1QpeuQGP7zs9llUIE+IikqxVioK6ZBiu/vad9keSlOMLD JPiMOQwwsnRgw7IXbqU1lSUTIz2cg1e5mlCKNeGvJ+c3f39e3Jvro7KtDIKJcqkOvCn4XrhPDxfo mZdnoUj8WZWuCg5DrpWO5ZVQ2HnVKeljFczeThxMZKaai+NkNdMw6ThwZF60vUPVqDBp9Pw1Rewh REe2giYshDzHqs0NY1cSGMJAcicTwxItVbTi2RjQTGxrkZFtGw4/JbHaVrx3WRahRrUQrN8MfIzG lG6OnTqqgCIKZJpN7iU0x3dlabO9ZHOzSpYc6xatJR9Eug+OMOUw54daG48GBnTPV2GhlRQW5Up1 Ux23snW9ZlTu286ulKlh2sXBT0gCgxN1nzvdFEZwexhdPe+6PmvpCH82k1E1cd1kd6xPe/a5fM3b xyfoznXz8Zwu6A6JH3Xw19+hPvgndwQN0hBOZJyk/QSqHyydBfMQi8BKazbq427qM25IO37DB8QP i9kNL8IyBoIj1WQ5GJgVnNaOtitX15uWUqllpJUFUspQwHsN/59CfVBvFHEA5DbptI4VU6lSRvr+ cfJbl6lDJoKT45NWrJTCDMMCW4gdaYk+nGPPTrp9rTbgyfEV+T/RnunwvKfNs41jmhiEfJA+MkXz AwCCf55RFaEkkQPU76vw5Nl8XRvs1LOnGzDlLWx83uT5fT6SLr87LpM92w6rD01mWSCIyh+uDwgM kk5hEWQq1tkROlhyhkP++DZmPcTQppfWhMytEXvLXkE0uQwIVGEgW5VyQhe+/N6ECSEPH1LHXSvZ JpTQSSRDOjJCCOtoV/aXgEoHnD0YpzgDthUH7mH+xs38qSf8N+6580yZFtYsTZlBVD5yYbHLG1ss YahsWyCg2Vm2N8T5+x9pY39EYkfiGUeU/KQmQiHdIcyEXUAnngAyUUL7s3k/NKbEyEgMwP6U62Kb aQKY1NJaphpqKVAoZpDVsllNtTDUUMVsxRqxZGFoi/VKm0gOmmUiRIKYShRKUaFQ/brFAPugdo80 oidZ/MbaF9bvxSP2vM7n6XKfsfT+edCbLJP5cufGpO98bIne72QST3KJPSzJICPgscKN4/WYgOgh 7yFXIVHwIFfohBFP9am1k5XteWROqvhUPW//thEh5LEJMog81NycFOgkwkJlGkUB4q+GpkQHwb5N B9dTCSc/cyQ2rZmAm+jJIT7aQk9CtIsIs/LkT4fHmbD+SGzPIDfj/X7dYZZqIlU/yEiIfzXxE/Bh N1AoRhS+gLgWRSaU7YJ++pani8JsH81RyOhhyHnA+8hA/YHhisyjsSru0fYxIyyfgWJJ+aUaFpVg m6Z0eTZVB5jhWSCrJOLJJqoOIs81Pxz6ZA+y60lhuLCRkfEtKPipqof8pGhKUpVX/gEovTBsdwxA mKJkIeEG8qcpVDmQiZKIbnZPSj0GJiSawyRlTaP7EQOvZR0EOH76DltFhvswSbKYrdpyyJo/B2zx bNbzfDwbyKwqWKK/GEhto63SmqnRBMC44RZt25yWk3W/dmbyzaaaH13HN344fwIwVuvEDED58cE9 ueEIP2EKu38yeHn0r+qYbzRy7WbT72ZX9P7/5WzwSQ+Kxjv5emtdj36d234Jpn80/XXcysmLIVRl jFabuzdPH3H0Ng+jGFGFGFa6Q/fEHa6m0stTzMyLSQtRTiQA4taf2XzGlmTAkgKGSZIiJFqwz3W7 fxc5Yn1XYdKalpGIoCCUiEgloeFAOd1Ox04mNtiDGr68ZXRYmRYGopDaQPa9Hr1jlsps3Ww7WyW1 M6bOQkTuHWPUPPHFbNu02Y7I3dxsdqXW0jbOWEsZ2TEth+B27beW1ZuO41Zna2wVtts65btLnG3I GrKbKzFbdMx0MZ02ZukoBKSYBoFoQpFaVRpBscVkkQRgggN1AN7WpUoalemhRum0+Tth17mc63pw lbx7HnpNonzbmc0vak583DJUNSiFItUhEiLhIRKm0gkllrDWt1ujZnT+00DRclAOi0BpSCWCNSK4 Ss1KlCBEqm8NUjhCsQf5d1f7nFqmz1p9yfrT9qPu1Zsst+Ez3uMR/HsvuRGxsT7cv+OpNkln8UpO awfB/gxy/57z/r2M+ZE0tM6faspT6K5bJWkpZVttPdmu+nCuasbJmdp5PJ+hps4jCiKotj4ieJ4m Ewo8l0+8NFYZTMj4kA2idz+n5YeZ3u9on+ZK7lV2jCKmN2jH+KGc7Xizzu6f/L/+M73qdjGL9W1/ /VzZ/W87htj5/rOL+7rOPMeYh+3/D2e92q3T8Dpg55cN8eulWPlr1pr8Ssar+Rz1srvZKaSnYrJq JkgfV7znBLO75eKNpcB+lYKVC2MqRCYFiBjBrb/pRV+kpamxDDyJKD7ut8Tq1E1LUSiZRiyzfFyT z8G2lCJ4Ic3shSekcMkOvDKfqOMNJsEutI9RwYbUNB0WMzDHLAwoNrkWkKjRtpyHUVRTsRj07n2e QbHn5nyHD9b6XZEj5/pTkkqV/ElP41VVN5OHNz7/yPOnV8o5j5ngng0J/hD4PnbOT3vzPw/yHzCT 6BU9E+96J8VV4Dh88epo0rKvr27orrOygaVA0veUAxMW+EgPdZjiWESX8y7I/Ic5+F7HbO2P/40/ QKxS9kwTjecRwo8MkxFXZfYjBqcjIwLgxk1zDxRFYMpvUX/5S5BD7Pul3sPbM0otCZEkf5jgiezq hMhkhjy3b5cTdpq8cOa1rhOUzwhJCMMDFxWNB2OaYNgQlH41/GbDPQ9j0thxLOTOk0ck3NhTYm2e TaOnUXl06dshwKl+dgCcYcEJlYZh7sB+MnYh1/jOZsjQtUsTHd1Ydf0BA5HrSEbMMgw7hi5sEfl2 fhWIhx2r6URspB+mkhkskWwk2djPid7IuHoojGZH52NtGsTIpLSOtY4DpQEHzgQwZZD1IsCjBVBY ml7GCs/5mYF2YMa//jRxws7UtK92H4RSnlK6JIG0BjE+rOaKDNPA0e4Q5Fz5A+RCTJHyuh2+X4/j eijVfjM62dvrYNlyVG1i+w61N34neQJiDxE2zt+VaqIithMHVPCp8tb9ePX5DOrTk1Ycbhx+RrlU Fu2U/vc9a564FW2cyZzMxQhJm8pshFnyv+J4jpQ1ado87ZrRnTyo6VbgBxFnBLTtVuc7vGh95lyr VMA7h7BBpRc08+w29cjRrbGwcA9lIkxYRknuCSjWu3VWgwmijO/kmKDR/rB53fpAy8ofYl72RlVe wiMZI0K7N6M7oesWefhWLp3n0E2iNkN2w/JN2+9K4i45p+v0PYeJEfOOTidp0p4zUiJy1RuDJDI/ CjgP3nL3GSAqiRcSiszYiKBczYxFp2YtGH2fF+olUGls38rgaJxH5hvCEBJQuGWKeIed4ooU/0Dh hh6QMFByZjzXTA+oTIJXLgMMWZbQ/NvuB27/gJ8GjpIi26RM6hELJ6cEkk5Lq0w2HHDINBcwoGzY 8vmIJsKBUeCyOBxXnJQyIBFOGRMBLDpAUDzDgo8UNnh2jUZgvxhhlfZ2JmFTvOsAhOwzHMmVMyRN ILAtWzDMYOYUxeR5GRNX06znh9Tr279rc3aTsckMEKLCHAmU8C32FySInIaq+NaI02FctQCh+jmP NXs5BZe9EkLkqi1SD3piyYXZRKk9ueup8HXp2TEfNDGD6CTE5lMjpO0oksWJprwu6oqveeFRcrOC EboQZwL6M8iERy6fr/6fKQiwhgJjCKA3M6carxZRQjhgRJirCFQNQ6i6KaS2F0U1mBUqOF1Qw97k hz2XUYdMig6QY1d9xhZh4VRRu7udeMWbVNZV/X8ljfp+p+ol5UWJWMFb5t+p1erhaptvC/k5+RAx CJpFx8MOyA1G6g1bDLoKrm0d081KQ9MkLPRxtPh0VXhLcGwOLJHLu02WSQebISTxpDceacEe3Xon V7HHxeHMjFR0J6ljVPQVDtp6jkfHi6j9OserMj0SBXN5o8fBmzhX2JbTRCIokkHh1KRDoMKZatw5 DGxc8HlgYMZV0gFuQEj8bBMWYTbwLlDVJ8AmysawohPWUclZDjmMqg4e8CJ3tRpOy5vJLsZL3Exg glbxvn1BksknoJB5GNXpwo6Dll4CRFOoexQiTFfT+KowPRnIQhtISmICMpkUegKIsMA/4nP4yJzR Rz0rFSIPFAitmNy9tZmVmJXqrf1tRy8Z/Ly+NwwbqsW3nj4Uz2kaGmb7+46xa9PJy7YbO8WJiGJi ZfFxNdRyJRCjDVkvVe8URLzYJFnFeGHCR7j1tmlUQ4iJGD2cG6hFgehjhMI2ATAU8fZRUldR9Hhm ST2hOs/BmCLo7MGfIxvM/WioleKu2Zk6rKiarL/eiwExdsmQyEk/ociszh8atIj1aFxzok04PK++ ZFJAkjkvETwH7FNoi7zNwVLXc+vkmS3ELP2uEH9a2nIAuCYScwop3VWMLcgsyawRc9OHLYXQYePn uV5LOU72JsriOsd8W32fwP7R8isYpwwfEOwOB6TAwB2y3jrd13Q6kV9GTMjTvTSynYdpUqWK6qrM NRDxbd3Xmy3dc8fkJIXMTLrqTLlY57xDANO4+3dlI4slkk89ro6ueH4V/93meL0HtPf1KaU7ojus LIjp0k0aVLSmaTWJ48ojJZwT+H4X4W2/qvy3XrzuyC7nRvpXN704c552j0s7dNWkylI1TDc3Kf1B ydGxbF4/CI5Pu6oqjffxGyMbG3JGNmThZfDSHahvvVVuhpDSGYVlmHj3ZcclEfFS8GcGVZTTEp7j +KE7O9gfoAYNHl6H23O/eFu7NkMyHfWs0fAQqvguzp9UBT6xnmkTBy//QKBARA/mKBs38hIQuUlA lgwopRWLF2wZrlykCqhJIp9p7Tg8zd5HH2B/vP+JYyzP0LYKpd/86yGmbL2C5CzZOQML98PQ/zP7 3xOm/pC14zy8Z37RHmWElUi31O162MY1Hm3nVrFOx+ImSaeYmJMeZNPjgcIaPRyfV0v8GGfldl3D w6vkF2Bw4srpUhcJe0sZO3Mdy4/UGIetvPyu38x3icnkdNnIqkV2Jpu0xWPYkm7zjQ7oYq5NJHHq h9RYUh0Sa1jua1pZRKy7BIdxOn4zAn3j9Q7I6S3bHc6D9aftHBJJmCoi9OZEST4TCAZZm/+HlFS0 Gz6INwv2GGMlcrIofXORJFzh686r2nEojUvsdjklms/kQ9vHTJon2u3Uv7/HYiT4n6r53jetKlUt pNSjLPqI/vXSyjsT0puUw2JsqpyrLuWZJZU2TWZaC1rQtlvoaPrzoiqDuIRwS4nGWqlIJGK1rVFJ ElqKpbUrKSVUVVMRE0BEw3dYz1h0HWbO2kSxLUkpCXz25WiVLC525JipBEWaoh7/h7TeyxiIpYKa kMxQyQPtCcrrObmuukqr6Ls+s08VJzi7uusAzDGcbGHHliaGlnWBgwRGR07s4m5WabrtZJrxzn/L BiKWvBu/w/L88x5TzV8nf8OxjdKak3K2zz1VQSkQUEfsQWlUBfmIe76FnD7LCJMm84a0fqntD5+b fk/lUaXRU7DuSNopUsgruNibl7ZnFhpOmQ+ONikUBh8ERHe9j4f1fVDWUozm99z4DwmGuSP42Qo6 oq4Cbz7bKkZy6LDodLyZnhMGSSIplmOGBU6aaxsxZ54wOW70/vfpHXmfqshzOWOZvbcWPGKx8tTb aM2qp3DJ4IrSbQeyQXO9EpNlJ1YHhOxmyyRxYYqXpegQ0nUkJsQzJoFTkr3BsfxDhjE4PNqJ2ppj Exj5/tB3FQadHe8U/Cxybtzc2aV8bZX+d0fEfKrzuam2vIyOaT4DAML5/P9qK/0bH2b/mGNw+Wry hN4ToxYxTx8aan+UheH1yesKA4cbIs9IcPHHMzZHRcwh0VCz2OiiBAcMMPBmZnEFl2WqmQNzmVIA axBxfVmaA8qL6hTHgpsCxtCc2Ibl/dmo1CRY6jgGX4q3CyUg8kDYFZWBPAkkzjmrwdq3HCiwqqjf JO/HVVaR3psQMC1FSvOM9AWhEUhRYBwF6iMD19EMBI3gSFAPEGC5GI8Khqb1qN1GFsmEQcxwYIzR xpUv1B3Wjg1yDGJhSkORFEP0SwMiInrgTtFrcMLLNu4h81QdUUT8uWxosyg0yMKRGMq5APcT8Tve /SSVS5Z+R+I8/Ke1RJ804QYct9BtCQQ0VfpF9A7Ho2InsdjHU3TE9622Y6FnnS7W7SoZqeBofEHL 2ViopTCTkVIjUZ9pB5jLCgl6Hcsk6sw2VmWcTRLO1T8bEd3b3cPVW3uPkh2D9j4AdEIDL4aZrvDZ 8v2A76H2HQ2PaYGyUBkgPsV6/Ds2OwXMJoYlmiJRjsNBoqyps9L1OEmZ+N9TzExM/0vvHTo9L2v7 mk7XZfq/Ye4/zWMjw/M7sllY8z2+NyPYDYqfWfqyD2LxHruPVPMnxiwlSwf3SxGMV8Da/M+xjtKs P53pEpKw0WEIkUYL9/71wL94g21rS5WTVn3o+k4PzeZ7BxJutFj606St3KHVHEPx8ps0pTzR5H3o MSSH20GC2DrZqZsrJDZ4zWbn2R8U22fMmb+2jbfxpg8TaTE/wt0fHf/mvnmfytoI2U/Vu+TX9Kor 5mPrRJI+2rN9uNEppajU1EtVSJVVE0rQ0BSlNAsQjTLKFBWVsqmVtqCmrVtaFZVGorKKo7uzVlKr U1a1IVIdk7nJksrNWi2rais1VsUyimltRUtpCWLTVkqas1ppYSbViRWSUIhLalaKAmUmGiokpIgm ZZpZVZq1oprWllkVo0KUpLKaLKytQmsqgEJIzDQlMsDElMLJMk1iabKKxbaCtRTRNaymtKImVWU1 i2stk1mtZZYtGkaqGpFtqq1WWtEKiNaxWy0ky0yWtZqahJbWa0U2kTWTRJKlWTa1mUiNUiJkttaa 1iTUshFsqVzs3LYk1NI1ZrSTNVltW0VbRZJlCRIylNq1ZFtZbS2kkoRCVUQtC6Oz7D2Po0P0O0nQ aH5yV606+gCSVg5i369FOEZ7TETJShE1DGBODDKmSo0hSKYYEYpSZto1oTTiGgiYcVaVZkqUUlSW Ug+qoh76RypGzniMlYlN9+hOvc5HwNaHmSmTU5gZMyiy1qWkWWqqJnLbdZU1rRYpO7djoso7rVG6 bSziZpRMUU1bZrWapbKMoygrbUYo5HKMzAKoIJaKiJpVXkftSGXowx6mImJI0h9MSH8JH7K3/63v a3CB4MkrCYQI8GEAd/6/p8md9T/P/T9JyKB+5UKRnFCBcyzf8ba5a6ZdYzVe1KBSDRJgzrlUtRUS +RcW4xlrOZmRmaaGMgpacaykyBibGVOC9ZJeCzjOd7JewI/O//Up/zJff+VAg8kNJ3rVJanu/i+P 5PxfNMp8ce2N5kfW8Dv8H1H1NmfYvvoQsb5VVClOJIohpwZBg0k1xklkpVmMditafpTs5O52NPDP p5PVPtpHp5dvE83d54yDymedB4Q2Cgf++i9NkeHVdyGkMgP7RnHc8PMglz54S0NspB0YE5fRgkfL d4Qi8brGma6+R2pMMo/nEfH5kTGBGLdHHk+dw9PY8D1s4e2m0p3PWlZ6HiVjOODwYiM+j8cNDyTz 1wznBcaiXcdpERinfVFzog8GCFRHFlQ1O3C6OQ8URdH81QKwvcdSpbBkRiZR8TmopwuxaNolA3N3 CtgyHjnTfXN7zYpQpSKl19Xm6PFr/PRrpp0KmiyBzgVLXTMULIoDrysKqj6YjNKPivJy6Jcawhpn bZKqrsJZenbc7ES6V1caYyixsZ6LHI3+z9qeniT1aWQG9wd9jdHrASqQuKBpYFVS1Wgw6H46kFGY 4GZ0RwixOssLmmlKrksnyxM87Q3FrWHLvx4or8Sat1aWhRMuqgnpfB02RElSbVVQ1DKi6mFNXdJG b3CHCvLazi3YjjBZy6iKGvbTH0ifxRx0zhj0RU61opeZtSGZDpu3+jgYJFFF7UYbfmRr4YZFyNaw tq8RVMjihy7Mtw3XJlkCgqBHcuhW6HO1Nqq6XhUMeHa5eGDGMzrjCYy+AYdeW/gdh/WIeoOJo8Rm ZB0QUrpAHb42ejXebnreRMenMLZt1DxzHOgEMhxe7kPbJrNfjFWhEZ5Ac4rKWqXTUOcZvetGoiML pYWNDJTMLjTxLliwOmlVybIR+DknDXTXhgumK8i7c9OaXQ7Nu4UzBzC6TPzjjsJNCnLDMocqHEiu 5BqOeZLZqFCGnZOh9j2im3mhMpOVZWUrTPjvK5OdIUfB4DpuIMoMgmMFWovLvF5+iNmZG6e47zkp aO7dVR087bsyuu88J4dOsG1iGaXCQCXR8qXnQ5PwcXyHtRH4exwBVMgHMkkOEyEHLPTny5uh05Og 6I6mYKtOAs/m2adWc1F5T6bMVnb89PjPlXKGLp1nT1Yw+NCl4uPba0arBxS8aHGMNUkrZR0p2Qi4 c+GZmXc5w80R4MRRb+5d5IcHZdgekFDogqDVFELqCHi/qGJjxGBnxTzI17fN5eXnxo2tsPPpd8PL vHRtinayvsBChUfbJTRrw+7Pn9BJk1559Gb8uKuWt+enV020bcg63BG9ImrWaxi+B8yIhyZ7LONX lWDv9he+k13nFag0d/J8ds22zlrmyGc02NDMjb8pxk3w4Lb8Mtg4aqWn12XRU2ro6qW8tSx48uhC 5LDaYU2xTsDBbYqFXDmot0nbEbYZ0NUsSK1tj4RSPVIWNTwqC2//L+z9ZSbUoXSnSmLyy11Ayu6M jVHX9IOlTF5nbU6CdmbuEhvvUfGjI39DJxrl0sjnnGl7ZPr/dmeyRsGOm07J4MMSRR6CWS9eE0ah z7bsmX2pCB9lxXxeXNeRuvaPtSfr3NklK1MpDIIMFbm2BnI0dPbhri0emYzNc1eyJDrqJRiQ1OiF yNrTGKQtrpVFlZFKFMWLqR9k2qIms3NbqF2wmPFsy0KKWMMqmCtvobhUVt41Ou5UVo5xvx36zG9I fMS5GsePl1kbwIPUOHtptZ5/Q+34oBCv6iip/nMPge5ICJiQ0IYIPPp+hfcXx6owWerL6W2U9fdO DMmy3J0iVK06RjPLXdjNLzEDim8unlYuoQrMg+cC3sZGCGGYYMM4q26EBnI4GOYuRwOFEXMgYC+t 8Py33embdnRZwF1Bs05Pg2l2i6j74oy03S/azq1J0xBN4EpBLSwI2NzoYlUdqdXp1UNr+h/wODqe qNKmln8t9L11TLWMFJz9DnsvDqo4YoFQuMIxEu8hB8OcRzcgwbn19KScczD8KsogvDUvMU8qcL8l auAaHiDUKRYusmlZWDUsMoRjuUorsKsrtbNEvJphlJwZEFYbSv7O4NKbS7dN0wzzRU1O6aSR4IXI xSi2hUaGQtmMSWHkZK4zTKalGVhUOCGJlrW9kaqc1pqge9uXjIk6Y0qGJTkaMFrRvMqOfJmuwQm4 sz2V+TkY07jNyHDNIh2gfqVEEOw58Xam1iTumCqrlH77WDHd8G1LX5hEva6kalUYcYeWwp6Np8Ow 65E2shGhyD61I7KCKiYQxySAku4iPAkaVVDhvRT4MMidGBAOUofGT6e2fv4cTbfZiSoPxplhID7O FbSkqCaHFPxyYSkKZbGiSeJqn3+hcCdr5W3NCml0uDM4pAqOqW3YzgnK0LWrhoO1L3DjqdcNcDRr UmYVeaxsJJFDjWyxtkkU2spqTljH4BFnZ6f0TuUng6Kt94Pe/LAmRBGQ+fK+YTpL6RhiaQeODMXX q8t77VVdimGPkliwNJVyZ3qJdVzCdn4C8tvwfQibLIGnfSkUifkojcq3gxNN5wHA52odMb6gDxUf +Pickc021SRrcy3HSVH0MTIydTl9LZu0TO3jqcUTlQtJIZ0TFRKUIFOV2vKkKLXJpY0t0x+e23pk Veaa6bNMZu1l3tpRVApmJhsW1aq5XZug2pCtumi51pr0wPQrAQKEXkkRCqpY6RpoVNUb/HRtASar jiXgeIQncDC6rc29YTykNVKQc0VLi4bIxfRxkxdabhW27wKh4LzAyV0xi6OhhDptltRLy0qKLi8S TiQgOSyJCj7Y3WXA5YLgyRroRxbOFciTzJwHfYhnYzoQyVqmjLGRkPGqHIZuSk3IPbDPEGrA0+c2 6b4gphFrqLRZDsQ7dLVlIBeYTUZJ9PniFc8tmZpx+eMw5yILMMJLRmWVtWH1DE74C4Ph+EQ8FOiB RTpUI+HLiqdcJUK8HbDCGDq8dT34ydrttPpURRTM06bdpNzt1hSLdUUSVpUNpO3mmh2EZQt31q7t D8k6BDfMyLjCeLLKV4lHMpQZUaDLKDgZuRsEz+TAi2L3rE1lbVsVmxSMHRauRLpdfUAluFC7Gnn8 nADugwh3Ah5zCAusF/WU1OWb1w2/NyuEJpoMOlHQwYQoFdkloI/JSVAq2ZdUwQv4F/wqMMUzgfHy cBiuTvuJ8Rpo33tCO8bu00Y3mfdi5Z0M40QB9Zzh1qGOM3I2HVLScH7qCyDwNg7CZJhjoB7DzxBG yFlxmzameuz6uaG7Ok8YfSobvpE9e46E43aRlQhd0LtC73vZHmwdHRurz+07lOWpxaVjca67Q6Gl 0bcUfgFQlaIHmEdnUHhq3HgulN3pHw0x1sY+zj+ie/9afX/pVJ2/jZOv0P6jd906P2R7v73X2v43 +xWK7n+6e2fpj9780xjMuTH0f1Jppew8JweBzj9dNK5pR+inPbIfj6mxSlU2n9MN05Kd6cJwmVxt NmlVn9pN2dyR35HnXmscbqn8DOXQ7mmmB+kFPSJs/tMO4jrhPWT5aMCP6zp2O08+dRoihV6NkBQ7 IVXX6WH6/Linw9RN9dNE0w22S6dXxbKU/WL2A5BwHe8nUoSjyipMTeUP2+h+YeeS/tPiWtiR/O3b K5TZp4Vi4p9luTJzdqPpT6nh9L4ufmadV6pnAwRjHGknqMV2c57LNItaqiHk4aa/WoiaP435nJyd XV1bJ9YrxGFMPh2eDr4PwzAYXuqJIgSPgRJjVGPQHULNaMk67yURjZZ4k5w7oeB8zEx4picJzSlK +Ds7Bg1hYm22PuRRU8aIDCh2PkHQchw/P4lWMzgsFIya8DVsrwfDmnB7Qylg+V+9sOiMwY/TDJ0k T5bqtBsUxUK64EjeDgQ7upRscw1JBKRtiyELTiNE5w8Hef7V8JDSOvM1RMmtv62Yx5MZ45iRmvmF DFpRYmLlxv0HJHVatPzPyziJXlTY5A8sz4o4+m5HKdpvi4H8srUqJRLpcUmJCWmS49Mi9JOk/U0F Le4owBhdJaNt2deVJcXEfs5P5xP6uuzQ52gMJgXJCDMYEWBNyIgoiVzqH946hkSHjst8F9i1b4kI /5CykCjkDpgfQqH6CPU2oa4m9mwWKEaBVagx9AjON437HZDCYP2DPIlyq8v3zAhgdNv45wd/IpvY uJOlCGyZGFCqYyke0PgAwwe41JhE7y5BUTheYhe0vQICLB5j1Y+QxIKqepIuQJEiAw49DmMc9woB YVDxGO8sQqJuYYGHHityRmSKkDA4iJsTsWT9pvgQYXem1IortF9gnCKngbfVZnTcl/EewGGBwMYx gavGcDryqnyfRo9qdhjDn8ObTvemnM8ZDJtDaab/C9XVsCTIZBlmQLDHhMVxaCkKwoi4OhgiqERS 5FWHXxk42Nx44YkWYZwx1PqJEIDZHW487xjBZ4FBQRFDHApJ6kor4ikKCZoOcGfNQ7yonSPYZnBj fI1B5QIQTGZytU5DhysmQ4YtXVQUHIcnJyQqhMnEJqIdT47oiQOAjcdPU9JhX6JPou+PokHuRaSO hewOh+BU4lMY0/QkQzzOPoRz8O50zHh6fmiA69ZzQ1po4P7CR1aUn6SLe+KVgtI7uExNI6bSkmF2 Iu4eeXwP6nHYehCfHsQZ/QTwlx9ZZ3ccFVROdO6mIf08I4qn8Fl5pzbtUV+wsoo17dqsJ+GvYMaE FKVKmfuFlJmYiQ6/MVD6FQsVihkTtexdioZsVAjt3lVZnU6Nu6B319anGrQrrDgYZA4H7QUPwVcw rK8NaEBzLamxntDnIzMEelAcZkhyqvAuPIDNeJyplN73vuU2iVkTMFTY3IjxioRNEGimSGTFywww UI5wCjDGCOOcn9A4KWkpV96DwIgFLs6TUcSPmlk7mnqVx4eTTg8VbPFXi72MGGwtXpoOl8HjTGMH tRB6I7ehcNEEw6F6pdXNDrxmJnAzg+0WbWa9Q5JROLFQp3LPoKObNj1fInCMdnB0KTYkqKh9qpoI IUGXCmP1YxUYtEZ8EVRi885FHzbQ1DQXIIL0XkJmO4TlM3DoLUUxaCzBxEzMx4mHmZdwooWfPvp4 cWl3uGYiczQ1OplBhlFh7CDNjPR5PXt9GtNyX6w3djzgoyh7yHuKrdps9xEdmnEOOuRwp6ENjFhz Nlnyvmeg5IPi3eTHl/lvIeylX10xUhhlfL3DGrgHscarZQUkyCJHIgKAmAYrEyQ0Q6NASHWdHQY7 xu4r4jID1Sw9Z0zdWdpj32Ym59vQaKbPdzVO7cekCcccjMHBgiUCqcGY0GHrwm0iHNjBQoPHBQSz KkCIxAYYyIuJHBcufeLf/PYX7DlhRU5Xo9HNuOfxoPDxgUTAYYUY8B+uxyrAIkkdE7UhAeMR94qQ FBH+rpof1i9Qycgx3YJhZ2w6Hkq9zttclx6D3oPnnOYcHiKxQMlw5QMON4gnH6hI+v3t+Y/ihAdz u8+39v8k+3x9CdYg+dnhHBjq/1O1j9hrJpoz96gHMkvqTAelSDDz7SRNWX7ZLvh4lh5B+pmMbPrP me1OT+Z3qqq6Q73i75XH3prnvMpaF4wPVYOCqX8+YVB9v9aP8sR6kREyGHMBVIuL9o8/qPSRyQgI KpgTtDofWQF9pIp4/6XKonMZkSFX9HuDz/O5eSZBDTvO7vScBZAV+X7ip7NEofEo+NTy7/y4zvY9 bFY6NNNHqjTI09Y3bNnPZRO+h53R8Vff4nJw5mI6lH362F7TyMT1eBuaDqJGBEtEeEj1b4+je1Hp R0g4Xc0GPMj4HkQGJhb/drz9EGBOWiqeb0PUeb0XqWTCwtKPT4x5/9beN+a+x0NggMgg2zlN4iAM AwMLGZD4nPaOj2M9TlFsxjqnE24jVhxLPf1bIfpsd9jvPB6GpOJhmWbMnJ3O+LOyWe52R2nLo7og qnIQRU1PirLZZJwZCESJDCPsQgLqK+X6s4IlV83k9DXrehU9imFaTwKicnKLh8kMB7EPW57SAxAu LCgOAksiA4MjKIwcFvmcSfGdLknCx1EhsiH9dGRBo7LI0sKp2vLc/iqIN0QWf9h8uOzZEGeW33hD xf6m8eM/vlTJZTY2Mn+M3TeeSx8zIIe9hxCYkgCQ5nM5meOZkktLG5JJYS4QwwhCFP0L+yNd/jTR ruifovB8hvITz0zQPIHGb93JO6jRNkh8jy89CbnJiqqrK5Q0h4LFd/hNJN2xBKGUehU/xr/PBsi8 g9Sl2cpLhHEQaL1iDyJ65JCeITSyHvOHONTFm0+Q8jySUxTQTCqgPWpUHhFSHIej72jabg+znH5X /7+vSJ3IjsIfd2OxT8OGWMzCrUMEIhEY3sLRSlgbiH4dvRb5HcpGombsOV9Im52edtpZOUr7k8XD 77wTA6hiL8psca+F8NB+jpdl+3Z9aqDpEDxBmj6a8FSqT0r5GMk7j+feIPyfCdb49CDyp5ONNKNg 7mDzkDpCGOw0lL+0elCXUHnBqOCDj5n1B7CG7ZOlMK9PJLTxA3cPVj4hkvrL7YmDwRn44f6TczlD g28ZPgnpMTTp9dpekwDDCCK6IQHJdNJ+2GlTyOnQoew/QRImh27Eygx3HcPD1HkF+xJAcwX3EzOl +mczw7G+BwxAXh3jkLJJIFI7fpmv6LmfuQkggGiY7uh6uXvt3T1p83zvQ83ksQxjFWHScR6tJsq4 7bchRKFoSZElF7Dk5MKAv2cZr34//Zt0xH4C4Zezk38fUxQIZiy8HGoCEZD4kg4R4gbODMxBhCPu 5hId58DZxU58mbQnk5MV6SyY6N56YjoyRZYfmP2frbdfwvm7D/a9Dm9Z+h73z+1+T0nkXmwzIFha Hm4Z/v173RCLe5jQwU5DJMMse8eeeO3b67nHO4slmWK2tNJQkkkh90hh68TcupNgMJmENHy/KQgE xk5DJlfQ8zMPIwSEUgvoarm/4sri+QKZ9Ppfb6eM7Z9KzltT1RBs289Kp6l1Fc3t3DRRt4cHm666 TqI++aKopH8J7z06DqR7Hvc1dXtZ3zu/bEHEQeh1dZ15RjFxYbvcOUMOT/M5pFx55Mwx/ceR0VEI DjMwxt5Fg+98H+tPXPypU7q5T7U/6H8MbD7nzNfBv/r+h87r75n4jB+5gsC/IKQTOgheTI7N3r28 vfWfgKi5plVdjf6fsS9scQv9z6TufW6urRnYu7TtY4PRkPIpptgjP36P6ukR6jUJUcq0mmGMHA03 URQpwM4XD0tV8ObKaIRD9PIycYpPbmIQDheiC1QfVARoKQkDAba/cE4kExcHY4ptZImjVJX2VLDb p3NojhRER2L/wFYhnIdHF8Rp1zRWi2sVB6aSt0QqicMSvkRA+J8gW4KTj2jgxWZjA/8TOSexPM/X zgYIEBxAmOGFGTlMctjkRIHgOPtKmp/PULfPmkU2LnCGFMCw6gkbDoREfQecoDkORqde4IvwwxBO HDMAweT8hsH1Vli5zIXhmdylHK6uxNTQgPssbXObfX9eSiNdlYLjnD5jPec99pjgdhEUppTi1aOJ lNZUTNYikb9wkLl5GA5DfZ/TofFlgr/f/V+BkrIqd/5f2F139fz/N6xp3MuoFHmS6nP1REOxlPX1 LXkCiOP2+a5zMZjEy9FBfNFTLWf3nycc3fNnk7K2/EaHkSzlGymx7Wo06teKmhAaN6DGnKpyY9hN O4KoQGhoSUfZYafczpqJHB79CDyN+VSPeuSLZETkcJ5/xZWQLf0B3BwfAQZFAX6U3e74xG+37A+8 GqcMpFj2H6/HtAd8+FyGPY3sYLqZ5/lP5RCAMoA3hFOOs5Bj5jINNHP2FT+vns0+rTD3RCA0MM16 BfrqOYwOQwczA8evU5GRUK6t3ue86eKA+o1uRbSU/cRKIkNyLIelA10sbGpuSPgPEhUpM31nhFOY 4qqanSMHq8Ha87kNz4Hj8mzcpgIFAUZE7s6FX3aKJ2pB2p26i2bkPOwOF+T8H7pohsZBjGDGYsyV 6srwYv5j3kSZEicDCcUGampiLzNZfivt2hqdBx1rAz90VktjImMbPek8i88tyJEF2DgePBhbkyDT +huYm7dyabExyOTZuxjk07BoqaXkM6PhBYj1GhsiR9phRYaKtPRoiiSSCWQ8LGoW0EAoFHZHQPgh b+v+ZRycRuclnKLJJydYx96+lxx9r0N1U2HvYweLumvfwva5OSG8sbsTtmn8lmn5PU7EfG3Sva+N pVV3vce42GzmdT5CkeAxQ2O/vOg76zvFMe47E8ojyY4TERgtb2YWyparnnzKzH9L5XD1Oi297ljv OpVVktVO2QOcluHmZNAXeLUcSFIJGRcPedx9q/Z6La52TxVzPS8xWna+nJjzow/Q8umsfE+NhKV5 2zZsmyTZ4HCu0+JFN37vX3WNdm6uZsKS+ATklqcg1DQ9Q8zCRJbA3qDzxPE+0iaeoz+mi+9WErjM MwzDAqwVwgSGcdjV4mZyeN8TeHX5OkSU3poueQPZX37hAGoyYX4m5A0Jo1X7/yBJ7NmMvE8uagkU OXqL6xLIuF+qYHa7E2xPfj0lcSqpubk707j1TJ8JieCQhITofM6dOhPczj1ISGBmzp9AGAuDwvFw Tc8rTv48zX3GA6VLoQE7h7j8S487HssBCFx6SA6H6F7NcLmSEaKbhxEdoooc3lHa3ZGo4NLMTYz8 Lhkyd0xPoh3z5lV9Zo6w7fR870s8Z+vEPrbv527uTxQ+OD2j+OPgdnLaDTR4uFsyfDMqqtHR7S8f TlLVnszJZPbOLgzmumnxu42dj1Hd+Rtqx3/R7+tbTIyxtZHhv8XE4GNz8ssyIOXmcal/2S+gvISE 7TlZmf8XAWLyRkyOER7iXrajB/AE9G7zuo7lVPRW31+3M8PQfR1a8a2HH0VKtz18Hk7orW3HbdqG kkslSWmcgrcVrznFU3lnaSaIkyFaJprWltIdK5K/TcSkmfB2Pvu99JzYfarm7X4XvcG70tnBsfK3 TStzjnMdzePW5xj0rEfQ6DxjsMYrR3tPO8p9bmyPhKHjK9E+EetpXr/h53rb9VdzHfpksxU/LKyV 933jUam7zMW/YOsfkt/ab6HMLB7R44dBTPOHo3eKbB77iIlsdg6MJLyTyEixuxAimPh7BIW64DNV KECbiBCDDhsENyTDGpgcf086kyri5rBH2tJWLGgnqOt/L2PUyPUvuODnEb7n4CRhjkRDCVwgFCSq wj6yQxM+QBqlQJmR4IpX+yJ+9iDPq2LVSMS7jTQhg0E1gTOhoP0ilavvtwyV4qebnizZ102fV38e p2+aHQ7iiHINgcJ4dDWRc7/YpgtI3OmQddNpNCNB4XPNOST1pAeEj5kQ1ZIR7ZuzZgrfpZA5jVbs e5tbnSdvsfS2Ty9hPU8DT4o17GYyWlkJJkmgmVxVwwGAwfk4PF5fSe0PTmSBpjEz3wHEkOQd5czT 4Jj3csqHZyNUegu+h08SkH3FobsTEzwYePJEXMhAMjebjCb37Qk2OiXfNzHwe00ezMC7zHk9pw9w net2Qwq1cKyPW+mxI5LW+U2h/f2/J64utGREZQDsH2EL32x+J7NzM6hUDTsui7t3H6DqDjTtEopG xoSBwDHU9h1rSgyHkZHIu7X1meFn6vp66HMOxk8xHiBmFBhhkF+LtQ/zCEXGh7zhZHxcdSNU5J3O hHJS0ITN6BoQQeAMOKDl4RHDfAWong8VhhOA0KRMEckzfSZG+6QeDo+hjj86ng9LFV3dFxVR9cPF OoGbC++eangRnqtdRmNkTbvbp7t4jy4WQeqe9LsRHeP/AzlLRSGwUH3aWZQBBoHuVZFU5HxGf3Ih fyPqS3V53isj2UiMKYMDgGZjIzOg9fS8gO437zyO/fkOHkwhBPst0RZaqsB0pC9RFTsQqUSkolTo THQ5rVjogd4SFqKAoAuhQURDC2GRqZLPvG8zYsrlmkxTo56TUPjTHxvF3TU71VMeEj1JsqaTE2G0 Nk0qYxP472/J3BodI5gwVcJiAQNvajw8InPlHAL8/Ju+5nWhb0UTrVS5RByXfv1cX9PTvO4I/Sll FIlJYUilSMdeZev7O99abTa/609E8J2cnT3LAHFNVIgOBsE/CEe90GkfMZBWyY9+cD74fa5M1GGK noxrqRT/tRWqhAzTuzFpDpOFee8SR6DC81LS2N9l7V7t5mZP4qb8UU4b+G75G7nO3JyMjJim5WLJ p/Mmiqna2fbHNsEtD2OTxMExOmIFyme08zLt9v+fUKoQFKg9ldszK7HtGhm6h1LJTYz4LPMFS3Ic PMDw8hw4wZKJIXu8RoHuYKH3nnlYiFxfUDDY2YeIOwh1WqObzZgmCYJgmDgL8Xl0l90ICNdzvkHp rWM4o0CDCFmEMvW/BoiUmmGwMCYMAw4qqH7k7AUQQUzb4DZzCZN5p/VzNlkTx2096oZPiWPGydKm 70P7Pivqh0HwaF01yi+F+vdp8X/L9+fbrX7GEAYJiX7IFrWgOlfYEdYkz453GnpCeCWY9gE+J6mG 6t27vPc/rUnE/T+jufE5/R952vGlKfcx9DjZ4Kqq5pEx6Cvock7YSlUGGBPmZlV50iFl5+pUPGqL UO8Yq0kKJ5NBknc7UPAhrM3alyQi+98jP0M8HcIea4F2KlKNRqfMO187dvE3mmGpTmp62jH1PYOO EJgBzAx5sBjL8GHp3X3kV0igtg/WPRmfE5c1qAckwCGGXwGSdJe5dSCi2oosRhUVVFKmNQhBVVYv yLsczc4B5zGSwhy+82NS4rpj3DC8v/AYIIYHGeL2h0hi6J8RwwiZs+AP4j0nB1HW8Csc6BUcZBFI CZqxoarGwjwNRXMRMCEk6Dlkh+gUI9yceJ9D39Nz2mW55mGMEGC4rg0jqEx8VfmaUPzl0Wy1ltbF TCJuHIQHU9jpEEUZTQyhyHOM0OOHp2SuGRUcR8TlWk1+V3wNKPHZPD0tnOUqzzq+B28nJsT3kWeC WYAxr2AoqlgDWH2FMN+qi93EPNMTrtHi3JhzAgEUzCXLPdh1V0ESHgMiEXNrN7R8Jt3ve90TIdxj CSA0B4OgGZcDWdCTuSHTW+K7nPsbP8e1hwh8kyNztfAp/bsZ2ubfboPth7VYSefQdDXFiD8rDo+o gcTfWJT1VfnF1oPc8d+ir2SM79/MmEejDg1TC9UjQOpByzak0/pVPBeTz/6MeTudrrt2MYeNfC3H v2H3Kfc+1id7Zh51PMnndiubRyblVHgTHEBiAwwDG4CckZYn3y75QJL62y7NV0ib1xl8B9uSqDHi OCQwkOyW6WiQMjmrpUJpfJG0w5Sp1k4jYWbjaafU/BNOh6UsqfFe7+E7kMR8YfllKcmjuomveGGi D2yA2PR9hAhAgoDKYMMjVRTyB3ET85UO4oqKhR6Yn0dZ3STodkwp2RsY0foseynDm9Turt8E2lRs fneG+vk9WBpCpYLIp7hg4JzB4GODbLY88l3Ym5Jvue4FxYIA6mJ5AhusAut5KodIHpTMYaebPnTy teBWb4nd0pafXTyrDGJHI1yNGm+xNutMSRs0VBSr1hEdzhFKJVobGwEvxUDW0Lrn72/c7iXcwBv2 YcHa8d+z1inf1mA9s0UbOojnq7fk1rNDaig6xobBqF9WqikhJAbnFUsI0iFgKGnBDUO+zEkf2tEG kUTs43vjnjLHkyTJ7rsR0grIFqOV2io9DwLR8Vnoezoz3/Dsjv8HEkeVilklqVQ7YNxFP3d+Kf6s mxmR4l1xMWTGNKwlyng1h5FnljufK3CbREb9qS8EArpcQsB7erAopFvQsNpZYOsI81ed1lbJvWrD 5cyskZDz2WLAyeTBu6Xvvtz3539fBavE45cctnhLIWSkiynP25BwdGIlsDFSynV5Zht4u1iJGm0z TEmhOyXwtiTUdTBok3WPFI40KaIDr6sRaaqjJDaVGBR3EOIJlHyPGeVVVVVUk9Ye9hzTPUCQ+RQ8 wXAfVzFwgdKQqGl1PvPZFo+u5Eh6kPHj2AVrMIx9JLSkyM1P8PqeERiRh1wvDeal1EHM93Ztl9mi cejyjlY7JvKOGDw5kfqGK+4TIueTHZIoZIzZQG93M5kSvMdI+H304vs8lFViJZi2Sa1oXb03UeRu wsvayYtfOT6hPR04b2Najk9705znp9F4ZF+09su/1f1ovDDigOdB2ZGoRSEGVHBCcU8VDQSF+y5q t2XxSQGc1r7YHUgJwIERCccuN+3t792mCOVhNIMd3gd4TZ4zDRbwb9hAHNF3mNa0YcCQnVVlnk/2 PcHkQd4Wkg47rOex7869jEA7RiRYmQWmstBJi1Wxpoo20a00jKaiisymlrM0a21Kok2mmjLZQy0z b4kOo3T+ipnqfxPX8A3PJfzUD7HBgeHcGPxUMKacQRIB0w7lp4FN5Bddc0c4Lss/Y/wqkOoT8+3e LJg+GUOEaCpYFF3zK2j6TJj4gyEB2d4wO+B/Fk8Z3OT87iOlH1P0HvZ8hmLenCNj8zIYYuBdtg/E eOJHHoOsESkjxUbC0P3FRSJooTS1Yee938ALQe1DWO8DwQ9FUUSsqxJIQEBkwyPJQVCQpczyYzps frHBVdTzAmMMakDM+CGy1HfPWSyJDl0awHyIOSMhlUzVXkT0IicDLRiZ9Z2IEBxQ5rkm0UV4mqWu 4RRkFEN2VESGJMOPtNJQR7DgzWsIox7banmvs9/DlISomELsO08wPjTDTSjdmCVHcrdSsTebknnj wJLaRbexzvLPZx49HptF5kxLFI8d0Xu2PCndkTzDZSpLYxCNFYwI2xCo1w5LTU2MRlBd0mJw1lsT hkZJSWGrGK3MhG7SFlbLG5dq1oxuTw08JnL0m9Iby2iG9tdVgm7RqSaJi5EpEyXBuZOPG8NRDA5R 5FTqotMs72TNsMeJ3qvVOHDIDNsxf1leaYfIsqiqa/tlLUpg+KdcuEw9oUAO4PvTAnH1rIrdeY72 kFATx6bBAmMtVN/uGX+yNWmf1aSBxZDlFHR/kw84aL+LD1Pl+lCAmvrXjEPZxx/2GSqhAZ+3TxNT WzM84eEFpYcz1xoPowGkzZ75pxQr/NAmxMDjdFiF0MD/yI00HIdPv+xkZVAjVjv9giJ4o43C4eGx Y5IYTgpGMGGE/N/oT1rt59zoT7DnV58fXMiDe6TvVqu+bJwMB3/CNII36lo1UiTMdg+pXmYlKY4d EZOQ4RZCAnGCEBKc8yIOg46DlbsSH8iqyD51d++R393BacSxmskxRbL+NkWa73eizqRB13GfMhsd 7r3bP1gJgvY6CCw8iJAFWEuZb6wtBflnoD4ZDjofXs5oNsvIjFGbAUTKp2bOx+Y7iAG58nHNl16M hfTk9C5H294ZsD6DXQjf6ecueSEB84zBfqpddnrA2WL9gM7XCcUjkj70IclVGyUc4myeOenpPJBo gkTWg0NQXJPUY+v2p607k2PndOhlfebJPZV+14kcxeDGYpQYBZHsCPkpp2rPJd5RHcv4CF2Q0vow wMaYn0M0DGBUs1QoeaOqQ0WRIfaZA+2VPwkC4KhABwOHEzoMujz7nsd+TGB+BsZdCdhGQ/vPlOs8 nl87HB4mPqHlhMMZfIeP2cNN4Pa8LqAf93+fPLIoFFN9U4emqkSHbQRyTJpDHwypzuSAYO8+dMwg kon6j1qOCUEmLt9eosBFfLZOeMj4oYsyWCAxGJskbS7GxGLueCS4fkfyHS+cOwzkkwPBgeNwu9cx fcffGcS70qZO7HHuepONR7hl3WTgjEsjSBC6TggtLkQFucmQSwMs8V7ik18zFxPwKrPb6zZ99y8q 4bK7GOjFbvL8Kv7xjZPqn2CogbLtgRkNIotsMpEboXieXPzUj3nzW/uCq168zgqzMOQyHvHJnNmO gyNYW0fY4ezYnnFbtnXsfg9DhE2Z5mWuT7+tDJnsNx93N9JMX7213QON2UZvuNFCAgkcnve9tlY8 nY2ahDZvqp1Yp+R8zybTye97f2+MdDtm/NZO+hI1RY08Pym5yZWmsemEzHiI1ThDCN5z4V5mNO9w yPdLOyWe91kR+NKbcOTHNXtve6T3TyMU2TcTSJzVVKlGm0xJkSaSaJsQkfcwYTYrEjYY5yzqntmT E1j4s83r2cyxhIuhhQg8PMwOHKBmwacDkhZoGVAOXtrHcT6AphiYqoZLDEiUbMCqicGMEYYboiBU xh/NF5DveJQLIfFbF2ZRfPL2ukUGDDSSD7Gw5iVE20ljg7HtHtO4w0OLD5jHuZ6jcOw62egd08Cn gZ4GlN5HhFllhR1m8xHIfLNu85YyK7ovcwxZOCrt6Hq+hftxnA8TdavTa3Bw8XQdvBi7Q/jOpM+k n0JETe4nuGQQgQEugw+CmhAXLFIcCmMehp+Z483Di2HwKhNOfLz6myDg6sw8sz6UrreT1uomX+ea 9jednYOw8lSiowmDhhOFgiMepyVcqS55OVw1XycVWqEB+CkdZi9kdLev14UMCHRs37PXOj4lgIwe xgQUT/RngXRlpSU9CQO+H6guxB3WI7BxjHryHPohAXQgP9SEBIwKw5QRYo+oRfEOlxVEYcGh4YQC HwTKuTekgZdiy9yTm8O6TG/Z7TOxjcTc4Y6fo5+l9xiHImH9D0fI7nc78aDhttJjY2S8ema0H3r7 TCgwg2PoMQTAZRIFjmcDj8g+Ls4ziRoxIdPluRDyTjkquBlJeaX3oYdNlaR4vmMq/r+5FUzEFE7n I7+wnLpcPyBDvPac5pWmJuBDY8ptuzGLWZzsPJXKzj3vBTZ0HM8HqOU3hzbjwTxiD+vmdeR3tPSI Zf5Pqg+9CAfHg+7y9uTv7jAUNRl1DqOrp4nnJG5NYRZKoOp6WJ6mn1t3ZK/BHTzvtdXXrU+7ymzr zOHHOyq27VK2/3Ej445xK8zvfofW+SdzFyG2hy+oXCQ1YB9oIsPEnu5OjEhF0Hr2xzgbGRmtkMO+ nV6nBOOgZJw9IHIHdZ0giKFErEPZQOyxgHzqyx7nEGPw4VEno5sqHOstYW61GbcTvv3S44Pu60WS 8nBqvMGFA5DgcOTLyRdOgL+5fkPEHiOoRTA4wMd+MOG8QgcjZZtYtNpAPGIdXLgXUkRR/jF2bJfe 8ciMxOXyDILkOR+Ex4/x7ew6zdwMe01pIxMWSZx9Wydg/H24fCqKCofgOE4cRCZ+MvENLae1GCRV lzGBwPHGBH8z14e0cusi6ecJvjlcLmgRE8dqGz3wK+41/dMnMk6pzZkZsFQhiw5xEoPJpweLz9Qb 8dwTRPKl7dJbPQgJTCBoDJyyCJ4AO2iHF7j2r1fX8rxbTkbtmzabJdlxWLyjSVPpLpMLzH6jAKHx Lg83AvJPHweDYfIz8Bt5HYVyjDmYOiE8ENM4H7J7Bfgwd8iAmmP3HUgIgEmqhAQMEx3UTjJjW52W gQoSP0H3boySC7T8QgMJCfkqAyyUk4Ghol6+pTBnUPo/in4wO69qfp8onkTR1CdhdYFfiAMyZgIG RxqYLMUPMyJTPDI1Tq89wxkCzO55Opyae2cJk4VWIda81kkdyyFqc5R94VA77EtSS7OGzY3s1GMc RJyBmGWrCTkQNEGCy9BUFhTX2BdwjIqtb8K8Z9LCtMfS0878TdPU4exVV9L/Q5YOTGTLPs5zuRYx +f9gZgP7HwO4eJ53DjPAk6W8h/yfSoOMDAwWWiexqQf4Ic6K3sghHLsyWl+s8eDpsfTY7cPBMxp8 j9LxaquS8WLVpQZmZaqBjP7XH9OIGs08qKkLKkrkldPU0eZZfqRQgVIGZMHIzPVEk4giaecjmnKa Y/WpL4v6DHc3SfsYe1+86XyTxTB7nHDsdk0h+x6Z8JpFVNp6XxtjvYsaa6rE2Saf2Ok4c09DMzMz MzMzM/UGIgdPM3+brzpPT69v074HUerXFeMYBmGdZ1nGr5+TpET5GV8pmV1znaEyZCYCGQjniOEt qcvpzNszPx5qPn2zl6M1PWl8dXBVFEn6sw4ww6cwK+uB4QnSTJG+BldVllkdWZin9xtmM8CyGt2h plYsBz1816KH4fcjb7Jt6N5mLlzG09TwcpHvezF4Lkex3Pin4mzHbLBqOiBYAOMFps0b5Iel3BMc JhZJhdk8VP9jajCoYHC+QSSPFspfadDY6Ew/mQgHzC4DOIlx/+SA4tAuOGdzz2UYu3/11/jH73+8 oOM7K3bLO387zSPCHIc3DwnOjK33XI4KFPwVd/wrg0cePe7E0rOTSD6jsNxamlvIEsnDsPCBumJK nuOGZeDIcOhBMWJBRbogtkZtGRkmifp2dL9g8tAKPcf1dkf4q6EBxZSOf0FEX8Xs5XCIkHMHr9Yy NyI6JX4rmHNgnlMcc/06PXMT0E5NIeeOtl8nDqPWidYRoY86dyd6fQ3d5PFOSffJU5ikUVKKJUlH tcuwntSk7U3ToTq1PSmJi6i+hIWjzck67iJorpj6GqHrcqcCzNBwiDh4Ojsh2MY2WrpHWYsXRZUy 53U7253tn6eTsvSWMdrk8xpUqVSxTk6lSbk1HCyYkJ1U8cshyeB7Pr6qaZDMIrfccX7DgctNk4eu dQ7CykR/VeB7jVUyXzPJHpCczGHWdw4euPQeXi9p6TxJkBo0w50OKGwfsNz33s5taQ8zllnVu7OO O7nInLZzk57POe87LVtnI9PLgZ2j1QN9Foy+oeRIyuM4uCC0HJiUi51LFGBmY/Gk80lUYGbtM2W4 vioicL8zs1LD7ZrxFIWgoieLwTku/zNl7sLu/N3vimKphZd6cukUu42F8Dqdjb8vNe4Z7M2Sc5w5 3M7h/ubRsOyOg5CAgV+GpA7xjMYc4cJxI9ThwdxckdwoBV4todn2kZR9Ir+8KMBKTHADcx4hy59Y zgRWpVvVw4y6Gp2TUISkDPPqHQPBhyEB5n6bL/s5xFe6HYwyOVEflLhLirO/4nRZEijIgBjIxYxE QgQYgMSao2AtV9ap3nVFl42FwhAUEICH17lQgIdoPr3dErDbNq+A5t434S+zk83lLivIYBxErzHG xkQ+0chKZmYMZKacOS2LoXdM0Q8xMw0OL27XsRQXLO0QOnod1EoVRJMnocPFAZ0ypnHeGx2VUaSs WLmXQkslg7BU4A0QhrSB7u5iIeQZvtRKEhIZh8aDZu2uII2VUXHBgYHq6orzUmcXoWgEBlqxyUVO qJDDLCgs2IETEjJ0AtOKKIrtlnYcHNHNqdw6zdjU2bN2MOJOqy0+SaTsadnTHBFbDRwMDAcgwjm0 JKB65FFwSJq8HXE5MhATMzWeV883VTUtVBSyEBUYkMmEPLlwreAQiWRRQkDFC+TBKKFpWmZRQSIm CtLlHfoow8IVPQlpSKGyktOMDQp2irgx02WDYIogKgpKQAlaj9kcEJwMw83xqdhUVFsZiGHPcZ3J qqvCX2aKt+YhZ4ISvliWbMxBRLGJOzr4LW60O/gogppBkjITYEWb6bxROEdPEw9WRgLImOE60hno zlFMSBygtldU2ZCAiOaOhj08p6TUW/UsHIzVyDGDm0mTZF2nW3hO/BT074sMvPnZ0ic90cO0JtgV UUMK4Ga4n19NdZXje74857vJPTWQ+1MWwfM2ZJ6K4MxGqUJlSAFgIJw62arV0P6FzJKROOvxYsdY e9h4fwGZ6EvyMwauHEgfi7oPjsmN+tIdinuAFXb2M4kmXsb5udyze9/vDT3KBvl2eSZNdzk3m8Gg OJxNo7TlOrm8PL7NzpRatRjkywPTO6k4B2H9U9KFJJmTnObVx0O53U6L1weWRg5jx6ec+zh1EWSe L8rknKir+u60ifz2fYbc7iQ/OJb+AwCdzlnNLBJczl0eO7yxVDU+eTjxhyZLO6df9wziLObHYhiM 3Icaq7PRimCHwZETZlgoO7ExSIhSoPsciO1bPCYa8i3OpPZYorK3oMVYW+aTvUc5iRRI3E8vCL0N t8f43Kz8jD3gwTKIzLdHjj4THzHHdU/niU5bcbngNmGw4yA5UEG4n6YNiaKocRNemZBUGDcKh/EK FU1uSd9ZUgBODlZxCEx1l58zkTK+ZIHsA8YnwIgPb3MvuCJmDKhAUQ97oNj57A9/xk8eOd3k+8cT DvCH4oKBz9wy9ARfdrPZVuXm5r5fEYuz8m+GjmMclk45mov3nl4ByVV0Tn3PlmfabfeBVCAd+f65 agbLeSkuQ5JdCSqoE2igTDA4ychMu6P0DnQxAMI2UQPRI+oERLAwRw5zHzkWbNMiLKZVbtJIsKII INhWrGZEX/Va1TFhUEAVFCQyX3Nt82bc9SAcTdYUsg+o/K6HixHL1InQ+fmJ6VBfkHmp4HRcIXy2 Do6y1Ia/0ngMMdbjExldxQm9AxYGUHpc919F1oOe72zgQHkoFRZnzFqeTkWJi3tLPu+xEh3jSUVP Hzx7p4QyYyPi3gwPKjDAx2XaA5i7wXEdNUZMjyKk/S4ViViu96ntjIgs2a42myxB2TwDM4KDiJ7H j1lBfvHouSHKR3uJD5+wgsHQ2BvH5lEI1Ymssj8kWgHWSSIKzezu6vczMQYYYspic5WiNE0a4+HZ wcEPMw5gRohjZWmlH4X72mhjGJOwZ35mszMUvX5u+NVKd7Jwsz4/wO5pOxWOR63ogOyVQTcfQQv8 t/g7nNzkNERCQynpCAmFU6+e+jtJ5K/KTCI+Y96RO1zR2mm85Jd2dWz1GlPnoyOIfUyfP4wh0adH zenp1mbm4/PmHKWfI7sU9Z0b7FPm7R2FzoZSA/qgw/zKPb8kVtNLtqP6jt7QrlXQGSODsZjkkj+4 Ts4+wfzQQBtyx+RDDLmewmPWO3cDhxPAxmy5yTtyraZjnjBKqqTEwC7pT7xpEnmNuM427iqkrztM LLTRIiDKfkBdwF6d1qT0RbZUWqK9l2+bL3GhAUGCw20Tl19Ip5AVlQehAPfJyngui0A77FNiqqbm MSK8hwZnOmniK6KaBn71+tCAuZlxp+c2Tni8wZ3fPl4uE2hAifiMtjgmHeWXwHvj3mG2HmJ9+eYm 34XCa5zqxyScYRPBBZZZ4d/zfT7G/gl7SilW9GjPGbJpHpE7w7jsF4OZKWjOfcPcaeYDRwLYEtMR ApKkk0z0sM9Af9I9QbHrEcM3bVCAbeu/RSQgG2CwP+neI2OSqMnccmj0q8joWUzFZMYlllm7hX1Z LOw+dyTwl8GO14t2ubjDhyc5uqqKSSyEkWxRLJp9Nx22xJmh7LmfpE2SyItCI+JJDdofKSTMkSJE iTFDFYpMVCpT2vf3aKkPDebE7E+3ONCWTu+Vj9DPk1+rRyTTjoSv1+TO/hxH/W/Kfr6p/Gv4eNhy e3GPDu0kbOTjyv+lpMzT1WUQQgoohSH+5QAXmup8j858g/O31l/q/wckmBRqZRRmobUZ9n9X3eHp JJfd72fvHZm2/mmUEjf6LaRjcRbAqyLZB9Py9PW/L7s+LbPzPZ/j8Wzfhx9o7EZBUgOA/sBgj5OZ gPAYx8D0wfyMz7uHNCO0MbB8RGvzcRGG1fHm2UJt/PyGfP/djX2fpxabd6RM6OX82YPV5PS7deYf 1qT/rFc0nsVD3PeYPtUadrCbGzGH4jCbKmkqpsrds2fQ753ed1fhfc4aSedTdRMV/nYmMVHnU/mV PlbMe17GPkfM+disVRsdzq7z5E/Q0+Dq+JzOiqqVU+l71dWzZOA8VYlV/a/rf7H+xuexUfQetWPY 3eLHc5PwvrYm7k8Hveh8T5HJX/Jppd8X/2/97dp/r2z6H/O9ivlVjuf8Wmyo+dibq0aVPt9Gnned Ini7fM7myRO5Ju4eljwad5Wz2JoqVKlRP0Tiepf8J/+jHa4TsVDzKpTzpTSmiVHofM+c8na6vB86 tNOG7BU+1sxoMMfPo45lTBI1OjByYXd860mbn5A+oSFg/AmOH/M/1PvI/C9apU/0u45PtVWN2mKb HJ/qUm7Tg05NmPcxurT7n8zDE2U5v0uj8THJwcmJhjd4PN+J8r5XxnhK9SzHxw/M+3MfdO2n/S88 TuZyNTZIjxv69BgfpeNC4nY4KbPBhv/0xmZ/R8ubD7Rj3H7h+0iHx+3YosHi/wSGeZ0f3tPCMb8u 3OchqDTls2eljh9r9rlxzp3m+n2Hj8HxPkfSf2n/YxofI958z1Rpy7H+J94/wPH304HO+v8x8jqG S9xI/4n+VQ+3xdT2+RdvaMnPPeSyIwE02P38EsOUHSPseHVnh+ocDkxy96enr0Tgu/q8/J00H8Kq cTX/UsOCB3nBzgzOJnA85DLYmQPAgOIn+JE+2I3w5XgJjoHqfMgKYx8xhfA+HwGoQQu3I8OaO9kC d9Y/kencLqTk9i3SY+Mz1Y03Gz0um3ycYJ06zI2fjPX9i3UT/ODJHCzXA9SenB9Ca+OTh2zhf0DA fF/9NB4yXsKb+0zM1sBqv+K6ntoVu8s2at+QcGoMyDD6c/e/T+1wux7nBt/Y4YP7YErGyDPT6t6W ZMWFknxU1RP/k7f4tTgzo/+l/od7Xl+95ytUxijqsNfR1qKV0n14Uf9f993VaTxs8aUgUZB2axj8 j+7I71k2Un1q73jOU/G98ehs8mMO33NGqMMMi8Cy/Yl/ui8XzV0vT4AT8b6ZEhSExM//AkqjK0/Q 8x9B3u9Xi0/E9zqft7czT+d+F4KdJ2nC80wUchUJJSDAwXCCIgpjEkZSSZRPs356f6snKev/+dfy 8f4Z+mP3n5z85/vIV933/n5HY8Bj/v/H5T+/iv4c8PP+4/0gx+FPM1/yN7z9D7X1vQx9o3VVMKpV f9rzPzv5H850SJzVPuH+YPjdj7W7T7UPwHnYieZX/Gf8z/4b2bZ6ezm6Tk523E0k0/ntvNPBpzmn aOaTnYpnP63jx44xQItH/hb/1H/emMSpowNgmIoRRkGutRdQqSlORNybkvW83P/l/+5w2bODCpMG SyGMVKr+2Y1ph/wQp5njbXOT9dPHjdk6Z4m/zPn57PYiz4I7PHJHaLbm0dnQcs5ucSB+dOsrW805 P5yctI3xc4R63NMvo681jx245u0cb/+XNGf9aestJJj5FuJPTG3NDLtcyycfSf6nrxb1py92TB/9 7JhRVbLOp5jkyabxLJuKM06OW2k9NHmtNaJ8+mYWq/8ckyO1XD/2qRh3Kc064jwc5ikwzEsQ8Mux p4NG2rBwPtM5EBqAhpJ4HMB5FsQ4E6Qu8kwrQZE6tIxpAWqx4MkOjWNks9sf9r3Mkn01//Vk/q7t +G2rHopzlCqGxjHucHpfDF2QRwgRcZBQ/zkKAxIqYo5iuBg/3h/nB4O4eD+g46CEPSLBuwmJGl5p W5LOLD3Kjwf/DE2Uw8xh2CbEfkNDrrwXGBlJJYkImS2iaR83508PjbZn0oftt/f3/U37X4P0JP9C VD+AUkP+qf4zif55tKn9+R/oHQScP0D+WCI5yaFOyTpKKlKfqTByIp4SS/ybGxvuAl/l/0deIRt2 2yyOpGEOc8Dt5rVWnhKNqXSU3JLDU3A4J2SdxDZu2nKbw5IZNRNpytuwnSCMAhzXS7ocD5L4wyS3 pTHmrJ5iJ9jgnpSZWA0GO+nZjQwlGEyGxqZJ/8CyD+p/pf9OuZe2xc/74+SfEG67BKP+4SWAGRX/ 1Y+qHQqQwRNlrZZ+Rf+acU/H/Zf9jZ29ZufnglJBZSAE/txUZt7arWxpIPIYmNaW2lNqJizal/Wc ci1mshEskPpbsbizWTLbLbStMsW1oiGUjWH+p+1+B9/7s3m2GHnNY+5RzS/B+Db9f//W23SZ/k7J n9/w/h9O40PYSf+4+yJ/vmfUZB/3xCn+v9Opcz2r+apUP8PtZf7l/3Lkv8T97P+ln3C9ISHaEPxn u/z58PjeDuP3nI/+rtwR++P6J1/+f8P+H+3Fv8GGPmdCO/uP/3bZk4Zx9iXzIh+33Bu8+z9x5w/+ 34OA5A+yEM/7H/Y/9r73/eP/WP4X/k6dnh/3PqeTp/E/4nufpYeTpwfrPsP6D7j8unzA/l+81nt0 f6/t2PUfMF+UYDBZH6l/S4Fl8a4zX/tH/lMP0oWBx7QZCQ7VH6ipPPLGWJaJzsKdVKn2qd3qxsU0 zwfY2MXVepZsXe85DiCWEH87jb6MGP0ljZ6I00/GjzjJ1ElLhGXqC8AleRDhgIRGPBjTUrH3MY1K krEuJUKlpyZDCypdlxTZadsxpXCmnNxNR4HDKWTkxRzcbRbItxjm5JsNNmppmU2Ix4DEdyB0yFIz EMhNDJkg9gx+vpcRQOZ/G5wEIf2ne/9+j95rbzEb4LdM/omH+FSkS1baxTu8fJpHmTZjZiq57S1V Yxt6WzRVVa0T/lNNNSbKTSU8XqbmzHK8hkNohISXR7KKz/Y2QWf9Ygc3oX/b/RVU/kdFB73DH7Wm zZs097weLZHk2YY4Ydqmjm8WPicNmwvlmeabEbOro0t4WToqdHAX1o5C32OCuDCkqFnJYKODikrA KKKMYligSQdFjjkSpjkCKhgzIZG3IOacVWWzkwLOCNBSiLFCPgsCogBC/X0yf/AOxqCz7MNDkBhj CqA1oKogjfC27XYNM07X9Tq7VacdTm5t3ex4qqyabGTzuTHZ0ePJsd/gsU11mDabUsvVVXTGGGKa SDTZK7Bjy2T317lSRlA4KheTCV52R8QeB//rTDZ/if9D/MfQ8yZ/0P9x/qIFP7/9BU3qZjIeMlsC G6DBsop61GWSeSzFLSedyets5sPgYcGBsvChBKaIcQCVZXifzGwc2nyuMFcMj766l4Uyqse+GRk3 YyHnSncUiPvqKj6GR3tkyGJye9ThsRD/bYTq5ORk03JZJ3OTc8zk3fK3yuenIkkkkkkkklKspu0c 1NPocjmOSVYNnJ+pzSYc2JPxx+ViRqJOxYeqyDp1yOaxIV2nqjUJ8xYjm3YN1SJhw/A0bvoYTl/M 3akh/2ub8Dow2SR0VJ6a00wclNLIYUbFadimK4Y9KnJLJJwbtMYsPQtxzb1RVFUemySJsbH9rx0b qjuf4DF0aMROccEp8ZD/bLCaerB/82D0lTsdGROmnpd7aIj/XN5k71k7zyVkjhpo1KlHao9FDsd7 dPGP0kN/Vtobk95q1N+bfy7nifAWJunYzxfcx2PRMtn+Ckfed5vPyRkmyvuJ9x4GfDTNRj+SlYtL U2brOnRg61YPtk8aWRnjTZIRIqZYhKB8SQJDH+qRXj5cZP+O+J2qiH2WGpYHmbPcak0ulDmqeh6C nkqabKrHxtOFm0/Q5tHmWT9v4GJ2naxO6KsWJZT/9099JhTnSR20nUncwj1KneV4FPNTxqDenmk5 hL2QpQQRJFKnsLRL0kdUqpBo6iHvM8DQ/qNHJQ6LaV93pX0GgR7k7hjhUYqD7OjLUk7qjdZMtsBE gnMPcYP2Sh4krwQnYQ95KmjjZeodkE+UhfMwKYRo8jg3IwiIa8Bsm6dWzEZUkffhSRMsRag6vf4d s2kifXGmIj6hYdpFml4PMyML3RzeT7DSdhCh7WEPpYUTkQApxBzITpJDtOZgGJzHPkOc00rLi4y5 MkRQnYpIjCkiTOTm0j/3h/awAz3/Cffn2j6GY0Jf0f/Wn8zhPEgfl+ifs8Qz8YJ39NTctaerTzYc 04f58/8Ykv8L/+CeZEw0n/9BvIPAeDh7iei1G9xyiev/P/MUIQ5qzyNIRrPEj2/tx7NaUQ7u7mIO aH9432NwYN3zvqPkQgaXZnxLp04DGU6xKlinzjYla0OZyOPZz9R1lAxRDHS45w9DX/sHjlsCtmM5 bFsjuKip+0UGzPpnevokQ2+/0kQtiEeA//Naxl/tXzBeBVZDRzqfD8gWHb+FnGoSB3UecTYJ67+H 4XH1PFmG7F78VIy2229iimKn/Hec6NgyICUxZCchDi6P+W2kTWxManRyTxzgI6uT+1zq6+OGDkcB Onk2tJ2yuTQOjr9vHrwO8iBbtgG0qRlbUis3MQn8n1koBly5xpiQkOYp5LIVBkjLclYuFwIHdxmd 4WS5ql1ewnKNizsya2JvUlNth6Xezj0PULTL/+WT4XSdBwoIovJb+floNcuCr0jhrCO+2u2xbD4i kaSrBKMkaHQkVMXKzCJV3w2H7HHs7iCsHo1pQB+eH+PLbLlLBuMbruTJy5VsXH+pdwZZ0YewDhnl V3rC93YV0bl/Bth5kGsoqFxf/UOcWWO8e+rlqNscrYDeKjAcOcTWE0iwH6prMvOuIXP8/VUCwghe R4HmuJk2Qnn+wMPMOYzBwyLwBDobaOIlwX7w+w2Le1V03DVZHRr+4HrVI1Wp72LjUF5Cf8AnBVGH n3ApnmQIA4d2LthcBYPA/9yR6TyBuBtJ7rwBhhZUvLX2nx8zpDQ6qwPVFzE8YlFaE7uWvLJXIvoE w3LnqQPYe4mZkhBzr4NATbKJ9XlmHnVf7Q94LC2/9ne3y91j/+/b7ZyGrV1YVqVrWNy4fh6o/H5b /d1B66/X73qCY9Xe1/kQDxe+6P9ln1K+ExlF57bZ4aDDnzC8Qwx9EyMKjS09aWkNYzeNPMySGH4t a1qTVHmXs9MsySmO0cVZLhJZNUeXy8qP2hJFnqlOjBMhnl+5e/peocDXDzXReoO9pSQGzSLB0Rfe w2Qv+NuWPrtxGY2dKJmmPiu5xg5U++QPHNna25ZIgyMlht6RUmToVgU8FBeGO1VuSi/FDE4k8I88 LPgX+4cRJuZsj1ovk/3/SbOF+89qh9h6sNqoeYjc4GMhnM7HtgeKcj2L7FEkRUkDlQb7/sFIMjkv ogwBQXcj1cf1PTDLAxhw73PTc/b57IqNIfqf+BPrT8ro/A6Pa9DSVMFY8J8fZtw+dJJ9qo8x5DpU +X3e8goHFHOHh84fKPeEyM6/wl3EXDcr3g9y/gZE497EmL7n1G32LZMu2RJI9nn81zgF/pp7vv5z J2jfpN33Go4YdspsnunE3H15iR9CfGnU7+HzSvXXPFU7CXHR8Uw1p+SR7p7WztIj4dupySHFnFkH 5vYdT2nIYyGA7iYj4qP8fh8o/S3Y5ybdGlF7R+pyEiAhfiPjxPjOA8Ah9VP0TodXuXo7nmVrTm2T E0cz6Z8kQY5vVjc1/L8GN/F9Uw1HZNnNtU9LJE3jpYypUpsmrM0a16TD8bRit4ux8Gnpbf7Y0Tia +N+DqmvuPWddefo+humxtTbdkrNj5jM5T83aQ1BeAjDx9XHp8VTo6AYxEKY7DsMN62ZGA/A7GImz hqdipzV4q2fax4OjGzq3eJ3vI5ni4NmzE0/G+hvGN25p5nk3PiMG59inc5OFOrZ3n5jY9JTh6HDF O29xup3GHVU0qOFG6k3UnY3YjhK7j8/Zp999LyfH+N5nVe5r+ytOcuEsofmZP4D2deNzu9/m0G98 Bxw5FW+WlmHUIVVdmqlyoGGh0lRZyroa7u4qCFKWoyqUupV4zNZc6UkyVJrTGWSf3/wP0H2Vw889 Ese/p2fy+o9nBTg/OS7xmlA9Dv5nw5S2B5W89BIKMjYtI8oUYiF4zswyGGYhAJo36HGZQoVyDIjy yBxsCNSYJ478v3DgrIce8wk8n0LIfc9Dg7GPSqbKfnK4f7X7mibJ+5wfYcgOBjU9exVOe8FoZB8n ioyScMjjvkEq/Fy1r8u7vsL9m/q9XG/y8zJuTROfNh3W5B7yPnIPuGEeAxpFxWxT6rERcipAeTIu SieQMTKL2kRC3+xZU5XoxyksHtyCDx3TqzgjsnWU/ztZSVZA9roJj38a/k7NqjZhmQqoMfQebL5F unXLSXcsBrDvj4Xna1dw7MeUQaYM4cN21Hwg6KUEMg00Bwv+o5Zj0djs4lkSHSUfKakRIi+P/EoE vIl42RDwHEMS+Z4kggRGOQwe0YLmYFlKFPs47HkdTW5fzdhshsmDzR+VlCEY8vyQdCFBWjYg+Aqv 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 r7/Htp/56/PL+fl8sfL9y3v4+vn8fTnv6tX7fbx9sfvu3s+Xs+3y/PJzj/H41N+/U6YfyX7+PGft 69/x7/b17Snj36/P39fn7197fK1Mc+X0n9/46+vy9rvw7119vx9admfPs697q55CQkkkJJKqpp7L WjMW7Vv7uXmy83X0TNv6f9Lfav1k6ts/P7Y+evPX1j8/ljzrePpCPz861un8fx9PlCbq8p5c3Pn8 ef3z718u+u/b5uj/H8NX39fufPnD3r8S9m/XvWzHvT91+ecb+0J/PM/f6yd9ttCXxiH3g/29v1qf j9e/0xuf2/Py16qO+fz9bb9eMRj56Pf5v+UPD8P15i35n9bQ+2vzaHvx2nS+I+vk+/Us9W+1fMPE KR84+nz/f8Q+16Hx9v33M+/UvjjyH8V8/fHIX+M+HuaUb3+cOvtPqMf16d9qS+Xb6/L2pCPcv4b4 lJ1fe/3xj8/e/68/GrfOX1d8/q/85+v19b+tzx68+neO684WhX7/Hnrx31isvHzfn+K2zKU/Xh3j 386pjy7XUXu69fuW57vWPX7j+/ef78P98Ybz49vVI/fxWH2jF/j6+PtJ1XWb5V9x1ff6fb4+dr/r f69vaX2+f1xT4565Tvv9fSMrY5HX48fY+nd4Y+UT598apPD/tK3xqMIeHvs5t/Ol6/fzOXecQfin vr7eKU6b9Vd4d5/P0+7pzh7/Tzb9R9v4pPue6U+l/11L6/v+Md/X6a78fT5fFeP61+ujz51h1uW3 6h8vUI3+/j9/J/jFdRs+NYZhv2x93eZ0n7e36fr7k5fn38bZ3rr3+ftD2/j31qP0pCDp7+lPD+ev fXXp1NRvXc4u7/Hq0y2uY98Y8Qpv7b+nzr8Ox1bxn0+j/l5+LWnqe+fv5z+94/KnPrD69bxCH1+0 /f8fP0/978e8Sft7fjX2fKP48w+Xv83efp7Ol/Hx+Pp7/x7Y9vp9Yedc6xv5ee6Y8ntPcsdP/iH6 /WMud9ufvr8/rP2+nk/cfhv1B/7+fvv9/T9/mOLu55+st/X6/fv+Pzd9Pm3v9859/nvHP4+n079/ vKv359XZ+Hzt+a/f9yxHHPn86frXUa107+Pr9ft6l+/dvnP8w0/rUvOfxHx+vp7+Pr3yv0/F8/b6 W+/3bz02/p9/fzDnf8Y51ffj7/r8/fH3xD29/w3j8fv8uGp3+d/G/2+mrV8/qPu+k/Ufp+8a+9b7 mfnz+/r86z/HNfnrz8/tCPh9fi3tD+Pt8vv6G9vp9vP1bGKHuzfOeOW+VL/r5/elvpKzo+9vx9Ne 8v39Pp5+kPX1n+t5/N6ay/X8X79n3hZ7evP2hb8N5x6/FPv88257T37Ny0OR+ffn52/fPO6ePk/5 N9v376x9/p5leXy/PjMvm2+fnn1+Xr5v+fPvjx7/n3/I3Xr7a83+T+fv8T3n884db9zvPt1OMXx9 X8x5rVr+TxinmnOp1+XHU01H/wweK31n435+cItVz7UlX6SpRrWnaE5fbHj7eZeZm9w3uLb3vcWj GMYR59/zL7/b7/S3mfrr5fnx468ePp1mP68+tQ8esb8S8dw9pVPx37+H+rfvkPPXxWmPX5+e9dfG lj5+3zzHj9Wc7y3Jzn9t/Pxj0+fzp568/S/5pTFaUyfHn5/n07z+Txr1n+Jz8y154fuuJMx8vluP 2tL59w958+n0+Uo+/t3it/s3f7j+L/Lv5fxGRYZvHy9/p+q+Lt+vn+vz+f4af1n9fPdX0efx7e34 8Tz7vy6PUIEvpK7Tn8pwjmz3s0XfT6/r2l9ft9P1+Y61+swG9/efvHz72+fx88++fl4xTTfWdIc8 ++913uu+vr9oP9V+2IDTEzk+joJv3D5vhzqHpoR5/Hh5N0qOP318t8cN0V/R+VGD/p/H3Hy1b9We QeatVg4V5q6E13c3Bv8vFo3TadUMXN06rsnBhYPTrS98S+nu/bBVs/xd8KHqP8G4RfCn0jX8t98n 2PbxttzHt9Ot/fH0U/B+vqWx0fLr8xfb42FOvl5xezfbna8a/XmUp/pfYHntQbxR/2kRo0/u6tPc bTvp8ea25rr159rwh8z2n074Pr+fr3v0780pzBfzeH8fOPp+5/b7u+8Gld/jrf77+Pl8j8/e/3z+ r/T7/O2va3y/h/u93z+nj60+fM/L9fP4x7/j6V++7euvt5j33Xx3j7e9PTfvx9b4+JT6+66/NqsQ P139v4N/mhWT1gz7+fv+adfhvM/rfePi3yr+m/LB3f7dP+kGjhuN6+ee/33H3tT+Ppx3sx+mfJnf p8GjArTnn8frmiW/nP41D37f9vzuX3+st5/NKeMEvDY9v1n640d2d+c39u/a1PX8Yf9f3+Yff9/j 5x+2vj7VGO/X659evv4+9vl+PPc/j9y/M/1bx6/Hvbx8T+Vcdd/iVuv19dtaGbfLHn8y/e/ePXcb +2/nb6fOf719d8+/g8/x8vb95i36+kf4m6vw/zx2WvanJfT9tn9e/j6P8+vrLv418tkCK5941/jx vy92Je/Pb23f3/XVW+PfqH5huH76j+e5fmz/jFvj8d39vr5+n568dfFfkZ/Xtn94+Xv9PiHU/fjf Xevpyfw/3719Pl9M/v7T2fP28/aXef359Tl9O4/WH8dzn8O+rp+XUn7dyl39vOtQd95dcxz5Y8fn Nm+3x+b9Gv3+fP4bX19/pDH1o9/XtKF/X3174p+/4/PXvOV3bdz9Q+PaXyz6f8vPtT29r/x/Hyvy su4+Pl93Y+f4z53/Fv4tHpq/fnXLuh84/qf1vzfnvH8UtP6a+vU3cx8W+H/jzH2/X25b3hufl/z/ jFPu9+vEZ/b5118fXGfz9fOvf9V1v+J/u/Vq+vnb5O+v48fjz+I18xv8P1+5/X85w3fvvd/b8vfH H34/18vX8SbNoOrP6WdXzb+P18+44p9vj919+pfehL36/j5/rDfnH1x9tHtP8W+/1p3L5eu/4jJs W+fWW+ePx9KZb+Pp/Hw2ep+vfj4R9vb8X/PefrOninXt8fTPvT8V9+/q/83fmH1d8oz+nWYevr9r 31Tx9/PU/4wfLx6+MfK33hps9e3Ql4A+YaEDwiIXwfdPtqI4hNQouwYncPtuhq4hmo1DyGoGIFUs myiqA4A5BgoYA6OmI/IUBiYBcgoYN1DGgBZoBmLkjoq6IeHHgpgEkiYA4HiJhYB4OAJqI1RwRyEg 4iaIYBFyCpcDFLJoGAQQsuAYWG4UHIG7BqJcKrEMANXjkhJPM/29vBeXy4o7rVdhfcfZ33vYX39M uZuyVbJYQcfAulT/q8DSiBP9v+f375Ff222c3n/V/qr5ufP+IEh/tdn2n7pp0GP5DULk8P8N4PzO MrD/JhG5BFKZ/Jg0q/H92ZNJn5/hrf3fpWn+VVm9/Nlb+AvfyS9ivv97/BJMGm4kVevanl/6k4gM 3h3TNb/dD/PzPOfs8tfrzyHliP/PFJ8i5X1A6g52rIbH+T+5nH8iFozv1dHuB7YU2QTsl7KQiCFR F38b6fggRje2eG/8ChGBhZ7KH/JfUp4bhjbEd2DuJ/gcfuEBm1NUIFSbBUZktj7in86WCV+G/3rl 55zKO/o+j+dev9v49TI3lE3+QIZD/vRjzvzqJUq9v+XGmHtqI/x/00em/TvZpQ+T8FY98hv5R9yn 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