Determining if mouse is inside the Allegro window (for v4.20)
Dennis

I'm in a rush and very tired as I'm writing this, please excuse typos.:P

I've been messing around with finding a way to determine if the mouse is inside the window, using only functions provided by Allegro 4.20 to keep it system-independent.

Using windows api functions this test would be a five liner as AJ told me,
however, I clubbed together a small test application to demonstrate the purely Allegro dependant method, which uses the mouse mickeys reported by allegro to keep track of an absolute mouse position that can get negative or larger than the screen dimensions, thus providing the base for checking if the mouse is inside or outside the window.

Problems follow after the codebox...

mouseintest.cpp

#SelectExpand
1#include <allegro.h> 2 3// Runlevel IDs 4enum runlevels 5{ 6 RL_NOTHING_OK=0, 7 RL_ALLEGRO_OK, 8 RL_KEYBOARD_OK, 9 RL_MOUSE_OK, 10 RL_GFXMODE_OK, 11 RL_BACKBUFFER_OK, 12 RL_TIMER_OK 13}; 14 15// global runlevel 16int g_runlevel; 17 18// backbuffers 19BITMAP* g_back = NULL; 20BITMAP* g_msafe = NULL; 21 22// for timing 23volatile int g_sp_counter = 0; 24void increment_sp_counter() 25{ 26 g_sp_counter++; 27} 28END_OF_FUNCTION(increment_sp_counter); 29 30// for close button handling 31volatile int g_quit = false; 32void close_bhandler() 33{ 34 g_quit = true; 35} 36END_OF_FUNCTION(close_bhandler); 37 38// START OF FUNCTIONS+VARS NEEDED FOR DETERMINING IF MOUSE IS OUTSIDE WINDOW 39// vars to be able to tell mouse movement and position(even if outside window) 40int g_abs_mx = 0; 41int g_abs_my = 0; 42int g_mickx = 0; 43int g_micky = 0; 44bool g_mouse_inside = false; 45 46// replacement function for position_mouse to help with initializing and 47// maintaining a more flexible mouse_position determination 48void position_mouse_override(int x, int y) 49{ 50 position_mouse(x,y); 51 g_abs_mx = x; 52 g_abs_my = y; 53 g_mickx = 0; 54 g_micky = 0; 55 if((g_abs_mx<0)||(g_abs_mx>=SCREEN_W)||(g_abs_my<0)||(g_abs_my>=SCREEN_H)) 56 g_mouse_inside = false; 57 else 58 g_mouse_inside = true; 59} 60 61 62// updates the internal mouse postion according to the mickeys reported 63// sets a global variable report the "mouse is inside the window" status 64// (can get negative or larger than the dimensions of allegros "screen") 65// (in fullscreen mode g_mouse_inside is not trustable, so you have to 66// use the usual mouse_x, mouse_y from allegro in fullscreen mode) 67void update_abs_mousepos() 68{ 69 if(mouse_needs_poll()) 70 poll_mouse(); 71 72 get_mouse_mickeys(&g_mickx,&g_micky); 73 g_abs_mx += g_mickx; 74 g_abs_my += g_micky; 75 if((g_abs_mx<0)||(g_abs_mx>=SCREEN_W)||(g_abs_my<0)||(g_abs_my>=SCREEN_H)) 76 g_mouse_inside = false; 77 else 78 g_mouse_inside = true; 79} 80// END OF FUNCTIONS+VARS NEEDED FOR DETERMINING IF MOUSE IS OUTSIDE WINDOW 81 82 83int initialize() 84{ 85 g_runlevel = RL_NOTHING_OK; 86 87 // Set UNICODE Format 88 set_uformat(U_ASCII); 89 90 // init allegro 91 if (allegro_init()!=0) 92 { 93 return -1; 94 } 95 g_runlevel = RL_ALLEGRO_OK; 96 97 // init keyboard 98 if (install_keyboard()!=0) 99 { 100 return -2; 101 } 102 three_finger_flag = false; // disable Allegros CTRL+ALT+END termination 103 g_runlevel = RL_KEYBOARD_OK; 104 105 disable_hardware_cursor(); 106 // init mouse 107 if (install_mouse()==-1) 108 { 109 return -3; 110 } 111 g_runlevel = RL_MOUSE_OK; 112 113 // init gfx subsystem 114 // try windowed 115 set_color_depth(desktop_color_depth()); 116 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED,640,480,0,0)!=0) 117 { 118 return -4; 119 } 120 g_runlevel = RL_GFXMODE_OK; 121 set_window_title("MouseInTest for Allegro 4.20"); 122 set_display_switch_mode(SWITCH_BACKGROUND); 123 124 // init backbuffer(s) 125 // large backbuffer for GUI 126 g_back = create_bitmap(640,480); 127 if(!g_back) 128 { 129 return -6; 130 } 131 // small backbuffer for mouse 132 g_msafe = create_bitmap(mouse_sprite->w,mouse_sprite->h); 133 if(!g_msafe) 134 { 135 destroy_bitmap(g_back); 136 g_back=NULL; 137 return -6; 138 } 139 g_runlevel = RL_BACKBUFFER_OK; 140 141 // init timer handler 142 if(install_timer()!=0) 143 { 144 return -7; 145 } 146 g_runlevel = RL_TIMER_OK; 147 148 return 0; 149} 150 151 152// Free and close everything 153void deinitialize() 154{ 155 if(g_runlevel >= RL_TIMER_OK) 156 { 157 remove_timer(); 158 } 159 if(g_runlevel >= RL_BACKBUFFER_OK) 160 { 161 destroy_bitmap(g_msafe); 162 g_msafe = NULL; 163 destroy_bitmap(g_back); 164 g_back = NULL; 165 } 166 if(g_runlevel >= RL_GFXMODE_OK) 167 { 168 set_gfx_mode(GFX_TEXT,0,0,0,0); 169 } 170 if(g_runlevel >= RL_MOUSE_OK) 171 { 172 remove_mouse(); 173 } 174 if(g_runlevel >= RL_KEYBOARD_OK) 175 { 176 remove_keyboard(); 177 } 178 if(g_runlevel >= RL_ALLEGRO_OK) 179 { 180 allegro_exit(); 181 } 182} 183 184int main(int arg_count, char *argument[]) 185{ 186 int m_x = 0; 187 int m_y = 0; 188 189 if(initialize()!=0) 190 { 191 deinitialize(); 192 return -1; 193 } 194 195 show_mouse(NULL); 196 position_mouse_override(320,240); 197 clear_to_color(g_back,makecol(200,200,255)); 198 clear_to_color(g_msafe,makecol(200,200,255)); 199 200 LOCK_VARIABLE(g_quit); 201 LOCK_FUNCTION(close_bhandler); 202 set_close_button_callback(close_bhandler); 203 204 rest(500); // not resting here seemed to cause trouble with timing 205 // and made the app respond very slowly after starting 206 207 LOCK_VARIABLE(g_sp_counter); 208 LOCK_FUNCTION(increment_sp_counter); 209 install_int_ex(increment_sp_counter, BPS_TO_TIMER(60)); 210 211 //Main loop 212 while (!g_quit) 213 { 214 while (g_sp_counter > 0) 215 { 216 // get input 217 if(keyboard_needs_poll()) 218 poll_keyboard(); 219 if(mouse_needs_poll()) 220 poll_mouse(); 221 222 if(key[KEY_ESC]) 223 g_quit=true; 224 225 update_abs_mousepos(); 226 227 g_sp_counter--; 228 rest(0); 229 } 230 231 // redraw information 232 rectfill(g_back,0,0,g_back->w-1,40,makecol(200,200,255)); 233 textprintf_ex(g_back, font, 10, 0, makecol(0, 0, 0),-1, "g_abs_mx: %i", g_abs_mx); 234 textprintf_ex(g_back, font, 10, 10, makecol(0, 0, 0),-1, "g_abs_my: %i", g_abs_my); 235 if((!g_mouse_inside)&&(is_windowed_mode())) 236 textprintf_ex(g_back,font,10,30,makecol(0,0,0),-1,"Mouse is outside of window."); 237 238 // save stuff under mouse cursor before displaying it 239 m_x = mouse_x; 240 m_y = mouse_y; 241 blit(g_back,g_msafe,m_x,m_y,0,0,g_msafe->w,g_msafe->h); 242 // draw mouse if inside of window 243 if((g_mouse_inside)&&(is_windowed_mode())) 244 draw_sprite(g_back,mouse_sprite,m_x,m_y); 245 // show backbuffer to screen 246 acquire_screen(); 247 blit(g_back,screen,0,0,0,0,g_back->w,g_back->h); 248 release_screen(); 249 // restore stuff under mouse 250 blit(g_msafe,g_back,0,0,m_x,m_y,g_msafe->w,g_msafe->h); 251 } 252 253 remove_int(increment_sp_counter); 254 255 deinitialize(); 256 return 0; 257} 258END_OF_MAIN()

...now the problems here are:
This test app sometimes freezes and I can't seem to find why. Anyone have an idea?
(this especially likes to happen when the window is being moved around)
I don't think the freezing has got anything to do with the method of updating the global mouse variables here, as I'm already using this method in another app, where it works fine and the only difference to that other app is that i'm not using that usual "timer logic to keep it running same speed everywhere"(as mentioned in allegros docs) there. So I assume, I'm doing something wrong about the timer.

The other problem is that this method so far has only been tested by me to report the mouse to be outside of the window correctly in Windows, since I don't have access to any other OSes, so it would be nice, if you could test it in the other OSes supported by Allegro, so we can see if it is a usable solution or just an ugly hack.
(Actually it would still be quite an ugly hack, even if it worked...)

Evert
Quote:

I clubbed together a small test application to demonstrate the purely Allegro dependant method, which uses the mouse mickeys reported by allegro to keep track of an absolute mouse position that can get negative or larger than the screen dimensions, thus providing the base for checking if the mouse is inside or outside the window.

This is unreliable because it relies on the programme receiving mouse events after the mouse has left the client window. This is not true in X11, for instance (actually, the situation there is more complicated: if you use mouse mickeys under X11, the mouse is trapped inside the client window and cannot be pulled out of it, unless you use a hardware cursor, in which case the behavior is as I said above).

There is no reliable way of detecting wether or not the window has mouse focus using just Allegro.

Onewing
Quote:

There is no reliable way of detecting wether or not the window has mouse focus using just Allegro.

Is this in the plans for future allegro versions?

Dennis
Quote:

This is unreliable because it relies on the programme receiving mouse events after the mouse has left the client window. This is not true in X11, for instance[..]

Ok that makes this method practically useless.:(
Now I would also appreciate it, if a check for that would get included in a future version of Allegro, so i'm posting the Windows specific solution here, which AJ gave me:

bool win_mouse_in_window()
{
  POINT mp;
  RECT wr;
  GetCursorPos(&mp);
  GetWindowRect(win_get_window(),&wr);
  if( (mp.x<wr.left)||(mp.x>wr.right)||(mp.y<wr.top)||(mp.y>wr.bottom) )
    return false;
  else
    return true;
}

Evert

I think someone (Andrei?) already posted a patch for that several weeks ago.

Dennis

A patch that fixes the mouse trapping in X11 and corrects the mickey reporting or a patch that provides a function to check if the mouse is inside the window?

Andrei Ellman

Ummm... I never made such a patch ??? . Although I agree that it would be nice if there was a function for reporting if the mouse was inside or outside the window, or even better, if it was to the left/right/top/bottom of the window. Better still, there could be a second version of te mouse_x and mouse_y variables that store the mouse position relative to the screen bitmap's origin. These variables would be negative if left/top of the window. If the mouse is inside the window, these would be equal to mouse_[x|y]. However, these new variables should be separate from mouse_x and mouse_y in order to maintain backwards compatibility - there's plenty of code out there that assume that the values reurtned by mouse_[x|y] are on-screen, positive, etc.

AE.

A J

I didn't actaully test that code i gave you Dennis, but am fairly confident it will work, i'd test it before any final commits.

Dennis

I tested it AJ (after reading the WinAPI docs and changing GetClientRect to GetWindowRect :)) and it works.
However, I don't know how to make patches for Allegro or how to commit anything and I also have no idea as to where in the code this should be inserted.
I'm currently reading the SVN documentation on sourceforge...

That aside, this function is still useless if noone writes equivalent functions for the other OSes.

A J

It might need some fullscreen/windowed mode test also.

Im sure we can find someone to commit it for you, thats not a concern.

Surely there is someone that wants to make the linux equivalent ?
Anyone ?

Dennis

Allegro already has "is_windowed_mode()".
In fullscreen mode the windowrectangle would be the full screen and the cursor position would be limited to the full screen anyway, so the function should only be used when is_windowed_mode reports true.

Considering what Andrei said though, maybe there should just be two other functions to allow for greater flexibility:
One that returns the window rectangle in screen(not allegros screen) coordinates and
One that returns the cursor position in screen(not allegros screen) coordinates.

Evert
Quote:

A patch that fixes the mouse trapping in X11 and corrects the mickey reporting

There's nothing to fix. It works as it does by design (to get an infinate range of mickeys).

Quote:

a patch that provides a function to check if the mouse is inside the window?

This one. If it wasn't Andrei, I'm sure it was someone else.

Quote:

One that returns the window rectangle in screen(not allegros screen) coordinates and
One that returns the cursor position in screen(not allegros screen) coordinates.

I don't think you can do this reliably everywhere. Having a function that tells you if your application has mouse focus should be enough.
However: if this requires vtable changes, it will not go into 4.2.1 and it's something for 4.3.

Dennis

My assumption would be that every windowingsystem has some way of retrieving the window position in screen coordinates and the mouse cursor as well, because otherwise I can't imagine how a windowingsystem would ever be able to tell which window is getting the focus.

For my current application I only need to know whether the mouse is in or not, but knowing those other values could be useful.(e.g. for a fun app like Xeyes, which is a small pair of eyes in a window that always looks at the mouse cursor)

For now I'm going to use the windows specific solution with conditional compiling to not break the code for other platforms. Will wait for version 4.2.1 for a true solution.

Thread #585599. Printed from Allegro.cc