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