[AD] Fix X11 mouse scroll bug

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


The problem is as follows: when you scroll the screen with Allegro, the 
upper-left corner of the display is not the origin of the screen bitmap 
anymore. The offset is automatically taken into account by the drawing code, 
so the coordinates of the mouse pointer must stay relative to the origin of 
the screen bitmap, not of the display.

The coordinates of the mouse pointer are equal to (mouse_x, mouse_y), that is 
the coordinates of the mouse. So when you scroll the screen, Allegro should 
scroll the coordinates of the mouse too.

This is implicitly done for most gfx driver/mouse driver combos, because the 
coordinates of the mouse relative to the screen are not tied to the 
coordinates of the mouse relative to the display, that is you can introduce 
an arbitrary implicit offset between them without harm.

Except with the X11 driver. Even in fullscreen mode, the driver constantly 
resyncs the coordinates of the mouse with those of X pointer. Now the former 
are relative to the bitmap that can scroll while the later are relative to 
the display that is fixed. So we must compensate when the screen is being 
scrolled, otherwise the mouse coordinates change even if the mouse is not 
moved.

BUT there is a problem: page flipping. When you page-flip, you don't change 
the origin of the "screen", that is the upper-left corner of the display is 
the origin of the video pages. Therefore the coordinates of the mouse 
pointer stay relative to the origin of the display, hence those of the mouse 
too.

Now, page flipping is done through scrolling for the X11 driver, that is the 
same method of the driver is invoked in both cases. So we don't know if we 
are in the scrolling case (and must compensate) or in the page flipping case 
(and must not compensate).

After thinking about this problem for about a week, I came to the conclusion 
that the most sensible fix (for 4.0.x and 4.1.x) is to use the mouse range 
to distinguish the two cases: when you scroll and want to use the mouse over 
the whole screen bitmap, you must extend the mouse range; when you 
page-flip, you don't need to alter the normal range.

Hence the attached patch. I don't plan to commit it immediately, in case 
someone come up with a better solution. I've also attached a testcase picked 
up from [AL] (you need to provide a 1884x1400 bitmap though).

-- 
Eric Botcazou
diff -u /home/eric/cvs/allegro/src/x/xmouse.c allegro/src/x/xmouse.c
--- /home/eric/cvs/allegro/src/x/xmouse.c	Tue Nov  6 18:16:41 2001
+++ allegro/src/x/xmouse.c	Wed Jul  2 16:54:34 2003
@@ -22,6 +22,16 @@
 #include "xwin.h"
 
 
+/* TRUE if the requested mouse range extends beyond the regular
+ * (0, 0, SCREEN_W-1, SCREEN_H-1) range. This is aimed at detecting
+ * whether the user mouse coordinates are relative to the 'screen'
+ * bitmap (semantics associated with scrolling) or to the video page
+ * currently being displayed (semantics associated with page flipping).
+ * We cannot differentiate them properly because both use the same
+ * scroll_screen() method.
+ */
+int _xwin_mouse_extended_range = FALSE;
+
 static int mouse_minx = 0;
 static int mouse_miny = 0;
 static int mouse_maxx = 319;
@@ -158,6 +168,11 @@
    mouse_maxx = x2;
    mouse_maxy = y2;
 
+   if ((mouse_maxx >= SCREEN_W) || (mouse_maxy >= SCREEN_H))
+      _xwin_mouse_extended_range = TRUE;
+   else
+      _xwin_mouse_extended_range = FALSE;
+
    XLOCK();
 
    _mouse_x = MID(mouse_minx, _mouse_x, mouse_maxx);
diff -u /home/eric/cvs/allegro/src/x/xwin.c allegro/src/x/xwin.c
--- /home/eric/cvs/allegro/src/x/xwin.c	Sun Jun 29 15:11:40 2003
+++ allegro/src/x/xwin.c	Wed Jul  2 16:56:13 2003
@@ -2128,8 +2128,8 @@
 	 mouse_was_warped = 0;
 	 if (!_xwin.mouse_warped) {
 	    /* Move Allegro cursor to X-cursor.  */
-	    dx = event->xmotion.x - _mouse_x;
-	    dy = event->xmotion.y - _mouse_y;
+	    dx = event->xmotion.x - (_mouse_x - (_xwin_mouse_extended_range ? _xwin.scroll_x : 0));
+	    dy = event->xmotion.y - (_mouse_y - (_xwin_mouse_extended_range ? _xwin.scroll_y : 0));
 	 }
 	 if (((dx != 0) || (dy != 0)) && _xwin_mouse_interrupt) {
 	    if (_xwin.mouse_warped && (mouse_warp_now++ & 4)) {
@@ -2163,8 +2163,8 @@
 	 mouse_was_warped = 0;
 	 if (!_xwin.mouse_warped) {
 	    /* Move Allegro cursor to X-cursor.  */
-	    dx = event->xcrossing.x - _mouse_x;
-	    dy = event->xcrossing.y - _mouse_y;
+	    dx = event->xcrossing.x - (_mouse_x - (_xwin_mouse_extended_range ? _xwin.scroll_x : 0));
+	    dy = event->xcrossing.y - (_mouse_y - (_xwin_mouse_extended_range ? _xwin.scroll_y : 0));
 	    if (((dx != 0) || (dy != 0)) && _xwin_mouse_interrupt)
 	       (*_xwin_mouse_interrupt)(dx, dy, 0, mouse_buttons);
 	 }
@@ -2222,7 +2222,8 @@
 	 /* Move X-cursor to Allegro cursor.  */
 	 XWarpPointer(_xwin.display, _xwin.window, _xwin.window,
 		      0, 0, _xwin.window_width, _xwin.window_height,
-		      _mouse_x, _mouse_y);
+		      _mouse_x - (_xwin_mouse_extended_range ? _xwin.scroll_x : 0),
+		      _mouse_y - (_xwin_mouse_extended_range ? _xwin.scroll_y : 0));
       }
 #ifdef ALLEGRO_XWINDOWS_WITH_XF86DGA
    }
diff -u /home/eric/cvs/allegro/src/x/xwin.h allegro/src/x/xwin.h
--- /home/eric/cvs/allegro/src/x/xwin.h	Thu Oct 31 13:56:24 2002
+++ allegro/src/x/xwin.h	Wed Jul  2 16:54:08 2003
@@ -20,6 +20,9 @@
 
 #include "xalleg.h"
 
+/* Defined in xmouse.c */
+AL_VAR(int, _xwin_mouse_extended_range);
+
 /* Defined in xwin.c.  */
 AL_VAR(int, _xwin_last_line);
 AL_VAR(int, _xwin_in_gfx_call);
#include <allegro.h>
#include <stdio.h>

const int red=255*65536;
const int green=255*255;
const int blue=255*1;
const int white=red|green|blue;

int main(int argc,char* argv[])
{
	int x, y;

	x = 1884; // width of the bmp
	y = 1400; // height of the bmp

	allegro_init();
	if(set_gfx_mode(GFX_AUTODETECT_FULLSCREEN,800,600,2048,1536)){
		allegro_message("GFX failed\n");exit(1);
	}
	char buf[100];
	sprintf(buf,"main:%dx%d, v:%dx%d.",SCREEN_W,SCREEN_H,VIRTUAL_W,VIRTUAL_H);
	// textout(screen,buf,25,10,white,0);
	allegro_message(buf);

	if(0>install_mouse()){
		allegro_message("mouse failed!\n");exit(2);
	}
	RGB pal[256];
	BITMAP *bg=load_bmp("image.bmp",pal);
	set_palette(pal);
	set_mouse_range(0,0,x - 1,y -1);
	set_clip(screen,0,0,x,y);
	blit(bg,screen,0,0,0,0,x,y);

	int pos_x=0,pos_y=0;
	int text_x, text_y;
	int y_screen_offset = 0, x_screen_offset = 0;
	text_x = SCREEN_W/2;
	text_y = SCREEN_H/2;

	while(!(mouse_b)){
		bool redraw = false; // if we need to redraw our text msg
		bool flip = false; // if we need to flip our virtual screen

		// this has the mouse scroll the screen and flips
		// to the next panel when the mouse reaches the edge of the screen
		if (pos_x != mouse_x || pos_y != mouse_y)
		{
			redraw = true;

			// check to see if the mouse is at a vertical edge of the screen
			if (mouse_x - x_screen_offset >= SCREEN_W - 1)
			{
				// right side of the screen
				flip = true;
				x_screen_offset += SCREEN_W;
				position_mouse(mouse_x + 2, mouse_y);
			} else if (mouse_x - x_screen_offset <= 0) {
				// left side of the screen
				flip = true;
				x_screen_offset -= SCREEN_W;
				position_mouse(mouse_x - 2, mouse_y);
			}

			// check to see if the mouse is at a horizontal edge of the screen
			if (mouse_y - y_screen_offset >= SCREEN_H - 1)
			{
		char buf2[100];
		sprintf(buf2,"a mousey:%d\n",mouse_y);
		allegro_message(buf2);
				// bottom of the screen
				flip = true;
				y_screen_offset += SCREEN_H;
				position_mouse(mouse_x, y_screen_offset + 2);
		sprintf(buf2,"a2 mousey:%d\n",mouse_y);
		allegro_message(buf2);
			} else if (mouse_y - y_screen_offset <= 0) {
		char buf2[100];
		sprintf(buf2,"b mousey:%d\n",mouse_y);
		allegro_message(buf2);
				// top of the screen
				flip = true;
				y_screen_offset -= SCREEN_H;
				position_mouse(mouse_x, y_screen_offset + SCREEN_H - 2);
		sprintf(buf2,"b2 mousey:%d\n",mouse_y);
		allegro_message(buf2);
			}

			// check to make sure we don't leave our bitmap area
			if (flip)
			{
				if (x_screen_offset < 0)
				{
					position_mouse(0, mouse_y);
					x_screen_offset = 0;
				}
				if (y_screen_offset < 0)
				{
		char buf2[100];
		sprintf(buf2,"c mousey:%d\n",mouse_y);
		allegro_message(buf2);
					position_mouse(mouse_x, 0);
					y_screen_offset = 0;
				}
				if (x_screen_offset + SCREEN_W > x)
				{
					position_mouse(x - 1, mouse_y);
					x_screen_offset = x - SCREEN_W;
				}
				if (y_screen_offset + SCREEN_H > y)
				{
		char buf2[100];
		sprintf(buf2,"d mousey:%d\n",mouse_y);
		allegro_message(buf2);
					position_mouse(mouse_x, y - 1);
					y_screen_offset = y - SCREEN_H;
				}
			}

			pos_x = mouse_x;
			pos_y = mouse_y;
		}

		//get_mouse_mickeys(&mx, &my);

		if (redraw)
		{
			// need to vsync() if we have the mouse pointer showing
			// otherwise the mouse pointer flashes something fierce
			vsync();
			if (flip)
			{
				scroll_screen(x_screen_offset,y_screen_offset);
			}
			show_mouse(NULL);

    		blit(bg, screen, text_x, text_y, text_x, text_y, gui_strlen(buf), text_height(font));
			//sprintf(buf,"scroll:%dx%d. old:%dx%d",pos_x,pos_y,old_x,old_y);
			sprintf(buf,"mouse:%dx%d, screen:%dx%d, text:%dx%d",mouse_x,mouse_y,x_screen_offset,y_screen_offset,text_x,text_y);
			text_x = x_screen_offset + SCREEN_W/2;
			text_y = y_screen_offset + SCREEN_H/2;
			textout_ex(screen, font, buf, text_x, text_y,makecol(0,255,0),-1);
			show_mouse(screen);
		}
	}
	return 0;
}
END_OF_MAIN()


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/