First import
[xorg_rtime.git] / xorg-server-1.4 / hw / dmx / input / dmxbackend.c
blobb1791098a81952a2a97b5a4111d4b908fe64dd16
1 /*
2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved.
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
25 * SOFTWARE.
29 * Authors:
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
35 /** \file
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>
41 #endif
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"
50 #include "dmxprop.h"
51 #include "dmxsync.h"
52 #include "dmxcb.h" /* For dmxGlobalWidth and dmxGlobalHeight */
53 #include "dmxevents.h" /* For dmxGetGlobalPosition */
54 #include "ChkNotMaskEv.h"
56 #include "inputstr.h"
57 #include "input.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 {
65 DMX_COMMON_PRIVATE;
66 int myScreen;
67 DMXScreenInfo *grabbedScreen;
69 int lastX, lastY;
70 int centerX, centerY;
71 int relative;
72 int newscreen;
73 int initialized;
74 DevicePtr mou, kbd;
75 int entered;
76 int offX, offY;
77 } myPrivate;
79 #if DMX_BACKEND_DEBUG
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)
90 #else
91 #define DMXDBG0(f)
92 #define DMXDBG1(f,a)
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)
101 #endif
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;
110 return priv;
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;
126 return NULL;
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 */
142 oldpriv = NULL;
143 oldscreen = -1;
144 retcode = 0;
145 return 0;
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;
153 else retcode = 0;
155 oldpriv = priv;
156 oldscreen = screen;
157 return retcode;
160 static void *dmxBackendTestEvents(DMXScreenInfo *dmxScreen, void *closure)
162 XEvent *X = (XEvent *)closure;
164 if (XCheckNotMaskEvent(dmxScreen->beDisplay, ExposureMask, X))
165 return dmxScreen;
166 return NULL;
169 static void *dmxBackendTestMotionEvent(DMXScreenInfo *dmxScreen, void *closure)
171 XEvent *X = (XEvent *)closure;
173 if (XCheckTypedEvent(dmxScreen->beDisplay, MotionNotify, X))
174 return dmxScreen;
175 return NULL;
178 static DMXScreenInfo *dmxBackendGetEvent(myPrivate *priv, XEvent *X)
180 DMXScreenInfo *dmxScreen;
182 if ((dmxScreen = dmxPropertyIterate(priv->be, dmxBackendTestEvents, X)))
183 return dmxScreen;
184 return NULL;
187 static DMXScreenInfo *dmxBackendPendingMotionEvent(myPrivate *priv, int save)
189 DMXScreenInfo *dmxScreen;
190 XEvent N;
192 if ((dmxScreen = dmxPropertyIterate(priv->be,
193 dmxBackendTestMotionEvent, &N))) {
194 if (save) XPutBackEvent(dmxScreen->beDisplay, &N);
195 return dmxScreen;
197 return NULL;
200 static void *dmxBackendTestWindow(DMXScreenInfo *dmxScreen, void *closure)
202 Window win = (Window)(long)closure;
203 if (dmxScreen->scrnWin == win) return dmxScreen;
204 return NULL;
207 static DMXScreenInfo *dmxBackendFindWindow(myPrivate *priv, Window win)
209 return dmxPropertyIterate(priv->be, dmxBackendTestWindow,
210 (void *)(long)win);
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];
219 DMXScreenInfo *pt;
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;
228 return screen;
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)
243 GETPRIVFROMPRIVATE;
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);
257 if (offscreen) {
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");
264 priv->relative = 0;
266 } else {
267 if (!priv->relative) {
268 DMXDBG0(" Off screen, but not relative\n");
269 priv->relative = 1;
272 } else {
273 if (topscreen != screen) {
274 DMXDBG2(" Using screen %d instead of %d (from mi)\n",
275 topscreen, screen);
277 if (same) {
278 if (priv->relative) {
279 DMXDBG0(" On screen, but not absolute\n");
280 priv->relative = 0;
282 } else {
283 if (!priv->relative) {
284 DMXDBG0(" Not on screen, but not relative\n");
285 priv->relative = 1;
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
302 * window). */
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);
313 } else {
314 priv->newscreen = 1;
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,
318 priv->myScreen);
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);
326 } else {
327 DMXDBG0(" Check cursor\n");
328 dmxCheckCursor();
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,
340 DMXBlockType block)
342 GETPRIVFROMPDEV;
343 GETDMXINPUTFROMPRIV;
344 XEvent X;
345 DMXScreenInfo *dmxScreen;
346 int left = 0;
347 int entered = priv->entered;
348 int ignoreLeave = 0;
349 int v[2];
350 int retcode;
352 while ((dmxScreen = dmxBackendGetEvent(priv, &X))) {
353 switch (X.type) {
354 case EnterNotify:
355 dmxCommonSaveState(priv);
356 if (entered++)
357 continue;
358 priv->entered = 1;
359 ignoreLeave = 1;
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,
366 dmxScreen->scrnWin,
367 True, 0, GrabModeAsync,
368 GrabModeAsync, None, None,
369 CurrentTime))) {
370 dmxLog(dmxError,
371 "XGrabPointer failed during backend enter (%d)\n",
372 retcode);
374 break;
375 case LeaveNotify:
376 if (ignoreLeave) {
377 ignoreLeave = 0;
378 continue;
380 dmxCommonRestoreState(priv);
381 if (left++)
382 continue;
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" : "",
387 dmxScreen->name);
388 if (priv->grabbedScreen) {
389 XUngrabPointer(priv->grabbedScreen->beDisplay, CurrentTime);
390 dmxSync(priv->grabbedScreen, TRUE);
391 priv->grabbedScreen = NULL;
393 break;
394 case MotionNotify:
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,
399 priv->newscreen,
400 X.xmotion.x, X.xmotion.y,
401 entered, priv->lastX, priv->lastY);
402 if (dmxBackendPendingMotionEvent(priv, TRUE))
403 continue;
404 if (!(dmxScreen = dmxBackendFindWindow(priv, X.xmotion.window)))
405 dmxLog(dmxFatal,
406 " Event on non-existant window %lu\n",
407 X.xmotion.window);
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;
420 priv->newscreen = 0;
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",
424 v[0], v[1],
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);
429 priv->entered = 0;
430 } else {
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;
435 v[0] = newX;
436 v[1] = newY;
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",
442 v[0], v[1],
443 (dmxScreen->rootXOrigin + X.xmotion.x
444 - dmxScreen->rootX),
445 (dmxScreen->rootYOrigin + X.xmotion.y
446 - dmxScreen->rootY));
448 break;
450 case KeyPress:
451 case KeyRelease:
452 enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
453 break;
454 case ButtonPress:
455 case ButtonRelease:
456 /* fall-through */
457 default:
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);
461 break;
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)
471 GETPRIVFROMPRIVATE;
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);
479 if (priv->relative
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)
494 int centerX;
495 int centerY;
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)
511 GETPRIVFROMPDEV;
512 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
513 DMXScreenInfo *dmxScreen;
514 int i;
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;
522 break;
526 if (i >= dmxNumScreens)
527 dmxLog(dmxFatal,
528 "%s is not an existing backend display - cannot initialize\n",
529 dmxInput->name);
531 return dmxScreen;
534 /** Re-initialized the backend device described by \a pDev (after a
535 * reconfig). */
536 void dmxBackendLateReInit(DevicePtr pDev)
538 GETPRIVFROMPDEV;
539 int x, y;
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)
555 GETPRIVFROMPDEV;
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;
572 priv->relative = 0;
573 priv->newscreen = 0;
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;
585 info->minval[0] = 0;
586 info->minval[1] = 0;
587 info->maxval[0] = dmxScreen->beWidth;
588 info->maxval[1] = dmxScreen->beHeight;
589 info->res[0] = 1;
590 info->minres[0] = 0;
591 info->maxres[0] = 1;
592 info->ptrFeedbackClass = 1;
595 /** Get information about the backend keyboard (for initialization). */
596 void dmxBackendKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
598 dmxCommonKbdGetInfo(pDev, info);
599 info->keyboard = 1;
600 info->keyClass = 1;
601 dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
602 info->freemap = 1;
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)
611 switch (function) {
612 case DMX_FUNCTION_TERMINATE:
613 return 1;
614 default:
615 return 0;