[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()