2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
36 * These routines support taking input from devices on the backend
37 * (output) displays. \see dmxcommon.c. */
39 #ifdef HAVE_DMX_CONFIG_H
40 #include <dmx-config.h>
43 #define DMX_BACKEND_DEBUG 0
45 #include "dmxinputinit.h"
46 #include "dmxbackend.h"
47 #include "dmxcommon.h"
48 #include "dmxconsole.h"
49 #include "dmxcursor.h"
52 #include "dmxcb.h" /* For dmxGlobalWidth and dmxGlobalHeight */
53 #include "dmxevents.h" /* For dmxGetGlobalPosition */
54 #include "ChkNotMaskEv.h"
58 #include <X11/keysym.h>
59 #include "mipointer.h"
60 #include "scrnintstr.h"
61 #include "windowstr.h"
63 /* Private area for backend devices. */
64 typedef struct _myPrivate {
67 DMXScreenInfo *grabbedScreen;
80 #define DMXDBG0(f) dmxLog(dmxDebug,f)
81 #define DMXDBG1(f,a) dmxLog(dmxDebug,f,a)
82 #define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
83 #define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c)
84 #define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d)
85 #define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
86 #define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g)
87 #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
88 #define DMXDBG8(f,a,b,c,d,e,g,h,i) dmxLog(dmxDebug,f,a,b,c,d,e,g,h,i)
89 #define DMXDBG9(f,a,b,c,d,e,g,h,i,j) dmxLog(dmxDebug,f,a,b,c,d,e,g,h,i,j)
93 #define DMXDBG2(f,a,b)
94 #define DMXDBG3(f,a,b,c)
95 #define DMXDBG4(f,a,b,c,d)
96 #define DMXDBG5(f,a,b,c,d,e)
97 #define DMXDBG6(f,a,b,c,d,e,g)
98 #define DMXDBG7(f,a,b,c,d,e,g,h)
99 #define DMXDBG8(f,a,b,c,d,e,g,h,i)
100 #define DMXDBG9(f,a,b,c,d,e,g,h,i,j)
103 /** Create and return a private data structure. */
104 pointer dmxBackendCreatePrivate(DeviceIntPtr pDevice)
106 GETDMXLOCALFROMPDEVICE;
107 myPrivate *priv = calloc(1, sizeof(*priv));
108 priv->dmxLocal = dmxLocal;
112 /** Destroy the private data structure. No checking is performed to
113 * verify that the structure was actually created by
114 * #dmxBackendCreatePrivate. */
115 void dmxBackendDestroyPrivate(pointer private)
120 static void *dmxBackendTestScreen(DMXScreenInfo *dmxScreen, void *closure)
122 long target = (long)closure;
124 if (dmxScreen->index == target) return dmxScreen;
128 /* Return non-zero if screen and priv->myScreen are on the same physical
129 * backend display (1 if they are the same screen, 2 if they are
130 * different screens). Since this is a common operation, the results
131 * are cached. The cache is invalidated if \a priv is NULL (this should
132 * be done with each server generation and reconfiguration). */
133 static int dmxBackendSameDisplay(myPrivate *priv, long screen)
135 static myPrivate *oldpriv = NULL;
136 static int oldscreen = -1;
137 static int retcode = 0;
139 if (priv == oldpriv && screen == oldscreen) return retcode;
140 if (!priv) { /* Invalidate cache */
147 if (screen == priv->myScreen) retcode = 1;
148 else if (screen < 0 || screen >= dmxNumScreens) retcode = 0;
149 else if (dmxPropertyIterate(priv->be,
150 dmxBackendTestScreen,
151 (void *)screen)) retcode = 2;
159 static void *dmxBackendTestEvents(DMXScreenInfo *dmxScreen, void *closure)
161 XEvent *X = (XEvent *)closure;
163 if (XCheckNotMaskEvent(dmxScreen->beDisplay, ExposureMask, X))
168 static void *dmxBackendTestMotionEvent(DMXScreenInfo *dmxScreen, void *closure)
170 XEvent *X = (XEvent *)closure;
172 if (XCheckTypedEvent(dmxScreen->beDisplay, MotionNotify, X))
177 static DMXScreenInfo *dmxBackendGetEvent(myPrivate *priv, XEvent *X)
179 DMXScreenInfo *dmxScreen;
181 if ((dmxScreen = dmxPropertyIterate(priv->be, dmxBackendTestEvents, X)))
186 static DMXScreenInfo *dmxBackendPendingMotionEvent(myPrivate *priv, int save)
188 DMXScreenInfo *dmxScreen;
191 if ((dmxScreen = dmxPropertyIterate(priv->be,
192 dmxBackendTestMotionEvent, &N))) {
193 if (save) XPutBackEvent(dmxScreen->beDisplay, &N);
199 static void *dmxBackendTestWindow(DMXScreenInfo *dmxScreen, void *closure)
201 Window win = (Window)(long)closure;
202 if (dmxScreen->scrnWin == win) return dmxScreen;
206 static DMXScreenInfo *dmxBackendFindWindow(myPrivate *priv, Window win)
208 return dmxPropertyIterate(priv->be, dmxBackendTestWindow,
212 /* If the cursor is over a set of overlapping screens and one of those
213 * screens takes backend input, then we want that particular screen to
214 * be current, not one of the other ones. */
215 static int dmxBackendFindOverlapping(myPrivate *priv, int screen, int x, int y)
217 DMXScreenInfo *start = &dmxScreens[screen];
220 if (!start->over) return screen;
222 for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
223 if (pt->index == priv->myScreen
224 && dmxOnScreen(x, y, &dmxScreens[pt->index])) return pt->index;
225 if (pt == start) break;
230 /* Return non-zero if \a x and \a y are off \a screen. */
231 static int dmxBackendOffscreen(int screen, int x, int y)
233 DMXScreenInfo *dmxScreen = &dmxScreens[screen];
235 return (!dmxOnScreen(x, y, dmxScreen));
238 /** This routine is called from #dmxCoreMotion for each motion
239 * event. \a x and \a y are global coordinants. */
240 void dmxBackendUpdatePosition(pointer private, int x, int y)
243 int screen = miPointerGetScreen(inputInfo.pointer)->myNum;
244 DMXScreenInfo *dmxScreen = &dmxScreens[priv->myScreen];
245 int oldRelative = priv->relative;
246 int topscreen = dmxBackendFindOverlapping(priv, screen, x, y);
247 int same = dmxBackendSameDisplay(priv, topscreen);
248 int offscreen = dmxBackendOffscreen(priv->myScreen, x, y);
249 int offthis = dmxBackendOffscreen(screen, x, y);
251 DMXDBG9("dmxBackendUpdatePosition(%d,%d) my=%d mi=%d rel=%d"
252 " topscreen=%d same=%d offscreen=%d offthis=%d\n",
253 x, y, priv->myScreen, screen, priv->relative,
254 topscreen, same, offscreen, offthis);
257 /* If the cursor is off the input screen, it should be moving
258 * relative unless it is visible on a screen of the same display
259 * (i.e., one that shares the mouse). */
260 if (same == 2 && !offthis) {
261 if (priv->relative) {
262 DMXDBG0(" Off screen, but not absolute\n");
266 if (!priv->relative) {
267 DMXDBG0(" Off screen, but not relative\n");
272 if (topscreen != screen) {
273 DMXDBG2(" Using screen %d instead of %d (from mi)\n",
277 if (priv->relative) {
278 DMXDBG0(" On screen, but not absolute\n");
282 if (!priv->relative) {
283 DMXDBG0(" Not on screen, but not relative\n");
289 if (oldRelative != priv->relative) {
290 DMXDBG2(" Do switch, relative=%d same=%d\n",
291 priv->relative, same);
292 /* Discard all pre-switch events */
293 dmxSync(dmxScreen, TRUE);
294 while (dmxBackendPendingMotionEvent(priv, FALSE));
296 if (dmxInput->console && offscreen) {
297 /* Our special case is a console window and a backend window
298 * share a display. In this case, the cursor is either on
299 * the backend window (taking absolute input), or not (in
300 * which case the cursor needs to be in the console
302 if (priv->grabbedScreen) {
303 DMXDBG2(" *** force ungrab on %s, display=%p\n",
304 priv->grabbedScreen->name,
305 priv->grabbedScreen->beDisplay);
306 XUngrabPointer(priv->grabbedScreen->beDisplay, CurrentTime);
307 dmxSync(priv->grabbedScreen, TRUE);
308 priv->grabbedScreen = NULL;
310 DMXDBG0(" Capturing console\n");
311 dmxConsoleCapture(dmxInput);
314 if (priv->relative && !dmxInput->console) {
315 DMXDBG5(" Hide cursor; warp from %d,%d to %d,%d on %d\n",
316 priv->lastX, priv->lastY, priv->centerX, priv->centerY,
318 dmxConsoleUncapture(dmxInput);
319 dmxHideCursor(dmxScreen);
320 priv->lastX = priv->centerX;
321 priv->lastY = priv->centerY;
322 XWarpPointer(priv->display, None, priv->window,
323 0, 0, 0, 0, priv->lastX, priv->lastY);
324 dmxSync(dmxScreen, TRUE);
326 DMXDBG0(" Check cursor\n");
333 /** Get events from the X queue on the backend servers and put the
334 * events into the DMX event queue. */
335 void dmxBackendCollectEvents(DevicePtr pDev,
336 dmxMotionProcPtr motion,
337 dmxEnqueueProcPtr enqueue,
338 dmxCheckSpecialProcPtr checkspecial,
344 DMXScreenInfo *dmxScreen;
346 int entered = priv->entered;
351 while ((dmxScreen = dmxBackendGetEvent(priv, &X))) {
354 dmxCommonSaveState(priv);
359 DMXDBG5("dmxBackendCollectEvents: Enter %lu %d,%d; GRAB %s %p\n",
360 X.xcrossing.root, X.xcrossing.x, X.xcrossing.y,
361 dmxScreen->name, dmxScreen->beDisplay);
362 XRaiseWindow(dmxScreen->beDisplay, dmxScreen->scrnWin);
363 priv->grabbedScreen = dmxScreen;
364 if ((retcode = XGrabPointer(dmxScreen->beDisplay,
366 True, 0, GrabModeAsync,
367 GrabModeAsync, None, None,
370 "XGrabPointer failed during backend enter (%d)\n",
379 dmxCommonRestoreState(priv);
382 DMXDBG7("dmxBackendCollectEvents: Leave %lu %d,%d %d %d %s %s\n",
383 X.xcrossing.root, X.xcrossing.x, X.xcrossing.y,
384 X.xcrossing.detail, X.xcrossing.focus,
385 priv->grabbedScreen ? "UNGRAB" : "",
387 if (priv->grabbedScreen) {
388 XUngrabPointer(priv->grabbedScreen->beDisplay, CurrentTime);
389 dmxSync(priv->grabbedScreen, TRUE);
390 priv->grabbedScreen = NULL;
394 DMXDBG9("dmxBackendCollectEvents: MotionNotify %d/%d (mi %d)"
395 " newscreen=%d: %d %d (e=%d; last=%d,%d)\n",
396 dmxScreen->index, priv->myScreen,
397 miPointerCurrentScreen()->myNum,
399 X.xmotion.x, X.xmotion.y,
400 entered, priv->lastX, priv->lastY);
401 if (dmxBackendPendingMotionEvent(priv, TRUE))
403 if (!(dmxScreen = dmxBackendFindWindow(priv, X.xmotion.window)))
405 " Event on non-existant window %lu\n",
407 if (!priv->relative || dmxInput->console) {
408 int newX = X.xmotion.x - dmxScreen->rootX;
409 int newY = X.xmotion.y - dmxScreen->rootY;
411 if (!priv->newscreen) {
412 int width = dmxScreen->rootWidth;
413 int height = dmxScreen->rootHeight;
414 if (!newX) newX = -1;
415 if (newX == width - 1) newX = width;
416 if (!newY) newY = -1;
417 if (newY == height - 1) newY = height;
420 v[0] = dmxScreen->rootXOrigin + newX;
421 v[1] = dmxScreen->rootYOrigin + newY;
422 DMXDBG8(" Absolute move: %d,%d (r=%dx%d+%d+%d s=%dx%d)\n",
424 priv->be->rootWidth, priv->be->rootHeight,
425 priv->be->rootX, priv->be->rootY,
426 priv->be->scrnWidth, priv->be->scrnHeight);
427 motion(priv->mou, v, 0, 2, DMX_ABSOLUTE, block);
430 int newX = priv->lastX - X.xmotion.x;
431 int newY = priv->lastY - X.xmotion.y;
432 priv->lastX = X.xmotion.x;
433 priv->lastY = X.xmotion.y;
436 DMXDBG2(" Relative move: %d, %d\n", v[0], v[1]);
437 motion(priv->mou, v, 0, 2, DMX_RELATIVE, block);
439 if (entered && priv->relative) {
440 DMXDBG4(" **** Relative %d %d instead of absolute %d %d\n",
442 (dmxScreen->rootXOrigin + X.xmotion.x
444 (dmxScreen->rootYOrigin + X.xmotion.y
445 - dmxScreen->rootY));
451 enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
457 /* Pass the whole event here, because
458 * this may be an extension event. */
459 enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block);
465 /** Called after input events are processed from the DMX queue. No
466 * event processing actually takes place here, but this is a convenient
467 * place to update the pointer. */
468 void dmxBackendProcessInput(pointer private)
472 DMXDBG6("dmxBackendProcessInput: myScreen=%d relative=%d"
473 " last=%d,%d center=%d,%d\n",
474 priv->myScreen, priv->relative,
475 priv->lastX, priv->lastY,
476 priv->centerX, priv->centerY);
479 && !dmxInput->console
480 && (priv->lastX != priv->centerX || priv->lastY != priv->centerY)) {
481 DMXDBG4(" warping pointer from last=%d,%d to center=%d,%d\n",
482 priv->lastX, priv->lastY, priv->centerX, priv->centerY);
483 priv->lastX = priv->centerX;
484 priv->lastY = priv->centerY;
485 XWarpPointer(priv->display, None, priv->window,
486 0, 0, 0, 0, priv->lastX, priv->lastY);
487 dmxSync(&dmxScreens[priv->myScreen], TRUE);
491 static void dmxBackendComputeCenter(myPrivate *priv)
496 centerX = priv->be->rootWidth / 2 + priv->be->rootX;
497 centerY = priv->be->rootHeight / 2 + priv->be->rootY;
499 if (centerX > priv->be->rootWidth) centerX = priv->be->rootWidth - 1;
500 if (centerY > priv->be->rootHeight) centerY = priv->be->rootHeight - 1;
501 if (centerX < 1) centerX = 1;
502 if (centerY < 1) centerY = 1;
504 priv->centerX = centerX;
505 priv->centerY = centerY;
508 static DMXScreenInfo *dmxBackendInitPrivate(DevicePtr pDev)
511 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
512 DMXScreenInfo *dmxScreen;
515 /* Fill in myPrivate */
516 for (i = 0,dmxScreen = &dmxScreens[0]; i<dmxNumScreens; i++,dmxScreen++) {
517 if (dmxPropertySameDisplay(dmxScreen, dmxInput->name)) {
518 priv->display = dmxScreen->beDisplay;
519 priv->window = dmxScreen->scrnWin;
520 priv->be = dmxScreen;
525 if (i >= dmxNumScreens)
527 "%s is not an existing backend display - cannot initialize\n",
533 /** Re-initialized the backend device described by \a pDev (after a
535 void dmxBackendLateReInit(DevicePtr pDev)
540 DMXDBG1("dmxBackendLateReInit miPointerCurrentScreen() = %p\n",
541 miPointerCurrentScreen());
543 dmxBackendSameDisplay(NULL, 0); /* Invalidate cache */
544 dmxBackendInitPrivate(pDev);
545 dmxBackendComputeCenter(priv);
546 dmxGetGlobalPosition(&x, &y);
547 dmxInvalidateGlobalPosition(); /* To force event processing */
548 dmxBackendUpdatePosition(priv, x, y);
551 /** Initialized the backend device described by \a pDev. */
552 void dmxBackendInit(DevicePtr pDev)
555 DMXScreenInfo *dmxScreen;
557 dmxBackendSameDisplay(NULL, 0); /* Invalidate cache */
559 if (dmxLocal->type == DMX_LOCAL_MOUSE) priv->mou = pDev;
560 if (dmxLocal->type == DMX_LOCAL_KEYBOARD) priv->kbd = pDev;
561 if (priv->initialized++) return; /* Only do once for mouse/keyboard pair */
563 dmxScreen = dmxBackendInitPrivate(pDev);
565 /* Finish initialization using computed values or constants. */
566 dmxBackendComputeCenter(priv);
567 priv->eventMask = (EnterWindowMask|LeaveWindowMask);
568 priv->myScreen = dmxScreen->index;
569 priv->lastX = priv->centerX;
570 priv->lastY = priv->centerY;
575 /** Get information about the backend pointer (for initialization). */
576 void dmxBackendMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
578 const DMXScreenInfo *dmxScreen = dmxBackendInitPrivate(pDev);
580 info->buttonClass = 1;
581 dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
582 info->valuatorClass = 1;
583 info->numRelAxes = 2;
586 info->maxval[0] = dmxScreen->beWidth;
587 info->maxval[1] = dmxScreen->beHeight;
591 info->ptrFeedbackClass = 1;
594 /** Get information about the backend keyboard (for initialization). */
595 void dmxBackendKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
597 dmxCommonKbdGetInfo(pDev, info);
600 dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
602 info->focusClass = 1;
603 info->kbdFeedbackClass = 1;
606 /** Process #DMXFunctionType functions. The only function handled here
607 * is to acknowledge a pending server shutdown. */
608 int dmxBackendFunctions(pointer private, DMXFunctionType function)
611 case DMX_FUNCTION_TERMINATE: