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