x11: Optimize SDL_GetGlobalMouseState() a little.

Use XInput2 to mark the global mouse state as dirty so we don't have to make
a bunch of roundtrips to the X server when nothing has changed.
This commit is contained in:
Ryan C. Gordon 2015-04-22 16:50:48 -04:00
parent 14e007772a
commit 8875a4014f
4 changed files with 51 additions and 22 deletions

View file

@ -366,39 +366,52 @@ X11_CaptureMouse(SDL_Window *window)
static Uint32
X11_GetGlobalMouseState(int *x, int *y)
{
SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
Display *display = GetDisplay();
const int num_screens = SDL_GetNumVideoDisplays();
int i;
/* !!! FIXME: should we XSync() here first? */
for (i = 0; i < num_screens; i++) {
SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
if (data != NULL) {
Window root, child;
int rootx, rooty, winx, winy;
unsigned int mask;
if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
XWindowAttributes root_attrs;
Uint32 retval = 0;
retval |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
retval |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
retval |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
/* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
* (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
*
* Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
X11_XGetWindowAttributes(display, root, &root_attrs);
*x = root_attrs.x + rootx;
*y = root_attrs.y + rooty;
return retval;
#if !SDL_VIDEO_DRIVER_X11_XINPUT2
videodata->global_mouse_changed = SDL_TRUE;
#endif
/* check if we have this cached since XInput last saw the mouse move. */
/* !!! FIXME: can we just calculate this from XInput's events? */
if (videodata->global_mouse_changed) {
for (i = 0; i < num_screens; i++) {
SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
if (data != NULL) {
Window root, child;
int rootx, rooty, winx, winy;
unsigned int mask;
if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
XWindowAttributes root_attrs;
Uint32 buttons = 0;
buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
/* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
* (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
*
* Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
X11_XGetWindowAttributes(display, root, &root_attrs);
videodata->global_mouse_position.x = root_attrs.x + rootx;
videodata->global_mouse_position.y = root_attrs.y + rooty;
videodata->global_mouse_buttons = buttons;
videodata->global_mouse_changed = SDL_FALSE;
break;
}
}
}
}
SDL_assert(0 && "The pointer wasn't on any X11 screen?!");
SDL_assert(!videodata->global_mouse_changed); /* The pointer wasn't on any X11 screen?! */
return 0;
*x = videodata->global_mouse_position.x;
*y = videodata->global_mouse_position.y;
return videodata->global_mouse_buttons;
}

View file

@ -175,6 +175,8 @@ X11_CreateDevice(int devindex)
}
device->driverdata = data;
data->global_mouse_changed = SDL_TRUE;
/* FIXME: Do we need this?
if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) ||
(SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) {

View file

@ -118,6 +118,10 @@ typedef struct SDL_VideoData
SDL_bool selection_waiting;
Uint32 last_mode_change_deadline;
SDL_bool global_mouse_changed;
SDL_Point global_mouse_position;
Uint32 global_mouse_buttons;
} SDL_VideoData;
extern SDL_bool X11_UseDirectColorVisuals(void);

View file

@ -118,6 +118,8 @@ X11_InitXinput2(_THIS)
eventmask.mask = mask;
XISetMask(mask, XI_RawMotion);
XISetMask(mask, XI_RawButtonPress);
XISetMask(mask, XI_RawButtonRelease);
if (X11_XISelectEvents(data->display,DefaultRootWindow(data->display),&eventmask,1) != Success) {
return;
@ -140,6 +142,8 @@ X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
static Time prev_time = 0;
static double prev_rel_coords[2];
videodata->global_mouse_changed = SDL_TRUE;
if (!mouse->relative_mode || mouse->relative_mode_warp) {
return 0;
}
@ -158,6 +162,12 @@ X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
return 1;
}
break;
case XI_RawButtonPress:
case XI_RawButtonRelease:
videodata->global_mouse_changed = SDL_TRUE;
break;
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
case XI_TouchBegin: {
const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;