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
= xalloc(sizeof(*priv
));
108 memset(priv
, 0, sizeof(*priv
));
109 priv
->dmxLocal
= dmxLocal
;
113 /** Destroy the private data structure. No checking is performed to
114 * verify that the structure was actually created by
115 * #dmxBackendCreatePrivate. */
116 void dmxBackendDestroyPrivate(pointer
private)
118 if (private) xfree(private);
121 static void *dmxBackendTestScreen(DMXScreenInfo
*dmxScreen
, void *closure
)
123 long target
= (long)closure
;
125 if (dmxScreen
->index
== target
) return dmxScreen
;
129 /* Return non-zero if screen and priv->myScreen are on the same physical
130 * backend display (1 if they are the same screen, 2 if they are
131 * different screens). Since this is a common operation, the results
132 * are cached. The cache is invalidated if \a priv is NULL (this should
133 * be done with each server generation and reconfiguration). */
134 static int dmxBackendSameDisplay(myPrivate
*priv
, long screen
)
136 static myPrivate
*oldpriv
= NULL
;
137 static int oldscreen
= -1;
138 static int retcode
= 0;
140 if (priv
== oldpriv
&& screen
== oldscreen
) return retcode
;
141 if (!priv
) { /* Invalidate cache */
148 if (screen
== priv
->myScreen
) retcode
= 1;
149 else if (screen
< 0 || screen
>= dmxNumScreens
) retcode
= 0;
150 else if (dmxPropertyIterate(priv
->be
,
151 dmxBackendTestScreen
,
152 (void *)screen
)) retcode
= 2;
160 static void *dmxBackendTestEvents(DMXScreenInfo
*dmxScreen
, void *closure
)
162 XEvent
*X
= (XEvent
*)closure
;
164 if (XCheckNotMaskEvent(dmxScreen
->beDisplay
, ExposureMask
, X
))
169 static void *dmxBackendTestMotionEvent(DMXScreenInfo
*dmxScreen
, void *closure
)
171 XEvent
*X
= (XEvent
*)closure
;
173 if (XCheckTypedEvent(dmxScreen
->beDisplay
, MotionNotify
, X
))
178 static DMXScreenInfo
*dmxBackendGetEvent(myPrivate
*priv
, XEvent
*X
)
180 DMXScreenInfo
*dmxScreen
;
182 if ((dmxScreen
= dmxPropertyIterate(priv
->be
, dmxBackendTestEvents
, X
)))
187 static DMXScreenInfo
*dmxBackendPendingMotionEvent(myPrivate
*priv
, int save
)
189 DMXScreenInfo
*dmxScreen
;
192 if ((dmxScreen
= dmxPropertyIterate(priv
->be
,
193 dmxBackendTestMotionEvent
, &N
))) {
194 if (save
) XPutBackEvent(dmxScreen
->beDisplay
, &N
);
200 static void *dmxBackendTestWindow(DMXScreenInfo
*dmxScreen
, void *closure
)
202 Window win
= (Window
)(long)closure
;
203 if (dmxScreen
->scrnWin
== win
) return dmxScreen
;
207 static DMXScreenInfo
*dmxBackendFindWindow(myPrivate
*priv
, Window win
)
209 return dmxPropertyIterate(priv
->be
, dmxBackendTestWindow
,
213 /* If the cursor is over a set of overlapping screens and one of those
214 * screens takes backend input, then we want that particular screen to
215 * be current, not one of the other ones. */
216 static int dmxBackendFindOverlapping(myPrivate
*priv
, int screen
, int x
, int y
)
218 DMXScreenInfo
*start
= &dmxScreens
[screen
];
221 if (!start
->over
) return screen
;
223 for (pt
= start
->over
; /* condition at end of loop */; pt
= pt
->over
) {
224 if (pt
->index
== priv
->myScreen
225 && dmxOnScreen(x
, y
, &dmxScreens
[pt
->index
])) return pt
->index
;
226 if (pt
== start
) break;
231 /* Return non-zero if \a x and \a y are off \a screen. */
232 static int dmxBackendOffscreen(int screen
, int x
, int y
)
234 DMXScreenInfo
*dmxScreen
= &dmxScreens
[screen
];
236 return (!dmxOnScreen(x
, y
, dmxScreen
));
239 /** This routine is called from #dmxCoreMotion for each motion
240 * event. #x and #y are global coordinants. */
241 void dmxBackendUpdatePosition(pointer
private, int x
, int y
)
244 int screen
= miPointerGetScreen(inputInfo
.pointer
)->myNum
;
245 DMXScreenInfo
*dmxScreen
= &dmxScreens
[priv
->myScreen
];
246 int oldRelative
= priv
->relative
;
247 int topscreen
= dmxBackendFindOverlapping(priv
, screen
, x
, y
);
248 int same
= dmxBackendSameDisplay(priv
, topscreen
);
249 int offscreen
= dmxBackendOffscreen(priv
->myScreen
, x
, y
);
250 int offthis
= dmxBackendOffscreen(screen
, x
, y
);
252 DMXDBG9("dmxBackendUpdatePosition(%d,%d) my=%d mi=%d rel=%d"
253 " topscreen=%d same=%d offscreen=%d offthis=%d\n",
254 x
, y
, priv
->myScreen
, screen
, priv
->relative
,
255 topscreen
, same
, offscreen
, offthis
);
258 /* If the cursor is off the input screen, it should be moving
259 * relative unless it is visible on a screen of the same display
260 * (i.e., one that shares the mouse). */
261 if (same
== 2 && !offthis
) {
262 if (priv
->relative
) {
263 DMXDBG0(" Off screen, but not absolute\n");
267 if (!priv
->relative
) {
268 DMXDBG0(" Off screen, but not relative\n");
273 if (topscreen
!= screen
) {
274 DMXDBG2(" Using screen %d instead of %d (from mi)\n",
278 if (priv
->relative
) {
279 DMXDBG0(" On screen, but not absolute\n");
283 if (!priv
->relative
) {
284 DMXDBG0(" Not on screen, but not relative\n");
290 if (oldRelative
!= priv
->relative
) {
291 DMXDBG2(" Do switch, relative=%d same=%d\n",
292 priv
->relative
, same
);
293 /* Discard all pre-switch events */
294 dmxSync(dmxScreen
, TRUE
);
295 while (dmxBackendPendingMotionEvent(priv
, FALSE
));
297 if (dmxInput
->console
&& offscreen
) {
298 /* Our special case is a console window and a backend window
299 * share a display. In this case, the cursor is either on
300 * the backend window (taking absolute input), or not (in
301 * which case the cursor needs to be in the console
303 if (priv
->grabbedScreen
) {
304 DMXDBG2(" *** force ungrab on %s, display=%p\n",
305 priv
->grabbedScreen
->name
,
306 priv
->grabbedScreen
->beDisplay
);
307 XUngrabPointer(priv
->grabbedScreen
->beDisplay
, CurrentTime
);
308 dmxSync(priv
->grabbedScreen
, TRUE
);
309 priv
->grabbedScreen
= NULL
;
311 DMXDBG0(" Capturing console\n");
312 dmxConsoleCapture(dmxInput
);
315 if (priv
->relative
&& !dmxInput
->console
) {
316 DMXDBG5(" Hide cursor; warp from %d,%d to %d,%d on %d\n",
317 priv
->lastX
, priv
->lastY
, priv
->centerX
, priv
->centerY
,
319 dmxConsoleUncapture(dmxInput
);
320 dmxHideCursor(dmxScreen
);
321 priv
->lastX
= priv
->centerX
;
322 priv
->lastY
= priv
->centerY
;
323 XWarpPointer(priv
->display
, None
, priv
->window
,
324 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
325 dmxSync(dmxScreen
, TRUE
);
327 DMXDBG0(" Check cursor\n");
334 /** Get events from the X queue on the backend servers and put the
335 * events into the DMX event queue. */
336 void dmxBackendCollectEvents(DevicePtr pDev
,
337 dmxMotionProcPtr motion
,
338 dmxEnqueueProcPtr enqueue
,
339 dmxCheckSpecialProcPtr checkspecial
,
345 DMXScreenInfo
*dmxScreen
;
347 int entered
= priv
->entered
;
352 while ((dmxScreen
= dmxBackendGetEvent(priv
, &X
))) {
355 dmxCommonSaveState(priv
);
360 DMXDBG5("dmxBackendCollectEvents: Enter %lu %d,%d; GRAB %s %p\n",
361 X
.xcrossing
.root
, X
.xcrossing
.x
, X
.xcrossing
.y
,
362 dmxScreen
->name
, dmxScreen
->beDisplay
);
363 XRaiseWindow(dmxScreen
->beDisplay
, dmxScreen
->scrnWin
);
364 priv
->grabbedScreen
= dmxScreen
;
365 if ((retcode
= XGrabPointer(dmxScreen
->beDisplay
,
367 True
, 0, GrabModeAsync
,
368 GrabModeAsync
, None
, None
,
371 "XGrabPointer failed during backend enter (%d)\n",
380 dmxCommonRestoreState(priv
);
383 DMXDBG7("dmxBackendCollectEvents: Leave %lu %d,%d %d %d %s %s\n",
384 X
.xcrossing
.root
, X
.xcrossing
.x
, X
.xcrossing
.y
,
385 X
.xcrossing
.detail
, X
.xcrossing
.focus
,
386 priv
->grabbedScreen
? "UNGRAB" : "",
388 if (priv
->grabbedScreen
) {
389 XUngrabPointer(priv
->grabbedScreen
->beDisplay
, CurrentTime
);
390 dmxSync(priv
->grabbedScreen
, TRUE
);
391 priv
->grabbedScreen
= NULL
;
395 DMXDBG9("dmxBackendCollectEvents: MotionNotify %d/%d (mi %d)"
396 " newscreen=%d: %d %d (e=%d; last=%d,%d)\n",
397 dmxScreen
->index
, priv
->myScreen
,
398 miPointerCurrentScreen()->myNum
,
400 X
.xmotion
.x
, X
.xmotion
.y
,
401 entered
, priv
->lastX
, priv
->lastY
);
402 if (dmxBackendPendingMotionEvent(priv
, TRUE
))
404 if (!(dmxScreen
= dmxBackendFindWindow(priv
, X
.xmotion
.window
)))
406 " Event on non-existant window %lu\n",
408 if (!priv
->relative
|| dmxInput
->console
) {
409 int newX
= X
.xmotion
.x
- dmxScreen
->rootX
;
410 int newY
= X
.xmotion
.y
- dmxScreen
->rootY
;
412 if (!priv
->newscreen
) {
413 int width
= dmxScreen
->rootWidth
;
414 int height
= dmxScreen
->rootHeight
;
415 if (!newX
) newX
= -1;
416 if (newX
== width
- 1) newX
= width
;
417 if (!newY
) newY
= -1;
418 if (newY
== height
- 1) newY
= height
;
421 v
[0] = dmxScreen
->rootXOrigin
+ newX
;
422 v
[1] = dmxScreen
->rootYOrigin
+ newY
;
423 DMXDBG8(" Absolute move: %d,%d (r=%dx%d+%d+%d s=%dx%d)\n",
425 priv
->be
->rootWidth
, priv
->be
->rootHeight
,
426 priv
->be
->rootX
, priv
->be
->rootY
,
427 priv
->be
->scrnWidth
, priv
->be
->scrnHeight
);
428 motion(priv
->mou
, v
, 0, 2, DMX_ABSOLUTE
, block
);
431 int newX
= priv
->lastX
- X
.xmotion
.x
;
432 int newY
= priv
->lastY
- X
.xmotion
.y
;
433 priv
->lastX
= X
.xmotion
.x
;
434 priv
->lastY
= X
.xmotion
.y
;
437 DMXDBG2(" Relative move: %d, %d\n", v
[0], v
[1]);
438 motion(priv
->mou
, v
, 0, 2, DMX_RELATIVE
, block
);
440 if (entered
&& priv
->relative
) {
441 DMXDBG4(" **** Relative %d %d instead of absolute %d %d\n",
443 (dmxScreen
->rootXOrigin
+ X
.xmotion
.x
445 (dmxScreen
->rootYOrigin
+ X
.xmotion
.y
446 - dmxScreen
->rootY
));
452 enqueue(priv
->kbd
, X
.type
, X
.xkey
.keycode
, 0, NULL
, block
);
458 /* Pass the whole event here, because
459 * this may be an extension event. */
460 enqueue(priv
->mou
, X
.type
, X
.xbutton
.button
, 0, &X
, block
);
466 /** Called after input events are processed from the DMX queue. No
467 * event processing actually takes place here, but this is a convenient
468 * place to update the pointer. */
469 void dmxBackendProcessInput(pointer
private)
473 DMXDBG6("dmxBackendProcessInput: myScreen=%d relative=%d"
474 " last=%d,%d center=%d,%d\n",
475 priv
->myScreen
, priv
->relative
,
476 priv
->lastX
, priv
->lastY
,
477 priv
->centerX
, priv
->centerY
);
480 && !dmxInput
->console
481 && (priv
->lastX
!= priv
->centerX
|| priv
->lastY
!= priv
->centerY
)) {
482 DMXDBG4(" warping pointer from last=%d,%d to center=%d,%d\n",
483 priv
->lastX
, priv
->lastY
, priv
->centerX
, priv
->centerY
);
484 priv
->lastX
= priv
->centerX
;
485 priv
->lastY
= priv
->centerY
;
486 XWarpPointer(priv
->display
, None
, priv
->window
,
487 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
488 dmxSync(&dmxScreens
[priv
->myScreen
], TRUE
);
492 static void dmxBackendComputeCenter(myPrivate
*priv
)
497 centerX
= priv
->be
->rootWidth
/ 2 + priv
->be
->rootX
;
498 centerY
= priv
->be
->rootHeight
/ 2 + priv
->be
->rootY
;
500 if (centerX
> priv
->be
->rootWidth
) centerX
= priv
->be
->rootWidth
- 1;
501 if (centerY
> priv
->be
->rootHeight
) centerY
= priv
->be
->rootHeight
- 1;
502 if (centerX
< 1) centerX
= 1;
503 if (centerY
< 1) centerY
= 1;
505 priv
->centerX
= centerX
;
506 priv
->centerY
= centerY
;
509 static DMXScreenInfo
*dmxBackendInitPrivate(DevicePtr pDev
)
512 DMXInputInfo
*dmxInput
= &dmxInputs
[dmxLocal
->inputIdx
];
513 DMXScreenInfo
*dmxScreen
;
516 /* Fill in myPrivate */
517 for (i
= 0,dmxScreen
= &dmxScreens
[0]; i
<dmxNumScreens
; i
++,dmxScreen
++) {
518 if (dmxPropertySameDisplay(dmxScreen
, dmxInput
->name
)) {
519 priv
->display
= dmxScreen
->beDisplay
;
520 priv
->window
= dmxScreen
->scrnWin
;
521 priv
->be
= dmxScreen
;
526 if (i
>= dmxNumScreens
)
528 "%s is not an existing backend display - cannot initialize\n",
534 /** Re-initialized the backend device described by \a pDev (after a
536 void dmxBackendLateReInit(DevicePtr pDev
)
541 DMXDBG1("dmxBackendLateReInit miPointerCurrentScreen() = %p\n",
542 miPointerCurrentScreen());
544 dmxBackendSameDisplay(NULL
, 0); /* Invalidate cache */
545 dmxBackendInitPrivate(pDev
);
546 dmxBackendComputeCenter(priv
);
547 dmxGetGlobalPosition(&x
, &y
);
548 dmxInvalidateGlobalPosition(); /* To force event processing */
549 dmxBackendUpdatePosition(priv
, x
, y
);
552 /** Initialized the backend device described by \a pDev. */
553 void dmxBackendInit(DevicePtr pDev
)
556 DMXScreenInfo
*dmxScreen
;
558 dmxBackendSameDisplay(NULL
, 0); /* Invalidate cache */
560 if (dmxLocal
->type
== DMX_LOCAL_MOUSE
) priv
->mou
= pDev
;
561 if (dmxLocal
->type
== DMX_LOCAL_KEYBOARD
) priv
->kbd
= pDev
;
562 if (priv
->initialized
++) return; /* Only do once for mouse/keyboard pair */
564 dmxScreen
= dmxBackendInitPrivate(pDev
);
566 /* Finish initialization using computed values or constants. */
567 dmxBackendComputeCenter(priv
);
568 priv
->eventMask
= (EnterWindowMask
|LeaveWindowMask
);
569 priv
->myScreen
= dmxScreen
->index
;
570 priv
->lastX
= priv
->centerX
;
571 priv
->lastY
= priv
->centerY
;
576 /** Get information about the backend pointer (for initialization). */
577 void dmxBackendMouGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
579 const DMXScreenInfo
*dmxScreen
= dmxBackendInitPrivate(pDev
);
581 info
->buttonClass
= 1;
582 dmxCommonMouGetMap(pDev
, info
->map
, &info
->numButtons
);
583 info
->valuatorClass
= 1;
584 info
->numRelAxes
= 2;
587 info
->maxval
[0] = dmxScreen
->beWidth
;
588 info
->maxval
[1] = dmxScreen
->beHeight
;
592 info
->ptrFeedbackClass
= 1;
595 /** Get information about the backend keyboard (for initialization). */
596 void dmxBackendKbdGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
598 dmxCommonKbdGetInfo(pDev
, info
);
601 dmxCommonKbdGetMap(pDev
, &info
->keySyms
, info
->modMap
);
603 info
->focusClass
= 1;
604 info
->kbdFeedbackClass
= 1;
607 /** Process #DMXFunctionType functions. The only function handled here
608 * is to acknowledge a pending server shutdown. */
609 int dmxBackendFunctions(pointer
private, DMXFunctionType function
)
612 case DMX_FUNCTION_TERMINATE
: