/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Display switching interface. * * By George Foot. * * State saving routines added by Shawn Hargreaves. * * Switch callbacks support added by Lorenzo Petrone, * based on code by Stefan Schimanski. * * See readme.txt for copyright information. */ #include "allegro.h" #include "allegro/internal/aintern.h" #ifdef ALLEGRO_DOS static int switch_mode = SWITCH_NONE; #else static int switch_mode = SWITCH_PAUSE; #endif /* remember things about the way our bitmaps are set up */ typedef struct BITMAP_INFORMATION { BITMAP *bmp; /* the bitmap */ BITMAP *other; /* replacement during switches */ struct BITMAP_INFORMATION *sibling; /* linked list of siblings */ struct BITMAP_INFORMATION *child; /* tree of sub-bitmaps */ void *acquire, *release; /* need to bodge the vtable, too */ } BITMAP_INFORMATION; static BITMAP_INFORMATION *info_list = NULL; int _dispsw_status = SWITCH_NONE; #define MAX_SWITCH_CALLBACKS 8 static void (*switch_in_cb[MAX_SWITCH_CALLBACKS])(void) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static void (*switch_out_cb[MAX_SWITCH_CALLBACKS])(void) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* set_display_switch_mode: * Sets the display switch mode. Returns zero and unregisters * all callbacks on success, returns non-zero on failure. */ int set_display_switch_mode(int mode) { int ret, i; if ((!system_driver)) return -1; /* platforms that don't support switching allow SWITCH_NONE */ if (!system_driver->set_display_switch_mode) { if (mode == SWITCH_NONE) return 0; else return -1; } ret = system_driver->set_display_switch_mode(mode); if (ret == 0) { /* unregister all callbacks */ for (i=0; iset_display_switch_mode)) return -1; for (i=0; ibmp == bmp) { *head_ret = head; return info; } if (info->child) { kid = find_switch_bitmap(&info->child, bmp, head_ret); if (kid) return kid; } head = &info->sibling; info = *head; } return NULL; } /* _register_switch_bitmap: * Lists this bitmap as an interesting thing to remember during console * switches. */ void _register_switch_bitmap(BITMAP *bmp, BITMAP *parent) { BITMAP_INFORMATION *info, *parent_info, **head; if (system_driver->display_switch_lock) system_driver->display_switch_lock(TRUE, FALSE); if (parent) { /* add a sub-bitmap */ parent_info = find_switch_bitmap(&info_list, parent, &head); if (!parent_info) goto getout; info = malloc(sizeof(BITMAP_INFORMATION)); if (!info) goto getout; info->bmp = bmp; info->other = NULL; info->sibling = parent_info->child; info->child = NULL; info->acquire = NULL; info->release = NULL; parent_info->child = info; } else { /* add a new top-level bitmap: must be in the foreground for this! */ ASSERT(_dispsw_status == SWITCH_NONE); info = malloc(sizeof(BITMAP_INFORMATION)); if (!info) goto getout; info->bmp = bmp; info->other = NULL; info->sibling = info_list; info->child = NULL; info->acquire = NULL; info->release = NULL; info_list = info; } getout: if (system_driver->display_switch_lock) system_driver->display_switch_lock(FALSE, FALSE); } /* _unregister_switch_bitmap: * Removes a bitmap from the list of things that need to be saved. */ void _unregister_switch_bitmap(BITMAP *bmp) { BITMAP_INFORMATION *info, **head; if (system_driver->display_switch_lock) system_driver->display_switch_lock(TRUE, FALSE); info = find_switch_bitmap(&info_list, bmp, &head); if (!info) goto getout; /* all the sub-bitmaps should be destroyed before we get to here */ ASSERT(!info->child); /* it's not cool to destroy things that have important state */ ASSERT(!info->other); *head = info->sibling; free(info); getout: if (system_driver->display_switch_lock) system_driver->display_switch_lock(FALSE, FALSE); } /* reconstruct_kids: * Recursive helper to rebuild any sub-bitmaps to point at their new * parents. */ static void reconstruct_kids(BITMAP *parent, BITMAP_INFORMATION *info) { int x, y, i; while (info) { info->bmp->vtable = parent->vtable; info->bmp->write_bank = parent->write_bank; info->bmp->read_bank = parent->read_bank; info->bmp->seg = parent->seg; info->bmp->id = parent->id | BMP_ID_SUB; x = info->bmp->x_ofs - parent->x_ofs; y = info->bmp->y_ofs - parent->y_ofs; if (is_planar_bitmap(info->bmp)) x /= 4; x *= BYTES_PER_PIXEL(bitmap_color_depth(info->bmp)); for (i=0; ibmp->h; i++) info->bmp->line[i] = parent->line[y+i] + x; reconstruct_kids(info->bmp, info->child); info = info->sibling; } } /* fudge_bitmap: * Makes b2 be similar to b1 (duplicate clip settings, ID, etc). */ static void fudge_bitmap(BITMAP *b1, BITMAP *b2) { int s, x1, y1, x2, y2; set_clip_state(b2, FALSE); blit(b1, b2, 0, 0, 0, 0, b1->w, b1->h); get_clip_rect(b1, &x1, &y1, &x2, &y2); s = get_clip_state(b1); set_clip_rect(b2, x1, y1, x2, y2); set_clip_state(b2, s); } /* swap_bitmap_contents: * Now remember boys and girls, don't try this at home! */ static void swap_bitmap_contents(BITMAP *b1, BITMAP *b2) { int size = sizeof(BITMAP) + sizeof(char *) * b1->h; unsigned char *s = (unsigned char *)b1; unsigned char *d = (unsigned char *)b2; unsigned char t; int c; for (c=0; cother = create_bitmap_ex(bitmap_color_depth(info->bmp), info->bmp->w, info->bmp->h); if (!info->other) return; fudge_bitmap(info->bmp, info->other); info->acquire = info->other->vtable->acquire; info->release = info->other->vtable->release; info->other->vtable->acquire = info->bmp->vtable->acquire; info->other->vtable->release = info->bmp->vtable->release; #define INTERESTING_ID_BITS (BMP_ID_VIDEO | BMP_ID_SYSTEM | \ BMP_ID_SUB | BMP_ID_MASK) info->other->id = (info->bmp->id & INTERESTING_ID_BITS) | (info->other->id & ~INTERESTING_ID_BITS); swap_bitmap_contents(info->bmp, info->other); } /* _save_switch_state: * Saves the graphics state before a console switch. */ void _save_switch_state(int switch_mode) { BITMAP_INFORMATION *info = info_list; int hadmouse; if (!screen) return; if (_al_linker_mouse && is_same_bitmap(*(_al_linker_mouse->mouse_screen_ptr), screen)) { _al_linker_mouse->show_mouse(NULL); hadmouse = TRUE; } else hadmouse = FALSE; while (info) { save_bitmap_state(info, switch_mode); reconstruct_kids(info->bmp, info->child); info = info->sibling; } _dispsw_status = switch_mode; if (hadmouse) _al_linker_mouse->show_mouse(screen); } /* restore_bitmap_state: * The King's Men are quite good at doing this with bitmaps... */ static void restore_bitmap_state(BITMAP_INFORMATION *info) { if (info->other) { swap_bitmap_contents(info->other, info->bmp); info->other->vtable->acquire = info->acquire; info->other->vtable->release = info->release; info->other->id &= ~INTERESTING_ID_BITS; fudge_bitmap(info->other, info->bmp); destroy_bitmap(info->other); info->other = NULL; } else clear_bitmap(info->bmp); } /* _restore_switch_state: * Restores the graphics state after a console switch. */ void _restore_switch_state(void) { BITMAP_INFORMATION *info = info_list; int hadmouse, hadtimer; if (!screen) return; if (_al_linker_mouse && is_same_bitmap(*(_al_linker_mouse->mouse_screen_ptr), screen)) { _al_linker_mouse->show_mouse(NULL); hadmouse = TRUE; } else hadmouse = FALSE; hadtimer = _timer_installed; _timer_installed = FALSE; while (info) { restore_bitmap_state(info); reconstruct_kids(info->bmp, info->child); info = info->sibling; } _dispsw_status = SWITCH_NONE; if (bitmap_color_depth(screen) == 8) { if (_got_prev_current_palette) gfx_driver->set_palette(_prev_current_palette, 0, 255, FALSE); else gfx_driver->set_palette(_current_palette, 0, 255, FALSE); } if (hadmouse) _al_linker_mouse->show_mouse(screen); _timer_installed = hadtimer; }