From 9cbfa4739a51e4cc6b8094833928bf8678f63876 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 6 Sep 2011 15:55:19 +1000 Subject: [PATCH 1/4] xfree86: remove xf86XInputSetScreen Keeping track of which screen the pointer within the input driver is obsolete now. To bind to a screen, use the transformation matrix instead. Signed-off-by: Peter Hutterer Reviewed-by: Jamey Sharp Reviewed-by: Daniel Stone --- hw/xfree86/common/xf86Xinput.c | 19 ------------------- hw/xfree86/common/xf86Xinput.h | 1 - 2 files changed, 20 deletions(-) diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c index ea1f92761..ad1833905 100644 --- a/hw/xfree86/common/xf86Xinput.c +++ b/hw/xfree86/common/xf86Xinput.c @@ -1345,25 +1345,6 @@ xf86ScaleAxis(int Cx, return X; } -/* - * This function checks the given screen against the current screen and - * makes changes if appropriate. It should be called from an XInput driver's - * ReadInput function before any events are posted, if the device is screen - * specific like a touch screen. - */ -void -xf86XInputSetScreen(InputInfoPtr pInfo, - int screen_number, - int x, - int y) -{ - if (miPointerGetScreen(pInfo->dev) != - screenInfo.screens[screen_number]) { - miPointerSetScreen(pInfo->dev, screen_number, x, y); - } -} - - Bool xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int maxval, int resolution, int min_res, int max_res, int mode) diff --git a/hw/xfree86/common/xf86Xinput.h b/hw/xfree86/common/xf86Xinput.h index 189f7abaf..909fb57d6 100644 --- a/hw/xfree86/common/xf86Xinput.h +++ b/hw/xfree86/common/xf86Xinput.h @@ -143,7 +143,6 @@ extern _X_EXPORT void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int ke int is_down); extern _X_EXPORT InputInfoPtr xf86FirstLocalDevice(void); extern _X_EXPORT int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min); -extern _X_EXPORT void xf86XInputSetScreen(InputInfoPtr pInfo, int screen_number, int x, int y); extern _X_EXPORT void xf86ProcessCommonOptions(InputInfoPtr pInfo, XF86OptionPtr options); extern _X_EXPORT Bool xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int maxval, int resolution, int min_res, From 8bebb4b4896d8b6ba3309b5b28fce883bb9f8a96 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 7 Sep 2011 14:04:10 +1000 Subject: [PATCH 2/4] Store desktop dimensions in screenInfo. For Zaphod mode screen crossing handling we need to know the size of all screens together (i.e. the whole desktop size). Store that in the screenInfo to have it readily available in events. Signed-off-by: Peter Hutterer --- dix/dispatch.c | 2 + dix/inpututils.c | 24 ++++++++ hw/xfree86/common/xf86Cursor.c | 2 + hw/xfree86/common/xf86RandR.c | 3 + hw/xfree86/modes/xf86RandR12.c | 2 + include/input.h | 1 + include/scrnintstr.h | 4 ++ test/misc.c | 104 +++++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+) diff --git a/dix/dispatch.c b/dix/dispatch.c index 43cb4d1d3..4a4481ab3 100644 --- a/dix/dispatch.c +++ b/dix/dispatch.c @@ -3898,6 +3898,8 @@ AddScreen( return -1; } + update_desktop_dimensions(); + dixRegisterScreenPrivateKey(&cursorScreenDevPriv, pScreen, PRIVATE_CURSOR, 0); return i; diff --git a/dix/inpututils.c b/dix/inpututils.c index eeae2a74f..8a834442c 100644 --- a/dix/inpututils.c +++ b/dix/inpututils.c @@ -628,6 +628,30 @@ point_on_screen(ScreenPtr pScreen, int x, int y) } /** + * Update desktop dimensions on the screenInfo struct. + */ +void +update_desktop_dimensions(void) +{ + int i; + int x1 = INT_MAX, y1 = INT_MAX; /* top-left */ + int x2 = INT_MIN, y2 = INT_MIN; /* bottom-right */ + + for (i = 0; i < screenInfo.numScreens; i++) { + ScreenPtr screen = screenInfo.screens[i]; + x1 = min(x1, screen->x); + y1 = min(y1, screen->y); + x2 = max(x2, screen->x + screen->width); + y2 = max(y2, screen->y + screen->height); + } + + screenInfo.x = x1; + screenInfo.y = y1; + screenInfo.width = x2 - x1; + screenInfo.height = y2 - y1; +} + +/* * Delete the element with the key from the list, freeing all memory * associated with the element.. */ diff --git a/hw/xfree86/common/xf86Cursor.c b/hw/xfree86/common/xf86Cursor.c index 929f047cc..6f5d726f0 100644 --- a/hw/xfree86/common/xf86Cursor.c +++ b/hw/xfree86/common/xf86Cursor.c @@ -838,6 +838,8 @@ xf86InitOrigins(void) FillOutEdge(pLayout->down, pScreen->width); } } + + update_desktop_dimensions(); } void diff --git a/hw/xfree86/common/xf86RandR.c b/hw/xfree86/common/xf86RandR.c index 4663d0366..d0e47841e 100644 --- a/hw/xfree86/common/xf86RandR.c +++ b/hw/xfree86/common/xf86RandR.c @@ -313,6 +313,9 @@ xf86RandRSetConfig (ScreenPtr pScreen, return FALSE; } + + update_desktop_dimensions(); + /* * Move the cursor back where it belongs; SwitchMode repositions it * FIXME: duplicated code, see modes/xf86RandR12.c diff --git a/hw/xfree86/modes/xf86RandR12.c b/hw/xfree86/modes/xf86RandR12.c index cb20d1c35..d5031a2f1 100644 --- a/hw/xfree86/modes/xf86RandR12.c +++ b/hw/xfree86/modes/xf86RandR12.c @@ -736,6 +736,8 @@ xf86RandR12ScreenSetSize (ScreenPtr pScreen, xf86SetViewport (pScreen, 0, 0); finish: + update_desktop_dimensions(); + if (pRoot && pScrn->vtSema) (*pScrn->EnableDisableFBAccess) (pScreen->myNum, TRUE); #if RANDR_12_INTERFACE diff --git a/include/input.h b/include/input.h index b7de5ca3d..2544996a6 100644 --- a/include/input.h +++ b/include/input.h @@ -609,5 +609,6 @@ extern _X_EXPORT void input_option_set_key(InputOption *opt, const char* key); extern _X_EXPORT void input_option_set_value(InputOption *opt, const char* value); extern _X_HIDDEN Bool point_on_screen(ScreenPtr pScreen, int x, int y); +extern _X_HIDDEN void update_desktop_dimensions(void); #endif /* INPUT_H */ diff --git a/include/scrnintstr.h b/include/scrnintstr.h index a9357e8a5..132a67193 100644 --- a/include/scrnintstr.h +++ b/include/scrnintstr.h @@ -561,6 +561,10 @@ typedef struct _ScreenInfo { formats[MAXFORMATS]; int numScreens; ScreenPtr screens[MAXSCREENS]; + int x; /* origin */ + int y; /* origin */ + int width; /* total width of all screens together */ + int height; /* total height of all screens together */ } ScreenInfo; extern _X_EXPORT ScreenInfo screenInfo; diff --git a/test/misc.c b/test/misc.c index 3d3b1a1e3..d98449ba0 100644 --- a/test/misc.c +++ b/test/misc.c @@ -27,6 +27,9 @@ #include #include "misc.h" +#include "scrnintstr.h" + +ScreenInfo screenInfo; static void dix_version_compare(void) { @@ -54,9 +57,110 @@ static void dix_version_compare(void) assert(rc < 0); } +static void dix_update_desktop_dimensions(void) +{ + int i; + int x, y, w, h; + int w2, h2; + ScreenRec screens[MAXSCREENS]; + + for (i = 0; i < MAXSCREENS; i++) + screenInfo.screens[i] = &screens[i]; + + x = 0; + y = 0; + w = 10; + h = 5; + w2 = 35; + h2 = 25; + +#define assert_dimensions(_x, _y, _w, _h) \ + update_desktop_dimensions(); \ + printf("%d %d %d %d\n", screenInfo.x, screenInfo.y, screenInfo.width, screenInfo.height); \ + assert(screenInfo.x == _x); \ + assert(screenInfo.y == _y); \ + assert(screenInfo.width == _w); \ + assert(screenInfo.height == _h); + +#define set_screen(idx, _x, _y, _w, _h) \ + screenInfo.screens[idx]->x = _x; \ + screenInfo.screens[idx]->y = _y; \ + screenInfo.screens[idx]->width = _w; \ + screenInfo.screens[idx]->height = _h; \ + + printf("Testing\n"); + + /* single screen */ + screenInfo.numScreens = 1; + set_screen(0, x, y, w, h); + assert_dimensions(x, y, w, h); + + /* dualhead rightof */ + screenInfo.numScreens = 2; + set_screen(1, w, 0, w2, h2); + assert_dimensions(x, y, w + w2, h2); + + /* dualhead belowof */ + screenInfo.numScreens = 2; + set_screen(1, 0, h, w2, h2); + assert_dimensions(x, y, w2, h + h2); + + /* triplehead L shape */ + screenInfo.numScreens = 3; + set_screen(1, 0, h, w2, h2); + set_screen(2, w2, h2, w, h); + assert_dimensions(x, y, w + w2, h + h2); + + /* quadhead 2x2 */ + screenInfo.numScreens = 4; + set_screen(1, 0, h, w, h); + set_screen(2, w, h, w, h2); + set_screen(3, w, 0, w2, h); + assert_dimensions(x, y, w + w2, h + h2); + + /* quadhead horiz line */ + screenInfo.numScreens = 4; + set_screen(1, w, 0, w, h); + set_screen(2, 2 * w, 0, w, h); + set_screen(3, 3 * w, 0, w, h); + assert_dimensions(x, y, 4 * w, h); + + /* quadhead vert line */ + screenInfo.numScreens = 4; + set_screen(1, 0, h, w, h); + set_screen(2, 0, 2 * h, w, h); + set_screen(3, 0, 3 * h, w, h); + assert_dimensions(x, y, w, 4 * h); + + + /* x overlap */ + screenInfo.numScreens = 2; + set_screen(0, 0, 0, w2, h2); + set_screen(1, w, 0, w2, h2); + assert_dimensions(x, y, w2 + w, h2); + + /* y overlap */ + screenInfo.numScreens = 2; + set_screen(0, 0, 0, w2, h2); + set_screen(1, 0, h, w2, h2); + assert_dimensions(x, y, w2, h2 + h); + + /* negative origin */ + screenInfo.numScreens = 1; + set_screen(0, -w2, -h2, w, h); + assert_dimensions(-w2, -h2, w, h); + + /* dualhead negative origin, overlap */ + screenInfo.numScreens = 2; + set_screen(0, -w2, -h2, w2, h2); + set_screen(1, -w, -h, w, h); + assert_dimensions(-w2, -h2, w2, h2); +} + int main(int argc, char** argv) { dix_version_compare(); + dix_update_desktop_dimensions(); return 0; } From 6bd0eff40fae1e5d8fed28751851eb5b932d131b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 12 Oct 2011 15:53:57 +1000 Subject: [PATCH 3/4] dix: extend rescaleValuatorAxis to take a minimum default Allow rescaling to non-zero based axis ranges as default (for when screen offsets are non-zero). Currently unused. Signed-off-by: Peter Hutterer --- dix/getevents.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dix/getevents.c b/dix/getevents.c index 874189f57..548cc8b81 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -257,10 +257,10 @@ CreateClassesChangedEvent(InternalEvent* event, */ static double rescaleValuatorAxis(double coord, AxisInfoPtr from, AxisInfoPtr to, - double defmax) + double defmin, double defmax) { - double fmin = 0.0, fmax = defmax; - double tmin = 0.0, tmax = defmax; + double fmin = defmin, fmax = defmax; + double tmin = defmin, tmax = defmax; if (from && from->min_value < from->max_value) { fmin = from->min_value; @@ -309,14 +309,14 @@ updateSlaveDeviceCoords(DeviceIntPtr master, DeviceIntPtr pDev) pDev->last.valuators[0] = rescaleValuatorAxis(pDev->last.valuators[0], NULL, pDev->valuator->axes + 0, - scr->width); + 0, scr->width); } if(pDev->valuator->numAxes > 1) { pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1], NULL, pDev->valuator->axes + 1, - scr->height); + 0, scr->height); } /* calculate the other axis as well based on info from the old @@ -333,7 +333,7 @@ updateSlaveDeviceCoords(DeviceIntPtr master, DeviceIntPtr pDev) { double val = pDev->last.valuators[i]; val = rescaleValuatorAxis(val, lastSlave->valuator->axes + i, - pDev->valuator->axes + i, 0); + pDev->valuator->axes + i, 0, 0); pDev->last.valuators[i] = val; } } @@ -445,7 +445,7 @@ GetMotionHistory(DeviceIntPtr pDev, xTimecoord **buff, unsigned long start, /* scale to screen coords */ to = &core_axis; to->max_value = pScreen->width; - coord = rescaleValuatorAxis(coord, &from, to, pScreen->width); + coord = rescaleValuatorAxis(coord, &from, to, 0, pScreen->width); memcpy(corebuf, &coord, sizeof(INT16)); corebuf++; @@ -456,7 +456,7 @@ GetMotionHistory(DeviceIntPtr pDev, xTimecoord **buff, unsigned long start, memcpy(&coord, icbuf++, sizeof(INT32)); to->max_value = pScreen->height; - coord = rescaleValuatorAxis(coord, &from, to, pScreen->height); + coord = rescaleValuatorAxis(coord, &from, to, 0, pScreen->height); memcpy(corebuf, &coord, sizeof(INT16)); } else if (IsMaster(pDev)) @@ -484,7 +484,7 @@ GetMotionHistory(DeviceIntPtr pDev, xTimecoord **buff, unsigned long start, from.max_value = pScreen->height; /* scale from stored range into current range */ - coord = rescaleValuatorAxis(coord, &from, to, 0); + coord = rescaleValuatorAxis(coord, &from, to, 0, 0); memcpy(ocbuf, &coord, sizeof(INT32)); ocbuf++; } @@ -770,14 +770,14 @@ scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) { scaled = rescaleValuatorAxis(valuator_mask_get_double(mask, 0), NULL, dev->valuator->axes + 0, - scr->width); + 0, scr->width); valuator_mask_set_double(mask, 0, scaled); } if (valuator_mask_isset(mask, 1)) { scaled = rescaleValuatorAxis(valuator_mask_get_double(mask, 1), NULL, dev->valuator->axes + 1, - scr->height); + 0, scr->height); valuator_mask_set_double(mask, 1, scaled); } } @@ -823,9 +823,9 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, /* scale x&y to screen */ *screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL, - scr->width); + 0, scr->width); *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL, - scr->height); + 0, scr->height); tmpx = *screenx; tmpy = *screeny; @@ -839,10 +839,10 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, */ if (tmpx != *screenx) x = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0, - scr->width); + 0, scr->width); if (tmpy != *screeny) y = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1, - scr->height); + 0, scr->height); if (valuator_mask_isset(mask, 0)) From 401150d7dcad08be7c1f07e076f810cd61e2105c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Oct 2011 11:41:17 +1000 Subject: [PATCH 4/4] input: change pointer screen crossing behaviour for multiple ScreenRecs miPointerSetPosition traditionally took coordinates on a per-screen basis, triggering a screen switch when these went out-of-bounds. For absolute devices, this prevented screen crossing in the negative x/y direction. This patch changes the event generation patch to handle screen coordinates in a desktop range (i.e. all screens together). Screen switches are triggered when these coordinates are not on the current screen. This unifies the pointer behaviour of single ScreenRec multihead and multiple ScreenRecs multihead in that the cursor by default moves about the whole screen rather than be confined to one single screen. The transformation matrix may then be used to actually confine the cursor to the screen again. Note: fill_pointer_events has to deal with several different coordinate systems. Make sure you read the comment before trying to understand the code. Signed-off-by: Peter Hutterer --- dix/devices.c | 10 ++-- dix/getevents.c | 120 +++++++++++++++++++++++++++++++++++---------- include/inputstr.h | 5 +- mi/mipointer.c | 18 +++++-- 4 files changed, 117 insertions(+), 36 deletions(-) diff --git a/dix/devices.c b/dix/devices.c index 64557aaf1..787940524 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -612,6 +612,7 @@ CorePointerProc(DeviceIntPtr pDev, int what) int i = 0; Atom btn_labels[NBUTTONS] = {0}; Atom axes_labels[NAXES] = {0}; + ScreenPtr scr = screenInfo.screens[0]; switch (what) { case DEVICE_INIT: @@ -638,10 +639,11 @@ CorePointerProc(DeviceIntPtr pDev, int what) pDev->name); return BadAlloc; /* IPDS only fails on allocs */ } - pDev->valuator->axisVal[0] = screenInfo.screens[0]->width / 2; - pDev->last.valuators[0] = pDev->valuator->axisVal[0]; - pDev->valuator->axisVal[1] = screenInfo.screens[0]->height / 2; - pDev->last.valuators[1] = pDev->valuator->axisVal[1]; + /* axisVal is per-screen, last.valuators is desktop-wide */ + pDev->valuator->axisVal[0] = scr->width / 2; + pDev->last.valuators[0] = pDev->valuator->axisVal[0] + scr->x; + pDev->valuator->axisVal[1] = scr->height / 2; + pDev->last.valuators[1] = pDev->valuator->axisVal[1] + scr->y; break; case DEVICE_CLOSE: diff --git a/dix/getevents.c b/dix/getevents.c index 548cc8b81..cd6260b2e 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -295,7 +295,7 @@ updateSlaveDeviceCoords(DeviceIntPtr master, DeviceIntPtr pDev) int i; DeviceIntPtr lastSlave; - /* master->last.valuators[0]/[1] is in screen coords and the actual + /* master->last.valuators[0]/[1] is in desktop-wide coords and the actual * position of the pointer */ pDev->last.valuators[0] = master->last.valuators[0]; pDev->last.valuators[1] = master->last.valuators[1]; @@ -757,8 +757,8 @@ accelPointer(DeviceIntPtr dev, ValuatorMask* valuators, CARD32 ms) * device's coordinate range. * * @param dev The device to scale for. - * @param[in, out] mask The mask in sceen coordinates, modified in place to - * contain device coordinate range. + * @param[in, out] mask The mask in desktop coordinates, modified in place + * to contain device coordinate range. */ static void scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) @@ -768,14 +768,16 @@ scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) if (valuator_mask_isset(mask, 0)) { - scaled = rescaleValuatorAxis(valuator_mask_get_double(mask, 0), + scaled = valuator_mask_get_double(mask, 0) + scr->x; + scaled = rescaleValuatorAxis(scaled, NULL, dev->valuator->axes + 0, 0, scr->width); valuator_mask_set_double(mask, 0, scaled); } if (valuator_mask_isset(mask, 1)) { - scaled = rescaleValuatorAxis(valuator_mask_get_double(mask, 1), + scaled = valuator_mask_get_double(mask, 1) + scr->y; + scaled = rescaleValuatorAxis(scaled, NULL, dev->valuator->axes + 1, 0, scr->height); valuator_mask_set_double(mask, 1, scaled); @@ -793,16 +795,21 @@ scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) * * The coordinates provided are always absolute. The parameter mode * specifies whether it was relative or absolute movement that landed us at - * those coordinates. + * those coordinates. see fill_pointer_events for information on coordinate + * systems. * * @param dev The device to be moved. * @param mode Movement mode (Absolute or Relative) - * @param mask Mask of axis values for this event - * @param screenx Screen x coordinate the sprite is on after the update. - * @param screeny Screen y coordinate the sprite is on after the update. + * @param[in,out] mask Mask of axis values for this event, returns the + * per-screen device coordinates after confinement + * @param[out] devx x desktop-wide coordinate in device coordinate system + * @param[out] devy y desktop-wide coordinate in device coordinate system + * @param[out] screenx x coordinate in desktop coordinate system + * @param[out] screeny y coordinate in desktop coordinate system */ static ScreenPtr positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, + double *devx, double *devy, double *screenx, double *screeny) { double x, y; @@ -821,16 +828,20 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, else y = dev->last.valuators[1]; - /* scale x&y to screen */ + /* scale x&y to desktop coordinates */ *screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL, - 0, scr->width); + screenInfo.x, screenInfo.width); *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL, - 0, scr->height); + screenInfo.y, screenInfo.height); tmpx = *screenx; tmpy = *screeny; + *devx = x; + *devy = y; + /* miPointerSetPosition takes care of crossing screens for us, as well as - * clipping to the current screen. */ + * clipping to the current screen. Coordinates returned are in desktop + * coord system */ scr = miPointerSetPosition(dev, mode, screenx, screeny); /* If we were constrained, rescale x/y from the screen coordinates so @@ -838,17 +849,24 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, * crossing this doesn't matter much, the coords would be 0 or max. */ if (tmpx != *screenx) - x = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0, - 0, scr->width); + *devx = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0, + screenInfo.x, screenInfo.width); + if (tmpy != *screeny) - y = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1, - 0, scr->height); + *devy = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1, + screenInfo.y, screenInfo.height); - - if (valuator_mask_isset(mask, 0)) + /* Recalculate the per-screen device coordinates */ + if (valuator_mask_isset(mask, 0)) { + x = rescaleValuatorAxis(*screenx - scr->x, NULL, dev->valuator->axes + 0, + 0, scr->width); valuator_mask_set_double(mask, 0, x); - if (valuator_mask_isset(mask, 1)) + } + if (valuator_mask_isset(mask, 1)) { + y = rescaleValuatorAxis(*screeny - scr->y, NULL, dev->valuator->axes + 1, + 0, scr->height); valuator_mask_set_double(mask, 1, y); + } return scr; } @@ -1105,6 +1123,38 @@ QueuePointerEvents(DeviceIntPtr device, int type, * * Should not be called by anyone other than GetPointerEvents. * + * We use several different coordinate systems and need to switch between + * the three in fill_pointer_events, positionSprite and + * miPointerSetPosition. "desktop" refers to the width/height of all + * screenInfo.screens[n]->width/height added up. "screen" is ScreenRec, not + * output. + * + * Coordinate systems: + * - relative events have a mask_in in relative coordinates, mapped to + * pixels. These events are mapped to the current position±delta. + * - absolute events have a mask_in in absolute device coordinates in + * device-specific range. This range is mapped to the desktop. + * - POINTER_SCREEN absolute events (x86WarpCursor) are in screen-relative + * screen coordinate range. + * - rootx/rooty in events must be be relative to the current screen's + * origin (screen coordinate system) + * - XI2 valuators must be relative to the current screen's origin. On + * the protocol the device min/max range maps to the current screen. + * + * For screen switching we need to get the desktop coordinates for each + * event, then map that to the respective position on each screen and + * position the cursor there. + * The device's last.valuator[] stores the last position in desktop-wide + * coordinates (in device range for slave devices, desktop range for master + * devices). + * + * screen-relative device coordinates requires scaling: A device coordinate + * x/y of range [n..m] that maps to positions Sx/Sy on Screen S must be + * rescaled to match Sx/Sy for [n..m]. In the simplest example, x of (m/2-1) + * is the last coordinate on the first screen and must be rescaled for the + * event to be m. XI2 clients that do their own coordinate mapping would + * otherwise interpret the position of the device elsewere to the cursor. + * * @return the number of events written into events. */ static int @@ -1115,8 +1165,10 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, int num_events = 1, i; DeviceEvent *event; RawDeviceEvent *raw; - double screenx = 0.0, screeny = 0.0; + double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */ + double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */ ValuatorMask mask; + ScreenPtr scr; switch (type) { @@ -1155,6 +1207,8 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, set_raw_valuators(raw, &mask, raw->valuators.data_raw); } + /* valuators are in driver-native format (rel or abs) */ + if (flags & POINTER_ABSOLUTE) { if (flags & POINTER_SCREEN) /* valuators are in screen coords */ @@ -1168,22 +1222,34 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, moveRelative(pDev, &mask); } + /* valuators are in device coordinate system in absolute coordinates */ + if ((flags & POINTER_NORAW) == 0) set_raw_valuators(raw, &mask, raw->valuators.data); - positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, - &mask, &screenx, &screeny); + scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, + &mask, &devx, &devy, &screenx, &screeny); + + /* screenx, screeny are in desktop coordinates, + mask is in device coordinates per-screen (the event data) + devx/devy is in device coordinate desktop-wide */ updateHistory(pDev, &mask, ms); clipValuators(pDev, &mask); - for (i = 0; i < valuator_mask_size(&mask); i++) + /* store desktop-wide in last.valuators */ + if (valuator_mask_isset(&mask, 0)) + pDev->last.valuators[0] = devx; + if (valuator_mask_isset(&mask, 1)) + pDev->last.valuators[1] = devy; + + for (i = 2; i < valuator_mask_size(&mask); i++) { if (valuator_mask_isset(&mask, i)) pDev->last.valuators[i] = valuator_mask_get_double(&mask, i); } - /* Update the MD's co-ordinates, which are always in screen space. */ + /* Update the MD's co-ordinates, which are always in desktop space. */ if (!IsMaster(pDev) || !IsFloating(pDev)) { DeviceIntPtr master = GetMaster(pDev, MASTER_POINTER); master->last.valuators[0] = screenx; @@ -1209,8 +1275,8 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, event->detail.button = buttons; } - /* root_x and root_y must be in screen co-ordinates */ - event_set_root_coordinates(event, screenx, screeny); + /* root_x and root_y must be in per-screen co-ordinates */ + event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y); if (flags & POINTER_EMULATED) { raw->flags = XIPointerEmulated; diff --git a/include/inputstr.h b/include/inputstr.h index 9d4108ef5..1238f930b 100644 --- a/include/inputstr.h +++ b/include/inputstr.h @@ -534,8 +534,9 @@ typedef struct _DeviceIntRec { DeviceIntPtr lastSlave; /* last slave device used */ /* last valuator values recorded, not posted to client; - * for slave devices, valuators is in device coordinates - * for master devices, valuators is in screen coordinates + * for slave devices, valuators is in device coordinates, mapped to the + * desktop + * for master devices, valuators is in desktop coordinates. * see dix/getevents.c * remainder supports acceleration */ diff --git a/mi/mipointer.c b/mi/mipointer.c index 55e4081f2..998c86c15 100644 --- a/mi/mipointer.c +++ b/mi/mipointer.c @@ -569,8 +569,8 @@ miPointerMoveNoEvent (DeviceIntPtr pDev, ScreenPtr pScreen, * * @param pDev The device to move * @param mode Movement mode (Absolute or Relative) - * @param[in,out] screenx The x coordinate in screen coordinates - * @param[in,out] screeny The y coordinate in screen coordinates + * @param[in,out] screenx The x coordinate in desktop coordinates + * @param[in,out] screeny The y coordinate in desktop coordinates */ ScreenPtr miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, double *screeny) @@ -579,6 +579,7 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, double *scree ScreenPtr pScreen; ScreenPtr newScreen; int x, y; + Bool switch_screen = FALSE; miPointerPtr pPointer; @@ -593,7 +594,14 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, double *scree x = trunc(*screenx); y = trunc(*screeny); - if (x < 0 || x >= pScreen->width || y < 0 || y >= pScreen->height) + switch_screen = !point_on_screen(pScreen, x, y); + + /* Switch to per-screen coordinates for CursorOffScreen and + * Pointer->limits */ + x -= pScreen->x; + y -= pScreen->y; + + if (switch_screen) { pScreenPriv = GetScreenPrivate (pScreen); if (!pPointer->confined) @@ -628,6 +636,10 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, double *scree pPointer->pScreen != pScreen) miPointerMoveNoEvent(pDev, pScreen, x, y); + /* Convert to desktop coordinates again */ + x += pScreen->x; + y += pScreen->y; + /* In the event we actually change screen or we get confined, we just * drop the float component on the floor * FIXME: only drop remainder for ConstrainCursorHarder, not for screen