xref: /OK3568_Linux_fs/external/xserver/hw/dmx/input/dmxconsole.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
36 /** \file
37  *
38  * This file implements the console input devices.
39  */
40 
41 #ifdef HAVE_DMX_CONFIG_H
42 #include <dmx-config.h>
43 #endif
44 
45 #define DMX_CONSOLE_DEBUG 0
46 #define DMX_WINDOW_DEBUG  0
47 
48 #include "dmxinputinit.h"
49 #include "dmxevents.h"
50 #include "dmxconsole.h"
51 #include "dmxcommon.h"
52 #include "dmxscrinit.h"
53 #include "dmxcb.h"
54 #include "dmxsync.h"
55 
56 #include "inputstr.h"
57 #include "input.h"
58 #include "mipointer.h"
59 #include "windowstr.h"
60 
61 #define CONSOLE_NUM 3
62 #define CONSOLE_DEN 4
63 #define DMX_CONSOLE_NAME "DMX Console"
64 #define DMX_RES_NAME     "Xdmx"
65 #define DMX_RES_CLASS    "XDmx"
66 #define CONSOLE_BG_COLOR "gray75"
67 #define CONSOLE_FG_COLOR "black"
68 #define CONSOLE_SCREEN_BG_COLOR "white"
69 #define CONSOLE_SCREEN_FG_COLOR "black"
70 #define CONSOLE_SCREEN_DET_COLOR "gray75"
71 #define CONSOLE_SCREEN_CUR_COLOR "red"
72 
73 #if DMX_CONSOLE_DEBUG
74 #define DMXDBG0(f)               dmxLog(dmxDebug,f)
75 #define DMXDBG1(f,a)             dmxLog(dmxDebug,f,a)
76 #define DMXDBG2(f,a,b)           dmxLog(dmxDebug,f,a,b)
77 #define DMXDBG3(f,a,b,c)         dmxLog(dmxDebug,f,a,b,c)
78 #define DMXDBG4(f,a,b,c,d)       dmxLog(dmxDebug,f,a,b,c,d)
79 #define DMXDBG5(f,a,b,c,d,e)     dmxLog(dmxDebug,f,a,b,c,d,e)
80 #define DMXDBG6(f,a,b,c,d,e,g)   dmxLog(dmxDebug,f,a,b,c,d,e,g)
81 #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
82 #else
83 #define DMXDBG0(f)
84 #define DMXDBG1(f,a)
85 #define DMXDBG2(f,a,b)
86 #define DMXDBG3(f,a,b,c)
87 #define DMXDBG4(f,a,b,c,d)
88 #define DMXDBG5(f,a,b,c,d,e)
89 #define DMXDBG6(f,a,b,c,d,e,g)
90 #define DMXDBG7(f,a,b,c,d,e,g,h)
91 #endif
92 
93 /* Private area for consoles. */
94 typedef struct _myPrivate {
95     DMX_COMMON_PRIVATE;
96     int lastX;
97     int lastY;
98     int globalX;
99     int globalY;
100     int curX;
101     int curY;
102     int width;
103     int height;
104     int consWidth;
105     int consHeight;
106     double xScale;
107     double yScale;
108     XlibGC gc, gcDet, gcRev, gcCur;
109     int grabbed, fine, captured;
110     Cursor cursorNormal, cursorGrabbed, cursorEmpty;
111     Pixmap pixmap;
112 
113     CloseScreenProcPtr CloseScreen;
114     struct _myPrivate *next;    /* for closing multiple consoles */
115     int initialized;
116     DevicePtr mou, kbd;
117 } myPrivate;
118 
119 static int
scalex(myPrivate * priv,int x)120 scalex(myPrivate * priv, int x)
121 {
122     return (int) ((x * priv->xScale) + .5);
123 }
124 
125 static int
scaley(myPrivate * priv,int y)126 scaley(myPrivate * priv, int y)
127 {
128     return (int) ((y * priv->yScale) + .5);
129 }
130 
131 static int
unscalex(myPrivate * priv,int x)132 unscalex(myPrivate * priv, int x)
133 {
134     return (int) ((x / priv->xScale) + .5);
135 }
136 
137 static int
unscaley(myPrivate * priv,int y)138 unscaley(myPrivate * priv, int y)
139 {
140     return (int) ((y / priv->yScale) + .5);
141 }
142 
143 /** Create the private area for \a pDevice. */
144 void *
dmxConsoleCreatePrivate(DeviceIntPtr pDevice)145 dmxConsoleCreatePrivate(DeviceIntPtr pDevice)
146 {
147     GETDMXLOCALFROMPDEVICE;
148     myPrivate *priv = calloc(1, sizeof(*priv));
149 
150     priv->dmxLocal = dmxLocal;
151     return priv;
152 }
153 
154 /** If \a private is non-NULL, free its associated memory. */
155 void
dmxConsoleDestroyPrivate(void * private)156 dmxConsoleDestroyPrivate(void *private)
157 {
158     free(private);
159 }
160 
161 static void
dmxConsoleDrawFineCursor(myPrivate * priv,XRectangle * rect)162 dmxConsoleDrawFineCursor(myPrivate * priv, XRectangle * rect)
163 {
164     int size = 6;
165     int x, y;
166 
167     XDrawLine(priv->display, priv->pixmap, priv->gcCur,
168               x = scalex(priv, priv->globalX) - size,
169               scaley(priv, priv->globalY),
170               scalex(priv, priv->globalX) + size, scaley(priv, priv->globalY));
171     XDrawLine(priv->display, priv->pixmap, priv->gcCur,
172               scalex(priv, priv->globalX),
173               y = scaley(priv, priv->globalY) - size,
174               scalex(priv, priv->globalX), scaley(priv, priv->globalY) + size);
175     if (priv->grabbed) {
176         XDrawLine(priv->display, priv->pixmap, priv->gcCur,
177                   scalex(priv, priv->globalX) - (int) (size / 1.4),
178                   scaley(priv, priv->globalY) - (int) (size / 1.4),
179                   scalex(priv, priv->globalX) + (int) (size / 1.4),
180                   scaley(priv, priv->globalY) + (int) (size / 1.4));
181         XDrawLine(priv->display, priv->pixmap, priv->gcCur,
182                   scalex(priv, priv->globalX) - (int) (size / 1.4),
183                   scaley(priv, priv->globalY) + (int) (size / 1.4),
184                   scalex(priv, priv->globalX) + (int) (size / 1.4),
185                   scaley(priv, priv->globalY) - (int) (size / 1.4));
186     }
187     if (rect) {
188         rect->x = x;
189         rect->y = y;
190         rect->width = 2 * size;
191         rect->height = 2 * size;
192     }
193 }
194 
195 static void
dmxConsoleDrawWindows(void * private)196 dmxConsoleDrawWindows(void *private)
197 {
198     GETONLYPRIVFROMPRIVATE;
199     Display *dpy = priv->display;
200     int i;
201     Region whole, used, avail;
202     XRectangle rect;
203 
204     whole = XCreateRegion();
205     used = XCreateRegion();
206     avail = XCreateRegion();
207     rect.x = 0;
208     rect.y = 0;
209     rect.width = priv->consWidth;
210     rect.height = priv->consHeight;
211     XUnionRectWithRegion(&rect, whole, whole);
212 
213     for (i = 0; i < dmxNumScreens; i++) {
214         ScreenPtr pScreen = screenInfo.screens[i];
215         WindowPtr pRoot = pScreen->root;
216         WindowPtr pChild;
217 
218 #if DMX_WINDOW_DEBUG
219         dmxLog(dmxDebug, "%lu %p %p %p 2\n",
220                pRoot->drawable.id,
221                pRoot->parent, pRoot->firstChild, pRoot->lastChild);
222 #endif
223 
224         for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
225             if (pChild->mapped && pChild->realized) {
226 #if DMX_WINDOW_DEBUG
227                 dmxLog(dmxDebug, "  %p %d,%d %dx%d %d %d  %d RECTS\n",
228                        pChild,
229                        pChild->drawable.x,
230                        pChild->drawable.y,
231                        pChild->drawable.width,
232                        pChild->drawable.height,
233                        pChild->visibility,
234                        pChild->overrideRedirect,
235                        RegionNumRects(&pChild->clipList));
236 #endif
237                 rect.x = scalex(priv, pChild->drawable.x + pScreen->x);
238                 rect.y = scaley(priv, pChild->drawable.y + pScreen->y);
239                 rect.width = scalex(priv, pChild->drawable.width);
240                 rect.height = scaley(priv, pChild->drawable.height);
241                 XDrawRectangle(dpy, priv->pixmap, priv->gc,
242                                rect.x, rect.y, rect.width, rect.height);
243                 XUnionRectWithRegion(&rect, used, used);
244                 XSubtractRegion(whole, used, avail);
245                 XSetRegion(dpy, priv->gc, avail);
246             }
247         }
248 #ifdef PANORAMIX
249         if (!noPanoramiXExtension)
250             break;              /* Screen 0 valid with Xinerama */
251 #endif
252     }
253     XDestroyRegion(avail);
254     XDestroyRegion(used);
255     XDestroyRegion(whole);
256     XSetClipMask(dpy, priv->gc, None);
257 }
258 
259 static void
dmxConsoleDraw(myPrivate * priv,int updateCursor,int update)260 dmxConsoleDraw(myPrivate * priv, int updateCursor, int update)
261 {
262     GETDMXINPUTFROMPRIV;
263     Display *dpy = priv->display;
264     int i;
265 
266     XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0,
267                    priv->consWidth, priv->consHeight);
268 
269     for (i = 0; i < dmxNumScreens; i++) {
270         DMXScreenInfo *dmxScreen = &dmxScreens[i];
271 
272         XFillRectangle(dpy, priv->pixmap,
273                        dmxScreen->beDisplay ? priv->gcRev : priv->gcDet,
274                        scalex(priv, screenInfo.screens[i]->x),
275                        scaley(priv, screenInfo.screens[i]->y),
276                        scalex(priv, screenInfo.screens[i]->width),
277                        scaley(priv, screenInfo.screens[i]->height));
278     }
279     for (i = 0; i < dmxNumScreens; i++) {
280         XDrawRectangle(dpy, priv->pixmap, priv->gc,
281                        scalex(priv, screenInfo.screens[i]->x),
282                        scaley(priv, screenInfo.screens[i]->y),
283                        scalex(priv, screenInfo.screens[i]->width),
284                        scaley(priv, screenInfo.screens[i]->height));
285     }
286     if (dmxInput->windows)
287         dmxConsoleDrawWindows(priv);
288     if (priv->fine && updateCursor)
289         dmxConsoleDrawFineCursor(priv, 0);
290     if (update) {
291         XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
292                   0, 0, priv->consWidth, priv->consHeight, 0, 0);
293         XSync(priv->display, False);    /* Not a backend display */
294     }
295 }
296 
297 static void
dmxConsoleClearCursor(myPrivate * priv,int x,int y,XRectangle * rect)298 dmxConsoleClearCursor(myPrivate * priv, int x, int y, XRectangle * rect)
299 {
300     int cw = 14, ch = 14;       /* Clear width and height */
301 
302     rect->x = scalex(priv, x) - cw / 2;
303     rect->y = scaley(priv, y) - ch / 2;
304     rect->width = cw;
305     rect->height = ch;
306     XSetClipRectangles(priv->display, priv->gc, 0, 0, rect, 1, Unsorted);
307     XSetClipRectangles(priv->display, priv->gcDet, 0, 0, rect, 1, Unsorted);
308     XSetClipRectangles(priv->display, priv->gcRev, 0, 0, rect, 1, Unsorted);
309     dmxConsoleDraw(priv, 0, 0);
310     XSetClipMask(priv->display, priv->gc, None);
311     XSetClipMask(priv->display, priv->gcDet, None);
312     XSetClipMask(priv->display, priv->gcRev, None);
313 }
314 
315 static void
dmxConsoleUpdateFineCursor(myPrivate * priv)316 dmxConsoleUpdateFineCursor(myPrivate * priv)
317 {
318     int leave = 0;
319     XRectangle rects[2];
320 
321     dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
322     if (priv->dmxLocal->sendsCore) {
323         dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
324     }
325     else {
326         priv->globalX = priv->dmxLocal->lastX;
327         priv->globalY = priv->dmxLocal->lastY;
328     }
329 
330     priv->lastX = scalex(priv, priv->width / 2);
331     priv->lastY = scaley(priv, priv->height / 2);
332 
333     /* Compute new warp position, which may be
334        outside the window */
335     if (priv->globalX < 1 || priv->globalX >= priv->width) {
336         if (priv->globalX < 1)
337             priv->lastX = 0;
338         else
339             priv->lastX = scalex(priv, priv->width);
340         priv->lastY = scaley(priv, priv->globalY);
341         ++leave;
342     }
343     if (priv->globalY < 1 || priv->globalY >= priv->height) {
344         if (priv->globalY < 1)
345             priv->lastY = 0;
346         else
347             priv->lastY = scaley(priv, priv->height);
348         priv->lastX = scalex(priv, priv->globalX);
349         ++leave;
350     }
351 
352     /* Draw pseudo cursor in window */
353     dmxConsoleDrawFineCursor(priv, &rects[1]);
354 
355     XSetClipRectangles(priv->display, priv->gc, 0, 0, rects, 2, Unsorted);
356     XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
357               0, 0, priv->consWidth, priv->consHeight, 0, 0);
358     XSetClipMask(priv->display, priv->gc, None);
359 
360     DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
361             priv->lastX, priv->lastY);
362     XWarpPointer(priv->display, priv->window, priv->window,
363                  0, 0, 0, 0, priv->lastX, priv->lastY);
364     XSync(priv->display, False);        /* Not a backend display */
365 
366     if (leave) {
367         XEvent X;
368 
369         while (XCheckMaskEvent(priv->display, PointerMotionMask, &X)) {
370             if (X.type == MotionNotify) {
371                 if (X.xmotion.x != priv->lastX || X.xmotion.y != priv->lastY) {
372                     DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
373                             X.xmotion.x, X.xmotion.y, priv->lastX, priv->lastY);
374                 }
375             }
376             else {
377                 dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n",
378                        X.type, dmxEventName(X.type));
379             }
380         }
381     }
382     DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
383             priv->lastX, priv->lastY,
384             scalex(priv, priv->width),
385             scaley(priv, priv->height), priv->globalX, priv->globalY);
386 }
387 
388 /** Whenever the window layout (size, position, stacking order) might be
389  * changed, this routine is called with the \a pWindow that changed and
390  * the \a type of change.  This routine is called in a conservative
391  * fashion: the actual layout of the windows of the screen might not
392  * have had any human-visible changes. */
393 void
dmxConsoleUpdateInfo(void * private,DMXUpdateType type,WindowPtr pWindow)394 dmxConsoleUpdateInfo(void *private, DMXUpdateType type, WindowPtr pWindow)
395 {
396     GETONLYPRIVFROMPRIVATE;
397     dmxConsoleDraw(priv, 1, 1);
398 }
399 
400 static void
dmxConsoleMoveAbsolute(myPrivate * priv,int x,int y,DevicePtr pDev,dmxMotionProcPtr motion,DMXBlockType block)401 dmxConsoleMoveAbsolute(myPrivate * priv, int x, int y,
402                        DevicePtr pDev, dmxMotionProcPtr motion,
403                        DMXBlockType block)
404 {
405     int tmpX, tmpY, v[2];
406 
407     tmpX = unscalex(priv, x);
408     tmpY = unscalex(priv, y);
409     DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
410             x, y, tmpX, tmpY, priv->curX, priv->curY);
411     if (tmpX == priv->curX && tmpY == priv->curY)
412         return;
413     v[0] = unscalex(priv, x);
414     v[1] = unscaley(priv, y);
415     motion(pDev, v, 0, 2, DMX_ABSOLUTE_CONFINED, block);
416     /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
417 }
418 
419 static void
dmxConsoleMoveRelative(myPrivate * priv,int x,int y,DevicePtr pDev,dmxMotionProcPtr motion,DMXBlockType block)420 dmxConsoleMoveRelative(myPrivate * priv, int x, int y,
421                        DevicePtr pDev, dmxMotionProcPtr motion,
422                        DMXBlockType block)
423 {
424     int v[2];
425 
426     /* Ignore the event generated from * warping back to middle */
427     if (x == priv->lastX && y == priv->lastY)
428         return;
429     v[0] = priv->lastX - x;
430     v[1] = priv->lastY - y;
431     motion(pDev, v, 0, 2, DMX_RELATIVE, block);
432     /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
433 }
434 
435 /** This routine gets called from #dmxCoreMotion for each motion.  This
436  * allows the console's notion of the cursor postion to change when
437  * another input device actually caused the change. */
438 void
dmxConsoleUpdatePosition(void * private,int x,int y)439 dmxConsoleUpdatePosition(void *private, int x, int y)
440 {
441     GETONLYPRIVFROMPRIVATE;
442     int tmpX, tmpY;
443     Display *dpy = priv->display;
444     static unsigned long dmxGeneration = 0;
445 
446     tmpX = scalex(priv, x);
447     tmpY = scaley(priv, y);
448     DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
449             x, y, tmpX, tmpY, priv->consWidth, priv->consHeight);
450 
451     if (priv->fine)
452         dmxConsoleUpdateFineCursor(priv);
453     if (tmpX != priv->curX || tmpY != priv->curY) {
454         if (tmpX < 0)
455             tmpX = 0;
456         if (tmpY < 0)
457             tmpY = 0;
458         if (tmpX >= priv->consWidth)
459             tmpX = priv->consWidth - 1;
460         if (tmpY >= priv->consHeight)
461             tmpY = priv->consHeight - 1;
462         priv->curX = tmpX;
463         priv->curY = tmpY;
464         if (!priv->fine) {
465             DMXDBG2("   WARP B %d %d\n", priv->curX, priv->curY);
466             XWarpPointer(dpy, priv->window,
467                          priv->window, 0, 0, 0, 0, tmpX, tmpY);
468             XSync(dpy, False);  /* Not a backend display */
469         }
470     }
471 
472     if (dmxGeneration != serverGeneration) {
473         dmxGeneration = serverGeneration;
474         dmxConsoleDraw(priv, 1, 1);
475     }
476 }
477 
478 /** Collect all pending events from the console's display.  Plase these
479  * events on the server event queue using the \a motion and \a enqueue
480  * routines.  The \a checkspecial routine is used to check for special
481  * keys that need handling.  \a block tells if signals should be blocked
482  * when updating the event queue. */
483 void
dmxConsoleCollectEvents(DevicePtr pDev,dmxMotionProcPtr motion,dmxEnqueueProcPtr enqueue,dmxCheckSpecialProcPtr checkspecial,DMXBlockType block)484 dmxConsoleCollectEvents(DevicePtr pDev,
485                         dmxMotionProcPtr motion,
486                         dmxEnqueueProcPtr enqueue,
487                         dmxCheckSpecialProcPtr checkspecial, DMXBlockType block)
488 {
489     GETPRIVFROMPDEV;
490     GETDMXINPUTFROMPRIV;
491     Display *dpy = priv->display;
492     Window win = priv->window;
493     int width = priv->width;
494     int height = priv->height;
495     XEvent X, N;
496     XSetWindowAttributes attribs;
497     static int rInitialized = 0;
498     static Region r;
499     XRectangle rect;
500     static int raising = 0, raiseX, raiseY;     /* FIXME */
501 
502     while (XPending(dpy)) {
503         XNextEvent(dpy, &X);
504         switch (X.type) {
505         case VisibilityNotify:
506             break;
507         case Expose:
508             DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
509                     X.xexpose.count,
510                     X.xexpose.x, X.xexpose.y,
511                     X.xexpose.width, X.xexpose.height);
512             if (!rInitialized++)
513                 r = XCreateRegion();
514             rect.x = X.xexpose.x;
515             rect.y = X.xexpose.y;
516             rect.width = X.xexpose.width;
517             rect.height = X.xexpose.height;
518             XUnionRectWithRegion(&rect, r, r);
519             if (X.xexpose.count == 0) {
520                 XSetRegion(dpy, priv->gc, r);
521                 XSetRegion(dpy, priv->gcDet, r);
522                 XSetRegion(dpy, priv->gcRev, r);
523                 dmxConsoleDraw(priv, 1, 1);
524                 XSetClipMask(dpy, priv->gc, None);
525                 XSetClipMask(dpy, priv->gcDet, None);
526                 XSetClipMask(dpy, priv->gcRev, None);
527                 XDestroyRegion(r);
528                 rInitialized = 0;
529             }
530             break;
531         case ResizeRequest:
532             DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
533                     X.xresizerequest.width, X.xresizerequest.height);
534             priv->consWidth = X.xresizerequest.width;
535             priv->consHeight = X.xresizerequest.height;
536             priv->xScale = (double) priv->consWidth / width;
537             priv->yScale = (double) priv->consHeight / height;
538             attribs.override_redirect = True;
539             XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
540             XResizeWindow(dpy, win, priv->consWidth, priv->consHeight);
541             XFreePixmap(dpy, priv->pixmap);
542             priv->pixmap = XCreatePixmap(dpy,
543                                          RootWindow(dpy, DefaultScreen(dpy)),
544                                          priv->consWidth,
545                                          priv->consHeight,
546                                          DefaultDepth(dpy, DefaultScreen(dpy)));
547             dmxConsoleDraw(priv, 1, 1);
548             attribs.override_redirect = False;
549             XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
550             break;
551         case LeaveNotify:
552             DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
553                     X.xcrossing.x, X.xcrossing.y, raising, priv->fine);
554             if (!priv->captured)
555                 dmxCommonRestoreState(priv);
556             else {
557                 dmxConsoleUncapture(dmxInput);
558                 dmxCommonRestoreState(priv);
559             }
560             break;
561         case EnterNotify:
562             DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
563                     X.xcrossing.x, X.xcrossing.y, raising, priv->fine,
564                     priv->curX, priv->curY);
565             dmxCommonSaveState(priv);
566             if (raising) {
567                 raising = 0;
568                 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
569                                        priv->mou, motion, block);
570             }
571             else {
572                 if (priv->fine) {
573                     /* The raise will generate an event near the center,
574                      * which is not where the cursor should be.  So we
575                      * save the real position, do the raise, and move
576                      * the cursor here again after the raise generates
577                      * the event. */
578                     raising = 1;
579                     raiseX = X.xcrossing.x;
580                     raiseY = X.xcrossing.y;
581                     XRaiseWindow(dpy, priv->window);
582                 }
583                 XSync(dpy, False);      /* Not a backend display */
584                 if (!X.xcrossing.x && !X.xcrossing.y)
585                     dmxConsoleMoveAbsolute(priv, priv->curX, priv->curY,
586                                            priv->mou, motion, block);
587             }
588             break;
589         case MotionNotify:
590             if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
591                 continue;
592             if (XPending(dpy)) {        /* do motion compression */
593                 XPeekEvent(dpy, &N);
594                 if (N.type == MotionNotify)
595                     continue;
596             }
597             DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
598                     X.xmotion.x, X.xmotion.y);
599             if (raising) {
600                 raising = 0;
601                 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
602                                        priv->mou, motion, block);
603             }
604             else {
605                 if (priv->fine)
606                     dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
607                                            priv->mou, motion, block);
608                 else
609                     dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
610                                            priv->mou, motion, block);
611             }
612             break;
613         case KeyPress:
614         case KeyRelease:
615             enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
616             break;
617         default:
618             /* Pass the whole event here, because
619              * this may be an extension event. */
620             enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block);
621             break;
622         }
623     }
624 }
625 
626 static void
dmxCloseConsole(myPrivate * priv)627 dmxCloseConsole(myPrivate * priv)
628 {
629     GETDMXINPUTFROMPRIV;
630     dmxCommonRestoreState(priv);
631     if (priv->display) {
632         XFreeGC(priv->display, priv->gc);
633         XFreeGC(priv->display, priv->gcDet);
634         XFreeGC(priv->display, priv->gcRev);
635         XFreeGC(priv->display, priv->gcCur);
636         if (!dmxInput->console)
637             XCloseDisplay(priv->display);
638     }
639     priv->display = NULL;
640 }
641 
642 static Bool
dmxCloseConsoleScreen(ScreenPtr pScreen)643 dmxCloseConsoleScreen(ScreenPtr pScreen)
644 {
645     myPrivate *priv, *last;
646 
647     for (last = priv = (myPrivate *) dixLookupPrivate(&pScreen->devPrivates,
648                                                       dmxScreenPrivateKey);
649          priv; priv = priv->next)
650         dmxCloseConsole(last = priv);
651 
652     DMX_UNWRAP(CloseScreen, last, pScreen);
653     return pScreen->CloseScreen(pScreen);
654 }
655 
656 static Cursor
dmxConsoleCreateEmptyCursor(myPrivate * priv)657 dmxConsoleCreateEmptyCursor(myPrivate * priv)
658 {
659     char noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
660     Pixmap pixmap;
661     Cursor cursor;
662     XColor color, tmpColor;
663     Display *dpy = priv->display;
664 
665     /* Create empty cursor for window */
666     pixmap = XCreateBitmapFromData(priv->display, priv->window,
667                                    noCursorData, 8, 8);
668     if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
669                           "black", &color, &tmpColor))
670         dmxLog(dmxFatal, "Cannot allocate color for cursor\n");
671     cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0);
672     XFreePixmap(dpy, pixmap);
673     return cursor;
674 }
675 
676 static void
dmxConsoleComputeWidthHeight(myPrivate * priv,int * width,int * height,double * xScale,double * yScale,int * consWidth,int * consHeight)677 dmxConsoleComputeWidthHeight(myPrivate * priv,
678                              int *width, int *height,
679                              double *xScale, double *yScale,
680                              int *consWidth, int *consHeight)
681 {
682     int screen;
683     Display *dpy = priv->display;
684 
685     *width = 0;
686     *height = 0;
687     *xScale = 1.0;
688     *yScale = 1.0;
689 
690     screen = DefaultScreen(dpy);
691     *consWidth = DisplayWidth(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
692     *consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
693 
694     if (*consWidth < 1)
695         *consWidth = 1;
696     if (*consHeight < 1)
697         *consHeight = 1;
698 
699 #if 1
700     /* Always keep the console size similar
701      * to the global bounding box. */
702     *width = dmxGlobalWidth;
703     *height = dmxGlobalHeight;
704 #else
705     /* Make the console window as big as
706      * possible by computing the visible
707      * bounding box. */
708     for (i = 0; i < dmxNumScreens; i++) {
709         if (screenInfo.screens[i]->x + screenInfo.screens[i]->width > *width)
710             *width = screenInfo.screens[i]->x + screenInfo.screens[i]->width;
711 
712         if (screenInfo.screens[i]->y + screenInfo.screens[i]->height > *height)
713             *height = screenInfo.screens[i]->y + screenInfo.screens[i]->height;
714     }
715 #endif
716 
717     if ((double) *consWidth / *width < (double) *consHeight / *height)
718         *xScale = *yScale = (double) *consWidth / *width;
719     else
720         *xScale = *yScale = (double) *consHeight / *height;
721 
722     *consWidth = scalex(priv, *width);
723     *consHeight = scaley(priv, *height);
724     if (*consWidth < 1)
725         *consWidth = 1;
726     if (*consHeight < 1)
727         *consHeight = 1;
728 }
729 
730 /** Re-initialized the console device described by \a pDev (after a
731  * reconfig). */
732 void
dmxConsoleReInit(DevicePtr pDev)733 dmxConsoleReInit(DevicePtr pDev)
734 {
735     GETPRIVFROMPDEV;
736     Display *dpy;
737 
738     if (!priv || !priv->initialized)
739         return;
740     dpy = priv->display;
741 
742     dmxConsoleComputeWidthHeight(priv,
743                                  &priv->width, &priv->height,
744                                  &priv->xScale, &priv->yScale,
745                                  &priv->consWidth, &priv->consHeight);
746     XResizeWindow(dpy, priv->window, priv->consWidth, priv->consHeight);
747     XFreePixmap(dpy, priv->pixmap);
748     priv->pixmap = XCreatePixmap(dpy,
749                                  RootWindow(dpy, DefaultScreen(dpy)),
750                                  priv->consWidth,
751                                  priv->consHeight,
752                                  DefaultDepth(dpy, DefaultScreen(dpy)));
753     dmxConsoleDraw(priv, 1, 1);
754 }
755 
756 /** Initialized the console device described by \a pDev. */
757 void
dmxConsoleInit(DevicePtr pDev)758 dmxConsoleInit(DevicePtr pDev)
759 {
760     GETPRIVFROMPDEV;
761     DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
762     int screen;
763     unsigned long mask;
764     XSetWindowAttributes attribs;
765     Display *dpy;
766     Window win;
767     XGCValues gcvals;
768     XColor color;
769     XClassHint class_hints;
770     unsigned long tmp;
771 
772     if (dmxLocal->type == DMX_LOCAL_MOUSE)
773         priv->mou = pDev;
774     if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
775         priv->kbd = pDev;
776     if (priv->initialized++)
777         return;                 /* Only do once for mouse/keyboard pair */
778 
779     if (!(dpy = priv->display = XOpenDisplay(dmxInput->name)))
780         dmxLog(dmxFatal,
781                "dmxOpenConsole: cannot open console display %s\n",
782                dmxInput->name);
783 
784     /* Set up defaults */
785     dmxConsoleComputeWidthHeight(priv,
786                                  &priv->width, &priv->height,
787                                  &priv->xScale, &priv->yScale,
788                                  &priv->consWidth, &priv->consHeight);
789 
790     /* Private initialization using computed values or constants. */
791     screen = DefaultScreen(dpy);
792     priv->initPointerX = scalex(priv, priv->width / 2);
793     priv->initPointerY = scaley(priv, priv->height / 2);
794     priv->eventMask = (ButtonPressMask
795                        | ButtonReleaseMask
796                        | PointerMotionMask
797                        | EnterWindowMask
798                        | LeaveWindowMask
799                        | KeyPressMask
800                        | KeyReleaseMask | ExposureMask | ResizeRedirectMask);
801 
802     mask = CWBackPixel | CWEventMask | CWColormap | CWOverrideRedirect;
803     attribs.colormap = DefaultColormap(dpy, screen);
804     if (XParseColor(dpy, attribs.colormap, CONSOLE_BG_COLOR, &color)
805         && XAllocColor(dpy, attribs.colormap, &color)) {
806         attribs.background_pixel = color.pixel;
807     }
808     else
809         attribs.background_pixel = WhitePixel(dpy, screen);
810 
811     attribs.event_mask = priv->eventMask;
812     attribs.override_redirect = False;
813 
814     win = priv->window = XCreateWindow(dpy,
815                                        RootWindow(dpy, screen),
816                                        0, 0, priv->consWidth, priv->consHeight,
817                                        0,
818                                        DefaultDepth(dpy, screen),
819                                        InputOutput,
820                                        DefaultVisual(dpy, screen),
821                                        mask, &attribs);
822     priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
823                                  priv->consWidth, priv->consHeight,
824                                  DefaultDepth(dpy, screen));
825 
826     /* Set up properties */
827     XStoreName(dpy, win, DMX_CONSOLE_NAME);
828     class_hints.res_name = (char *) DMX_RES_NAME;
829     class_hints.res_class = (char *) DMX_RES_CLASS;
830     XSetClassHint(dpy, win, &class_hints);
831 
832     /* Map the window */
833     XMapWindow(dpy, win);
834 
835     /* Create cursors */
836     priv->cursorNormal = XCreateFontCursor(dpy, XC_circle);
837     priv->cursorGrabbed = XCreateFontCursor(dpy, XC_spider);
838     priv->cursorEmpty = dmxConsoleCreateEmptyCursor(priv);
839     XDefineCursor(dpy, priv->window, priv->cursorNormal);
840 
841     /* Create GC */
842     mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground |
843             GCBackground | GCLineWidth | GCLineStyle | GCCapStyle |
844             GCFillStyle | GCGraphicsExposures);
845     gcvals.function = GXcopy;
846     gcvals.plane_mask = AllPlanes;
847     gcvals.clip_mask = None;
848     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_FG_COLOR, &color)
849         && XAllocColor(dpy, attribs.colormap, &color)) {
850         gcvals.foreground = color.pixel;
851     }
852     else
853         gcvals.foreground = BlackPixel(dpy, screen);
854     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_BG_COLOR, &color)
855         && XAllocColor(dpy, attribs.colormap, &color)) {
856         gcvals.background = color.pixel;
857     }
858     else
859         gcvals.background = WhitePixel(dpy, screen);
860     gcvals.line_width = 0;
861     gcvals.line_style = LineSolid;
862     gcvals.cap_style = CapNotLast;
863     gcvals.fill_style = FillSolid;
864     gcvals.graphics_exposures = False;
865 
866     priv->gc = XCreateGC(dpy, win, mask, &gcvals);
867 
868     tmp = gcvals.foreground;
869     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_DET_COLOR, &color)
870         && XAllocColor(dpy, attribs.colormap, &color)) {
871         gcvals.foreground = color.pixel;
872     }
873     else
874         gcvals.foreground = BlackPixel(dpy, screen);
875     priv->gcDet = XCreateGC(dpy, win, mask, &gcvals);
876     gcvals.foreground = tmp;
877 
878     tmp = gcvals.background;
879     gcvals.background = gcvals.foreground;
880     gcvals.foreground = tmp;
881     priv->gcRev = XCreateGC(dpy, win, mask, &gcvals);
882 
883     gcvals.background = gcvals.foreground;
884     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_CUR_COLOR, &color)
885         && XAllocColor(dpy, attribs.colormap, &color)) {
886         gcvals.foreground = color.pixel;
887     }
888     else
889         gcvals.foreground = BlackPixel(dpy, screen);
890     priv->gcCur = XCreateGC(dpy, win, mask, &gcvals);
891 
892     dmxConsoleDraw(priv, 1, 1);
893 
894     if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
895                          dmxScreenPrivateKey))
896         priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
897                                       dmxScreenPrivateKey);
898     else
899         DMX_WRAP(CloseScreen, dmxCloseConsoleScreen,
900                  priv, screenInfo.screens[0]);
901     dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey,
902                   priv);
903 }
904 
905 /** Fill in the \a info structure for the specified \a pDev.  Only used
906  * for pointers. */
907 void
dmxConsoleMouGetInfo(DevicePtr pDev,DMXLocalInitInfoPtr info)908 dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
909 {
910     GETPRIVFROMPDEV;
911 
912     info->buttonClass = 1;
913     dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
914     info->valuatorClass = 1;
915     info->numRelAxes = 2;
916     info->minval[0] = 0;
917     info->minval[1] = 0;
918     /* max possible console window size: */
919     info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display));
920     info->maxval[1] =
921         DisplayHeight(priv->display, DefaultScreen(priv->display));
922     info->res[0] = 1;
923     info->minres[0] = 0;
924     info->maxres[0] = 1;
925     info->ptrFeedbackClass = 1;
926 }
927 
928 /** Fill in the \a info structure for the specified \a pDev.  Only used
929  * for keyboard. */
930 void
dmxConsoleKbdGetInfo(DevicePtr pDev,DMXLocalInitInfoPtr info)931 dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
932 {
933     dmxCommonKbdGetInfo(pDev, info);
934     info->keyboard = 1;
935     info->keyClass = 1;
936     dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
937     info->freemap = 1;
938     info->focusClass = 1;
939     info->kbdFeedbackClass = 1;
940 }
941 
942 /** Handle special console-only keys. */
943 int
dmxConsoleFunctions(void * private,DMXFunctionType function)944 dmxConsoleFunctions(void *private, DMXFunctionType function)
945 {
946     GETONLYPRIVFROMPRIVATE;
947     XRectangle rect;
948     Display *dpy = priv->display;
949 
950     switch (function) {
951     case DMX_FUNCTION_FINE:
952         if (priv->fine) {
953             priv->fine = 0;
954             dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rect);
955             XSetClipRectangles(dpy, priv->gc, 0, 0, &rect, 1, Unsorted);
956             XCopyArea(dpy, priv->pixmap, priv->window, priv->gc,
957                       0, 0, priv->consWidth, priv->consHeight, 0, 0);
958             XSetClipMask(dpy, priv->gc, None);
959 
960             XDefineCursor(dpy, priv->window,
961                           priv->grabbed
962                           ? priv->cursorGrabbed : priv->cursorNormal);
963             XWarpPointer(dpy, priv->window, priv->window,
964                          0, 0, 0, 0,
965                          scalex(priv, priv->globalX),
966                          scaley(priv, priv->globalY));
967             XSync(dpy, False);  /* Not a backend display */
968         }
969         else {
970             priv->fine = 1;
971             XRaiseWindow(dpy, priv->window);
972             XDefineCursor(dpy, priv->window, priv->cursorEmpty);
973             dmxConsoleUpdateFineCursor(priv);
974         }
975         return 1;
976     case DMX_FUNCTION_GRAB:
977         if (priv->grabbed) {
978             XUngrabKeyboard(dpy, CurrentTime);
979             XUngrabPointer(dpy, CurrentTime);
980             XDefineCursor(dpy, priv->window,
981                           priv->fine ? priv->cursorEmpty : priv->cursorNormal);
982         }
983         else {
984             if (XGrabPointer(dpy, priv->window, True,
985                              0, GrabModeAsync, GrabModeAsync, priv->window,
986                              None, CurrentTime)) {
987                 dmxLog(dmxError, "XGrabPointer failed\n");
988                 return 0;
989             }
990             if (XGrabKeyboard(dpy, priv->window, True,
991                               GrabModeAsync, GrabModeAsync, CurrentTime)) {
992                 dmxLog(dmxError, "XGrabKeyboard failed\n");
993                 XUngrabPointer(dpy, CurrentTime);
994                 return 0;
995             }
996             XDefineCursor(dpy, priv->window,
997                           priv->fine ? priv->cursorEmpty : priv->cursorGrabbed);
998         }
999         priv->grabbed = !priv->grabbed;
1000         if (priv->fine)
1001             dmxConsoleUpdateFineCursor(priv);
1002         return 1;
1003     case DMX_FUNCTION_TERMINATE:
1004         return 1;
1005     default:
1006         return 0;
1007     }
1008 }
1009 
1010 static void
dmxDump(void)1011 dmxDump(void)
1012 {
1013     int i, j;
1014     DMXInputInfo *dmxInput;
1015     XEvent X;
1016 
1017     for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
1018         for (j = 0; j < dmxInput->numDevs; j++) {
1019             DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
1020             myPrivate *priv = dmxLocal->private;
1021 
1022             while (priv
1023                    && priv->display
1024                    && XCheckTypedEvent(priv->display, MotionNotify, &X)) {
1025                 DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
1026                         dmxInput->name, j, X.type, dmxEventName(X.type));
1027             }
1028         }
1029     }
1030 }
1031 
1032 /** This routine is used to warp the pointer into the console window
1033  * from anywhere on the screen.  It is used when backend and console
1034  * input are both being taken from the same X display. */
1035 void
dmxConsoleCapture(DMXInputInfo * dmxInput)1036 dmxConsoleCapture(DMXInputInfo * dmxInput)
1037 {
1038     int i;
1039     XEvent X;
1040 
1041     DMXDBG0("dmxConsoleCapture\n");
1042     dmxSync(NULL, TRUE);
1043     for (i = 0; i < dmxInput->numDevs; i++) {
1044         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
1045         myPrivate *priv = dmxLocal->private;
1046 
1047         if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
1048             continue;
1049         if (dmxLocal->type != DMX_LOCAL_MOUSE)
1050             continue;
1051         if (priv->captured)
1052             continue;
1053         priv->captured = 2;     /* Ungrab only after proximal events. */
1054         XRaiseWindow(priv->display, priv->window);
1055         XSync(priv->display, False);    /* Not a backend display */
1056         while (XCheckTypedEvent(priv->display, MotionNotify, &X)) {
1057             DMXDBG3("   Ignoring motion to %d %d after capture on %s\n",
1058                     X.xmotion.x, X.xmotion.y, dmxInput->name);
1059         }
1060         XWarpPointer(priv->display, None,
1061                      priv->window, 0, 0, 0, 0, priv->curX, priv->curY);
1062         XSync(priv->display, False);    /* Not a backend display */
1063         dmxDump();
1064         if (priv->fine)
1065             dmxConsoleUpdateFineCursor(priv);
1066     }
1067 }
1068 
1069 /** Undo the capture that was done by #dmxConsoleCapture. */
1070 void
dmxConsoleUncapture(DMXInputInfo * dmxInput)1071 dmxConsoleUncapture(DMXInputInfo * dmxInput)
1072 {
1073     int i;
1074 
1075     DMXDBG0("dmxConsoleUncapture\n");
1076     dmxSync(NULL, TRUE);
1077     for (i = 0; i < dmxInput->numDevs; i++) {
1078         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
1079         myPrivate *priv = dmxLocal->private;
1080 
1081         if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
1082             continue;
1083         if (dmxLocal->type != DMX_LOCAL_MOUSE)
1084             continue;
1085         if (!priv->captured)
1086             continue;
1087         priv->captured = 0;
1088         XSync(priv->display, False);    /* Not a backend display */
1089     }
1090 }
1091