1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Mouse input routines.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      Mark Wodrich added double-buffered drawing of the mouse pointer and
16  *      the set_mouse_sprite_focus() function.
17  *
18  *      See readme.txt for copyright information.
19  */
20 
21 
22 #include "allegro.h"
23 #include "allegro/internal/aintern.h"
24 
25 
26 
27 /* dummy driver for systems without a mouse */
nomouse_init(void)28 static int nomouse_init(void) { return 0; }
nomouse_exit(void)29 static void nomouse_exit(void) { }
30 
31 MOUSE_DRIVER mousedrv_none =
32 {
33    MOUSEDRV_NONE,
34    empty_string,
35    empty_string,
36    "No mouse",
37    nomouse_init,
38    nomouse_exit,
39    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
40 };
41 
42 
43 MOUSE_DRIVER *mouse_driver = NULL;     /* the active driver */
44 int _mouse_type = MOUSEDRV_AUTODETECT;  /* driver ID */
45 int _mouse_installed = FALSE;
46 
47 volatile int mouse_x = 0;              /* user-visible position */
48 volatile int mouse_y = 0;
49 volatile int mouse_z = 0;
50 volatile int mouse_w = 0;
51 volatile int mouse_b = 0;
52 volatile int mouse_pos = 0;
53 
54 int _mouse_x = 0;                      /* internal position */
55 int _mouse_y = 0;
56 int _mouse_z = 0;
57 int _mouse_w = 0;
58 int _mouse_b = 0;
59 int _mouse_on = TRUE;
60 
61 static int mon = TRUE;
62 
63 static int emulate_three = FALSE;
64 
65 volatile int freeze_mouse_flag = FALSE;
66 
67 void (*mouse_callback)(int flags) = NULL;
68 
69 int mouse_x_focus = 1;                 /* focus point in mouse sprite */
70 int mouse_y_focus = 1;
71 
72 
73 #define MOUSE_OFFSCREEN    -4096       /* somewhere to put unwanted cursors */
74 
75 
76 /* default mouse cursor sizes */
77 #define DEFAULT_SPRITE_W   16
78 #define DEFAULT_SPRITE_H   16
79 
80 /* Default cursor shapes */
81 /* TODO: add other shapes! */
82 static char mouse_arrow_data[DEFAULT_SPRITE_H * DEFAULT_SPRITE_W] =
83 {
84    2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85    2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86    2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87    2, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88    2, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89    2, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90    2, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0,
91    2, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0,
92    2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0,
93    2, 1, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,
94    2, 1, 1, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
95    2, 1, 2, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0,
96    0, 2, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0,
97    0, 0, 0, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0,
98    0, 0, 0, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0,
99    0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0
100 };
101 
102 static char mouse_busy_data[DEFAULT_SPRITE_H * DEFAULT_SPRITE_W] =
103 {
104    0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0,
105    0, 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 0, 0, 0,
106    0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0,
107    0, 0, 0, 2, 1, 1, 1, 0, 0, 1, 1, 1, 2, 0, 0, 0,
108    0, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0,
109    0, 2, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 2, 0,
110    0, 2, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 0,
111    2, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 2,
112    2, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 2,
113    0, 2, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 0,
114    0, 2, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 2, 0,
115    0, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0,
116    0, 0, 0, 2, 1, 1, 1, 0, 0, 1, 1, 1, 2, 0, 0, 0,
117    0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0,
118    0, 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 0, 0, 0,
119    0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0
120 };
121 
122 
123 BITMAP *_mouse_pointer = NULL;         /* default mouse pointer */
124 
125 BITMAP *mouse_sprite = NULL;	       /* current mouse pointer */
126 
127 BITMAP *_mouse_screen = NULL;          /* where to draw the pointer */
128 
129 static BITMAP *default_cursors[AL_NUM_MOUSE_CURSORS];
130 static BITMAP *cursors[AL_NUM_MOUSE_CURSORS];
131 
132 static int allow_system_cursor;        /* Allow native OS cursor? */
133 static int use_system_cursor = FALSE;  /* Use native OS cursor? */
134 static int got_hw_cursor = FALSE;      /* hardware pointer available? */
135 static int hw_cursor_dirty = FALSE;    /* need to set a new pointer? */
136 static int current_cursor = MOUSE_CURSOR_ALLEGRO;
137 
138 static int mx, my;                     /* previous mouse pointer position */
139 static BITMAP *ms = NULL;              /* previous screen data */
140 static BITMAP *mtemp = NULL;           /* double-buffer drawing area */
141 
142 #define SCARED_SIZE   16               /* for unscare_mouse() */
143 
144 static BITMAP *scared_screen[SCARED_SIZE];
145 static int scared_freeze[SCARED_SIZE];
146 static int scared_size = 0;
147 
148 static int mouse_polled = FALSE;       /* are we in polling mode? */
149 
150 static int mouse_semaphore = FALSE;    /* reentrant interrupt? */
151 
152 
153 
154 /* draw_mouse_doublebuffer:
155  *  Eliminates mouse-cursor flicker by using an off-screen buffer for
156  *  updating the cursor, and blitting only the final screen image.
157  *  newx and newy contain the new cursor position, and mx and my are
158  *  assumed to contain previous cursor pos. This routine is called if
159  *  mouse cursor is to be erased and redrawn, and the two position overlap.
160  */
draw_mouse_doublebuffer(int newx,int newy)161 static void draw_mouse_doublebuffer(int newx, int newy)
162 {
163    int x1, y1, w, h;
164 
165    /* grab bit of screen containing where we are and where we'll be */
166    x1 = MIN(mx, newx) - mouse_x_focus;
167    y1 = MIN(my, newy) - mouse_y_focus;
168 
169    /* get width of area */
170    w = MAX(mx, newx) - MIN(mx, newx) + mouse_sprite->w+1;
171    h = MAX(my, newy) - MIN(my, newy) + mouse_sprite->h+1;
172 
173    /* make new co-ords relative to 'mtemp' bitmap co-ords */
174    newx -= mouse_x_focus+x1;
175    newy -= mouse_y_focus+y1;
176 
177    /* save screen image in 'mtemp' */
178    blit(_mouse_screen, mtemp, x1, y1, 0, 0, w, h);
179 
180    /* blit saved image in 'ms' to corect place in this buffer */
181    blit(ms, mtemp, 0, 0, mx-mouse_x_focus-x1, my-mouse_y_focus-y1,
182 			 mouse_sprite->w, mouse_sprite->h);
183 
184    /* draw mouse at correct place in 'mtemp' */
185    blit(mtemp, ms, newx, newy, 0, 0, mouse_sprite->w, mouse_sprite->h);
186    draw_sprite(mtemp, mouse_sprite, newx, newy);
187 
188    /* blit 'mtemp' to screen */
189    blit(mtemp, _mouse_screen, 0, 0, x1, y1, w, h);
190 }
191 
192 END_OF_STATIC_FUNCTION(draw_mouse_doublebuffer);
193 
194 
195 
196 /* draw_mouse:
197  *  Mouse pointer drawing routine. If remove is set, deletes the old mouse
198  *  pointer. If add is set, draws a new one.
199  */
draw_mouse(int remove,int add)200 static void draw_mouse(int remove, int add)
201 {
202    int normal_draw = (remove ^ add);
203 
204    int newmx = _mouse_x;
205    int newmy = _mouse_y;
206 
207    int cf = _mouse_screen->clip;
208    int cl = _mouse_screen->cl;
209    int cr = _mouse_screen->cr;
210    int ct = _mouse_screen->ct;
211    int cb = _mouse_screen->cb;
212 
213    _mouse_screen->clip = TRUE;
214    _mouse_screen->cl = _mouse_screen->ct = 0;
215    _mouse_screen->cr = _mouse_screen->w;
216    _mouse_screen->cb = _mouse_screen->h;
217 
218    if (!_mouse_on) {
219       newmx = MOUSE_OFFSCREEN;
220       newmy = MOUSE_OFFSCREEN;
221       mon = FALSE;
222    }
223    else
224       mon = TRUE;
225 
226    if (!normal_draw) {
227       if ((newmx <= mx-mouse_sprite->w) || (newmx >= mx+mouse_sprite->w) ||
228 	  (newmy <= my-mouse_sprite->h) || (newmy >= my+mouse_sprite->h))
229 	 normal_draw = 1;
230    }
231 
232    if (normal_draw) {
233       if (remove)
234 	 blit(ms, _mouse_screen, 0, 0, mx-mouse_x_focus, my-mouse_y_focus, mouse_sprite->w, mouse_sprite->h);
235 
236       if (add) {
237 	 blit(_mouse_screen, ms, newmx-mouse_x_focus, newmy-mouse_y_focus, 0, 0, mouse_sprite->w, mouse_sprite->h);
238 	 draw_sprite(_mouse_screen, cursors[current_cursor], newmx-mouse_x_focus, newmy-mouse_y_focus);
239       }
240    }
241    else
242       draw_mouse_doublebuffer(newmx, newmy);
243 
244    mx = newmx;
245    my = newmy;
246 
247    _mouse_screen->clip = cf;
248    _mouse_screen->cl = cl;
249    _mouse_screen->cr = cr;
250    _mouse_screen->ct = ct;
251    _mouse_screen->cb = cb;
252 }
253 
254 END_OF_STATIC_FUNCTION(draw_mouse);
255 
256 
257 
258 /* update_mouse:
259  *  Worker function to update the mouse position variables with new values.
260  */
update_mouse(void)261 static void update_mouse(void)
262 {
263    int x, y, z, w, b, flags = 0;
264 
265    if (freeze_mouse_flag) {
266       x = mx;
267       y = my;
268    }
269    else {
270       x = _mouse_x;
271       y = _mouse_y;
272    }
273 
274    z = _mouse_z;
275    w = _mouse_w;
276    b = _mouse_b;
277 
278    if (emulate_three) {
279       if ((b & 3) == 3)
280 	 b = 4;
281    }
282 
283    if ((mouse_x != x) ||
284        (mouse_y != y) ||
285        (mouse_z != z) ||
286        (mouse_w != w) ||
287        (mouse_b != b)) {
288 
289       if (mouse_callback) {
290 	 if ((mouse_x != x) || (mouse_y != y))
291 	    flags |= MOUSE_FLAG_MOVE;
292 
293 	 if (mouse_z != z)
294 	    flags |= MOUSE_FLAG_MOVE_Z;
295 
296         if (mouse_w != w)
297 	    flags |= MOUSE_FLAG_MOVE_W;
298 
299 	 if ((b & 1) && !(mouse_b & 1))
300 	    flags |= MOUSE_FLAG_LEFT_DOWN;
301 	 else if (!(b & 1) && (mouse_b & 1))
302 	    flags |= MOUSE_FLAG_LEFT_UP;
303 
304 	 if ((b & 2) && !(mouse_b & 2))
305 	    flags |= MOUSE_FLAG_RIGHT_DOWN;
306 	 else if (!(b & 2) && (mouse_b & 2))
307 	    flags |= MOUSE_FLAG_RIGHT_UP;
308 
309 	 if ((b & 4) && !(mouse_b & 4))
310 	    flags |= MOUSE_FLAG_MIDDLE_DOWN;
311 	 else if (!(b & 4) && (mouse_b & 4))
312 	    flags |= MOUSE_FLAG_MIDDLE_UP;
313 
314 	 mouse_x = x;
315 	 mouse_y = y;
316 	 mouse_z = z;
317          mouse_w = w;
318 	 mouse_b = b;
319 	 mouse_pos = ((x & 0xFFFF) << 16) | (y & 0xFFFF);
320 
321 	 mouse_callback(flags);
322       }
323       else {
324 	 mouse_x = x;
325 	 mouse_y = y;
326 	 mouse_z = z;
327          mouse_w = w;
328 	 mouse_b = b;
329 	 mouse_pos = ((x & 0xFFFF) << 16) | (y & 0xFFFF);
330       }
331    }
332 }
333 
334 END_OF_STATIC_FUNCTION(update_mouse);
335 
336 
337 
338 /* mouse_move:
339  *  Timer interrupt handler for redrawing the mouse pointer.
340  */
mouse_move(void)341 static void mouse_move(void)
342 {
343    if (mouse_semaphore)
344       return;
345 
346    mouse_semaphore = TRUE;
347 
348    /* periodic poll */
349    if (mouse_driver->timer_poll) {
350       mouse_driver->timer_poll();
351       if (!mouse_polled)
352 	 update_mouse();
353    }
354 
355    /* redraw pointer */
356    if ((!freeze_mouse_flag) && (_mouse_screen) && ((mx != _mouse_x) || (my != _mouse_y) || (mon != _mouse_on))) {
357       acquire_bitmap(_mouse_screen);
358 
359       if (gfx_capabilities & GFX_HW_CURSOR) {
360 	 if (_mouse_on) {
361 	    gfx_driver->move_mouse(mx=_mouse_x, my=_mouse_y);
362 	    mon = TRUE;
363 	 }
364 	 else {
365 	    gfx_driver->move_mouse(mx=MOUSE_OFFSCREEN, my=MOUSE_OFFSCREEN);
366 	    mon = FALSE;
367 	 }
368       }
369       else {
370 #ifdef ALLEGRO_DOS
371 	 /* bodge to avoid using non legacy 386 asm code inside a timer handler */
372 	 int old_capabilities = cpu_capabilities;
373 	 cpu_capabilities = 0;
374 #endif
375 	 draw_mouse(TRUE, TRUE);
376 #ifdef ALLEGRO_DOS
377 	 cpu_capabilities = old_capabilities;
378 #endif
379       }
380 
381       release_bitmap(_mouse_screen);
382    }
383 
384    mouse_semaphore = FALSE;
385 }
386 
387 END_OF_STATIC_FUNCTION(mouse_move);
388 
389 
390 
391 /* _handle_mouse_input:
392  *  Callback for an asynchronous driver to tell us when it has changed the
393  *  position.
394  */
_handle_mouse_input(void)395 void _handle_mouse_input(void)
396 {
397    if (!mouse_polled)
398       update_mouse();
399 }
400 
401 END_OF_FUNCTION(_handle_mouse_input);
402 
403 
404 
405 /* create_mouse_pointer:
406  *  Creates the default arrow mouse sprite using the current color depth
407  *  and palette.
408  */
create_mouse_pointer(char * data)409 static BITMAP *create_mouse_pointer(char *data)
410 {
411    BITMAP *bmp;
412    int x, y;
413    int col;
414 
415    bmp = create_bitmap(DEFAULT_SPRITE_W, DEFAULT_SPRITE_H);
416 
417    for (y=0; y<DEFAULT_SPRITE_H; y++) {
418       for (x=0; x<DEFAULT_SPRITE_W; x++) {
419 	 switch (data[x+y*DEFAULT_SPRITE_W]) {
420 	    case 1:  col = makecol(255, 255, 255);  break;
421 	    case 2:  col = makecol(0, 0, 0);        break;
422 	    default: col = bmp->vtable->mask_color; break;
423 	 }
424 	 putpixel(bmp, x, y, col);
425       }
426    }
427 
428    return bmp;
429 }
430 
431 
432 
433 /* set_mouse_sprite:
434  *  Sets the sprite to be used for the mouse pointer. If the sprite is
435  *  NULL, restores the default arrow.
436  */
set_mouse_sprite(struct BITMAP * sprite)437 void set_mouse_sprite(struct BITMAP *sprite)
438 {
439    BITMAP *old_mouse_screen = _mouse_screen;
440    int am_using_sys_cursor = use_system_cursor;
441 
442    if (!mouse_driver)
443       return;
444 
445    if (_mouse_screen && !am_using_sys_cursor)
446       show_mouse(NULL);
447 
448    if (sprite)
449       mouse_sprite = sprite;
450    else {
451       if (_mouse_pointer)
452 	 destroy_bitmap(_mouse_pointer);
453       _mouse_pointer = create_mouse_pointer(mouse_arrow_data);
454       mouse_sprite = _mouse_pointer;
455    }
456 
457    cursors[MOUSE_CURSOR_ALLEGRO] = mouse_sprite;
458 
459    lock_bitmap((struct BITMAP*)mouse_sprite);
460 
461    /* make sure the ms bitmap is big enough */
462    if ((!ms) || (ms->w < mouse_sprite->w) || (ms->h < mouse_sprite->h) ||
463        (bitmap_color_depth(mouse_sprite) != bitmap_color_depth(ms))) {
464       if (ms) {
465 	 destroy_bitmap(ms);
466 	 destroy_bitmap(mtemp);
467       }
468 
469       ms = create_bitmap(mouse_sprite->w, mouse_sprite->h);
470       lock_bitmap(ms);
471 
472       mtemp = create_bitmap(mouse_sprite->w*2, mouse_sprite->h*2);
473       lock_bitmap(mtemp);
474    }
475 
476    mouse_x_focus = 1;
477    mouse_y_focus = 1;
478 
479    if (!am_using_sys_cursor)
480       hw_cursor_dirty = TRUE;
481 
482    if (old_mouse_screen && !am_using_sys_cursor)
483       show_mouse(old_mouse_screen);
484 }
485 
486 
487 
488 /* select_mouse_cursor:
489  *  Selects the shape of the mouse cursor.
490  */
select_mouse_cursor(int cursor)491 void select_mouse_cursor(int cursor)
492 {
493    ASSERT(cursor >= 0);
494    ASSERT(cursor < AL_NUM_MOUSE_CURSORS);
495 
496    current_cursor = cursor;
497 }
498 
499 
500 
501 /* set_mouse_cursor_bitmap:
502  *  Changes the default Allegro cursor for a mouse cursor
503  */
set_mouse_cursor_bitmap(int cursor,struct BITMAP * bmp)504 void set_mouse_cursor_bitmap(int cursor, struct BITMAP *bmp)
505 {
506    ASSERT(cursor >= 0);
507    ASSERT(cursor != MOUSE_CURSOR_NONE);
508    ASSERT(cursor < AL_NUM_MOUSE_CURSORS);
509 
510    cursors[cursor] = bmp?bmp:default_cursors[cursor];
511 }
512 
513 
514 
515 /* set_mouse_sprite_focus:
516  *  Sets co-ordinate (x, y) in the sprite to be the mouse location.
517  *  Call after set_mouse_sprite(). Doesn't redraw the sprite.
518  */
set_mouse_sprite_focus(int x,int y)519 void set_mouse_sprite_focus(int x, int y)
520 {
521    if (!mouse_driver)
522       return;
523 
524    mouse_x_focus = x;
525    mouse_y_focus = y;
526 
527    hw_cursor_dirty = TRUE;
528 }
529 
530 
531 
532 /* show_os_cursor:
533  *  Tries to display the OS cursor. Returns 0 if a cursor is displayed after the
534  *  function returns, else -1. This is similar to calling show_mouse(screen)
535  *  after calling enable_hardware_cursor and checking gfx_capabilities for
536  *  GFX_HW_CURSOR, but is easier to use in cases where you don't need Allegro's
537  *  software cursor even if no os cursor is available.
538  */
show_os_cursor(int cursor)539 int show_os_cursor(int cursor)
540 {
541    int r = -1;
542    if (!mouse_driver)
543       return r;
544 
545    remove_int(mouse_move);
546 
547    gfx_capabilities &= ~(GFX_HW_CURSOR|GFX_SYSTEM_CURSOR);
548    if (cursor != MOUSE_CURSOR_NONE) {
549 
550       if (mouse_driver->enable_hardware_cursor) {
551          mouse_driver->enable_hardware_cursor(TRUE);
552       }
553 
554       /* default system cursor? */
555       if (cursor != MOUSE_CURSOR_ALLEGRO) {
556          if (mouse_driver->select_system_cursor) {
557             if (mouse_driver->select_system_cursor(cursor) != 0) {
558                gfx_capabilities |= (GFX_HW_CURSOR|GFX_SYSTEM_CURSOR);
559                r = 0;
560                goto done;
561             }
562          }
563          goto done;
564       }
565       else {
566          /* set custom hardware cursor */
567          if (gfx_driver) {
568             if (gfx_driver->set_mouse_sprite) {
569                if (gfx_driver->set_mouse_sprite(mouse_sprite, mouse_x_focus, mouse_y_focus))
570                   goto done;
571             }
572             if (gfx_driver->show_mouse) {
573                if (gfx_driver->show_mouse(screen, mouse_x, mouse_y))
574                   goto done;
575             }
576             gfx_capabilities |= GFX_HW_CURSOR;
577             r = 0;
578             goto done;
579          }
580       }
581    }
582    else {
583       if (gfx_driver && gfx_driver->hide_mouse)
584          gfx_driver->hide_mouse();
585    }
586 
587 done:
588    if (mouse_driver->timer_poll)
589       install_int(mouse_move, 10);
590    return r;
591 }
592 
593 
594 
595 /* show_mouse:
596  *  Tells Allegro to display a mouse pointer. This only works when the timer
597  *  module is active. The mouse pointer will be drawn onto the bitmap bmp,
598  *  which should normally be the hardware screen. To turn off the mouse
599  *  pointer, which you must do before you draw anything onto the screen, call
600  *  show_mouse(NULL). If you forget to turn off the mouse pointer when
601  *  drawing something, the SVGA bank switching code will become confused and
602  *  will produce garbage all over the screen.
603  */
show_mouse(BITMAP * bmp)604 void show_mouse(BITMAP *bmp)
605 {
606    if (!mouse_driver)
607       return;
608 
609    remove_int(mouse_move);
610 
611    /* Remove the mouse cursor */
612    if (_mouse_screen) {
613       acquire_bitmap(_mouse_screen);
614 
615       if (gfx_capabilities & GFX_HW_CURSOR) {
616 	 gfx_driver->hide_mouse();
617 	 gfx_capabilities &= ~(GFX_HW_CURSOR|GFX_SYSTEM_CURSOR);
618  	 hw_cursor_dirty = TRUE;
619       }
620       else
621 	 draw_mouse(TRUE, FALSE);
622 
623       release_bitmap(_mouse_screen);
624    }
625 
626    _mouse_screen = bmp;
627 
628    if (bmp && (current_cursor != MOUSE_CURSOR_NONE)) {
629       acquire_bitmap(_mouse_screen);
630 
631       /* Default system cursor? */
632       if ((current_cursor != MOUSE_CURSOR_ALLEGRO) && allow_system_cursor) {
633          if (mouse_driver && mouse_driver->select_system_cursor) {
634             use_system_cursor = mouse_driver->select_system_cursor(current_cursor);
635             if (use_system_cursor) {
636                gfx_capabilities |= GFX_HW_CURSOR|GFX_SYSTEM_CURSOR;
637                hw_cursor_dirty = FALSE;
638                got_hw_cursor = TRUE;
639             }
640          }
641       }
642       else {
643          use_system_cursor = FALSE;
644       }
645 
646       /* Custom hardware cursor? */
647       if (hw_cursor_dirty) {
648 	 got_hw_cursor = FALSE;
649 
650 	 if ((gfx_driver) && (gfx_driver->set_mouse_sprite) && (!_dispsw_status))
651 	    if (gfx_driver->set_mouse_sprite(mouse_sprite, mouse_x_focus, mouse_y_focus) == 0)
652 	       got_hw_cursor = TRUE;
653 
654 	 hw_cursor_dirty = FALSE;
655       }
656 
657       /* Try to display hardware (custom or system) cursor */
658       if ((got_hw_cursor) && (is_same_bitmap(bmp, screen)))
659 	 if (gfx_driver->show_mouse(bmp, mx=mouse_x, my=mouse_y) == 0)
660 	    gfx_capabilities |= GFX_HW_CURSOR;
661 
662       /* Draw cursor manually if we can't do that */
663       if (!(gfx_capabilities & GFX_HW_CURSOR)) {
664 	 draw_mouse(FALSE, TRUE);
665          use_system_cursor = FALSE;
666       }
667 
668       release_bitmap(_mouse_screen);
669 
670       install_int(mouse_move, 10);
671    }
672    else {
673       if (mouse_driver->timer_poll)
674 	 install_int(mouse_move, 10);
675    }
676 }
677 
678 
679 
680 /* scare_mouse:
681  *  Removes the mouse pointer prior to a drawing operation, if that is
682  *  required (ie. noop if the mouse is on a memory bitmap, or a hardware
683  *  cursor is in use). This operation can later be reversed by calling
684  *  unscare_mouse().
685  */
scare_mouse(void)686 void scare_mouse(void)
687 {
688    if (!mouse_driver)
689       return;
690 
691    if ((is_same_bitmap(screen, _mouse_screen)) && (!(gfx_capabilities & GFX_HW_CURSOR))) {
692       if (scared_size < SCARED_SIZE) {
693 	 scared_screen[scared_size] = _mouse_screen;
694 	 scared_freeze[scared_size] = FALSE;
695       }
696       show_mouse(NULL);
697    }
698    else {
699       if (scared_size < SCARED_SIZE) {
700 	 scared_screen[scared_size] = NULL;
701 	 scared_freeze[scared_size] = FALSE;
702       }
703    }
704 
705    scared_size++;
706 }
707 
708 
709 
710 /* scare_mouse_area:
711  *  Removes the mouse pointer prior to a drawing operation, if that is
712  *  required (ie. noop if the mouse is on a memory bitmap, or a hardware
713  *  cursor is in use, or the mouse lies outside of the specified bounds
714  *  (in this last case, the mouse is frozen)). This operation can later
715  *  be reversed by calling unscare_mouse().
716  */
scare_mouse_area(int x,int y,int w,int h)717 void scare_mouse_area(int x, int y, int w, int h)
718 {
719    int was_frozen;
720 
721    if (!mouse_driver)
722       return;
723 
724    if ((is_same_bitmap(screen, _mouse_screen)) && (!(gfx_capabilities & GFX_HW_CURSOR))) {
725       was_frozen = freeze_mouse_flag;
726       freeze_mouse_flag = TRUE;
727 
728       if ((mx - mouse_x_focus < x + w) &&
729 	   (my - mouse_y_focus < y + h) &&
730 	   (mx - mouse_x_focus + mouse_sprite->w >= x) &&
731 	   (my - mouse_y_focus + mouse_sprite->h >= y)) {
732 
733 	 if (scared_size < SCARED_SIZE) {
734 	    scared_screen[scared_size] = _mouse_screen;
735 	    scared_freeze[scared_size] = FALSE;
736 	 }
737 
738 	 freeze_mouse_flag = was_frozen;
739 	 show_mouse(NULL);
740       }
741       else {
742 	 if (scared_size < SCARED_SIZE) {
743 	    scared_screen[scared_size] = NULL;
744 
745 	    if (was_frozen) {
746 	       scared_freeze[scared_size] = FALSE;
747 	       freeze_mouse_flag = was_frozen;
748 	    }
749 	    else
750 	       scared_freeze[scared_size] = TRUE;
751 	 }
752       }
753    }
754    else {
755       if (scared_size < SCARED_SIZE) {
756 	 scared_screen[scared_size] = NULL;
757 	 scared_freeze[scared_size] = FALSE;
758       }
759    }
760 
761    scared_size++;
762 }
763 
764 
765 
766 /* unscare_mouse:
767  *  Restores the original mouse state, after a call to scare_mouse() or
768  *  scare_mouse_area.
769  */
unscare_mouse(void)770 void unscare_mouse(void)
771 {
772    if (!mouse_driver)
773       return;
774 
775    if (scared_size > 0)
776       scared_size--;
777 
778    if (scared_size < SCARED_SIZE) {
779       if (scared_screen[scared_size])
780 	 show_mouse(scared_screen[scared_size]);
781 
782       if (scared_freeze[scared_size])
783 	 freeze_mouse_flag = FALSE;
784 
785       scared_screen[scared_size] = NULL;
786       scared_freeze[scared_size] = FALSE;
787    }
788 }
789 
790 
791 
792 /* position_mouse:
793  *  Moves the mouse to screen position x, y. This is safe to call even
794  *  when a mouse pointer is being displayed.
795  */
position_mouse(int x,int y)796 void position_mouse(int x, int y)
797 {
798    BITMAP *old_mouse_screen = _mouse_screen;
799 
800    if (!mouse_driver)
801       return;
802 
803    if (_mouse_screen)
804       show_mouse(NULL);
805 
806    if (mouse_driver->position) {
807       mouse_driver->position(x, y);
808    }
809    else {
810       _mouse_x = x;
811       _mouse_y = y;
812    }
813 
814    update_mouse();
815 
816    if (old_mouse_screen)
817       show_mouse(old_mouse_screen);
818 }
819 
820 
821 
822 /* position_mouse_z:
823  *  Sets the mouse third axis to position z.
824  */
position_mouse_z(int z)825 void position_mouse_z(int z)
826 {
827    if (!mouse_driver)
828       return;
829 
830    _mouse_z = z;
831    update_mouse();
832 }
833 
834 
835 
836 /* position_mouse_w:
837  *  Sets the mouse fourth axis to position w.
838  */
position_mouse_w(int w)839 void position_mouse_w(int w)
840 {
841    if (!mouse_driver)
842       return;
843 
844    _mouse_w = w;
845    update_mouse();
846 }
847 
848 
849 
850 /* set_mouse_range:
851  *  Sets the screen area within which the mouse can move. Pass the top left
852  *  corner and the bottom right corner (inclusive). If you don't call this
853  *  function the range defaults to (0, 0, SCREEN_W-1, SCREEN_H-1).
854  */
set_mouse_range(int x1,int y1,int x2,int y2)855 void set_mouse_range(int x1, int y1, int x2, int y2)
856 {
857    BITMAP *old_mouse_screen = _mouse_screen;
858    ASSERT(x1 >= 0);
859    ASSERT(y1 >= 0);
860    ASSERT(x2 >= x1);
861    ASSERT(y2 >= y2);
862 
863    if (!mouse_driver)
864       return;
865 
866    if (_mouse_screen)
867       show_mouse(NULL);
868 
869    if (mouse_driver->set_range)
870       mouse_driver->set_range(x1, y1, x2, y2);
871 
872    update_mouse();
873 
874    if (old_mouse_screen)
875       show_mouse(old_mouse_screen);
876 }
877 
878 
879 
880 /* set_mouse_speed:
881  *  Sets the mouse speed. Larger values of xspeed and yspeed represent
882  *  slower mouse movement: the default for both is 2.
883  */
set_mouse_speed(int xspeed,int yspeed)884 void set_mouse_speed(int xspeed, int yspeed)
885 {
886    if ((mouse_driver) && (mouse_driver->set_speed))
887       mouse_driver->set_speed(xspeed, yspeed);
888 }
889 
890 
891 
892 /* get_mouse_mickeys:
893  *  Measures the mickey count (how far the mouse has moved since the last
894  *  call to this function).
895  */
get_mouse_mickeys(int * mickeyx,int * mickeyy)896 void get_mouse_mickeys(int *mickeyx, int *mickeyy)
897 {
898    if ((mouse_driver) && (mouse_driver->get_mickeys)) {
899       mouse_driver->get_mickeys(mickeyx, mickeyy);
900    }
901    else {
902       *mickeyx = 0;
903       *mickeyy = 0;
904    }
905 }
906 
907 
908 
909 /* mouse_on_screen:
910  *  Tells whether the mouse pointer position is currently on the screen.
911  *  Returns 0 when offscreen, and non-zero otherwise.
912  */
mouse_on_screen()913 int mouse_on_screen()
914 {
915    return _mouse_on;
916 }
917 
918 
919 
920 /* enable_hardware_cursor:
921  *  enabels the hardware cursor on platforms where this needs to be done
922  *  explicitly and allows system cursors to be used.
923  */
enable_hardware_cursor(void)924 void enable_hardware_cursor(void)
925 {
926    if ((mouse_driver) && (mouse_driver->enable_hardware_cursor)) {
927       mouse_driver->enable_hardware_cursor(TRUE);
928       allow_system_cursor = TRUE;
929       if (is_same_bitmap(_mouse_screen, screen)) {
930          BITMAP *bmp = _mouse_screen;
931          show_mouse(NULL);
932          show_mouse(bmp);
933       }
934    }
935 }
936 
937 
938 
939 /* disable_hardware_cursor:
940  *  disables the hardware cursor on platforms where this interferes with
941  *  mickeys and disables system cursors.
942  */
disable_hardware_cursor(void)943 void disable_hardware_cursor(void)
944 {
945    if ((mouse_driver) && (mouse_driver->enable_hardware_cursor)) {
946       mouse_driver->enable_hardware_cursor(FALSE);
947       allow_system_cursor = FALSE;
948       if (is_same_bitmap(_mouse_screen, screen)) {
949          BITMAP *bmp = _mouse_screen;
950          show_mouse(NULL);
951          show_mouse(bmp);
952       }
953    }
954 }
955 
956 
957 
958 /* poll_mouse:
959  *  Polls the current mouse state, and updates the user-visible information
960  *  accordingly. On some drivers this is actually required to get the
961  *  input, while on others it is only present to keep compatibility with
962  *  systems that do need it. So that people can test their polling code
963  *  even on platforms that don't strictly require it, after this function
964  *  has been called once, the entire system will switch into polling mode
965  *  and will no longer operate asynchronously even if the driver actually
966  *  does support that.
967  */
poll_mouse(void)968 int poll_mouse(void)
969 {
970    if (!mouse_driver)
971       return -1;
972 
973    if (mouse_driver->poll)
974       mouse_driver->poll();
975 
976    update_mouse();
977 
978    mouse_polled = TRUE;
979 
980    return 0;
981 }
982 
983 END_OF_FUNCTION(poll_mouse);
984 
985 
986 
987 /* mouse_needs_poll:
988  *  Checks whether the current driver uses polling.
989  */
mouse_needs_poll(void)990 int mouse_needs_poll(void)
991 {
992    return mouse_polled;
993 }
994 
995 END_OF_FUNCTION(mouse_needs_poll);
996 
997 
998 
999 /* set_mouse_etc:
1000  *  Hook for setting up the motion range, cursor graphic, etc, called by
1001  *  the mouse init and whenever we change the graphics mode.
1002  */
set_mouse_etc(void)1003 static void set_mouse_etc(void)
1004 {
1005    if ((!mouse_driver) || (!gfx_driver))
1006       return;
1007 
1008    if ((!_mouse_pointer) ||
1009        ((screen) && (_mouse_pointer) &&
1010 	(bitmap_color_depth(_mouse_pointer) != bitmap_color_depth(screen))))
1011       set_mouse_sprite(NULL);
1012    else
1013       hw_cursor_dirty = TRUE;
1014 
1015    set_mouse_range(0, 0, SCREEN_W-1, SCREEN_H-1);
1016    set_mouse_speed(2, 2);
1017    position_mouse(SCREEN_W/2, SCREEN_H/2);
1018 }
1019 
1020 
1021 
1022 /* install_mouse:
1023  *  Installs the Allegro mouse handler. You must do this before using any
1024  *  other mouse functions. Return -1 if it can't find a mouse driver,
1025  *  otherwise the number of buttons on the mouse.
1026  */
install_mouse(void)1027 int install_mouse(void)
1028 {
1029    _DRIVER_INFO *driver_list;
1030    int num_buttons = -1;
1031    int config_num_buttons;
1032    AL_CONST char *emulate;
1033    char tmp1[64], tmp2[64];
1034    int i;
1035 
1036    if (mouse_driver)
1037       return 0;
1038 
1039    LOCK_VARIABLE(mouse_driver);
1040    LOCK_VARIABLE(mousedrv_none);
1041    LOCK_VARIABLE(mouse_x);
1042    LOCK_VARIABLE(mouse_y);
1043    LOCK_VARIABLE(mouse_z);
1044    LOCK_VARIABLE(mouse_w);
1045    LOCK_VARIABLE(mouse_b);
1046    LOCK_VARIABLE(mouse_pos);
1047    LOCK_VARIABLE(_mouse_x);
1048    LOCK_VARIABLE(_mouse_y);
1049    LOCK_VARIABLE(_mouse_z);
1050    LOCK_VARIABLE(_mouse_w);
1051    LOCK_VARIABLE(_mouse_b);
1052    LOCK_VARIABLE(_mouse_on);
1053    LOCK_VARIABLE(mon);
1054    LOCK_VARIABLE(emulate_three);
1055    LOCK_VARIABLE(freeze_mouse_flag);
1056    LOCK_VARIABLE(mouse_callback);
1057    LOCK_VARIABLE(mouse_x_focus);
1058    LOCK_VARIABLE(mouse_y_focus);
1059    LOCK_VARIABLE(mouse_sprite);
1060    LOCK_VARIABLE(_mouse_pointer);
1061    LOCK_VARIABLE(_mouse_screen);
1062    LOCK_VARIABLE(mx);
1063    LOCK_VARIABLE(my);
1064    LOCK_VARIABLE(ms);
1065    LOCK_VARIABLE(mtemp);
1066    LOCK_VARIABLE(mouse_polled);
1067    LOCK_VARIABLE(mouse_semaphore);
1068    LOCK_VARIABLE(cursors);
1069    LOCK_FUNCTION(draw_mouse_doublebuffer);
1070    LOCK_FUNCTION(draw_mouse);
1071    LOCK_FUNCTION(update_mouse);
1072    LOCK_FUNCTION(mouse_move);
1073    LOCK_FUNCTION(poll_mouse);
1074    LOCK_FUNCTION(mouse_needs_poll);
1075    LOCK_FUNCTION(_handle_mouse_input);
1076 
1077    /* Construct mouse pointers */
1078    if (!default_cursors[MOUSE_CURSOR_ARROW])
1079       default_cursors[MOUSE_CURSOR_ARROW] = create_mouse_pointer(mouse_arrow_data);
1080    if (!default_cursors[MOUSE_CURSOR_BUSY])
1081       default_cursors[MOUSE_CURSOR_BUSY] = create_mouse_pointer(mouse_busy_data);
1082    if (!default_cursors[MOUSE_CURSOR_QUESTION])
1083       default_cursors[MOUSE_CURSOR_QUESTION] = create_mouse_pointer(mouse_arrow_data);
1084    if (!default_cursors[MOUSE_CURSOR_EDIT])
1085       default_cursors[MOUSE_CURSOR_EDIT] = create_mouse_pointer(mouse_arrow_data);
1086 
1087    cursors[MOUSE_CURSOR_ARROW] = default_cursors[MOUSE_CURSOR_ARROW];
1088    cursors[MOUSE_CURSOR_BUSY] = default_cursors[MOUSE_CURSOR_BUSY];
1089    cursors[MOUSE_CURSOR_QUESTION] = default_cursors[MOUSE_CURSOR_QUESTION];
1090    cursors[MOUSE_CURSOR_EDIT] = default_cursors[MOUSE_CURSOR_EDIT];
1091 
1092    if (system_driver->mouse_drivers)
1093       driver_list = system_driver->mouse_drivers();
1094    else
1095       driver_list = _mouse_driver_list;
1096 
1097    if (_mouse_type == MOUSEDRV_AUTODETECT)
1098       _mouse_type = get_config_id(uconvert_ascii("mouse", tmp1), uconvert_ascii("mouse", tmp2), MOUSEDRV_AUTODETECT);
1099 
1100    if (_mouse_type != MOUSEDRV_AUTODETECT) {
1101       for (i=0; driver_list[i].driver; i++) {
1102 	 if (driver_list[i].id == _mouse_type) {
1103 	    mouse_driver = driver_list[i].driver;
1104 	    break;
1105 	 }
1106       }
1107    }
1108 
1109    if (mouse_driver) {
1110       mouse_driver->name = mouse_driver->desc = get_config_text(mouse_driver->ascii_name);
1111       num_buttons = mouse_driver->init();
1112    }
1113    else {
1114       for (i=0; num_buttons<0; i++) {
1115 	 if (!driver_list[i].driver)
1116 	    break;
1117 
1118 	 mouse_driver = driver_list[i].driver;
1119 	 mouse_driver->name = mouse_driver->desc = get_config_text(mouse_driver->ascii_name);
1120 	 num_buttons = mouse_driver->init();
1121       }
1122    }
1123 
1124    if (num_buttons < 0) {
1125       mouse_driver = NULL;
1126       return -1;
1127    }
1128 
1129    config_num_buttons = get_config_int(uconvert_ascii("mouse", tmp1), uconvert_ascii("num_buttons", tmp2), -1);
1130    emulate = get_config_string(uconvert_ascii("mouse", tmp1), uconvert_ascii("emulate_three", tmp2), NULL);
1131 
1132    /* clamp config_num_buttons to zero/positive values */
1133    if (config_num_buttons >= 0)
1134       num_buttons = config_num_buttons;
1135 
1136    if ((emulate) && ((i = ugetc(emulate)) != 0)) {
1137       if ((i == 'y') || (i == 'Y') || (i == '1'))
1138 	 emulate_three = TRUE;
1139       else
1140 	 emulate_three = FALSE;
1141    }
1142    else {
1143       emulate_three = FALSE;
1144    }
1145 
1146    mouse_polled = (mouse_driver->poll) ? TRUE : FALSE;
1147 
1148    _mouse_installed = TRUE;
1149 
1150    disable_hardware_cursor();
1151 
1152    set_mouse_etc();
1153    _add_exit_func(remove_mouse, "remove_mouse");
1154 
1155    if (mouse_driver->timer_poll)
1156       install_int(mouse_move, 10);
1157 
1158    return num_buttons;
1159 }
1160 
1161 
1162 
1163 /* remove_mouse:
1164  *  Removes the mouse handler. You don't normally need to call this, because
1165  *  allegro_exit() will do it for you.
1166  */
remove_mouse(void)1167 void remove_mouse(void)
1168 {
1169    if (!mouse_driver)
1170       return;
1171 
1172    show_mouse(NULL);
1173    remove_int(mouse_move);
1174 
1175    mouse_driver->exit();
1176    mouse_driver = NULL;
1177 
1178    _mouse_installed = FALSE;
1179 
1180    mouse_x = mouse_y = _mouse_x = _mouse_y = 0;
1181    mouse_z = _mouse_z = 0;
1182    mouse_w = _mouse_w = 0;
1183    mouse_b = _mouse_b = 0;
1184    mouse_pos = 0;
1185 
1186    mouse_polled = FALSE;
1187 
1188    destroy_bitmap(default_cursors[MOUSE_CURSOR_ARROW]);
1189    destroy_bitmap(default_cursors[MOUSE_CURSOR_BUSY]);
1190    destroy_bitmap(default_cursors[MOUSE_CURSOR_QUESTION]);
1191    destroy_bitmap(default_cursors[MOUSE_CURSOR_EDIT]);
1192 
1193    cursors[MOUSE_CURSOR_ARROW] = default_cursors[MOUSE_CURSOR_ARROW] = NULL;
1194    cursors[MOUSE_CURSOR_BUSY] = default_cursors[MOUSE_CURSOR_BUSY] = NULL;
1195    cursors[MOUSE_CURSOR_QUESTION] = default_cursors[MOUSE_CURSOR_QUESTION] = NULL;
1196    cursors[MOUSE_CURSOR_EDIT] = default_cursors[MOUSE_CURSOR_EDIT] = NULL;
1197 
1198    if (_mouse_pointer) {
1199       destroy_bitmap(_mouse_pointer);
1200       _mouse_pointer = NULL;
1201    }
1202 
1203    if (ms) {
1204       destroy_bitmap(ms);
1205       ms = NULL;
1206 
1207       destroy_bitmap(mtemp);
1208       mtemp = NULL;
1209    }
1210 
1211    _remove_exit_func(remove_mouse);
1212 }
1213 
1214 
1215 
1216 /* _mouse_constructor:
1217  *  Register mouse functions if this object file is linked in.
1218  */
1219 #ifdef ALLEGRO_USE_CONSTRUCTOR
1220    CONSTRUCTOR_FUNCTION(void _mouse_constructor(void));
1221 #endif
1222 
1223 static struct _AL_LINKER_MOUSE mouse_linker = {
1224    set_mouse_etc,
1225    show_mouse,
1226    &_mouse_screen
1227 };
1228 
_mouse_constructor(void)1229 void _mouse_constructor(void)
1230 {
1231    _al_linker_mouse = &mouse_linker;
1232 }
1233