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. */
105 dmxBackendCreatePrivate(DeviceIntPtr pDevice
)
107 GETDMXLOCALFROMPDEVICE
;
108 myPrivate
*priv
= calloc(1, sizeof(*priv
));
110 priv
->dmxLocal
= dmxLocal
;
114 /** Destroy the private data structure. No checking is performed to
115 * verify that the structure was actually created by
116 * #dmxBackendCreatePrivate. */
118 dmxBackendDestroyPrivate(pointer
private)
124 dmxBackendTestScreen(DMXScreenInfo
* dmxScreen
, void *closure
)
126 long target
= (long) closure
;
128 if (dmxScreen
->index
== target
)
133 /* Return non-zero if screen and priv->myScreen are on the same physical
134 * backend display (1 if they are the same screen, 2 if they are
135 * different screens). Since this is a common operation, the results
136 * are cached. The cache is invalidated if \a priv is NULL (this should
137 * be done with each server generation and reconfiguration). */
139 dmxBackendSameDisplay(myPrivate
* priv
, long screen
)
141 static myPrivate
*oldpriv
= NULL
;
142 static int oldscreen
= -1;
143 static int retcode
= 0;
145 if (priv
== oldpriv
&& screen
== oldscreen
)
147 if (!priv
) { /* Invalidate cache */
154 if (screen
== priv
->myScreen
)
156 else if (screen
< 0 || screen
>= dmxNumScreens
)
158 else if (dmxPropertyIterate(priv
->be
,
159 dmxBackendTestScreen
, (void *) screen
))
170 dmxBackendTestEvents(DMXScreenInfo
* dmxScreen
, void *closure
)
172 XEvent
*X
= (XEvent
*) closure
;
174 if (XCheckNotMaskEvent(dmxScreen
->beDisplay
, ExposureMask
, X
))
180 dmxBackendTestMotionEvent(DMXScreenInfo
* dmxScreen
, void *closure
)
182 XEvent
*X
= (XEvent
*) closure
;
184 if (XCheckTypedEvent(dmxScreen
->beDisplay
, MotionNotify
, X
))
189 static DMXScreenInfo
*
190 dmxBackendGetEvent(myPrivate
* priv
, XEvent
* X
)
192 DMXScreenInfo
*dmxScreen
;
194 if ((dmxScreen
= dmxPropertyIterate(priv
->be
, dmxBackendTestEvents
, X
)))
199 static DMXScreenInfo
*
200 dmxBackendPendingMotionEvent(myPrivate
* priv
, int save
)
202 DMXScreenInfo
*dmxScreen
;
205 if ((dmxScreen
= dmxPropertyIterate(priv
->be
,
206 dmxBackendTestMotionEvent
, &N
))) {
208 XPutBackEvent(dmxScreen
->beDisplay
, &N
);
215 dmxBackendTestWindow(DMXScreenInfo
* dmxScreen
, void *closure
)
217 Window win
= (Window
) (long) closure
;
219 if (dmxScreen
->scrnWin
== win
)
224 static DMXScreenInfo
*
225 dmxBackendFindWindow(myPrivate
* priv
, Window win
)
227 return dmxPropertyIterate(priv
->be
, dmxBackendTestWindow
,
228 (void *) (long) win
);
231 /* If the cursor is over a set of overlapping screens and one of those
232 * screens takes backend input, then we want that particular screen to
233 * be current, not one of the other ones. */
235 dmxBackendFindOverlapping(myPrivate
* priv
, int screen
, int x
, int y
)
237 DMXScreenInfo
*start
= &dmxScreens
[screen
];
243 for (pt
= start
->over
; /* condition at end of loop */ ; pt
= pt
->over
) {
244 if (pt
->index
== priv
->myScreen
245 && dmxOnScreen(x
, y
, &dmxScreens
[pt
->index
]))
253 /* Return non-zero if \a x and \a y are off \a screen. */
255 dmxBackendOffscreen(int screen
, int x
, int y
)
257 DMXScreenInfo
*dmxScreen
= &dmxScreens
[screen
];
259 return (!dmxOnScreen(x
, y
, dmxScreen
));
262 /** This routine is called from #dmxCoreMotion for each motion
263 * event. \a x and \a y are global coordinants. */
265 dmxBackendUpdatePosition(pointer
private, int x
, int y
)
268 int screen
= miPointerGetScreen(inputInfo
.pointer
)->myNum
;
269 DMXScreenInfo
*dmxScreen
= &dmxScreens
[priv
->myScreen
];
270 int oldRelative
= priv
->relative
;
271 int topscreen
= dmxBackendFindOverlapping(priv
, screen
, x
, y
);
272 int same
= dmxBackendSameDisplay(priv
, topscreen
);
273 int offscreen
= dmxBackendOffscreen(priv
->myScreen
, x
, y
);
274 int offthis
= dmxBackendOffscreen(screen
, x
, y
);
276 DMXDBG9("dmxBackendUpdatePosition(%d,%d) my=%d mi=%d rel=%d"
277 " topscreen=%d same=%d offscreen=%d offthis=%d\n",
278 x
, y
, priv
->myScreen
, screen
, priv
->relative
,
279 topscreen
, same
, offscreen
, offthis
);
282 /* If the cursor is off the input screen, it should be moving
283 * relative unless it is visible on a screen of the same display
284 * (i.e., one that shares the mouse). */
285 if (same
== 2 && !offthis
) {
286 if (priv
->relative
) {
287 DMXDBG0(" Off screen, but not absolute\n");
292 if (!priv
->relative
) {
293 DMXDBG0(" Off screen, but not relative\n");
299 if (topscreen
!= screen
) {
300 DMXDBG2(" Using screen %d instead of %d (from mi)\n",
304 if (priv
->relative
) {
305 DMXDBG0(" On screen, but not absolute\n");
310 if (!priv
->relative
) {
311 DMXDBG0(" Not on screen, but not relative\n");
317 if (oldRelative
!= priv
->relative
) {
318 DMXDBG2(" Do switch, relative=%d same=%d\n", priv
->relative
, same
);
319 /* Discard all pre-switch events */
320 dmxSync(dmxScreen
, TRUE
);
321 while (dmxBackendPendingMotionEvent(priv
, FALSE
));
323 if (dmxInput
->console
&& offscreen
) {
324 /* Our special case is a console window and a backend window
325 * share a display. In this case, the cursor is either on
326 * the backend window (taking absolute input), or not (in
327 * which case the cursor needs to be in the console
329 if (priv
->grabbedScreen
) {
330 DMXDBG2(" *** force ungrab on %s, display=%p\n",
331 priv
->grabbedScreen
->name
,
332 priv
->grabbedScreen
->beDisplay
);
333 XUngrabPointer(priv
->grabbedScreen
->beDisplay
, CurrentTime
);
334 dmxSync(priv
->grabbedScreen
, TRUE
);
335 priv
->grabbedScreen
= NULL
;
337 DMXDBG0(" Capturing console\n");
338 dmxConsoleCapture(dmxInput
);
342 if (priv
->relative
&& !dmxInput
->console
) {
343 DMXDBG5(" Hide cursor; warp from %d,%d to %d,%d on %d\n",
344 priv
->lastX
, priv
->lastY
, priv
->centerX
, priv
->centerY
,
346 dmxConsoleUncapture(dmxInput
);
347 dmxHideCursor(dmxScreen
);
348 priv
->lastX
= priv
->centerX
;
349 priv
->lastY
= priv
->centerY
;
350 XWarpPointer(priv
->display
, None
, priv
->window
,
351 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
352 dmxSync(dmxScreen
, TRUE
);
355 DMXDBG0(" Check cursor\n");
362 /** Get events from the X queue on the backend servers and put the
363 * events into the DMX event queue. */
365 dmxBackendCollectEvents(DevicePtr pDev
,
366 dmxMotionProcPtr motion
,
367 dmxEnqueueProcPtr enqueue
,
368 dmxCheckSpecialProcPtr checkspecial
, DMXBlockType block
)
373 DMXScreenInfo
*dmxScreen
;
375 int entered
= priv
->entered
;
380 while ((dmxScreen
= dmxBackendGetEvent(priv
, &X
))) {
383 dmxCommonSaveState(priv
);
388 DMXDBG5("dmxBackendCollectEvents: Enter %lu %d,%d; GRAB %s %p\n",
389 X
.xcrossing
.root
, X
.xcrossing
.x
, X
.xcrossing
.y
,
390 dmxScreen
->name
, dmxScreen
->beDisplay
);
391 XRaiseWindow(dmxScreen
->beDisplay
, dmxScreen
->scrnWin
);
392 priv
->grabbedScreen
= dmxScreen
;
393 if ((retcode
= XGrabPointer(dmxScreen
->beDisplay
,
395 True
, 0, GrabModeAsync
,
396 GrabModeAsync
, None
, None
,
399 "XGrabPointer failed during backend enter (%d)\n",
408 dmxCommonRestoreState(priv
);
411 DMXDBG7("dmxBackendCollectEvents: Leave %lu %d,%d %d %d %s %s\n",
412 X
.xcrossing
.root
, X
.xcrossing
.x
, X
.xcrossing
.y
,
413 X
.xcrossing
.detail
, X
.xcrossing
.focus
,
414 priv
->grabbedScreen
? "UNGRAB" : "", dmxScreen
->name
);
415 if (priv
->grabbedScreen
) {
416 XUngrabPointer(priv
->grabbedScreen
->beDisplay
, CurrentTime
);
417 dmxSync(priv
->grabbedScreen
, TRUE
);
418 priv
->grabbedScreen
= NULL
;
422 DMXDBG8("dmxBackendCollectEvents: MotionNotify %d/%d"
423 " newscreen=%d: %d %d (e=%d; last=%d,%d)\n",
424 dmxScreen
->index
, priv
->myScreen
,
426 X
.xmotion
.x
, X
.xmotion
.y
,
427 entered
, priv
->lastX
, priv
->lastY
);
428 if (dmxBackendPendingMotionEvent(priv
, TRUE
))
430 if (!(dmxScreen
= dmxBackendFindWindow(priv
, X
.xmotion
.window
)))
432 " Event on non-existant window %lu\n",
434 if (!priv
->relative
|| dmxInput
->console
) {
435 int newX
= X
.xmotion
.x
- dmxScreen
->rootX
;
436 int newY
= X
.xmotion
.y
- dmxScreen
->rootY
;
438 if (!priv
->newscreen
) {
439 int width
= dmxScreen
->rootWidth
;
440 int height
= dmxScreen
->rootHeight
;
444 if (newX
== width
- 1)
448 if (newY
== height
- 1)
452 v
[0] = dmxScreen
->rootXOrigin
+ newX
;
453 v
[1] = dmxScreen
->rootYOrigin
+ newY
;
454 DMXDBG8(" Absolute move: %d,%d (r=%dx%d+%d+%d s=%dx%d)\n",
456 priv
->be
->rootWidth
, priv
->be
->rootHeight
,
457 priv
->be
->rootX
, priv
->be
->rootY
,
458 priv
->be
->scrnWidth
, priv
->be
->scrnHeight
);
459 motion(priv
->mou
, v
, 0, 2, DMX_ABSOLUTE
, block
);
463 int newX
= priv
->lastX
- X
.xmotion
.x
;
464 int newY
= priv
->lastY
- X
.xmotion
.y
;
466 priv
->lastX
= X
.xmotion
.x
;
467 priv
->lastY
= X
.xmotion
.y
;
470 DMXDBG2(" Relative move: %d, %d\n", v
[0], v
[1]);
471 motion(priv
->mou
, v
, 0, 2, DMX_RELATIVE
, block
);
473 if (entered
&& priv
->relative
) {
474 DMXDBG4(" **** Relative %d %d instead of absolute %d %d\n",
476 (dmxScreen
->rootXOrigin
+ X
.xmotion
.x
478 (dmxScreen
->rootYOrigin
+ X
.xmotion
.y
479 - dmxScreen
->rootY
));
485 enqueue(priv
->kbd
, X
.type
, X
.xkey
.keycode
, 0, NULL
, block
);
491 /* Pass the whole event here, because
492 * this may be an extension event. */
493 enqueue(priv
->mou
, X
.type
, X
.xbutton
.button
, 0, &X
, block
);
499 /** Called after input events are processed from the DMX queue. No
500 * event processing actually takes place here, but this is a convenient
501 * place to update the pointer. */
503 dmxBackendProcessInput(pointer
private)
507 DMXDBG6("dmxBackendProcessInput: myScreen=%d relative=%d"
508 " last=%d,%d center=%d,%d\n",
509 priv
->myScreen
, priv
->relative
,
510 priv
->lastX
, priv
->lastY
, priv
->centerX
, priv
->centerY
);
513 && !dmxInput
->console
514 && (priv
->lastX
!= priv
->centerX
|| priv
->lastY
!= priv
->centerY
)) {
515 DMXDBG4(" warping pointer from last=%d,%d to center=%d,%d\n",
516 priv
->lastX
, priv
->lastY
, priv
->centerX
, priv
->centerY
);
517 priv
->lastX
= priv
->centerX
;
518 priv
->lastY
= priv
->centerY
;
519 XWarpPointer(priv
->display
, None
, priv
->window
,
520 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
521 dmxSync(&dmxScreens
[priv
->myScreen
], TRUE
);
526 dmxBackendComputeCenter(myPrivate
* priv
)
531 centerX
= priv
->be
->rootWidth
/ 2 + priv
->be
->rootX
;
532 centerY
= priv
->be
->rootHeight
/ 2 + priv
->be
->rootY
;
534 if (centerX
> priv
->be
->rootWidth
)
535 centerX
= priv
->be
->rootWidth
- 1;
536 if (centerY
> priv
->be
->rootHeight
)
537 centerY
= priv
->be
->rootHeight
- 1;
543 priv
->centerX
= centerX
;
544 priv
->centerY
= centerY
;
547 static DMXScreenInfo
*
548 dmxBackendInitPrivate(DevicePtr pDev
)
551 DMXInputInfo
*dmxInput
= &dmxInputs
[dmxLocal
->inputIdx
];
552 DMXScreenInfo
*dmxScreen
;
555 /* Fill in myPrivate */
556 for (i
= 0, dmxScreen
= &dmxScreens
[0]; i
< dmxNumScreens
; i
++, dmxScreen
++) {
557 if (dmxPropertySameDisplay(dmxScreen
, dmxInput
->name
)) {
558 priv
->display
= dmxScreen
->beDisplay
;
559 priv
->window
= dmxScreen
->scrnWin
;
560 priv
->be
= dmxScreen
;
565 if (i
>= dmxNumScreens
)
567 "%s is not an existing backend display - cannot initialize\n",
573 /** Re-initialized the backend device described by \a pDev (after a
576 dmxBackendLateReInit(DevicePtr pDev
)
581 dmxBackendSameDisplay(NULL
, 0); /* Invalidate cache */
582 dmxBackendInitPrivate(pDev
);
583 dmxBackendComputeCenter(priv
);
584 dmxGetGlobalPosition(&x
, &y
);
585 dmxInvalidateGlobalPosition(); /* To force event processing */
586 dmxBackendUpdatePosition(priv
, x
, y
);
589 /** Initialized the backend device described by \a pDev. */
591 dmxBackendInit(DevicePtr pDev
)
594 DMXScreenInfo
*dmxScreen
;
596 dmxBackendSameDisplay(NULL
, 0); /* Invalidate cache */
598 if (dmxLocal
->type
== DMX_LOCAL_MOUSE
)
600 if (dmxLocal
->type
== DMX_LOCAL_KEYBOARD
)
602 if (priv
->initialized
++)
603 return; /* Only do once for mouse/keyboard pair */
605 dmxScreen
= dmxBackendInitPrivate(pDev
);
607 /* Finish initialization using computed values or constants. */
608 dmxBackendComputeCenter(priv
);
609 priv
->eventMask
= (EnterWindowMask
| LeaveWindowMask
);
610 priv
->myScreen
= dmxScreen
->index
;
611 priv
->lastX
= priv
->centerX
;
612 priv
->lastY
= priv
->centerY
;
617 /** Get information about the backend pointer (for initialization). */
619 dmxBackendMouGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
621 const DMXScreenInfo
*dmxScreen
= dmxBackendInitPrivate(pDev
);
623 info
->buttonClass
= 1;
624 dmxCommonMouGetMap(pDev
, info
->map
, &info
->numButtons
);
625 info
->valuatorClass
= 1;
626 info
->numRelAxes
= 2;
629 info
->maxval
[0] = dmxScreen
->beWidth
;
630 info
->maxval
[1] = dmxScreen
->beHeight
;
634 info
->ptrFeedbackClass
= 1;
637 /** Get information about the backend keyboard (for initialization). */
639 dmxBackendKbdGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
641 dmxCommonKbdGetInfo(pDev
, info
);
644 dmxCommonKbdGetMap(pDev
, &info
->keySyms
, info
->modMap
);
646 info
->focusClass
= 1;
647 info
->kbdFeedbackClass
= 1;
650 /** Process #DMXFunctionType functions. The only function handled here
651 * is to acknowledge a pending server shutdown. */
653 dmxBackendFunctions(pointer
private, DMXFunctionType function
)
656 case DMX_FUNCTION_TERMINATE
: