1 /*
2  * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
3  *
4  * All Rights Reserved.
5  *
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:
13  *
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.
17  *
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.
26  */
27 
28 /*
29  * Authors:
30  *   David H. Dawes <dawes@xfree86.org>
31  *   Kevin E. Martin <kem@redhat.com>
32  *   Rickard E. (Rik) Faith <faith@redhat.com>
33  */
34 
35 /** \file
36  * These routines support taking input from devices on the backend
37  * (output) displays.  \see dmxcommon.c. */
38 
39 #ifdef HAVE_DMX_CONFIG_H
40 #include <dmx-config.h>
41 #endif
42 
43 #define DMX_BACKEND_DEBUG 0
44 
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"
55 
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"
62 
63 /* Private area for backend devices. */
64 typedef struct _myPrivate {
65     DMX_COMMON_PRIVATE;
66     int myScreen;
67     DMXScreenInfo *grabbedScreen;
68 
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;
78 
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
102 
103 /** Create and return a private data structure. */
104 void *
dmxBackendCreatePrivate(DeviceIntPtr pDevice)105 dmxBackendCreatePrivate(DeviceIntPtr pDevice)
106 {
107     GETDMXLOCALFROMPDEVICE;
108     myPrivate *priv = calloc(1, sizeof(*priv));
109 
110     priv->dmxLocal = dmxLocal;
111     return priv;
112 }
113 
114 /** Destroy the private data structure.  No checking is performed to
115  * verify that the structure was actually created by
116  * #dmxBackendCreatePrivate. */
117 void
dmxBackendDestroyPrivate(void * private)118 dmxBackendDestroyPrivate(void *private)
119 {
120     free(private);
121 }
122 
123 static void *
dmxBackendTestScreen(DMXScreenInfo * dmxScreen,void * closure)124 dmxBackendTestScreen(DMXScreenInfo * dmxScreen, void *closure)
125 {
126     long target = (long) closure;
127 
128     if (dmxScreen->index == target)
129         return dmxScreen;
130     return NULL;
131 }
132 
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). */
138 static int
dmxBackendSameDisplay(myPrivate * priv,long screen)139 dmxBackendSameDisplay(myPrivate * priv, long screen)
140 {
141     static myPrivate *oldpriv = NULL;
142     static int oldscreen = -1;
143     static int retcode = 0;
144 
145     if (priv == oldpriv && screen == oldscreen)
146         return retcode;
147     if (!priv) {                /* Invalidate cache */
148         oldpriv = NULL;
149         oldscreen = -1;
150         retcode = 0;
151         return 0;
152     }
153 
154     if (screen == priv->myScreen)
155         retcode = 1;
156     else if (screen < 0 || screen >= dmxNumScreens)
157         retcode = 0;
158     else if (dmxPropertyIterate(priv->be,
159                                 dmxBackendTestScreen, (void *) screen))
160         retcode = 2;
161     else
162         retcode = 0;
163 
164     oldpriv = priv;
165     oldscreen = screen;
166     return retcode;
167 }
168 
169 static void *
dmxBackendTestEvents(DMXScreenInfo * dmxScreen,void * closure)170 dmxBackendTestEvents(DMXScreenInfo * dmxScreen, void *closure)
171 {
172     XEvent *X = (XEvent *) closure;
173 
174     if (XCheckNotMaskEvent(dmxScreen->beDisplay, ExposureMask, X))
175         return dmxScreen;
176     return NULL;
177 }
178 
179 static void *
dmxBackendTestMotionEvent(DMXScreenInfo * dmxScreen,void * closure)180 dmxBackendTestMotionEvent(DMXScreenInfo * dmxScreen, void *closure)
181 {
182     XEvent *X = (XEvent *) closure;
183 
184     if (XCheckTypedEvent(dmxScreen->beDisplay, MotionNotify, X))
185         return dmxScreen;
186     return NULL;
187 }
188 
189 static DMXScreenInfo *
dmxBackendGetEvent(myPrivate * priv,XEvent * X)190 dmxBackendGetEvent(myPrivate * priv, XEvent * X)
191 {
192     DMXScreenInfo *dmxScreen;
193 
194     if ((dmxScreen = dmxPropertyIterate(priv->be, dmxBackendTestEvents, X)))
195         return dmxScreen;
196     return NULL;
197 }
198 
199 static DMXScreenInfo *
dmxBackendPendingMotionEvent(myPrivate * priv,int save)200 dmxBackendPendingMotionEvent(myPrivate * priv, int save)
201 {
202     DMXScreenInfo *dmxScreen;
203     XEvent N;
204 
205     if ((dmxScreen = dmxPropertyIterate(priv->be,
206                                         dmxBackendTestMotionEvent, &N))) {
207         if (save)
208             XPutBackEvent(dmxScreen->beDisplay, &N);
209         return dmxScreen;
210     }
211     return NULL;
212 }
213 
214 static void *
dmxBackendTestWindow(DMXScreenInfo * dmxScreen,void * closure)215 dmxBackendTestWindow(DMXScreenInfo * dmxScreen, void *closure)
216 {
217     Window win = (Window) (long) closure;
218 
219     if (dmxScreen->scrnWin == win)
220         return dmxScreen;
221     return NULL;
222 }
223 
224 static DMXScreenInfo *
dmxBackendFindWindow(myPrivate * priv,Window win)225 dmxBackendFindWindow(myPrivate * priv, Window win)
226 {
227     return dmxPropertyIterate(priv->be, dmxBackendTestWindow,
228                               (void *) (long) win);
229 }
230 
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. */
234 static int
dmxBackendFindOverlapping(myPrivate * priv,int screen,int x,int y)235 dmxBackendFindOverlapping(myPrivate * priv, int screen, int x, int y)
236 {
237     DMXScreenInfo *start = &dmxScreens[screen];
238     DMXScreenInfo *pt;
239 
240     if (!start->over)
241         return screen;
242 
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]))
246             return pt->index;
247         if (pt == start)
248             break;
249     }
250     return screen;
251 }
252 
253 /* Return non-zero if \a x and \a y are off \a screen. */
254 static int
dmxBackendOffscreen(int screen,int x,int y)255 dmxBackendOffscreen(int screen, int x, int y)
256 {
257     DMXScreenInfo *dmxScreen = &dmxScreens[screen];
258 
259     return (!dmxOnScreen(x, y, dmxScreen));
260 }
261 
262 /** This routine is called from #dmxCoreMotion for each motion
263  * event. \a x and \a y are global coordinants. */
264 void
dmxBackendUpdatePosition(void * private,int x,int y)265 dmxBackendUpdatePosition(void *private, int x, int y)
266 {
267     GETPRIVFROMPRIVATE;
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);
275 
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);
280 
281     if (offscreen) {
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");
288                 priv->relative = 0;
289             }
290         }
291         else {
292             if (!priv->relative) {
293                 DMXDBG0("   Off screen, but not relative\n");
294                 priv->relative = 1;
295             }
296         }
297     }
298     else {
299         if (topscreen != screen) {
300             DMXDBG2("   Using screen %d instead of %d (from mi)\n",
301                     topscreen, screen);
302         }
303         if (same) {
304             if (priv->relative) {
305                 DMXDBG0("   On screen, but not absolute\n");
306                 priv->relative = 0;
307             }
308         }
309         else {
310             if (!priv->relative) {
311                 DMXDBG0("   Not on screen, but not relative\n");
312                 priv->relative = 1;
313             }
314         }
315     }
316 
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));
322 
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
328              * window). */
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;
336             }
337             DMXDBG0("   Capturing console\n");
338             dmxConsoleCapture(dmxInput);
339         }
340         else {
341             priv->newscreen = 1;
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,
345                         priv->myScreen);
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);
353             }
354             else {
355                 DMXDBG0("   Check cursor\n");
356                 dmxCheckCursor();
357             }
358         }
359     }
360 }
361 
362 /** Get events from the X queue on the backend servers and put the
363  * events into the DMX event queue. */
364 void
dmxBackendCollectEvents(DevicePtr pDev,dmxMotionProcPtr motion,dmxEnqueueProcPtr enqueue,dmxCheckSpecialProcPtr checkspecial,DMXBlockType block)365 dmxBackendCollectEvents(DevicePtr pDev,
366                         dmxMotionProcPtr motion,
367                         dmxEnqueueProcPtr enqueue,
368                         dmxCheckSpecialProcPtr checkspecial, DMXBlockType block)
369 {
370     GETPRIVFROMPDEV;
371     GETDMXINPUTFROMPRIV;
372     XEvent X;
373     DMXScreenInfo *dmxScreen;
374     int left = 0;
375     int entered = priv->entered;
376     int ignoreLeave = 0;
377     int v[2];
378     int retcode;
379 
380     while ((dmxScreen = dmxBackendGetEvent(priv, &X))) {
381         switch (X.type) {
382         case EnterNotify:
383             dmxCommonSaveState(priv);
384             if (entered++)
385                 continue;
386             priv->entered = 1;
387             ignoreLeave = 1;
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,
394                                         dmxScreen->scrnWin,
395                                         True, 0, GrabModeAsync,
396                                         GrabModeAsync, None, None,
397                                         CurrentTime))) {
398                 dmxLog(dmxError,
399                        "XGrabPointer failed during backend enter (%d)\n",
400                        retcode);
401             }
402             break;
403         case LeaveNotify:
404             if (ignoreLeave) {
405                 ignoreLeave = 0;
406                 continue;
407             }
408             dmxCommonRestoreState(priv);
409             if (left++)
410                 continue;
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;
419             }
420             break;
421         case MotionNotify:
422             DMXDBG8("dmxBackendCollectEvents: MotionNotify %d/%d"
423                     " newscreen=%d: %d %d (e=%d; last=%d,%d)\n",
424                     dmxScreen->index, priv->myScreen,
425                     priv->newscreen,
426                     X.xmotion.x, X.xmotion.y,
427                     entered, priv->lastX, priv->lastY);
428             if (dmxBackendPendingMotionEvent(priv, TRUE))
429                 continue;
430             if (!(dmxScreen = dmxBackendFindWindow(priv, X.xmotion.window)))
431                 dmxLog(dmxFatal,
432                        "   Event on non-existant window %lu\n",
433                        X.xmotion.window);
434             if (!priv->relative || dmxInput->console) {
435                 int newX = X.xmotion.x - dmxScreen->rootX;
436                 int newY = X.xmotion.y - dmxScreen->rootY;
437 
438                 if (!priv->newscreen) {
439                     int width = dmxScreen->rootWidth;
440                     int height = dmxScreen->rootHeight;
441 
442                     if (!newX)
443                         newX = -1;
444                     if (newX == width - 1)
445                         newX = width;
446                     if (!newY)
447                         newY = -1;
448                     if (newY == height - 1)
449                         newY = height;
450                 }
451                 priv->newscreen = 0;
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",
455                         v[0], v[1],
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);
460                 priv->entered = 0;
461             }
462             else {
463                 int newX = priv->lastX - X.xmotion.x;
464                 int newY = priv->lastY - X.xmotion.y;
465 
466                 priv->lastX = X.xmotion.x;
467                 priv->lastY = X.xmotion.y;
468                 v[0] = newX;
469                 v[1] = newY;
470                 DMXDBG2("   Relative move: %d, %d\n", v[0], v[1]);
471                 motion(priv->mou, v, 0, 2, DMX_RELATIVE, block);
472             }
473             if (entered && priv->relative) {
474                 DMXDBG4("   **** Relative %d %d instead of absolute %d %d\n",
475                         v[0], v[1],
476                         (dmxScreen->rootXOrigin + X.xmotion.x
477                          - dmxScreen->rootX),
478                         (dmxScreen->rootYOrigin + X.xmotion.y
479                          - dmxScreen->rootY));
480             }
481             break;
482 
483         case KeyPress:
484         case KeyRelease:
485             enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
486             break;
487         case ButtonPress:
488         case ButtonRelease:
489             /* fall-through */
490         default:
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);
494             break;
495         }
496     }
497 }
498 
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. */
502 void
dmxBackendProcessInput(void * private)503 dmxBackendProcessInput(void *private)
504 {
505     GETPRIVFROMPRIVATE;
506 
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);
511 
512     if (priv->relative
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);
522     }
523 }
524 
525 static void
dmxBackendComputeCenter(myPrivate * priv)526 dmxBackendComputeCenter(myPrivate * priv)
527 {
528     int centerX;
529     int centerY;
530 
531     centerX = priv->be->rootWidth / 2 + priv->be->rootX;
532     centerY = priv->be->rootHeight / 2 + priv->be->rootY;
533 
534     if (centerX > priv->be->rootWidth)
535         centerX = priv->be->rootWidth - 1;
536     if (centerY > priv->be->rootHeight)
537         centerY = priv->be->rootHeight - 1;
538     if (centerX < 1)
539         centerX = 1;
540     if (centerY < 1)
541         centerY = 1;
542 
543     priv->centerX = centerX;
544     priv->centerY = centerY;
545 }
546 
547 static DMXScreenInfo *
dmxBackendInitPrivate(DevicePtr pDev)548 dmxBackendInitPrivate(DevicePtr pDev)
549 {
550     GETPRIVFROMPDEV;
551     DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
552     DMXScreenInfo *dmxScreen;
553     int i;
554 
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;
561             break;
562         }
563     }
564 
565     if (i >= dmxNumScreens)
566         dmxLog(dmxFatal,
567                "%s is not an existing backend display - cannot initialize\n",
568                dmxInput->name);
569 
570     return dmxScreen;
571 }
572 
573 /** Re-initialized the backend device described by \a pDev (after a
574  * reconfig). */
575 void
dmxBackendLateReInit(DevicePtr pDev)576 dmxBackendLateReInit(DevicePtr pDev)
577 {
578     GETPRIVFROMPDEV;
579     int x, y;
580 
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);
587 }
588 
589 /** Initialized the backend device described by \a pDev. */
590 void
dmxBackendInit(DevicePtr pDev)591 dmxBackendInit(DevicePtr pDev)
592 {
593     GETPRIVFROMPDEV;
594     DMXScreenInfo *dmxScreen;
595 
596     dmxBackendSameDisplay(NULL, 0);     /* Invalidate cache */
597 
598     if (dmxLocal->type == DMX_LOCAL_MOUSE)
599         priv->mou = pDev;
600     if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
601         priv->kbd = pDev;
602     if (priv->initialized++)
603         return;                 /* Only do once for mouse/keyboard pair */
604 
605     dmxScreen = dmxBackendInitPrivate(pDev);
606 
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;
613     priv->relative = 0;
614     priv->newscreen = 0;
615 }
616 
617 /** Get information about the backend pointer (for initialization). */
618 void
dmxBackendMouGetInfo(DevicePtr pDev,DMXLocalInitInfoPtr info)619 dmxBackendMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
620 {
621     const DMXScreenInfo *dmxScreen = dmxBackendInitPrivate(pDev);
622 
623     info->buttonClass = 1;
624     dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
625     info->valuatorClass = 1;
626     info->numRelAxes = 2;
627     info->minval[0] = 0;
628     info->minval[1] = 0;
629     info->maxval[0] = dmxScreen->beWidth;
630     info->maxval[1] = dmxScreen->beHeight;
631     info->res[0] = 1;
632     info->minres[0] = 0;
633     info->maxres[0] = 1;
634     info->ptrFeedbackClass = 1;
635 }
636 
637 /** Get information about the backend keyboard (for initialization). */
638 void
dmxBackendKbdGetInfo(DevicePtr pDev,DMXLocalInitInfoPtr info)639 dmxBackendKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
640 {
641     dmxCommonKbdGetInfo(pDev, info);
642     info->keyboard = 1;
643     info->keyClass = 1;
644     dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
645     info->freemap = 1;
646     info->focusClass = 1;
647     info->kbdFeedbackClass = 1;
648 }
649 
650 /** Process #DMXFunctionType functions.  The only function handled here
651  * is to acknowledge a pending server shutdown. */
652 int
dmxBackendFunctions(void * private,DMXFunctionType function)653 dmxBackendFunctions(void *private, DMXFunctionType function)
654 {
655     switch (function) {
656     case DMX_FUNCTION_TERMINATE:
657         return 1;
658     default:
659         return 0;
660     }
661 }
662