/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * GUI routines. * * The graphics mode selection dialog. * * By Shawn Hargreaves. * * Rewritten by Henrik Stokseth. * * Magnus Henoch modified it to keep the current selection * across the changes as much as possible. * * gfx_mode_select_filter() added by Vincent Penquerc'h. * * See readme.txt for copyright information. */ #include #include #include "allegro.h" #include "allegro/internal/aintern.h" static AL_CONST char *gfx_mode_getter(int index, int *list_size); static AL_CONST char *gfx_card_getter(int index, int *list_size); static AL_CONST char *gfx_depth_getter(int index, int *list_size); static int change_proc(int msg, DIALOG *d, int c); #define ALL_BPP(w, h) { w, h, { TRUE, TRUE, TRUE, TRUE, TRUE }} #define N_COLOR_DEPTH 5 static int bpp_value_list[N_COLOR_DEPTH] = {8, 15, 16, 24, 32}; typedef struct MODE_LIST { int w, h; char has_bpp[N_COLOR_DEPTH]; } MODE_LIST; #define DRVNAME_SIZE 128 typedef struct DRIVER_LIST { int id; char name[DRVNAME_SIZE]; void *fetch_mode_list_ptr; MODE_LIST *mode_list; int mode_count; } DRIVER_LIST; static MODE_LIST default_mode_list[] = { ALL_BPP(320, 200 ), ALL_BPP(320, 240 ), ALL_BPP(640, 400 ), ALL_BPP(640, 480 ), ALL_BPP(800, 600 ), ALL_BPP(1024, 768 ), ALL_BPP(1280, 960 ), ALL_BPP(1280, 1024), ALL_BPP(1600, 1200), ALL_BPP(80, 80 ), ALL_BPP(160, 120 ), ALL_BPP(256, 200 ), ALL_BPP(256, 224 ), ALL_BPP(256, 240 ), ALL_BPP(256, 256 ), ALL_BPP(320, 100 ), ALL_BPP(320, 350 ), ALL_BPP(320, 400 ), ALL_BPP(320, 480 ), ALL_BPP(320, 600 ), ALL_BPP(360, 200 ), ALL_BPP(360, 240 ), ALL_BPP(360, 270 ), ALL_BPP(360, 360 ), ALL_BPP(360, 400 ), ALL_BPP(360, 480 ), ALL_BPP(360, 600 ), ALL_BPP(376, 282 ), ALL_BPP(376, 308 ), ALL_BPP(376, 564 ), ALL_BPP(400, 150 ), ALL_BPP(400, 300 ), ALL_BPP(400, 600 ), ALL_BPP(0, 0 ) }; typedef int (*FILTER_FUNCTION)(int, int, int, int); static DRIVER_LIST *driver_list; static int driver_count; static char mode_string[64]; static DIALOG *what_dialog; static DIALOG gfx_mode_dialog[] = { /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ { _gui_shadow_box_proc, 0, 0, 313, 159, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { change_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { _gui_ctext_proc, 156, 8, 1, 1, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { _gui_button_proc, 196, 105, 101, 17, 0, 0, 0, D_EXIT, 0, 0, NULL, NULL, NULL }, { _gui_button_proc, 196, 127, 101, 17, 0, 0, 27, D_EXIT, 0, 0, NULL, NULL, NULL }, { _gui_list_proc, 16, 28, 165, 116, 0, 0, 0, D_EXIT, 0, 0, (void*)gfx_card_getter, NULL, NULL }, { _gui_list_proc, 196, 28, 101, 68, 0, 0, 0, D_EXIT, 3, 0, (void*)gfx_mode_getter, NULL, NULL }, { d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } }; static DIALOG gfx_mode_ex_dialog[] = { /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ { _gui_shadow_box_proc, 0, 0, 313, 159, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { change_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { _gui_ctext_proc, 156, 8, 1, 1, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { _gui_button_proc, 196, 105, 101, 17, 0, 0, 0, D_EXIT, 0, 0, NULL, NULL, NULL }, { _gui_button_proc, 196, 127, 101, 17, 0, 0, 27, D_EXIT, 0, 0, NULL, NULL, NULL }, { _gui_list_proc, 16, 28, 165, 68, 0, 0, 0, D_EXIT, 0, 0, (void*)gfx_card_getter, NULL, NULL }, { _gui_list_proc, 196, 28, 101, 68, 0, 0, 0, D_EXIT, 3, 0, (void*)gfx_mode_getter, NULL, NULL }, { _gui_list_proc, 16, 105, 165, 44, 0, 0, 0, D_EXIT, 0, 0, (void*)gfx_depth_getter, NULL, NULL }, { d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } }; #define GFX_CHANGEPROC 1 #define GFX_TITLE 2 #define GFX_OK 3 #define GFX_CANCEL 4 #define GFX_DRIVERLIST 5 #define GFX_MODELIST 6 #define GFX_DEPTHLIST 7 /* bpp_value: * Returns the bpp value of the color depth pointed to by INDEX. */ static INLINE int bpp_value(int index) { ASSERT(index < N_COLOR_DEPTH); return bpp_value_list[index]; } /* bpp_value_for_mode: * Returns the bpp value of the color depth pointed to by INDEX * for the specified MODE of the specified DRIVER or -1 if INDEX * is out of bound. */ static int bpp_value_for_mode(int index, int driver, int mode) { int i, j = -1; ASSERT(index < N_COLOR_DEPTH); for (i=0; i < N_COLOR_DEPTH; i++) { if (driver_list[driver].mode_list[mode].has_bpp[i]) { if (++j == index) return bpp_value(i); } } return -1; } /* bpp_index: * Returns the bpp index of the color depth given by DEPTH. */ static int bpp_index(int depth) { int i; for (i=0; i < N_COLOR_DEPTH; i++) { if (bpp_value_list[i] == depth) return i; } ASSERT(FALSE); return -1; } /* bpp_index_for_mode: * Returns the bpp index of the color depth given by DEPTH for * the specified MODE of the specified DRIVER or -1 if DEPTH * is not supported. */ static int bpp_index_for_mode(int depth, int driver, int mode) { int i, index = -1; for (i=0; i < N_COLOR_DEPTH; i++) { if (driver_list[driver].mode_list[mode].has_bpp[i]) { index++; if (bpp_value_list[i] == depth) return index; } } return -1; } /* change_proc: * Stores the current driver in d1 and graphics mode in d2; if a new * driver is selected in the listbox, it updates the resolution and * color depth listboxes so that they redraw; if a new resolution is * selected in the listbox, it updates the color depth listbox. The * current selection is kept across the changes as much as possible. */ static int change_proc(int msg, DIALOG *d, int c) { int width = driver_list[d->d1].mode_list[d->d2].w; int height = driver_list[d->d1].mode_list[d->d2].h; int depth = bpp_value_for_mode(what_dialog[GFX_DEPTHLIST].d1, d->d1, d->d2); int i; ASSERT(d); if (msg != MSG_IDLE) return D_O_K; if (what_dialog[GFX_DRIVERLIST].d1 != d->d1) { /* record the new setting */ d->d1 = what_dialog[GFX_DRIVERLIST].d1; /* try to preserve the resolution */ what_dialog[GFX_MODELIST].d1 = 0; for (i = 0; i < driver_list[d->d1].mode_count; i++) { if (driver_list[d->d1].mode_list[i].w == width && driver_list[d->d1].mode_list[i].h == height) { what_dialog[GFX_MODELIST].d1 = i; break; } } what_dialog[GFX_MODELIST].d2 = 0; object_message(&what_dialog[GFX_MODELIST], MSG_DRAW, 0); /* record the new setting */ d->d2 = what_dialog[GFX_MODELIST].d1; if (what_dialog == gfx_mode_ex_dialog) { /* try to preserve the color depth */ what_dialog[GFX_DEPTHLIST].d1 = bpp_index_for_mode(depth, d->d1, d->d2); if (what_dialog[GFX_DEPTHLIST].d1 < 0) what_dialog[GFX_DEPTHLIST].d1 = 0; what_dialog[GFX_DEPTHLIST].d2 = 0; object_message(&what_dialog[GFX_DEPTHLIST], MSG_DRAW, 0); } } if (what_dialog[GFX_MODELIST].d1 != d->d2) { /* record the new setting */ d->d2 = what_dialog[GFX_MODELIST].d1; if (what_dialog == gfx_mode_ex_dialog) { /* try to preserve the color depth */ what_dialog[GFX_DEPTHLIST].d1 = bpp_index_for_mode(depth, d->d1, d->d2); if (what_dialog[GFX_DEPTHLIST].d1 < 0) what_dialog[GFX_DEPTHLIST].d1 = 0; what_dialog[GFX_DEPTHLIST].d2 = 0; object_message(&what_dialog[GFX_DEPTHLIST], MSG_DRAW, 0); } } return D_O_K; } /* add_mode: * Helper function for adding a mode to a mode list. * Returns 0 on success and -1 on failure. */ static int add_mode(MODE_LIST **list, int *size, int w, int h, int bpp) { int mode, n; /* Resolution already there? */ for (mode = 0; mode < *size; mode++) { if ((w == (*list)[mode].w) && (h == (*list)[mode].h)) { (*list)[mode].has_bpp[bpp_index(bpp)] = TRUE; return 0; } } /* Add new resolution. */ *list = _al_sane_realloc(*list, sizeof(MODE_LIST) * ++(*size)); if (!list) return -1; mode = *size - 1; (*list)[mode].w = w; (*list)[mode].h = h; for (n = 0; n < N_COLOR_DEPTH; n++) (*list)[mode].has_bpp[n] = (bpp == bpp_value(n)); return 0; } /* terminate_list: * Helper function for terminating a mode list. */ static int terminate_list(MODE_LIST **list, int size) { *list = _al_sane_realloc(*list, sizeof(MODE_LIST) * (size + 1)); if (!list) return -1; /* Add terminating marker. */ (*list)[size].w = 0; (*list)[size].h = 0; return 0; } /* create_mode_list: * Creates a mode list table. * Returns 0 on success and -1 on failure. */ static int create_mode_list(DRIVER_LIST *driver_list_entry, FILTER_FUNCTION filter) { MODE_LIST *temp_mode_list = NULL; GFX_MODE_LIST *gfx_mode_list; GFX_MODE *gfx_mode_entry; int mode, n, w, h, bpp; int is_autodetected = ((driver_list_entry->id == GFX_AUTODETECT) || (driver_list_entry->id == GFX_AUTODETECT_WINDOWED) || (driver_list_entry->id == GFX_AUTODETECT_FULLSCREEN)); /* Start with zero modes. */ driver_list_entry->mode_count = 0; /* We use the default mode list in two cases: * - the driver is GFX_AUTODETECT*, * - the driver doesn't support get_gfx_mode_list(). */ if (is_autodetected || !(gfx_mode_list = get_gfx_mode_list(driver_list_entry->id))) { /* Simply return the default mode list if we can. */ if (!filter) { driver_list_entry->mode_count = sizeof(default_mode_list) / sizeof(MODE_LIST) - 1; driver_list_entry->mode_list = default_mode_list; driver_list_entry->fetch_mode_list_ptr = NULL; /* static */ return 0; } /* Build mode list from the default list. */ for (mode = 0; default_mode_list[mode].w; mode++) { w = default_mode_list[mode].w; h = default_mode_list[mode].h; for (n = 0; n < N_COLOR_DEPTH; n++) { bpp = bpp_value(n); if (filter(driver_list_entry->id, w, h, bpp) == 0) { if (add_mode(&temp_mode_list, &driver_list_entry->mode_count, w, h, bpp) != 0) return -1; } } } /* We should not terminate the list if the list is empty since the caller * of this function will simply discard this driver_list entry in that * case. */ if (driver_list_entry->mode_count > 0) { if (terminate_list(&temp_mode_list, driver_list_entry->mode_count) != 0) return -1; } driver_list_entry->mode_list = temp_mode_list; driver_list_entry->fetch_mode_list_ptr = (void *)1L; /* private dynamic */ return 0; } /* Build mode list from the fetched list. */ for (gfx_mode_entry = gfx_mode_list->mode; gfx_mode_entry->width; gfx_mode_entry++) { w = gfx_mode_entry->width; h = gfx_mode_entry->height; bpp = gfx_mode_entry->bpp; if (!filter || filter(driver_list_entry->id, w, h, bpp) == 0) { if (add_mode(&temp_mode_list, &driver_list_entry->mode_count, w, h, bpp) != 0) { destroy_gfx_mode_list(gfx_mode_list); return -1; } } } if (driver_list_entry->mode_count > 0) { if (terminate_list(&temp_mode_list, driver_list_entry->mode_count) != 0) { destroy_gfx_mode_list(gfx_mode_list); return -1; } } driver_list_entry->mode_list = temp_mode_list; destroy_gfx_mode_list(gfx_mode_list); return 0; } /* create_driver_list: * Fills the list of video cards with info about the available drivers. * Returns -1 on failure. */ static int create_driver_list(FILTER_FUNCTION filter) { _DRIVER_INFO *driver_info; GFX_DRIVER *gfx_driver; int i, j, used_prefetched; int list_pos; if (system_driver->gfx_drivers) driver_info = system_driver->gfx_drivers(); else driver_info = _gfx_driver_list; driver_list = malloc(sizeof(DRIVER_LIST) * 3); if (!driver_list) return -1; list_pos = 0; driver_list[list_pos].id = GFX_AUTODETECT; ustrzcpy(driver_list[list_pos].name, DRVNAME_SIZE, get_config_text("Autodetect")); create_mode_list(&driver_list[list_pos], filter); if (driver_list[list_pos].mode_count > 0) list_pos++; driver_list[list_pos].id = GFX_AUTODETECT_FULLSCREEN; ustrzcpy(driver_list[list_pos].name, DRVNAME_SIZE, get_config_text("Auto fullscreen")); create_mode_list(&driver_list[list_pos], filter); if (driver_list[list_pos].mode_count > 0) list_pos++; driver_list[list_pos].id = GFX_AUTODETECT_WINDOWED; ustrzcpy(driver_list[list_pos].name, DRVNAME_SIZE, get_config_text("Auto windowed")); create_mode_list(&driver_list[list_pos], filter); if (driver_list[list_pos].mode_count > 0) list_pos++; for (i = 0; driver_info[i].driver; i++) { driver_list = _al_sane_realloc(driver_list, sizeof(DRIVER_LIST) * (list_pos + 1)); if (!driver_list) return -1; driver_list[list_pos].id = driver_info[i].id; gfx_driver = driver_info[i].driver; do_uconvert(gfx_driver->ascii_name, U_ASCII, driver_list[list_pos].name, U_CURRENT, DRVNAME_SIZE); driver_list[list_pos].fetch_mode_list_ptr = gfx_driver->fetch_mode_list; used_prefetched = FALSE; /* use already fetched mode-list if possible */ for (j=0; j < list_pos; j++) { if (driver_list[list_pos].fetch_mode_list_ptr == driver_list[j].fetch_mode_list_ptr) { driver_list[list_pos].mode_list = driver_list[j].mode_list; driver_list[list_pos].mode_count = driver_list[j].mode_count; /* the following line prevents a mode-list from beeing free'd more than once */ driver_list[list_pos].fetch_mode_list_ptr = NULL; used_prefetched = TRUE; break; } } /* didn't find an already fetched mode-list */ if (!used_prefetched) { create_mode_list(&driver_list[list_pos], filter); } if (driver_list[list_pos].mode_count > 0) { list_pos++; } else { ASSERT(driver_list[list_pos].mode_list == NULL); } } /* update global variable */ driver_count = list_pos; return 0; } /* destroy_driver_list: * Frees allocated memory used by driver lists and mode lists. */ static void destroy_driver_list(void) { int driver; for (driver=0; driver < driver_count; driver++) { if (driver_list[driver].fetch_mode_list_ptr) free(driver_list[driver].mode_list); } free(driver_list); } /* gfx_card_getter: * Listbox data getter routine for the graphics card list. */ static AL_CONST char *gfx_card_getter(int index, int *list_size) { if (index < 0) { if (list_size) *list_size = driver_count; return NULL; } return driver_list[index].name; } /* gfx_mode_getter: * Listbox data getter routine for the graphics mode list. */ static AL_CONST char *gfx_mode_getter(int index, int *list_size) { int entry; char tmp[32]; entry = what_dialog[GFX_DRIVERLIST].d1; if (index < 0) { if (list_size) { *list_size = driver_list[entry].mode_count; return NULL; } } uszprintf(mode_string, sizeof(mode_string), uconvert_ascii("%ix%i", tmp), driver_list[entry].mode_list[index].w, driver_list[entry].mode_list[index].h); return mode_string; } /* gfx_depth_getter: * Listbox data getter routine for the color depth list. */ static AL_CONST char *gfx_depth_getter(int index, int *list_size) { static char *bpp_string_list[N_COLOR_DEPTH] = {"256", "32K", "64K", "16M", "16M"}; MODE_LIST *mode; int card_entry, mode_entry, bpp_entry, bpp_count, bpp_index; char tmp[128]; card_entry = what_dialog[GFX_DRIVERLIST].d1; mode_entry = what_dialog[GFX_MODELIST].d1; mode = &driver_list[card_entry].mode_list[mode_entry]; if (index < 0) { if (list_size) { /* Count the number of BPP entries for the mode. */ for (bpp_count = 0, bpp_entry = 0; bpp_entry < N_COLOR_DEPTH; bpp_entry++) { if (mode->has_bpp[bpp_entry]) bpp_count++; } *list_size = bpp_count; return NULL; } } /* Find the BPP entry for the mode corresponding to the zero-based index. */ bpp_index = -1; bpp_entry = -1; while (bpp_index < index) { if (mode->has_bpp[++bpp_entry]) bpp_index++; } uszprintf(mode_string, sizeof(mode_string), uconvert_ascii("%2d ", tmp), bpp_value(bpp_entry)); ustrzcat(mode_string, sizeof(mode_string), get_config_text("bpp")); ustrzcat(mode_string, sizeof(mode_string), uconvert_ascii(" (", tmp)); ustrzcat(mode_string, sizeof(mode_string), uconvert_ascii(bpp_string_list[bpp_entry], tmp)); ustrzcat(mode_string, sizeof(mode_string), uconvert_ascii(" ", tmp)); ustrzcat(mode_string, sizeof(mode_string), get_config_text("colors")); ustrzcat(mode_string, sizeof(mode_string), uconvert_ascii(")", tmp)); return mode_string; } /* gfx_mode_select_filter: * Extended version of the graphics mode selection dialog, which allows the * user to select the color depth as well as the resolution and hardware * driver. This version of the function reads the initial values from the * parameters when it activates, so you can specify the default values. * An optional filter can be passed to check whether a particular entry * should be displayed or not. */ int gfx_mode_select_filter(int *card, int *w, int *h, int *color_depth, FILTER_FUNCTION filter) { int i, ret, what_driver, what_mode, what_bpp, extd; ASSERT(card); ASSERT(w); ASSERT(h); clear_keybuf(); extd = color_depth ? TRUE : FALSE; while (gui_mouse_b()); what_dialog = extd ? gfx_mode_ex_dialog : gfx_mode_dialog; what_dialog[GFX_TITLE].dp = (void*)get_config_text("Graphics Mode"); what_dialog[GFX_OK].dp = (void*)get_config_text("OK"); what_dialog[GFX_CANCEL].dp = (void*)get_config_text("Cancel"); create_driver_list(filter); /* We try to use the values passed through the argument pointers * as initial settings for the dialog boxes, but only if we have * been called from the extended function. */ if (extd) { /* firstly the driver */ what_dialog[GFX_DRIVERLIST].d1 = 0; /* GFX_AUTODETECT */ for (i=0; i