1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Display switching interface.
12 *
13 * By George Foot.
14 *
15 * State saving routines added by Shawn Hargreaves.
16 *
17 * Switch callbacks support added by Lorenzo Petrone,
18 * based on code by Stefan Schimanski.
19 *
20 * See readme.txt for copyright information.
21 */
22
23
24 #include "allegro.h"
25 #include "allegro/internal/aintern.h"
26
27
28 #ifdef ALLEGRO_DOS
29 static int switch_mode = SWITCH_NONE;
30 #else
31 static int switch_mode = SWITCH_PAUSE;
32 #endif
33
34
35 /* remember things about the way our bitmaps are set up */
36 typedef struct BITMAP_INFORMATION
37 {
38 BITMAP *bmp; /* the bitmap */
39 BITMAP *other; /* replacement during switches */
40 struct BITMAP_INFORMATION *sibling; /* linked list of siblings */
41 struct BITMAP_INFORMATION *child; /* tree of sub-bitmaps */
42 void *acquire, *release; /* need to bodge the vtable, too */
43 int blit_on_restore; /* whether the bitmap contents need to be copied back */
44 } BITMAP_INFORMATION;
45
46 static BITMAP_INFORMATION *info_list = NULL;
47
48
49 int _dispsw_status = SWITCH_NONE;
50
51 #define MAX_SWITCH_CALLBACKS 8
52 static void (*switch_in_cb[MAX_SWITCH_CALLBACKS])(void) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
53 static void (*switch_out_cb[MAX_SWITCH_CALLBACKS])(void) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
54
55
56
57 /* set_display_switch_mode:
58 * Sets the display switch mode. Returns zero and unregisters
59 * all callbacks on success, returns non-zero on failure.
60 */
set_display_switch_mode(int mode)61 int set_display_switch_mode(int mode)
62 {
63 int ret, i;
64
65 if ((!system_driver))
66 return -1;
67
68 /* platforms that don't support switching allow SWITCH_NONE */
69 if (!system_driver->set_display_switch_mode) {
70 if (mode == SWITCH_NONE)
71 return 0;
72 else
73 return -1;
74 }
75
76 ret = system_driver->set_display_switch_mode(mode);
77
78 if (ret == 0) {
79 /* unregister all callbacks */
80 for (i=0; i<MAX_SWITCH_CALLBACKS; i++)
81 switch_in_cb[i] = switch_out_cb[i] = NULL;
82
83 switch_mode = mode;
84 }
85
86 return ret;
87 }
88
89
90
91 /* get_display_switch_mode:
92 * Returns the current display switch mode.
93 */
get_display_switch_mode(void)94 int get_display_switch_mode(void)
95 {
96 return switch_mode;
97 }
98
99
100
101 /* set_display_switch_callback:
102 * Registers a display switch callback. The first parameter indicates
103 * the direction (SWITCH_IN or SWITCH_OUT). Returns zero on success,
104 * non-zero on failure (e.g. feature not supported).
105 */
set_display_switch_callback(int dir,void (* cb)(void))106 int set_display_switch_callback(int dir, void (*cb)(void))
107 {
108 int i;
109
110 if ((dir != SWITCH_IN) && (dir != SWITCH_OUT))
111 return -1;
112
113 if ((!system_driver) || (!system_driver->set_display_switch_mode))
114 return -1;
115
116 for (i=0; i<MAX_SWITCH_CALLBACKS; i++) {
117 if (dir == SWITCH_IN) {
118 if (!switch_in_cb[i]) {
119 switch_in_cb[i] = cb;
120 return 0;
121 }
122 }
123 else {
124 if (!switch_out_cb[i]) {
125 switch_out_cb[i] = cb;
126 return 0;
127 }
128 }
129 }
130
131 return -1;
132 }
133
134
135
136 /* remove_display_switch_callback:
137 * Unregisters a display switch callback.
138 */
remove_display_switch_callback(void (* cb)(void))139 void remove_display_switch_callback(void (*cb)(void))
140 {
141 int i;
142
143 for (i=0; i<MAX_SWITCH_CALLBACKS; i++) {
144 if (switch_in_cb[i] == cb)
145 switch_in_cb[i] = NULL;
146
147 if (switch_out_cb[i] == cb)
148 switch_out_cb[i] = NULL;
149 }
150 }
151
152
153
154 /* _switch_in:
155 * Handles a SWITCH_IN event.
156 */
_switch_in(void)157 void _switch_in(void)
158 {
159 int i;
160
161 for (i=0; i<MAX_SWITCH_CALLBACKS; i++) {
162 if (switch_in_cb[i])
163 switch_in_cb[i]();
164 }
165 }
166
167
168
169 /* _switch_out:
170 * Handles a SWITCH_OUT event.
171 */
_switch_out(void)172 void _switch_out(void)
173 {
174 int i;
175
176 for (i=0; i<MAX_SWITCH_CALLBACKS; i++) {
177 if (switch_out_cb[i])
178 switch_out_cb[i]();
179 }
180 }
181
182
183
184 /* find_switch_bitmap:
185 * Recursively searches the tree for a particular bitmap.
186 */
find_switch_bitmap(BITMAP_INFORMATION ** head,BITMAP * bmp,BITMAP_INFORMATION *** head_ret)187 static BITMAP_INFORMATION *find_switch_bitmap(BITMAP_INFORMATION **head, BITMAP *bmp, BITMAP_INFORMATION ***head_ret)
188 {
189 BITMAP_INFORMATION *info = *head, *kid;
190
191 while (info) {
192 if (info->bmp == bmp) {
193 *head_ret = head;
194 return info;
195 }
196
197 if (info->child) {
198 kid = find_switch_bitmap(&info->child, bmp, head_ret);
199 if (kid)
200 return kid;
201 }
202
203 head = &info->sibling;
204 info = *head;
205 }
206
207 return NULL;
208 }
209
210
211
212 /* _register_switch_bitmap:
213 * Lists this bitmap as an interesting thing to remember during console
214 * switches.
215 */
_register_switch_bitmap(BITMAP * bmp,BITMAP * parent)216 void _register_switch_bitmap(BITMAP *bmp, BITMAP *parent)
217 {
218 BITMAP_INFORMATION *info, *parent_info, **head;
219
220 if (system_driver->display_switch_lock)
221 system_driver->display_switch_lock(TRUE, FALSE);
222
223 if (parent) {
224 /* add a sub-bitmap */
225 parent_info = find_switch_bitmap(&info_list, parent, &head);
226 if (!parent_info)
227 goto getout;
228
229 info = _AL_MALLOC(sizeof(BITMAP_INFORMATION));
230 if (!info)
231 goto getout;
232
233 info->bmp = bmp;
234 info->other = NULL;
235 info->sibling = parent_info->child;
236 info->child = NULL;
237 info->acquire = NULL;
238 info->release = NULL;
239 info->blit_on_restore = FALSE;
240
241 parent_info->child = info;
242 }
243 else {
244 /* add a new top-level bitmap: must be in the foreground for this! */
245 ASSERT(_dispsw_status == SWITCH_NONE);
246
247 info = _AL_MALLOC(sizeof(BITMAP_INFORMATION));
248 if (!info)
249 goto getout;
250
251 info->bmp = bmp;
252 info->other = NULL;
253 info->sibling = info_list;
254 info->child = NULL;
255 info->acquire = NULL;
256 info->release = NULL;
257 info->blit_on_restore = FALSE;
258
259 info_list = info;
260 }
261
262 getout:
263
264 if (system_driver->display_switch_lock)
265 system_driver->display_switch_lock(FALSE, FALSE);
266 }
267
268
269
270 /* _unregister_switch_bitmap:
271 * Removes a bitmap from the list of things that need to be saved.
272 */
_unregister_switch_bitmap(BITMAP * bmp)273 void _unregister_switch_bitmap(BITMAP *bmp)
274 {
275 BITMAP_INFORMATION *info, **head;
276
277 if (system_driver->display_switch_lock)
278 system_driver->display_switch_lock(TRUE, FALSE);
279
280 info = find_switch_bitmap(&info_list, bmp, &head);
281 if (!info)
282 goto getout;
283
284 /* all the sub-bitmaps should be destroyed before we get to here */
285 ASSERT(!info->child);
286
287 /* it's not cool to destroy things that have important state */
288 ASSERT(!info->other);
289
290 *head = info->sibling;
291 _AL_FREE(info);
292
293 getout:
294
295 if (system_driver->display_switch_lock)
296 system_driver->display_switch_lock(FALSE, FALSE);
297 }
298
299
300
301 /* reconstruct_kids:
302 * Recursive helper to rebuild any sub-bitmaps to point at their new
303 * parents.
304 */
reconstruct_kids(BITMAP * parent,BITMAP_INFORMATION * info)305 static void reconstruct_kids(BITMAP *parent, BITMAP_INFORMATION *info)
306 {
307 int x, y, i;
308
309 while (info) {
310 info->bmp->vtable = parent->vtable;
311 info->bmp->write_bank = parent->write_bank;
312 info->bmp->read_bank = parent->read_bank;
313 info->bmp->seg = parent->seg;
314 info->bmp->id = parent->id | BMP_ID_SUB;
315
316 x = info->bmp->x_ofs - parent->x_ofs;
317 y = info->bmp->y_ofs - parent->y_ofs;
318
319 if (is_planar_bitmap(info->bmp))
320 x /= 4;
321
322 x *= BYTES_PER_PIXEL(bitmap_color_depth(info->bmp));
323
324 for (i=0; i<info->bmp->h; i++)
325 info->bmp->line[i] = parent->line[y+i] + x;
326
327 reconstruct_kids(info->bmp, info->child);
328 info = info->sibling;
329 }
330 }
331
332
333
334 /* fudge_bitmap:
335 * Makes b2 be similar to b1 (duplicate clip settings, ID, etc).
336 */
fudge_bitmap(BITMAP * b1,BITMAP * b2,int copy)337 static void fudge_bitmap(BITMAP *b1, BITMAP *b2, int copy)
338 {
339 int s, x1, y1, x2, y2;
340
341 set_clip_state(b2, FALSE);
342
343 if (copy)
344 blit(b1, b2, 0, 0, 0, 0, b1->w, b1->h);
345
346 get_clip_rect(b1, &x1, &y1, &x2, &y2);
347 s = get_clip_state(b1);
348
349 set_clip_rect(b2, x1, y1, x2, y2);
350 set_clip_state(b2, s);
351 }
352
353
354
355 /* swap_bitmap_contents:
356 * Now remember boys and girls, don't try this at home!
357 */
swap_bitmap_contents(BITMAP * b1,BITMAP * b2)358 static void swap_bitmap_contents(BITMAP *b1, BITMAP *b2)
359 {
360 int size = sizeof(BITMAP) + sizeof(char *) * b1->h;
361 unsigned char *s = (unsigned char *)b1;
362 unsigned char *d = (unsigned char *)b2;
363 unsigned char t;
364 int c;
365
366 for (c=0; c<size; c++) {
367 t = s[c];
368 s[c] = d[c];
369 d[c] = t;
370 }
371 }
372
373
374
375 /* save_bitmap_state:
376 * Remember everything important about a screen bitmap.
377 *
378 * Note: this must run even for SWITCH_BACKAMNESIA. With the fbcon driver,
379 * writes to the screen would still show up while we are switched away.
380 * So we let this function run to redirect the screen to a memory bitmap while
381 * we are switched away. We also let this run for SWITCH_AMNESIA just for
382 * consistency.
383 */
save_bitmap_state(BITMAP_INFORMATION * info,int switch_mode)384 static void save_bitmap_state(BITMAP_INFORMATION *info, int switch_mode)
385 {
386 int copy;
387
388 info->other = create_bitmap_ex(bitmap_color_depth(info->bmp), info->bmp->w, info->bmp->h);
389 if (!info->other)
390 return;
391
392 copy = (switch_mode != SWITCH_AMNESIA) && (switch_mode != SWITCH_BACKAMNESIA);
393 fudge_bitmap(info->bmp, info->other, copy);
394 info->blit_on_restore = copy;
395
396 info->acquire = info->other->vtable->acquire;
397 info->release = info->other->vtable->release;
398
399 info->other->vtable->acquire = info->bmp->vtable->acquire;
400 info->other->vtable->release = info->bmp->vtable->release;
401
402 #define INTERESTING_ID_BITS (BMP_ID_VIDEO | BMP_ID_SYSTEM | \
403 BMP_ID_SUB | BMP_ID_MASK)
404
405 info->other->id = (info->bmp->id & INTERESTING_ID_BITS) |
406 (info->other->id & ~INTERESTING_ID_BITS);
407
408 swap_bitmap_contents(info->bmp, info->other);
409 }
410
411
412
413 /* _save_switch_state:
414 * Saves the graphics state before a console switch.
415 */
_save_switch_state(int switch_mode)416 void _save_switch_state(int switch_mode)
417 {
418 BITMAP_INFORMATION *info = info_list;
419 int hadmouse;
420
421 if (!screen)
422 return;
423
424 if (_al_linker_mouse &&
425 is_same_bitmap(*(_al_linker_mouse->mouse_screen_ptr), screen)) {
426 _al_linker_mouse->show_mouse(NULL);
427 hadmouse = TRUE;
428 }
429 else
430 hadmouse = FALSE;
431
432 while (info) {
433 save_bitmap_state(info, switch_mode);
434 reconstruct_kids(info->bmp, info->child);
435 info = info->sibling;
436 }
437
438 _dispsw_status = switch_mode;
439
440 if (hadmouse)
441 _al_linker_mouse->show_mouse(screen);
442 }
443
444
445
446 /* restore_bitmap_state:
447 * The King's Men are quite good at doing this with bitmaps...
448 */
restore_bitmap_state(BITMAP_INFORMATION * info)449 static void restore_bitmap_state(BITMAP_INFORMATION *info)
450 {
451 if (info->other) {
452 swap_bitmap_contents(info->other, info->bmp);
453 info->other->vtable->acquire = info->acquire;
454 info->other->vtable->release = info->release;
455 info->other->id &= ~INTERESTING_ID_BITS;
456 fudge_bitmap(info->other, info->bmp, info->blit_on_restore);
457 destroy_bitmap(info->other);
458 info->other = NULL;
459 }
460 else
461 clear_bitmap(info->bmp);
462 }
463
464
465
466 /* _restore_switch_state:
467 * Restores the graphics state after a console switch.
468 */
_restore_switch_state(void)469 void _restore_switch_state(void)
470 {
471 BITMAP_INFORMATION *info = info_list;
472 int hadmouse, hadtimer;
473
474 if (!screen)
475 return;
476
477 if (_al_linker_mouse &&
478 is_same_bitmap(*(_al_linker_mouse->mouse_screen_ptr), screen)) {
479 _al_linker_mouse->show_mouse(NULL);
480 hadmouse = TRUE;
481 }
482 else
483 hadmouse = FALSE;
484
485 hadtimer = _timer_installed;
486 _timer_installed = FALSE;
487
488 while (info) {
489 restore_bitmap_state(info);
490 reconstruct_kids(info->bmp, info->child);
491 info = info->sibling;
492 }
493
494 _dispsw_status = SWITCH_NONE;
495
496 if (bitmap_color_depth(screen) == 8) {
497 if (_got_prev_current_palette)
498 gfx_driver->set_palette(_prev_current_palette, 0, 255, FALSE);
499 else
500 gfx_driver->set_palette(_current_palette, 0, 255, FALSE);
501 }
502
503 if (hadmouse)
504 _al_linker_mouse->show_mouse(screen);
505
506 _timer_installed = hadtimer;
507 }
508
509
510