1091 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1091 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
 | |
|  *
 | |
|  * All Rights Reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining
 | |
|  * a copy of this software and associated documentation files (the
 | |
|  * "Software"), to deal in the Software without restriction, including
 | |
|  * without limitation on the rights to use, copy, modify, merge,
 | |
|  * publish, distribute, sublicense, and/or sell copies of the Software,
 | |
|  * and to permit persons to whom the Software is furnished to do so,
 | |
|  * subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the
 | |
|  * next paragraph) shall be included in all copies or substantial
 | |
|  * portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 | |
|  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | |
|  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | |
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Authors:
 | |
|  *   David H. Dawes <dawes@xfree86.org>
 | |
|  *   Kevin E. Martin <kem@redhat.com>
 | |
|  *   Rickard E. (Rik) Faith <faith@redhat.com>
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  *
 | |
|  * This file implements the console input devices.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_DMX_CONFIG_H
 | |
| #include <dmx-config.h>
 | |
| #endif
 | |
| 
 | |
| #define DMX_CONSOLE_DEBUG 0
 | |
| #define DMX_WINDOW_DEBUG  0
 | |
| 
 | |
| #include "dmxinputinit.h"
 | |
| #include "dmxevents.h"
 | |
| #include "dmxconsole.h"
 | |
| #include "dmxcommon.h"
 | |
| #include "dmxscrinit.h"
 | |
| #include "dmxcb.h"
 | |
| #include "dmxsync.h"
 | |
| 
 | |
| #include "inputstr.h"
 | |
| #include "input.h"
 | |
| #include "mipointer.h"
 | |
| #include "windowstr.h"
 | |
| 
 | |
| #define CONSOLE_NUM 3
 | |
| #define CONSOLE_DEN 4
 | |
| #define DMX_CONSOLE_NAME "DMX Console"
 | |
| #define DMX_RES_NAME     "Xdmx"
 | |
| #define DMX_RES_CLASS    "XDmx"
 | |
| #define CONSOLE_BG_COLOR "gray75"
 | |
| #define CONSOLE_FG_COLOR "black"
 | |
| #define CONSOLE_SCREEN_BG_COLOR "white"
 | |
| #define CONSOLE_SCREEN_FG_COLOR "black"
 | |
| #define CONSOLE_SCREEN_DET_COLOR "gray75"
 | |
| #define CONSOLE_SCREEN_CUR_COLOR "red"
 | |
| 
 | |
| #if DMX_CONSOLE_DEBUG
 | |
| #define DMXDBG0(f)               dmxLog(dmxDebug,f)
 | |
| #define DMXDBG1(f,a)             dmxLog(dmxDebug,f,a)
 | |
| #define DMXDBG2(f,a,b)           dmxLog(dmxDebug,f,a,b)
 | |
| #define DMXDBG3(f,a,b,c)         dmxLog(dmxDebug,f,a,b,c)
 | |
| #define DMXDBG4(f,a,b,c,d)       dmxLog(dmxDebug,f,a,b,c,d)
 | |
| #define DMXDBG5(f,a,b,c,d,e)     dmxLog(dmxDebug,f,a,b,c,d,e)
 | |
| #define DMXDBG6(f,a,b,c,d,e,g)   dmxLog(dmxDebug,f,a,b,c,d,e,g)
 | |
| #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
 | |
| #else
 | |
| #define DMXDBG0(f)
 | |
| #define DMXDBG1(f,a)
 | |
| #define DMXDBG2(f,a,b)
 | |
| #define DMXDBG3(f,a,b,c)
 | |
| #define DMXDBG4(f,a,b,c,d)
 | |
| #define DMXDBG5(f,a,b,c,d,e)
 | |
| #define DMXDBG6(f,a,b,c,d,e,g)
 | |
| #define DMXDBG7(f,a,b,c,d,e,g,h)
 | |
| #endif
 | |
| 
 | |
| /* Private area for consoles. */
 | |
| typedef struct _myPrivate {
 | |
|     DMX_COMMON_PRIVATE;
 | |
|     int lastX;
 | |
|     int lastY;
 | |
|     int globalX;
 | |
|     int globalY;
 | |
|     int curX;
 | |
|     int curY;
 | |
|     int width;
 | |
|     int height;
 | |
|     int consWidth;
 | |
|     int consHeight;
 | |
|     double xScale;
 | |
|     double yScale;
 | |
|     XlibGC gc, gcDet, gcRev, gcCur;
 | |
|     int grabbed, fine, captured;
 | |
|     Cursor cursorNormal, cursorGrabbed, cursorEmpty;
 | |
|     Pixmap pixmap;
 | |
| 
 | |
|     CloseScreenProcPtr CloseScreen;
 | |
|     struct _myPrivate *next;    /* for closing multiple consoles */
 | |
|     int initialized;
 | |
|     DevicePtr mou, kbd;
 | |
| } myPrivate;
 | |
| 
 | |
| static int
 | |
| scalex(myPrivate * priv, int x)
 | |
| {
 | |
|     return (int) ((x * priv->xScale) + .5);
 | |
| }
 | |
| 
 | |
| static int
 | |
| scaley(myPrivate * priv, int y)
 | |
| {
 | |
|     return (int) ((y * priv->yScale) + .5);
 | |
| }
 | |
| 
 | |
| static int
 | |
| unscalex(myPrivate * priv, int x)
 | |
| {
 | |
|     return (int) ((x / priv->xScale) + .5);
 | |
| }
 | |
| 
 | |
| static int
 | |
| unscaley(myPrivate * priv, int y)
 | |
| {
 | |
|     return (int) ((y / priv->yScale) + .5);
 | |
| }
 | |
| 
 | |
| /** Create the private area for \a pDevice. */
 | |
| pointer
 | |
| dmxConsoleCreatePrivate(DeviceIntPtr pDevice)
 | |
| {
 | |
|     GETDMXLOCALFROMPDEVICE;
 | |
|     myPrivate *priv = calloc(1, sizeof(*priv));
 | |
| 
 | |
|     priv->dmxLocal = dmxLocal;
 | |
|     return priv;
 | |
| }
 | |
| 
 | |
| /** If \a private is non-NULL, free its associated memory. */
 | |
| void
 | |
| dmxConsoleDestroyPrivate(pointer private)
 | |
| {
 | |
|     free(private);
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleDrawFineCursor(myPrivate * priv, XRectangle * rect)
 | |
| {
 | |
|     int size = 6;
 | |
|     int x, y;
 | |
| 
 | |
|     XDrawLine(priv->display, priv->pixmap, priv->gcCur,
 | |
|               x = scalex(priv, priv->globalX) - size,
 | |
|               scaley(priv, priv->globalY),
 | |
|               scalex(priv, priv->globalX) + size, scaley(priv, priv->globalY));
 | |
|     XDrawLine(priv->display, priv->pixmap, priv->gcCur,
 | |
|               scalex(priv, priv->globalX),
 | |
|               y = scaley(priv, priv->globalY) - size,
 | |
|               scalex(priv, priv->globalX), scaley(priv, priv->globalY) + size);
 | |
|     if (priv->grabbed) {
 | |
|         XDrawLine(priv->display, priv->pixmap, priv->gcCur,
 | |
|                   scalex(priv, priv->globalX) - (int) (size / 1.4),
 | |
|                   scaley(priv, priv->globalY) - (int) (size / 1.4),
 | |
|                   scalex(priv, priv->globalX) + (int) (size / 1.4),
 | |
|                   scaley(priv, priv->globalY) + (int) (size / 1.4));
 | |
|         XDrawLine(priv->display, priv->pixmap, priv->gcCur,
 | |
|                   scalex(priv, priv->globalX) - (int) (size / 1.4),
 | |
|                   scaley(priv, priv->globalY) + (int) (size / 1.4),
 | |
|                   scalex(priv, priv->globalX) + (int) (size / 1.4),
 | |
|                   scaley(priv, priv->globalY) - (int) (size / 1.4));
 | |
|     }
 | |
|     if (rect) {
 | |
|         rect->x = x;
 | |
|         rect->y = y;
 | |
|         rect->width = 2 * size;
 | |
|         rect->height = 2 * size;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleDrawWindows(pointer private)
 | |
| {
 | |
|     GETONLYPRIVFROMPRIVATE;
 | |
|     Display *dpy = priv->display;
 | |
|     int i;
 | |
|     Region whole, used, avail;
 | |
|     XRectangle rect;
 | |
| 
 | |
|     whole = XCreateRegion();
 | |
|     used = XCreateRegion();
 | |
|     avail = XCreateRegion();
 | |
|     rect.x = 0;
 | |
|     rect.y = 0;
 | |
|     rect.width = priv->consWidth;
 | |
|     rect.height = priv->consHeight;
 | |
|     XUnionRectWithRegion(&rect, whole, whole);
 | |
| 
 | |
|     for (i = 0; i < dmxNumScreens; i++) {
 | |
|         ScreenPtr pScreen = screenInfo.screens[i];
 | |
|         WindowPtr pRoot = pScreen->root;
 | |
|         WindowPtr pChild;
 | |
| 
 | |
| #if DMX_WINDOW_DEBUG
 | |
|         dmxLog(dmxDebug, "%lu %p %p %p 2\n",
 | |
|                pRoot->drawable.id,
 | |
|                pRoot->parent, pRoot->firstChild, pRoot->lastChild);
 | |
| #endif
 | |
| 
 | |
|         for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
 | |
|             if (pChild->mapped && pChild->realized) {
 | |
| #if DMX_WINDOW_DEBUG
 | |
|                 dmxLog(dmxDebug, "  %p %d,%d %dx%d %d %d  %d RECTS\n",
 | |
|                        pChild,
 | |
|                        pChild->drawable.x,
 | |
|                        pChild->drawable.y,
 | |
|                        pChild->drawable.width,
 | |
|                        pChild->drawable.height,
 | |
|                        pChild->visibility,
 | |
|                        pChild->overrideRedirect,
 | |
|                        RegionNumRects(&pChild->clipList));
 | |
| #endif
 | |
|                 rect.x = scalex(priv, pChild->drawable.x + pScreen->x);
 | |
|                 rect.y = scaley(priv, pChild->drawable.y + pScreen->y);
 | |
|                 rect.width = scalex(priv, pChild->drawable.width);
 | |
|                 rect.height = scaley(priv, pChild->drawable.height);
 | |
|                 XDrawRectangle(dpy, priv->pixmap, priv->gc,
 | |
|                                rect.x, rect.y, rect.width, rect.height);
 | |
|                 XUnionRectWithRegion(&rect, used, used);
 | |
|                 XSubtractRegion(whole, used, avail);
 | |
|                 XSetRegion(dpy, priv->gc, avail);
 | |
|             }
 | |
|         }
 | |
| #ifdef PANORAMIX
 | |
|         if (!noPanoramiXExtension)
 | |
|             break;              /* Screen 0 valid with Xinerama */
 | |
| #endif
 | |
|     }
 | |
|     XDestroyRegion(avail);
 | |
|     XDestroyRegion(used);
 | |
|     XDestroyRegion(whole);
 | |
|     XSetClipMask(dpy, priv->gc, None);
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleDraw(myPrivate * priv, int updateCursor, int update)
 | |
| {
 | |
|     GETDMXINPUTFROMPRIV;
 | |
|     Display *dpy = priv->display;
 | |
|     int i;
 | |
| 
 | |
|     XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0,
 | |
|                    priv->consWidth, priv->consHeight);
 | |
| 
 | |
|     for (i = 0; i < dmxNumScreens; i++) {
 | |
|         DMXScreenInfo *dmxScreen = &dmxScreens[i];
 | |
| 
 | |
|         XFillRectangle(dpy, priv->pixmap,
 | |
|                        dmxScreen->beDisplay ? priv->gcRev : priv->gcDet,
 | |
|                        scalex(priv, screenInfo.screens[i]->x),
 | |
|                        scaley(priv, screenInfo.screens[i]->y),
 | |
|                        scalex(priv, screenInfo.screens[i]->width),
 | |
|                        scaley(priv, screenInfo.screens[i]->height));
 | |
|     }
 | |
|     for (i = 0; i < dmxNumScreens; i++) {
 | |
|         XDrawRectangle(dpy, priv->pixmap, priv->gc,
 | |
|                        scalex(priv, screenInfo.screens[i]->x),
 | |
|                        scaley(priv, screenInfo.screens[i]->y),
 | |
|                        scalex(priv, screenInfo.screens[i]->width),
 | |
|                        scaley(priv, screenInfo.screens[i]->height));
 | |
|     }
 | |
|     if (dmxInput->windows)
 | |
|         dmxConsoleDrawWindows(priv);
 | |
|     if (priv->fine && updateCursor)
 | |
|         dmxConsoleDrawFineCursor(priv, 0);
 | |
|     if (update) {
 | |
|         XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
 | |
|                   0, 0, priv->consWidth, priv->consHeight, 0, 0);
 | |
|         XSync(priv->display, False);    /* Not a backend display */
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleClearCursor(myPrivate * priv, int x, int y, XRectangle * rect)
 | |
| {
 | |
|     int cw = 14, ch = 14;       /* Clear width and height */
 | |
| 
 | |
|     rect->x = scalex(priv, x) - cw / 2;
 | |
|     rect->y = scaley(priv, y) - ch / 2;
 | |
|     rect->width = cw;
 | |
|     rect->height = ch;
 | |
|     XSetClipRectangles(priv->display, priv->gc, 0, 0, rect, 1, Unsorted);
 | |
|     XSetClipRectangles(priv->display, priv->gcDet, 0, 0, rect, 1, Unsorted);
 | |
|     XSetClipRectangles(priv->display, priv->gcRev, 0, 0, rect, 1, Unsorted);
 | |
|     dmxConsoleDraw(priv, 0, 0);
 | |
|     XSetClipMask(priv->display, priv->gc, None);
 | |
|     XSetClipMask(priv->display, priv->gcDet, None);
 | |
|     XSetClipMask(priv->display, priv->gcRev, None);
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleUpdateFineCursor(myPrivate * priv)
 | |
| {
 | |
|     int leave = 0;
 | |
|     XRectangle rects[2];
 | |
| 
 | |
|     dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
 | |
|     if (priv->dmxLocal->sendsCore) {
 | |
|         dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
 | |
|     }
 | |
|     else {
 | |
|         priv->globalX = priv->dmxLocal->lastX;
 | |
|         priv->globalY = priv->dmxLocal->lastY;
 | |
|     }
 | |
| 
 | |
|     priv->lastX = scalex(priv, priv->width / 2);
 | |
|     priv->lastY = scaley(priv, priv->height / 2);
 | |
| 
 | |
|     /* Compute new warp position, which may be
 | |
|        outside the window */
 | |
|     if (priv->globalX < 1 || priv->globalX >= priv->width) {
 | |
|         if (priv->globalX < 1)
 | |
|             priv->lastX = 0;
 | |
|         else
 | |
|             priv->lastX = scalex(priv, priv->width);
 | |
|         priv->lastY = scaley(priv, priv->globalY);
 | |
|         ++leave;
 | |
|     }
 | |
|     if (priv->globalY < 1 || priv->globalY >= priv->height) {
 | |
|         if (priv->globalY < 1)
 | |
|             priv->lastY = 0;
 | |
|         else
 | |
|             priv->lastY = scaley(priv, priv->height);
 | |
|         priv->lastX = scalex(priv, priv->globalX);
 | |
|         ++leave;
 | |
|     }
 | |
| 
 | |
|     /* Draw pseudo cursor in window */
 | |
|     dmxConsoleDrawFineCursor(priv, &rects[1]);
 | |
| 
 | |
|     XSetClipRectangles(priv->display, priv->gc, 0, 0, rects, 2, Unsorted);
 | |
|     XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
 | |
|               0, 0, priv->consWidth, priv->consHeight, 0, 0);
 | |
|     XSetClipMask(priv->display, priv->gc, None);
 | |
| 
 | |
|     DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
 | |
|             priv->lastX, priv->lastY);
 | |
|     XWarpPointer(priv->display, priv->window, priv->window,
 | |
|                  0, 0, 0, 0, priv->lastX, priv->lastY);
 | |
|     XSync(priv->display, False);        /* Not a backend display */
 | |
| 
 | |
|     if (leave) {
 | |
|         XEvent X;
 | |
| 
 | |
|         while (XCheckMaskEvent(priv->display, PointerMotionMask, &X)) {
 | |
|             if (X.type == MotionNotify) {
 | |
|                 if (X.xmotion.x != priv->lastX || X.xmotion.y != priv->lastY) {
 | |
|                     DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
 | |
|                             X.xmotion.x, X.xmotion.y, priv->lastX, priv->lastY);
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n",
 | |
|                        X.type, dmxEventName(X.type));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
 | |
|             priv->lastX, priv->lastY,
 | |
|             scalex(priv, priv->width),
 | |
|             scaley(priv, priv->height), priv->globalX, priv->globalY);
 | |
| }
 | |
| 
 | |
| /** Whenever the window layout (size, position, stacking order) might be
 | |
|  * changed, this routine is called with the \a pWindow that changed and
 | |
|  * the \a type of change.  This routine is called in a conservative
 | |
|  * fashion: the actual layout of the windows of the screen might not
 | |
|  * have had any human-visible changes. */
 | |
| void
 | |
| dmxConsoleUpdateInfo(pointer private, DMXUpdateType type, WindowPtr pWindow)
 | |
| {
 | |
|     GETONLYPRIVFROMPRIVATE;
 | |
|     dmxConsoleDraw(priv, 1, 1);
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleMoveAbsolute(myPrivate * priv, int x, int y,
 | |
|                        DevicePtr pDev, dmxMotionProcPtr motion,
 | |
|                        DMXBlockType block)
 | |
| {
 | |
|     int tmpX, tmpY, v[2];
 | |
| 
 | |
|     tmpX = unscalex(priv, x);
 | |
|     tmpY = unscalex(priv, y);
 | |
|     DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
 | |
|             x, y, tmpX, tmpY, priv->curX, priv->curY);
 | |
|     if (tmpX == priv->curX && tmpY == priv->curY)
 | |
|         return;
 | |
|     v[0] = unscalex(priv, x);
 | |
|     v[1] = unscaley(priv, y);
 | |
|     motion(pDev, v, 0, 2, DMX_ABSOLUTE_CONFINED, block);
 | |
|     /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleMoveRelative(myPrivate * priv, int x, int y,
 | |
|                        DevicePtr pDev, dmxMotionProcPtr motion,
 | |
|                        DMXBlockType block)
 | |
| {
 | |
|     int v[2];
 | |
| 
 | |
|     /* Ignore the event generated from * warping back to middle */
 | |
|     if (x == priv->lastX && y == priv->lastY)
 | |
|         return;
 | |
|     v[0] = priv->lastX - x;
 | |
|     v[1] = priv->lastY - y;
 | |
|     motion(pDev, v, 0, 2, DMX_RELATIVE, block);
 | |
|     /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
 | |
| }
 | |
| 
 | |
| /** This routine gets called from #dmxCoreMotion for each motion.  This
 | |
|  * allows the console's notion of the cursor postion to change when
 | |
|  * another input device actually caused the change. */
 | |
| void
 | |
| dmxConsoleUpdatePosition(pointer private, int x, int y)
 | |
| {
 | |
|     GETONLYPRIVFROMPRIVATE;
 | |
|     int tmpX, tmpY;
 | |
|     Display *dpy = priv->display;
 | |
|     static unsigned long dmxGeneration = 0;
 | |
| 
 | |
|     tmpX = scalex(priv, x);
 | |
|     tmpY = scaley(priv, y);
 | |
|     DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
 | |
|             x, y, tmpX, tmpY, priv->consWidth, priv->consHeight);
 | |
| 
 | |
|     if (priv->fine)
 | |
|         dmxConsoleUpdateFineCursor(priv);
 | |
|     if (tmpX != priv->curX || tmpY != priv->curY) {
 | |
|         if (tmpX < 0)
 | |
|             tmpX = 0;
 | |
|         if (tmpY < 0)
 | |
|             tmpY = 0;
 | |
|         if (tmpX >= priv->consWidth)
 | |
|             tmpX = priv->consWidth - 1;
 | |
|         if (tmpY >= priv->consHeight)
 | |
|             tmpY = priv->consHeight - 1;
 | |
|         priv->curX = tmpX;
 | |
|         priv->curY = tmpY;
 | |
|         if (!priv->fine) {
 | |
|             DMXDBG2("   WARP B %d %d\n", priv->curX, priv->curY);
 | |
|             XWarpPointer(dpy, priv->window,
 | |
|                          priv->window, 0, 0, 0, 0, tmpX, tmpY);
 | |
|             XSync(dpy, False);  /* Not a backend display */
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (dmxGeneration != serverGeneration) {
 | |
|         dmxGeneration = serverGeneration;
 | |
|         dmxConsoleDraw(priv, 1, 1);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /** Collect all pending events from the console's display.  Plase these
 | |
|  * events on the server event queue using the \a motion and \a enqueue
 | |
|  * routines.  The \a checkspecial routine is used to check for special
 | |
|  * keys that need handling.  \a block tells if signals should be blocked
 | |
|  * when updating the event queue. */
 | |
| void
 | |
| dmxConsoleCollectEvents(DevicePtr pDev,
 | |
|                         dmxMotionProcPtr motion,
 | |
|                         dmxEnqueueProcPtr enqueue,
 | |
|                         dmxCheckSpecialProcPtr checkspecial, DMXBlockType block)
 | |
| {
 | |
|     GETPRIVFROMPDEV;
 | |
|     GETDMXINPUTFROMPRIV;
 | |
|     Display *dpy = priv->display;
 | |
|     Window win = priv->window;
 | |
|     int width = priv->width;
 | |
|     int height = priv->height;
 | |
|     XEvent X, N;
 | |
|     XSetWindowAttributes attribs;
 | |
|     static int rInitialized = 0;
 | |
|     static Region r;
 | |
|     XRectangle rect;
 | |
|     static int raising = 0, raiseX, raiseY;     /* FIXME */
 | |
| 
 | |
|     while (XPending(dpy)) {
 | |
|         XNextEvent(dpy, &X);
 | |
|         switch (X.type) {
 | |
|         case VisibilityNotify:
 | |
|             break;
 | |
|         case Expose:
 | |
|             DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
 | |
|                     X.xexpose.count,
 | |
|                     X.xexpose.x, X.xexpose.y,
 | |
|                     X.xexpose.width, X.xexpose.height);
 | |
|             if (!rInitialized++)
 | |
|                 r = XCreateRegion();
 | |
|             rect.x = X.xexpose.x;
 | |
|             rect.y = X.xexpose.y;
 | |
|             rect.width = X.xexpose.width;
 | |
|             rect.height = X.xexpose.height;
 | |
|             XUnionRectWithRegion(&rect, r, r);
 | |
|             if (X.xexpose.count == 0) {
 | |
|                 XSetRegion(dpy, priv->gc, r);
 | |
|                 XSetRegion(dpy, priv->gcDet, r);
 | |
|                 XSetRegion(dpy, priv->gcRev, r);
 | |
|                 dmxConsoleDraw(priv, 1, 1);
 | |
|                 XSetClipMask(dpy, priv->gc, None);
 | |
|                 XSetClipMask(dpy, priv->gcDet, None);
 | |
|                 XSetClipMask(dpy, priv->gcRev, None);
 | |
|                 XDestroyRegion(r);
 | |
|                 rInitialized = 0;
 | |
|             }
 | |
|             break;
 | |
|         case ResizeRequest:
 | |
|             DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
 | |
|                     X.xresizerequest.width, X.xresizerequest.height);
 | |
|             priv->consWidth = X.xresizerequest.width;
 | |
|             priv->consHeight = X.xresizerequest.height;
 | |
|             priv->xScale = (double) priv->consWidth / width;
 | |
|             priv->yScale = (double) priv->consHeight / height;
 | |
|             attribs.override_redirect = True;
 | |
|             XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
 | |
|             XResizeWindow(dpy, win, priv->consWidth, priv->consHeight);
 | |
|             XFreePixmap(dpy, priv->pixmap);
 | |
|             priv->pixmap = XCreatePixmap(dpy,
 | |
|                                          RootWindow(dpy, DefaultScreen(dpy)),
 | |
|                                          priv->consWidth,
 | |
|                                          priv->consHeight,
 | |
|                                          DefaultDepth(dpy, DefaultScreen(dpy)));
 | |
|             dmxConsoleDraw(priv, 1, 1);
 | |
|             attribs.override_redirect = False;
 | |
|             XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
 | |
|             break;
 | |
|         case LeaveNotify:
 | |
|             DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
 | |
|                     X.xcrossing.x, X.xcrossing.y, raising, priv->fine);
 | |
|             if (!priv->captured)
 | |
|                 dmxCommonRestoreState(priv);
 | |
|             else {
 | |
|                 dmxConsoleUncapture(dmxInput);
 | |
|                 dmxCommonRestoreState(priv);
 | |
|             }
 | |
|             break;
 | |
|         case EnterNotify:
 | |
|             DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
 | |
|                     X.xcrossing.x, X.xcrossing.y, raising, priv->fine,
 | |
|                     priv->curX, priv->curY);
 | |
|             dmxCommonSaveState(priv);
 | |
|             if (raising) {
 | |
|                 raising = 0;
 | |
|                 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
 | |
|                                        priv->mou, motion, block);
 | |
|             }
 | |
|             else {
 | |
|                 if (priv->fine) {
 | |
|                     /* The raise will generate an event near the center,
 | |
|                      * which is not where the cursor should be.  So we
 | |
|                      * save the real position, do the raise, and move
 | |
|                      * the cursor here again after the raise generates
 | |
|                      * the event. */
 | |
|                     raising = 1;
 | |
|                     raiseX = X.xcrossing.x;
 | |
|                     raiseY = X.xcrossing.y;
 | |
|                     XRaiseWindow(dpy, priv->window);
 | |
|                 }
 | |
|                 XSync(dpy, False);      /* Not a backend display */
 | |
|                 if (!X.xcrossing.x && !X.xcrossing.y)
 | |
|                     dmxConsoleMoveAbsolute(priv, priv->curX, priv->curY,
 | |
|                                            priv->mou, motion, block);
 | |
|             }
 | |
|             break;
 | |
|         case MotionNotify:
 | |
|             if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
 | |
|                 continue;
 | |
|             if (XPending(dpy)) {        /* do motion compression */
 | |
|                 XPeekEvent(dpy, &N);
 | |
|                 if (N.type == MotionNotify)
 | |
|                     continue;
 | |
|             }
 | |
|             DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
 | |
|                     X.xmotion.x, X.xmotion.y);
 | |
|             if (raising) {
 | |
|                 raising = 0;
 | |
|                 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
 | |
|                                        priv->mou, motion, block);
 | |
|             }
 | |
|             else {
 | |
|                 if (priv->fine)
 | |
|                     dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
 | |
|                                            priv->mou, motion, block);
 | |
|                 else
 | |
|                     dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
 | |
|                                            priv->mou, motion, block);
 | |
|             }
 | |
|             break;
 | |
|         case KeyPress:
 | |
|         case KeyRelease:
 | |
|             enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
 | |
|             break;
 | |
|         default:
 | |
|             /* Pass the whole event here, because
 | |
|              * this may be an extension event. */
 | |
|             enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxCloseConsole(myPrivate * priv)
 | |
| {
 | |
|     GETDMXINPUTFROMPRIV;
 | |
|     dmxCommonRestoreState(priv);
 | |
|     if (priv->display) {
 | |
|         XFreeGC(priv->display, priv->gc);
 | |
|         XFreeGC(priv->display, priv->gcDet);
 | |
|         XFreeGC(priv->display, priv->gcRev);
 | |
|         XFreeGC(priv->display, priv->gcCur);
 | |
|         if (!dmxInput->console)
 | |
|             XCloseDisplay(priv->display);
 | |
|     }
 | |
|     priv->display = NULL;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| dmxCloseConsoleScreen(int idx, ScreenPtr pScreen)
 | |
| {
 | |
|     myPrivate *priv, *last;
 | |
| 
 | |
|     for (last = priv = (myPrivate *) dixLookupPrivate(&pScreen->devPrivates,
 | |
|                                                       dmxScreenPrivateKey);
 | |
|          priv; priv = priv->next)
 | |
|         dmxCloseConsole(last = priv);
 | |
| 
 | |
|     DMX_UNWRAP(CloseScreen, last, pScreen);
 | |
|     return pScreen->CloseScreen(idx, pScreen);
 | |
| }
 | |
| 
 | |
| static Cursor
 | |
| dmxConsoleCreateEmptyCursor(myPrivate * priv)
 | |
| {
 | |
|     char noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 | |
|     Pixmap pixmap;
 | |
|     Cursor cursor;
 | |
|     XColor color, tmpColor;
 | |
|     Display *dpy = priv->display;
 | |
| 
 | |
|     /* Create empty cursor for window */
 | |
|     pixmap = XCreateBitmapFromData(priv->display, priv->window,
 | |
|                                    noCursorData, 8, 8);
 | |
|     if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
 | |
|                           "black", &color, &tmpColor))
 | |
|         dmxLog(dmxFatal, "Cannot allocate color for cursor\n");
 | |
|     cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0);
 | |
|     XFreePixmap(dpy, pixmap);
 | |
|     return cursor;
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxConsoleComputeWidthHeight(myPrivate * priv,
 | |
|                              int *width, int *height,
 | |
|                              double *xScale, double *yScale,
 | |
|                              int *consWidth, int *consHeight)
 | |
| {
 | |
|     int screen;
 | |
|     Display *dpy = priv->display;
 | |
| 
 | |
|     *width = 0;
 | |
|     *height = 0;
 | |
|     *xScale = 1.0;
 | |
|     *yScale = 1.0;
 | |
| 
 | |
|     screen = DefaultScreen(dpy);
 | |
|     *consWidth = DisplayWidth(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
 | |
|     *consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
 | |
| 
 | |
|     if (*consWidth < 1)
 | |
|         *consWidth = 1;
 | |
|     if (*consHeight < 1)
 | |
|         *consHeight = 1;
 | |
| 
 | |
| #if 1
 | |
|     /* Always keep the console size similar
 | |
|      * to the global bounding box. */
 | |
|     *width = dmxGlobalWidth;
 | |
|     *height = dmxGlobalHeight;
 | |
| #else
 | |
|     /* Make the console window as big as
 | |
|      * possible by computing the visible
 | |
|      * bounding box. */
 | |
|     for (i = 0; i < dmxNumScreens; i++) {
 | |
|         if (screenInfo.screens[i]->x + screenInfo.screens[i]->width > *width)
 | |
|             *width = screenInfo.screens[i]->x + screenInfo.screens[i]->width;
 | |
| 
 | |
|         if (screenInfo.screens[i]->y + screenInfo.screens[i]->height > *height)
 | |
|             *height = screenInfo.screens[i]->y + screenInfo.screens[i]->height;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if ((double) *consWidth / *width < (double) *consHeight / *height)
 | |
|         *xScale = *yScale = (double) *consWidth / *width;
 | |
|     else
 | |
|         *xScale = *yScale = (double) *consHeight / *height;
 | |
| 
 | |
|     *consWidth = scalex(priv, *width);
 | |
|     *consHeight = scaley(priv, *height);
 | |
|     if (*consWidth < 1)
 | |
|         *consWidth = 1;
 | |
|     if (*consHeight < 1)
 | |
|         *consHeight = 1;
 | |
| }
 | |
| 
 | |
| /** Re-initialized the console device described by \a pDev (after a
 | |
|  * reconfig). */
 | |
| void
 | |
| dmxConsoleReInit(DevicePtr pDev)
 | |
| {
 | |
|     GETPRIVFROMPDEV;
 | |
|     Display *dpy;
 | |
| 
 | |
|     if (!priv || !priv->initialized)
 | |
|         return;
 | |
|     dpy = priv->display;
 | |
| 
 | |
|     dmxConsoleComputeWidthHeight(priv,
 | |
|                                  &priv->width, &priv->height,
 | |
|                                  &priv->xScale, &priv->yScale,
 | |
|                                  &priv->consWidth, &priv->consHeight);
 | |
|     XResizeWindow(dpy, priv->window, priv->consWidth, priv->consHeight);
 | |
|     XFreePixmap(dpy, priv->pixmap);
 | |
|     priv->pixmap = XCreatePixmap(dpy,
 | |
|                                  RootWindow(dpy, DefaultScreen(dpy)),
 | |
|                                  priv->consWidth,
 | |
|                                  priv->consHeight,
 | |
|                                  DefaultDepth(dpy, DefaultScreen(dpy)));
 | |
|     dmxConsoleDraw(priv, 1, 1);
 | |
| }
 | |
| 
 | |
| /** Initialized the console device described by \a pDev. */
 | |
| void
 | |
| dmxConsoleInit(DevicePtr pDev)
 | |
| {
 | |
|     GETPRIVFROMPDEV;
 | |
|     DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
 | |
|     int screen;
 | |
|     unsigned long mask;
 | |
|     XSetWindowAttributes attribs;
 | |
|     Display *dpy;
 | |
|     Window win;
 | |
|     XGCValues gcvals;
 | |
|     XColor color;
 | |
|     XClassHint class_hints;
 | |
|     unsigned long tmp;
 | |
| 
 | |
|     if (dmxLocal->type == DMX_LOCAL_MOUSE)
 | |
|         priv->mou = pDev;
 | |
|     if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
 | |
|         priv->kbd = pDev;
 | |
|     if (priv->initialized++)
 | |
|         return;                 /* Only do once for mouse/keyboard pair */
 | |
| 
 | |
|     if (!(dpy = priv->display = XOpenDisplay(dmxInput->name)))
 | |
|         dmxLog(dmxFatal,
 | |
|                "dmxOpenConsole: cannot open console display %s\n",
 | |
|                dmxInput->name);
 | |
| 
 | |
|     /* Set up defaults */
 | |
|     dmxConsoleComputeWidthHeight(priv,
 | |
|                                  &priv->width, &priv->height,
 | |
|                                  &priv->xScale, &priv->yScale,
 | |
|                                  &priv->consWidth, &priv->consHeight);
 | |
| 
 | |
|     /* Private initialization using computed values or constants. */
 | |
|     screen = DefaultScreen(dpy);
 | |
|     priv->initPointerX = scalex(priv, priv->width / 2);
 | |
|     priv->initPointerY = scaley(priv, priv->height / 2);
 | |
|     priv->eventMask = (ButtonPressMask
 | |
|                        | ButtonReleaseMask
 | |
|                        | PointerMotionMask
 | |
|                        | EnterWindowMask
 | |
|                        | LeaveWindowMask
 | |
|                        | KeyPressMask
 | |
|                        | KeyReleaseMask | ExposureMask | ResizeRedirectMask);
 | |
| 
 | |
|     mask = CWBackPixel | CWEventMask | CWColormap | CWOverrideRedirect;
 | |
|     attribs.colormap = DefaultColormap(dpy, screen);
 | |
|     if (XParseColor(dpy, attribs.colormap, CONSOLE_BG_COLOR, &color)
 | |
|         && XAllocColor(dpy, attribs.colormap, &color)) {
 | |
|         attribs.background_pixel = color.pixel;
 | |
|     }
 | |
|     else
 | |
|         attribs.background_pixel = WhitePixel(dpy, screen);
 | |
| 
 | |
|     attribs.event_mask = priv->eventMask;
 | |
|     attribs.override_redirect = False;
 | |
| 
 | |
|     win = priv->window = XCreateWindow(dpy,
 | |
|                                        RootWindow(dpy, screen),
 | |
|                                        0, 0, priv->consWidth, priv->consHeight,
 | |
|                                        0,
 | |
|                                        DefaultDepth(dpy, screen),
 | |
|                                        InputOutput,
 | |
|                                        DefaultVisual(dpy, screen),
 | |
|                                        mask, &attribs);
 | |
|     priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
 | |
|                                  priv->consWidth, priv->consHeight,
 | |
|                                  DefaultDepth(dpy, screen));
 | |
| 
 | |
|     /* Set up properties */
 | |
|     XStoreName(dpy, win, DMX_CONSOLE_NAME);
 | |
|     class_hints.res_name = DMX_RES_NAME;
 | |
|     class_hints.res_class = DMX_RES_CLASS;
 | |
|     XSetClassHint(dpy, win, &class_hints);
 | |
| 
 | |
|     /* Map the window */
 | |
|     XMapWindow(dpy, win);
 | |
| 
 | |
|     /* Create cursors */
 | |
|     priv->cursorNormal = XCreateFontCursor(dpy, XC_circle);
 | |
|     priv->cursorGrabbed = XCreateFontCursor(dpy, XC_spider);
 | |
|     priv->cursorEmpty = dmxConsoleCreateEmptyCursor(priv);
 | |
|     XDefineCursor(dpy, priv->window, priv->cursorNormal);
 | |
| 
 | |
|     /* Create GC */
 | |
|     mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground |
 | |
|             GCBackground | GCLineWidth | GCLineStyle | GCCapStyle |
 | |
|             GCFillStyle | GCGraphicsExposures);
 | |
|     gcvals.function = GXcopy;
 | |
|     gcvals.plane_mask = AllPlanes;
 | |
|     gcvals.clip_mask = None;
 | |
|     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_FG_COLOR, &color)
 | |
|         && XAllocColor(dpy, attribs.colormap, &color)) {
 | |
|         gcvals.foreground = color.pixel;
 | |
|     }
 | |
|     else
 | |
|         gcvals.foreground = BlackPixel(dpy, screen);
 | |
|     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_BG_COLOR, &color)
 | |
|         && XAllocColor(dpy, attribs.colormap, &color)) {
 | |
|         gcvals.background = color.pixel;
 | |
|     }
 | |
|     else
 | |
|         gcvals.background = WhitePixel(dpy, screen);
 | |
|     gcvals.line_width = 0;
 | |
|     gcvals.line_style = LineSolid;
 | |
|     gcvals.cap_style = CapNotLast;
 | |
|     gcvals.fill_style = FillSolid;
 | |
|     gcvals.graphics_exposures = False;
 | |
| 
 | |
|     priv->gc = XCreateGC(dpy, win, mask, &gcvals);
 | |
| 
 | |
|     tmp = gcvals.foreground;
 | |
|     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_DET_COLOR, &color)
 | |
|         && XAllocColor(dpy, attribs.colormap, &color)) {
 | |
|         gcvals.foreground = color.pixel;
 | |
|     }
 | |
|     else
 | |
|         gcvals.foreground = BlackPixel(dpy, screen);
 | |
|     priv->gcDet = XCreateGC(dpy, win, mask, &gcvals);
 | |
|     gcvals.foreground = tmp;
 | |
| 
 | |
|     tmp = gcvals.background;
 | |
|     gcvals.background = gcvals.foreground;
 | |
|     gcvals.foreground = tmp;
 | |
|     priv->gcRev = XCreateGC(dpy, win, mask, &gcvals);
 | |
| 
 | |
|     gcvals.background = gcvals.foreground;
 | |
|     if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_CUR_COLOR, &color)
 | |
|         && XAllocColor(dpy, attribs.colormap, &color)) {
 | |
|         gcvals.foreground = color.pixel;
 | |
|     }
 | |
|     else
 | |
|         gcvals.foreground = BlackPixel(dpy, screen);
 | |
|     priv->gcCur = XCreateGC(dpy, win, mask, &gcvals);
 | |
| 
 | |
|     dmxConsoleDraw(priv, 1, 1);
 | |
| 
 | |
|     if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
 | |
|                          dmxScreenPrivateKey))
 | |
|         priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
 | |
|                                       dmxScreenPrivateKey);
 | |
|     else
 | |
|         DMX_WRAP(CloseScreen, dmxCloseConsoleScreen,
 | |
|                  priv, screenInfo.screens[0]);
 | |
|     dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey,
 | |
|                   priv);
 | |
| }
 | |
| 
 | |
| /** Fill in the \a info structure for the specified \a pDev.  Only used
 | |
|  * for pointers. */
 | |
| void
 | |
| dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
 | |
| {
 | |
|     GETPRIVFROMPDEV;
 | |
| 
 | |
|     info->buttonClass = 1;
 | |
|     dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
 | |
|     info->valuatorClass = 1;
 | |
|     info->numRelAxes = 2;
 | |
|     info->minval[0] = 0;
 | |
|     info->minval[1] = 0;
 | |
|     /* max possible console window size: */
 | |
|     info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display));
 | |
|     info->maxval[1] =
 | |
|         DisplayHeight(priv->display, DefaultScreen(priv->display));
 | |
|     info->res[0] = 1;
 | |
|     info->minres[0] = 0;
 | |
|     info->maxres[0] = 1;
 | |
|     info->ptrFeedbackClass = 1;
 | |
| }
 | |
| 
 | |
| /** Fill in the \a info structure for the specified \a pDev.  Only used
 | |
|  * for keyboard. */
 | |
| void
 | |
| dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
 | |
| {
 | |
|     dmxCommonKbdGetInfo(pDev, info);
 | |
|     info->keyboard = 1;
 | |
|     info->keyClass = 1;
 | |
|     dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
 | |
|     info->freemap = 1;
 | |
|     info->focusClass = 1;
 | |
|     info->kbdFeedbackClass = 1;
 | |
| }
 | |
| 
 | |
| /** Handle special console-only keys. */
 | |
| int
 | |
| dmxConsoleFunctions(pointer private, DMXFunctionType function)
 | |
| {
 | |
|     GETONLYPRIVFROMPRIVATE;
 | |
|     XRectangle rect;
 | |
|     Display *dpy = priv->display;
 | |
| 
 | |
|     switch (function) {
 | |
|     case DMX_FUNCTION_FINE:
 | |
|         if (priv->fine) {
 | |
|             priv->fine = 0;
 | |
|             dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rect);
 | |
|             XSetClipRectangles(dpy, priv->gc, 0, 0, &rect, 1, Unsorted);
 | |
|             XCopyArea(dpy, priv->pixmap, priv->window, priv->gc,
 | |
|                       0, 0, priv->consWidth, priv->consHeight, 0, 0);
 | |
|             XSetClipMask(dpy, priv->gc, None);
 | |
| 
 | |
|             XDefineCursor(dpy, priv->window,
 | |
|                           priv->grabbed
 | |
|                           ? priv->cursorGrabbed : priv->cursorNormal);
 | |
|             XWarpPointer(dpy, priv->window, priv->window,
 | |
|                          0, 0, 0, 0,
 | |
|                          scalex(priv, priv->globalX),
 | |
|                          scaley(priv, priv->globalY));
 | |
|             XSync(dpy, False);  /* Not a backend display */
 | |
|         }
 | |
|         else {
 | |
|             priv->fine = 1;
 | |
|             XRaiseWindow(dpy, priv->window);
 | |
|             XDefineCursor(dpy, priv->window, priv->cursorEmpty);
 | |
|             dmxConsoleUpdateFineCursor(priv);
 | |
|         }
 | |
|         return 1;
 | |
|     case DMX_FUNCTION_GRAB:
 | |
|         if (priv->grabbed) {
 | |
|             XUngrabKeyboard(dpy, CurrentTime);
 | |
|             XUngrabPointer(dpy, CurrentTime);
 | |
|             XDefineCursor(dpy, priv->window,
 | |
|                           priv->fine ? priv->cursorEmpty : priv->cursorNormal);
 | |
|         }
 | |
|         else {
 | |
|             if (XGrabPointer(dpy, priv->window, True,
 | |
|                              0, GrabModeAsync, GrabModeAsync, priv->window,
 | |
|                              None, CurrentTime)) {
 | |
|                 dmxLog(dmxError, "XGrabPointer failed\n");
 | |
|                 return 0;
 | |
|             }
 | |
|             if (XGrabKeyboard(dpy, priv->window, True,
 | |
|                               GrabModeAsync, GrabModeAsync, CurrentTime)) {
 | |
|                 dmxLog(dmxError, "XGrabKeyboard failed\n");
 | |
|                 XUngrabPointer(dpy, CurrentTime);
 | |
|                 return 0;
 | |
|             }
 | |
|             XDefineCursor(dpy, priv->window,
 | |
|                           priv->fine ? priv->cursorEmpty : priv->cursorGrabbed);
 | |
|         }
 | |
|         priv->grabbed = !priv->grabbed;
 | |
|         if (priv->fine)
 | |
|             dmxConsoleUpdateFineCursor(priv);
 | |
|         return 1;
 | |
|     case DMX_FUNCTION_TERMINATE:
 | |
|         return 1;
 | |
|     default:
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| dmxDump(void)
 | |
| {
 | |
|     int i, j;
 | |
|     DMXInputInfo *dmxInput;
 | |
|     XEvent X;
 | |
| 
 | |
|     for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
 | |
|         for (j = 0; j < dmxInput->numDevs; j++) {
 | |
|             DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
 | |
|             myPrivate *priv = dmxLocal->private;
 | |
| 
 | |
|             while (priv
 | |
|                    && priv->display
 | |
|                    && XCheckTypedEvent(priv->display, MotionNotify, &X)) {
 | |
|                 DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
 | |
|                         dmxInput->name, j, X.type, dmxEventName(X.type));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /** This routine is used to warp the pointer into the console window
 | |
|  * from anywhere on the screen.  It is used when backend and console
 | |
|  * input are both being taken from the same X display. */
 | |
| void
 | |
| dmxConsoleCapture(DMXInputInfo * dmxInput)
 | |
| {
 | |
|     int i;
 | |
|     XEvent X;
 | |
| 
 | |
|     DMXDBG0("dmxConsoleCapture\n");
 | |
|     dmxSync(NULL, TRUE);
 | |
|     for (i = 0; i < dmxInput->numDevs; i++) {
 | |
|         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
 | |
|         myPrivate *priv = dmxLocal->private;
 | |
| 
 | |
|         if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
 | |
|             continue;
 | |
|         if (dmxLocal->type != DMX_LOCAL_MOUSE)
 | |
|             continue;
 | |
|         if (priv->captured)
 | |
|             continue;
 | |
|         priv->captured = 2;     /* Ungrab only after proximal events. */
 | |
|         XRaiseWindow(priv->display, priv->window);
 | |
|         XSync(priv->display, False);    /* Not a backend display */
 | |
|         while (XCheckTypedEvent(priv->display, MotionNotify, &X)) {
 | |
|             DMXDBG3("   Ignoring motion to %d %d after capture on %s\n",
 | |
|                     X.xmotion.x, X.xmotion.y, dmxInput->name);
 | |
|         }
 | |
|         XWarpPointer(priv->display, None,
 | |
|                      priv->window, 0, 0, 0, 0, priv->curX, priv->curY);
 | |
|         XSync(priv->display, False);    /* Not a backend display */
 | |
|         dmxDump();
 | |
|         if (priv->fine)
 | |
|             dmxConsoleUpdateFineCursor(priv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /** Undo the capture that was done by #dmxConsoleCapture. */
 | |
| void
 | |
| dmxConsoleUncapture(DMXInputInfo * dmxInput)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     DMXDBG0("dmxConsoleUncapture\n");
 | |
|     dmxSync(NULL, TRUE);
 | |
|     for (i = 0; i < dmxInput->numDevs; i++) {
 | |
|         DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
 | |
|         myPrivate *priv = dmxLocal->private;
 | |
| 
 | |
|         if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
 | |
|             continue;
 | |
|         if (dmxLocal->type != DMX_LOCAL_MOUSE)
 | |
|             continue;
 | |
|         if (!priv->captured)
 | |
|             continue;
 | |
|         priv->captured = 0;
 | |
|         XSync(priv->display, False);    /* Not a backend display */
 | |
|     }
 | |
| }
 |