1706f2543Smrg/*
2706f2543Smrg * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
3706f2543Smrg *
4706f2543Smrg * All Rights Reserved.
5706f2543Smrg *
6706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining
7706f2543Smrg * a copy of this software and associated documentation files (the
8706f2543Smrg * "Software"), to deal in the Software without restriction, including
9706f2543Smrg * without limitation on the rights to use, copy, modify, merge,
10706f2543Smrg * publish, distribute, sublicense, and/or sell copies of the Software,
11706f2543Smrg * and to permit persons to whom the Software is furnished to do so,
12706f2543Smrg * subject to the following conditions:
13706f2543Smrg *
14706f2543Smrg * The above copyright notice and this permission notice (including the
15706f2543Smrg * next paragraph) shall be included in all copies or substantial
16706f2543Smrg * portions of the Software.
17706f2543Smrg *
18706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19706f2543Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20706f2543Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21706f2543Smrg * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22706f2543Smrg * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23706f2543Smrg * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24706f2543Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25706f2543Smrg * SOFTWARE.
26706f2543Smrg */
27706f2543Smrg
28706f2543Smrg/*
29706f2543Smrg * Authors:
30706f2543Smrg *   David H. Dawes <dawes@xfree86.org>
31706f2543Smrg *   Kevin E. Martin <kem@redhat.com>
32706f2543Smrg *   Rickard E. (Rik) Faith <faith@redhat.com>
33706f2543Smrg *
34706f2543Smrg */
35706f2543Smrg
36706f2543Smrg/** \file
37706f2543Smrg *
38706f2543Smrg * This file implements the console input devices.
39706f2543Smrg */
40706f2543Smrg
41706f2543Smrg#ifdef HAVE_DMX_CONFIG_H
42706f2543Smrg#include <dmx-config.h>
43706f2543Smrg#endif
44706f2543Smrg
45706f2543Smrg#define DMX_CONSOLE_DEBUG 0
46706f2543Smrg#define DMX_WINDOW_DEBUG  0
47706f2543Smrg
48706f2543Smrg#include "dmxinputinit.h"
49706f2543Smrg#include "dmxevents.h"
50706f2543Smrg#include "dmxconsole.h"
51706f2543Smrg#include "dmxcommon.h"
52706f2543Smrg#include "dmxscrinit.h"
53706f2543Smrg#include "dmxcb.h"
54706f2543Smrg#include "dmxsync.h"
55706f2543Smrg
56706f2543Smrg#include "inputstr.h"
57706f2543Smrg#include "input.h"
58706f2543Smrg#include "mipointer.h"
59706f2543Smrg#include "windowstr.h"
60706f2543Smrg
61706f2543Smrg#define CONSOLE_NUM 3
62706f2543Smrg#define CONSOLE_DEN 4
63706f2543Smrg#define DMX_CONSOLE_NAME "DMX Console"
64706f2543Smrg#define DMX_RES_NAME     "Xdmx"
65706f2543Smrg#define DMX_RES_CLASS    "XDmx"
66706f2543Smrg#define CONSOLE_BG_COLOR "gray75"
67706f2543Smrg#define CONSOLE_FG_COLOR "black"
68706f2543Smrg#define CONSOLE_SCREEN_BG_COLOR "white"
69706f2543Smrg#define CONSOLE_SCREEN_FG_COLOR "black"
70706f2543Smrg#define CONSOLE_SCREEN_DET_COLOR "gray75"
71706f2543Smrg#define CONSOLE_SCREEN_CUR_COLOR "red"
72706f2543Smrg
73706f2543Smrg#if DMX_CONSOLE_DEBUG
74706f2543Smrg#define DMXDBG0(f)               dmxLog(dmxDebug,f)
75706f2543Smrg#define DMXDBG1(f,a)             dmxLog(dmxDebug,f,a)
76706f2543Smrg#define DMXDBG2(f,a,b)           dmxLog(dmxDebug,f,a,b)
77706f2543Smrg#define DMXDBG3(f,a,b,c)         dmxLog(dmxDebug,f,a,b,c)
78706f2543Smrg#define DMXDBG4(f,a,b,c,d)       dmxLog(dmxDebug,f,a,b,c,d)
79706f2543Smrg#define DMXDBG5(f,a,b,c,d,e)     dmxLog(dmxDebug,f,a,b,c,d,e)
80706f2543Smrg#define DMXDBG6(f,a,b,c,d,e,g)   dmxLog(dmxDebug,f,a,b,c,d,e,g)
81706f2543Smrg#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
82706f2543Smrg#else
83706f2543Smrg#define DMXDBG0(f)
84706f2543Smrg#define DMXDBG1(f,a)
85706f2543Smrg#define DMXDBG2(f,a,b)
86706f2543Smrg#define DMXDBG3(f,a,b,c)
87706f2543Smrg#define DMXDBG4(f,a,b,c,d)
88706f2543Smrg#define DMXDBG5(f,a,b,c,d,e)
89706f2543Smrg#define DMXDBG6(f,a,b,c,d,e,g)
90706f2543Smrg#define DMXDBG7(f,a,b,c,d,e,g,h)
91706f2543Smrg#endif
92706f2543Smrg
93706f2543Smrg/* Private area for consoles. */
94706f2543Smrgtypedef struct _myPrivate {
95706f2543Smrg    DMX_COMMON_PRIVATE;
96706f2543Smrg    int                     lastX;
97706f2543Smrg    int                     lastY;
98706f2543Smrg    int                     globalX;
99706f2543Smrg    int                     globalY;
100706f2543Smrg    int                     curX;
101706f2543Smrg    int                     curY;
102706f2543Smrg    int                     width;
103706f2543Smrg    int                     height;
104706f2543Smrg    int                     consWidth;
105706f2543Smrg    int                     consHeight;
106706f2543Smrg    double                  xScale;
107706f2543Smrg    double                  yScale;
108706f2543Smrg    XlibGC                  gc, gcDet, gcRev, gcCur;
109706f2543Smrg    int                     grabbed, fine, captured;
110706f2543Smrg    Cursor                  cursorNormal, cursorGrabbed, cursorEmpty;
111706f2543Smrg    Pixmap                  pixmap;
112706f2543Smrg
113706f2543Smrg    CloseScreenProcPtr      CloseScreen;
114706f2543Smrg    struct _myPrivate       *next; /* for closing multiple consoles */
115706f2543Smrg    int                     initialized;
116706f2543Smrg    DevicePtr               mou, kbd;
117706f2543Smrg} myPrivate;
118706f2543Smrg
119706f2543Smrgstatic int scalex(myPrivate *priv, int x)
120706f2543Smrg{
121706f2543Smrg    return (int)((x * priv->xScale) + .5);
122706f2543Smrg}
123706f2543Smrg
124706f2543Smrgstatic int scaley(myPrivate *priv, int y)
125706f2543Smrg{
126706f2543Smrg    return (int)((y * priv->yScale) + .5);
127706f2543Smrg}
128706f2543Smrg
129706f2543Smrgstatic int unscalex(myPrivate *priv, int x)
130706f2543Smrg{
131706f2543Smrg    return (int)((x / priv->xScale) + .5);
132706f2543Smrg}
133706f2543Smrg
134706f2543Smrgstatic int unscaley(myPrivate *priv, int y)
135706f2543Smrg{
136706f2543Smrg    return (int)((y / priv->yScale) + .5);
137706f2543Smrg}
138706f2543Smrg
139706f2543Smrg/** Create the private area for \a pDevice. */
140706f2543Smrgpointer dmxConsoleCreatePrivate(DeviceIntPtr pDevice)
141706f2543Smrg{
142706f2543Smrg    GETDMXLOCALFROMPDEVICE;
143706f2543Smrg    myPrivate *priv = calloc(1, sizeof(*priv));
144706f2543Smrg    priv->dmxLocal  = dmxLocal;
145706f2543Smrg    return priv;
146706f2543Smrg}
147706f2543Smrg
148706f2543Smrg/** If \a private is non-NULL, free its associated memory. */
149706f2543Smrgvoid dmxConsoleDestroyPrivate(pointer private)
150706f2543Smrg{
151706f2543Smrg    free(private);
152706f2543Smrg}
153706f2543Smrg
154706f2543Smrgstatic void dmxConsoleDrawFineCursor(myPrivate *priv, XRectangle *rect)
155706f2543Smrg{
156706f2543Smrg    int size  = 6;
157706f2543Smrg    int x, y;
158706f2543Smrg
159706f2543Smrg    XDrawLine(priv->display, priv->pixmap, priv->gcCur,
160706f2543Smrg              x = scalex(priv, priv->globalX) - size,
161706f2543Smrg              scaley(priv, priv->globalY),
162706f2543Smrg              scalex(priv, priv->globalX) + size,
163706f2543Smrg              scaley(priv, priv->globalY));
164706f2543Smrg    XDrawLine(priv->display, priv->pixmap, priv->gcCur,
165706f2543Smrg              scalex(priv, priv->globalX),
166706f2543Smrg              y = scaley(priv, priv->globalY) - size,
167706f2543Smrg              scalex(priv, priv->globalX),
168706f2543Smrg              scaley(priv, priv->globalY) + size);
169706f2543Smrg    if (priv->grabbed) {
170706f2543Smrg        XDrawLine(priv->display, priv->pixmap, priv->gcCur,
171706f2543Smrg                  scalex(priv, priv->globalX) - (int)(size / 1.4),
172706f2543Smrg                  scaley(priv, priv->globalY) - (int)(size / 1.4),
173706f2543Smrg                  scalex(priv, priv->globalX) + (int)(size / 1.4),
174706f2543Smrg                  scaley(priv, priv->globalY) + (int)(size / 1.4));
175706f2543Smrg        XDrawLine(priv->display, priv->pixmap, priv->gcCur,
176706f2543Smrg                  scalex(priv, priv->globalX) - (int)(size / 1.4),
177706f2543Smrg                  scaley(priv, priv->globalY) + (int)(size / 1.4),
178706f2543Smrg                  scalex(priv, priv->globalX) + (int)(size / 1.4),
179706f2543Smrg                  scaley(priv, priv->globalY) - (int)(size / 1.4));
180706f2543Smrg    }
181706f2543Smrg    if (rect) {
182706f2543Smrg        rect->x      = x;
183706f2543Smrg        rect->y      = y;
184706f2543Smrg        rect->width  = 2 * size;
185706f2543Smrg        rect->height = 2 * size;
186706f2543Smrg    }
187706f2543Smrg}
188706f2543Smrg
189706f2543Smrgstatic void dmxConsoleDrawWindows(pointer private)
190706f2543Smrg{
191706f2543Smrg    GETONLYPRIVFROMPRIVATE;
192706f2543Smrg    Display    *dpy   = priv->display;
193706f2543Smrg    int        i;
194706f2543Smrg    Region     whole, used, avail;
195706f2543Smrg    XRectangle rect;
196706f2543Smrg
197706f2543Smrg    whole       = XCreateRegion();
198706f2543Smrg    used        = XCreateRegion();
199706f2543Smrg    avail       = XCreateRegion();
200706f2543Smrg    rect.x      = 0;
201706f2543Smrg    rect.y      = 0;
202706f2543Smrg    rect.width  = priv->consWidth;
203706f2543Smrg    rect.height = priv->consHeight;
204706f2543Smrg    XUnionRectWithRegion(&rect, whole, whole);
205706f2543Smrg
206706f2543Smrg    for (i = 0; i < dmxNumScreens; i++) {
207706f2543Smrg        ScreenPtr     pScreen     = screenInfo.screens[i];
208706f2543Smrg        WindowPtr     pRoot       = pScreen->root;
209706f2543Smrg        WindowPtr     pChild;
210706f2543Smrg
211706f2543Smrg#if DMX_WINDOW_DEBUG
212706f2543Smrg        dmxLog(dmxDebug, "%lu %p %p %p 2\n",
213706f2543Smrg               pRoot->drawable.id,
214706f2543Smrg               pRoot->parent, pRoot->firstChild, pRoot->lastChild);
215706f2543Smrg#endif
216706f2543Smrg
217706f2543Smrg        for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
218706f2543Smrg            if (pChild->mapped
219706f2543Smrg                && pChild->realized) {
220706f2543Smrg#if DMX_WINDOW_DEBUG
221706f2543Smrg                dmxLog(dmxDebug, "  %p %d,%d %dx%d %d %d  %d RECTS\n",
222706f2543Smrg                       pChild,
223706f2543Smrg                       pChild->drawable.x,
224706f2543Smrg                       pChild->drawable.y,
225706f2543Smrg                       pChild->drawable.width,
226706f2543Smrg                       pChild->drawable.height,
227706f2543Smrg                       pChild->visibility,
228706f2543Smrg                       pChild->overrideRedirect,
229706f2543Smrg                       RegionNumRects(&pChild->clipList));
230706f2543Smrg#endif
231706f2543Smrg                rect.x      = scalex(priv, pChild->drawable.x + pScreen->x);
232706f2543Smrg                rect.y      = scaley(priv, pChild->drawable.y + pScreen->y);
233706f2543Smrg                rect.width  = scalex(priv, pChild->drawable.width);
234706f2543Smrg                rect.height = scaley(priv, pChild->drawable.height);
235706f2543Smrg                XDrawRectangle(dpy, priv->pixmap, priv->gc,
236706f2543Smrg                               rect.x, rect.y, rect.width, rect.height);
237706f2543Smrg                XUnionRectWithRegion(&rect, used, used);
238706f2543Smrg                XSubtractRegion(whole, used, avail);
239706f2543Smrg                XSetRegion(dpy, priv->gc, avail);
240706f2543Smrg            }
241706f2543Smrg        }
242706f2543Smrg#ifdef PANORAMIX
243706f2543Smrg        if (!noPanoramiXExtension) break; /* Screen 0 valid with Xinerama */
244706f2543Smrg#endif
245706f2543Smrg    }
246706f2543Smrg    XDestroyRegion(avail);
247706f2543Smrg    XDestroyRegion(used);
248706f2543Smrg    XDestroyRegion(whole);
249706f2543Smrg    XSetClipMask(dpy, priv->gc, None);
250706f2543Smrg}
251706f2543Smrg
252706f2543Smrgstatic void dmxConsoleDraw(myPrivate *priv, int updateCursor, int update)
253706f2543Smrg{
254706f2543Smrg    GETDMXINPUTFROMPRIV;
255706f2543Smrg    Display       *dpy     = priv->display;
256706f2543Smrg    int           i;
257706f2543Smrg
258706f2543Smrg    XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0,
259706f2543Smrg                   priv->consWidth, priv->consHeight);
260706f2543Smrg
261706f2543Smrg    for (i = 0; i < dmxNumScreens; i++) {
262706f2543Smrg        DMXScreenInfo *dmxScreen = &dmxScreens[i];
263706f2543Smrg	XFillRectangle(dpy, priv->pixmap,
264706f2543Smrg                       dmxScreen->beDisplay ? priv->gcRev : priv->gcDet,
265706f2543Smrg                       scalex(priv, screenInfo.screens[i]->x),
266706f2543Smrg                       scaley(priv, screenInfo.screens[i]->y),
267706f2543Smrg                       scalex(priv, screenInfo.screens[i]->width),
268706f2543Smrg                       scaley(priv, screenInfo.screens[i]->height));
269706f2543Smrg    }
270706f2543Smrg    for (i = 0; i < dmxNumScreens; i++) {
271706f2543Smrg        XDrawRectangle(dpy, priv->pixmap, priv->gc,
272706f2543Smrg                       scalex(priv, screenInfo.screens[i]->x),
273706f2543Smrg                       scaley(priv, screenInfo.screens[i]->y),
274706f2543Smrg                       scalex(priv, screenInfo.screens[i]->width),
275706f2543Smrg                       scaley(priv, screenInfo.screens[i]->height));
276706f2543Smrg    }
277706f2543Smrg    if (dmxInput->windows)          dmxConsoleDrawWindows(priv);
278706f2543Smrg    if (priv->fine && updateCursor) dmxConsoleDrawFineCursor(priv, 0);
279706f2543Smrg    if (update) {
280706f2543Smrg        XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
281706f2543Smrg                  0, 0, priv->consWidth, priv->consHeight, 0, 0);
282706f2543Smrg        XSync(priv->display, False);          /* Not a backend display */
283706f2543Smrg    }
284706f2543Smrg}
285706f2543Smrg
286706f2543Smrgstatic void dmxConsoleClearCursor(myPrivate *priv, int x, int y,
287706f2543Smrg                                  XRectangle *rect)
288706f2543Smrg{
289706f2543Smrg    int        cw = 14, ch = 14;    /* Clear width and height */
290706f2543Smrg
291706f2543Smrg    rect->x      = scalex(priv, x) - cw/2;
292706f2543Smrg    rect->y      = scaley(priv, y) - ch/2;
293706f2543Smrg    rect->width  = cw;
294706f2543Smrg    rect->height = ch;
295706f2543Smrg    XSetClipRectangles(priv->display, priv->gc, 0, 0, rect, 1, Unsorted);
296706f2543Smrg    XSetClipRectangles(priv->display, priv->gcDet, 0, 0, rect, 1, Unsorted);
297706f2543Smrg    XSetClipRectangles(priv->display, priv->gcRev, 0, 0, rect, 1, Unsorted);
298706f2543Smrg    dmxConsoleDraw(priv, 0, 0);
299706f2543Smrg    XSetClipMask(priv->display, priv->gc, None);
300706f2543Smrg    XSetClipMask(priv->display, priv->gcDet, None);
301706f2543Smrg    XSetClipMask(priv->display, priv->gcRev, None);
302706f2543Smrg}
303706f2543Smrg
304706f2543Smrg
305706f2543Smrgstatic void dmxConsoleUpdateFineCursor(myPrivate *priv)
306706f2543Smrg{
307706f2543Smrg    int        leave = 0;
308706f2543Smrg    XRectangle rects[2];
309706f2543Smrg
310706f2543Smrg    dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
311706f2543Smrg    if (priv->dmxLocal->sendsCore) {
312706f2543Smrg        dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
313706f2543Smrg    } else {
314706f2543Smrg        priv->globalX = priv->dmxLocal->lastX;
315706f2543Smrg        priv->globalY = priv->dmxLocal->lastY;
316706f2543Smrg    }
317706f2543Smrg
318706f2543Smrg    priv->lastX   = scalex(priv, priv->width / 2);
319706f2543Smrg    priv->lastY   = scaley(priv, priv->height / 2);
320706f2543Smrg
321706f2543Smrg                                /* Compute new warp position, which may be
322706f2543Smrg                                   outside the window */
323706f2543Smrg    if (priv->globalX < 1 || priv->globalX >= priv->width) {
324706f2543Smrg        if (priv->globalX < 1) priv->lastX = 0;
325706f2543Smrg        else                   priv->lastX = scalex(priv, priv->width);
326706f2543Smrg        priv->lastY = scaley(priv, priv->globalY);
327706f2543Smrg        ++leave;
328706f2543Smrg    }
329706f2543Smrg    if (priv->globalY < 1 || priv->globalY >= priv->height) {
330706f2543Smrg        if (priv->globalY < 1) priv->lastY = 0;
331706f2543Smrg        else                   priv->lastY = scaley(priv, priv->height);
332706f2543Smrg        priv->lastX = scalex(priv, priv->globalX);
333706f2543Smrg        ++leave;
334706f2543Smrg    }
335706f2543Smrg
336706f2543Smrg                                /* Draw pseudo cursor in window */
337706f2543Smrg    dmxConsoleDrawFineCursor(priv, &rects[1]);
338706f2543Smrg
339706f2543Smrg    XSetClipRectangles(priv->display, priv->gc, 0, 0, rects, 2, Unsorted);
340706f2543Smrg    XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
341706f2543Smrg              0, 0, priv->consWidth, priv->consHeight, 0, 0);
342706f2543Smrg    XSetClipMask(priv->display, priv->gc, None);
343706f2543Smrg
344706f2543Smrg    DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
345706f2543Smrg            priv->lastX, priv->lastY);
346706f2543Smrg    XWarpPointer(priv->display, priv->window, priv->window,
347706f2543Smrg                 0, 0, 0, 0, priv->lastX, priv->lastY);
348706f2543Smrg    XSync(priv->display, False); /* Not a backend display */
349706f2543Smrg
350706f2543Smrg    if (leave) {
351706f2543Smrg        XEvent X;
352706f2543Smrg        while (XCheckMaskEvent(priv->display, PointerMotionMask, &X)) {
353706f2543Smrg            if (X.type == MotionNotify) {
354706f2543Smrg                if (X.xmotion.x != priv->lastX || X.xmotion.y != priv->lastY) {
355706f2543Smrg                    DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
356706f2543Smrg                            X.xmotion.x, X.xmotion.y,
357706f2543Smrg                            priv->lastX, priv->lastY);
358706f2543Smrg                }
359706f2543Smrg            } else {
360706f2543Smrg                dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n",
361706f2543Smrg                       X.type, dmxEventName(X.type));
362706f2543Smrg            }
363706f2543Smrg        }
364706f2543Smrg    }
365706f2543Smrg    DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
366706f2543Smrg            priv->lastX, priv->lastY,
367706f2543Smrg            scalex(priv, priv->width),
368706f2543Smrg            scaley(priv, priv->height),
369706f2543Smrg            priv->globalX, priv->globalY);
370706f2543Smrg}
371706f2543Smrg
372706f2543Smrg/** Whenever the window layout (size, position, stacking order) might be
373706f2543Smrg * changed, this routine is called with the \a pWindow that changed and
374706f2543Smrg * the \a type of change.  This routine is called in a conservative
375706f2543Smrg * fashion: the actual layout of the windows of the screen might not
376706f2543Smrg * have had any human-visible changes. */
377706f2543Smrgvoid dmxConsoleUpdateInfo(pointer private, DMXUpdateType type,
378706f2543Smrg                          WindowPtr pWindow)
379706f2543Smrg{
380706f2543Smrg    GETONLYPRIVFROMPRIVATE;
381706f2543Smrg    dmxConsoleDraw(priv, 1, 1);
382706f2543Smrg}
383706f2543Smrg
384706f2543Smrgstatic void dmxConsoleMoveAbsolute(myPrivate *priv, int x, int y,
385706f2543Smrg                                   DevicePtr pDev, dmxMotionProcPtr motion,
386706f2543Smrg                                   DMXBlockType block)
387706f2543Smrg{
388706f2543Smrg    int tmpX, tmpY, v[2];
389706f2543Smrg
390706f2543Smrg    tmpX = unscalex(priv, x);
391706f2543Smrg    tmpY = unscalex(priv, y);
392706f2543Smrg    DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
393706f2543Smrg            x, y, tmpX, tmpY, priv->curX, priv->curY);
394706f2543Smrg    if (tmpX == priv->curX && tmpY == priv->curY) return;
395706f2543Smrg    v[0] = unscalex(priv, x);
396706f2543Smrg    v[1] = unscaley(priv, y);
397706f2543Smrg    motion(pDev, v, 0, 2, DMX_ABSOLUTE_CONFINED, block);
398706f2543Smrg    /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
399706f2543Smrg}
400706f2543Smrg
401706f2543Smrgstatic void dmxConsoleMoveRelative(myPrivate *priv, int x, int y,
402706f2543Smrg                                   DevicePtr pDev, dmxMotionProcPtr motion,
403706f2543Smrg                                   DMXBlockType block)
404706f2543Smrg{
405706f2543Smrg    int v[2];
406706f2543Smrg    /* Ignore the event generated from * warping back to middle */
407706f2543Smrg    if (x == priv->lastX && y == priv->lastY) return;
408706f2543Smrg    v[0] = priv->lastX - x;
409706f2543Smrg    v[1] = priv->lastY - y;
410706f2543Smrg    motion(pDev, v, 0, 2, DMX_RELATIVE, block);
411706f2543Smrg    /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
412706f2543Smrg}
413706f2543Smrg
414706f2543Smrg/** This routine gets called from #dmxCoreMotion for each motion.  This
415706f2543Smrg * allows the console's notion of the cursor postion to change when
416706f2543Smrg * another input device actually caused the change. */
417706f2543Smrgvoid dmxConsoleUpdatePosition(pointer private, int x, int y)
418706f2543Smrg{
419706f2543Smrg    GETONLYPRIVFROMPRIVATE;
420706f2543Smrg    int                  tmpX, tmpY;
421706f2543Smrg    Display              *dpy          = priv->display;
422706f2543Smrg    static unsigned long dmxGeneration = 0;
423706f2543Smrg
424706f2543Smrg
425706f2543Smrg    tmpX = scalex(priv, x);
426706f2543Smrg    tmpY = scaley(priv, y);
427706f2543Smrg    DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
428706f2543Smrg            x, y, tmpX, tmpY, priv->consWidth, priv->consHeight);
429706f2543Smrg
430706f2543Smrg    if (priv->fine) dmxConsoleUpdateFineCursor(priv);
431706f2543Smrg    if (tmpX != priv->curX || tmpY != priv->curY) {
432706f2543Smrg        if (tmpX < 0)                 tmpX = 0;
433706f2543Smrg        if (tmpY < 0)                 tmpY = 0;
434706f2543Smrg        if (tmpX >= priv->consWidth)  tmpX = priv->consWidth  - 1;
435706f2543Smrg        if (tmpY >= priv->consHeight) tmpY = priv->consHeight - 1;
436706f2543Smrg        priv->curX = tmpX;
437706f2543Smrg        priv->curY = tmpY;
438706f2543Smrg        if (!priv->fine) {
439706f2543Smrg            DMXDBG2("   WARP B %d %d\n", priv->curX, priv->curY);
440706f2543Smrg            XWarpPointer(dpy, priv->window,
441706f2543Smrg                         priv->window, 0, 0, 0, 0, tmpX, tmpY);
442706f2543Smrg            XSync(dpy, False); /* Not a backend display */
443706f2543Smrg        }
444706f2543Smrg    }
445706f2543Smrg
446706f2543Smrg    if (dmxGeneration != serverGeneration) {
447706f2543Smrg        dmxGeneration = serverGeneration;
448706f2543Smrg        dmxConsoleDraw(priv, 1, 1);
449706f2543Smrg    }
450706f2543Smrg}
451706f2543Smrg
452706f2543Smrg/** Collect all pending events from the console's display.  Plase these
453706f2543Smrg * events on the server event queue using the \a motion and \a enqueue
454706f2543Smrg * routines.  The \a checkspecial routine is used to check for special
455706f2543Smrg * keys that need handling.  \a block tells if signals should be blocked
456706f2543Smrg * when updating the event queue. */
457706f2543Smrgvoid dmxConsoleCollectEvents(DevicePtr pDev,
458706f2543Smrg                             dmxMotionProcPtr motion,
459706f2543Smrg                             dmxEnqueueProcPtr enqueue,
460706f2543Smrg                             dmxCheckSpecialProcPtr checkspecial,
461706f2543Smrg                             DMXBlockType block)
462706f2543Smrg{
463706f2543Smrg    GETPRIVFROMPDEV;
464706f2543Smrg    GETDMXINPUTFROMPRIV;
465706f2543Smrg    Display              *dpy        = priv->display;
466706f2543Smrg    Window               win         = priv->window;
467706f2543Smrg    int                  width       = priv->width;
468706f2543Smrg    int                  height      = priv->height;
469706f2543Smrg    XEvent               X, N;
470706f2543Smrg    XSetWindowAttributes attribs;
471706f2543Smrg    static int           rInitialized = 0;
472706f2543Smrg    static Region        r;
473706f2543Smrg    XRectangle           rect;
474706f2543Smrg    static int           raising = 0, raiseX, raiseY; /* FIXME */
475706f2543Smrg
476706f2543Smrg    while (XPending(dpy)) {
477706f2543Smrg        XNextEvent(dpy, &X);
478706f2543Smrg	switch(X.type) {
479706f2543Smrg        case VisibilityNotify:
480706f2543Smrg            break;
481706f2543Smrg	case Expose:
482706f2543Smrg            DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
483706f2543Smrg                    X.xexpose.count,
484706f2543Smrg                    X.xexpose.x, X.xexpose.y,
485706f2543Smrg                    X.xexpose.width, X.xexpose.height);
486706f2543Smrg            if (!rInitialized++) r = XCreateRegion();
487706f2543Smrg            rect.x      = X.xexpose.x;
488706f2543Smrg            rect.y      = X.xexpose.y;
489706f2543Smrg            rect.width  = X.xexpose.width;
490706f2543Smrg            rect.height = X.xexpose.height;
491706f2543Smrg            XUnionRectWithRegion(&rect, r, r);
492706f2543Smrg	    if (X.xexpose.count == 0) {
493706f2543Smrg                XSetRegion(dpy, priv->gc, r);
494706f2543Smrg                XSetRegion(dpy, priv->gcDet, r);
495706f2543Smrg                XSetRegion(dpy, priv->gcRev, r);
496706f2543Smrg                dmxConsoleDraw(priv, 1, 1);
497706f2543Smrg                XSetClipMask(dpy, priv->gc, None);
498706f2543Smrg                XSetClipMask(dpy, priv->gcDet, None);
499706f2543Smrg                XSetClipMask(dpy, priv->gcRev, None);
500706f2543Smrg                XDestroyRegion(r);
501706f2543Smrg                rInitialized = 0;
502706f2543Smrg            }
503706f2543Smrg	    break;
504706f2543Smrg	case ResizeRequest:
505706f2543Smrg            DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
506706f2543Smrg                    X.xresizerequest.width, X.xresizerequest.height);
507706f2543Smrg            priv->consWidth           = X.xresizerequest.width;
508706f2543Smrg            priv->consHeight          = X.xresizerequest.height;
509706f2543Smrg	    priv->xScale              = (double)priv->consWidth  / width;
510706f2543Smrg	    priv->yScale              = (double)priv->consHeight / height;
511706f2543Smrg	    attribs.override_redirect = True;
512706f2543Smrg	    XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
513706f2543Smrg	    XResizeWindow(dpy, win, priv->consWidth, priv->consHeight);
514706f2543Smrg            XFreePixmap(dpy, priv->pixmap);
515706f2543Smrg            priv->pixmap = XCreatePixmap(dpy,
516706f2543Smrg                                         RootWindow(dpy, DefaultScreen(dpy)),
517706f2543Smrg                                         priv->consWidth,
518706f2543Smrg                                         priv->consHeight,
519706f2543Smrg                                         DefaultDepth(dpy,DefaultScreen(dpy)));
520706f2543Smrg	    dmxConsoleDraw(priv, 1, 1);
521706f2543Smrg	    attribs.override_redirect = False;
522706f2543Smrg	    XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
523706f2543Smrg	    break;
524706f2543Smrg        case LeaveNotify:
525706f2543Smrg            DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
526706f2543Smrg                    X.xcrossing.x, X.xcrossing.y, raising, priv->fine);
527706f2543Smrg            if (!priv->captured) dmxCommonRestoreState(priv);
528706f2543Smrg            else {
529706f2543Smrg                dmxConsoleUncapture(dmxInput);
530706f2543Smrg                dmxCommonRestoreState(priv);
531706f2543Smrg            }
532706f2543Smrg            break;
533706f2543Smrg        case EnterNotify:
534706f2543Smrg            DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
535706f2543Smrg                    X.xcrossing.x, X.xcrossing.y, raising, priv->fine,
536706f2543Smrg                    priv->curX, priv->curY);
537706f2543Smrg            dmxCommonSaveState(priv);
538706f2543Smrg            if (raising) {
539706f2543Smrg                raising = 0;
540706f2543Smrg                dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
541706f2543Smrg                                       priv->mou, motion, block);
542706f2543Smrg            } else {
543706f2543Smrg                if (priv->fine) {
544706f2543Smrg                    /* The raise will generate an event near the center,
545706f2543Smrg                     * which is not where the cursor should be.  So we
546706f2543Smrg                     * save the real position, do the raise, and move
547706f2543Smrg                     * the cursor here again after the raise generates
548706f2543Smrg                     * the event. */
549706f2543Smrg                    raising = 1;
550706f2543Smrg                    raiseX = X.xcrossing.x;
551706f2543Smrg                    raiseY = X.xcrossing.y;
552706f2543Smrg                    XRaiseWindow(dpy, priv->window);
553706f2543Smrg                }
554706f2543Smrg                XSync(dpy, False); /* Not a backend display */
555706f2543Smrg                if (!X.xcrossing.x && !X.xcrossing.y)
556706f2543Smrg                    dmxConsoleMoveAbsolute(priv, priv->curX, priv->curY,
557706f2543Smrg                                           priv->mou, motion, block);
558706f2543Smrg            }
559706f2543Smrg            break;
560706f2543Smrg	case MotionNotify:
561706f2543Smrg            if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
562706f2543Smrg                continue;
563706f2543Smrg            if (XPending(dpy)) { /* do motion compression */
564706f2543Smrg                XPeekEvent(dpy, &N);
565706f2543Smrg                if (N.type == MotionNotify) continue;
566706f2543Smrg            }
567706f2543Smrg            DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
568706f2543Smrg                    X.xmotion.x, X.xmotion.y);
569706f2543Smrg            if (raising) {
570706f2543Smrg                raising = 0;
571706f2543Smrg                dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
572706f2543Smrg                                       priv->mou, motion, block);
573706f2543Smrg            } else {
574706f2543Smrg                if (priv->fine)
575706f2543Smrg                    dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
576706f2543Smrg                                           priv->mou, motion, block);
577706f2543Smrg                else
578706f2543Smrg                    dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
579706f2543Smrg                                           priv->mou, motion, block);
580706f2543Smrg            }
581706f2543Smrg	    break;
582706f2543Smrg        case KeyPress:
583706f2543Smrg        case KeyRelease:
584706f2543Smrg            enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
585706f2543Smrg            break;
586706f2543Smrg        default:
587706f2543Smrg                                /* Pass the whole event here, because
588706f2543Smrg                                 * this may be an extension event. */
589706f2543Smrg            enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block);
590706f2543Smrg	    break;
591706f2543Smrg	}
592706f2543Smrg    }
593706f2543Smrg}
594706f2543Smrg
595706f2543Smrgstatic void dmxCloseConsole(myPrivate *priv)
596706f2543Smrg{
597706f2543Smrg    GETDMXINPUTFROMPRIV;
598706f2543Smrg    dmxCommonRestoreState(priv);
599706f2543Smrg    if (priv->display) {
600706f2543Smrg        XFreeGC(priv->display, priv->gc);
601706f2543Smrg        XFreeGC(priv->display, priv->gcDet);
602706f2543Smrg        XFreeGC(priv->display, priv->gcRev);
603706f2543Smrg        XFreeGC(priv->display, priv->gcCur);
604706f2543Smrg        if (!dmxInput->console) XCloseDisplay(priv->display);
605706f2543Smrg    }
606706f2543Smrg    priv->display = NULL;
607706f2543Smrg}
608706f2543Smrg
609706f2543Smrgstatic Bool dmxCloseConsoleScreen(int idx, ScreenPtr pScreen)
610706f2543Smrg{
611706f2543Smrg    myPrivate *priv, *last;
612706f2543Smrg
613706f2543Smrg    for (last = priv = (myPrivate *)dixLookupPrivate(&pScreen->devPrivates,
614706f2543Smrg						     dmxScreenPrivateKey);
615706f2543Smrg         priv;
616706f2543Smrg         priv = priv->next) dmxCloseConsole(last = priv);
617706f2543Smrg
618706f2543Smrg    DMX_UNWRAP(CloseScreen, last, pScreen);
619706f2543Smrg    return pScreen->CloseScreen(idx, pScreen);
620706f2543Smrg}
621706f2543Smrg
622706f2543Smrgstatic Cursor dmxConsoleCreateEmptyCursor(myPrivate *priv)
623706f2543Smrg{
624706f2543Smrg    char    noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
625706f2543Smrg    Pixmap  pixmap;
626706f2543Smrg    Cursor  cursor;
627706f2543Smrg    XColor  color, tmpColor;
628706f2543Smrg    Display *dpy = priv->display;
629706f2543Smrg
630706f2543Smrg                                 /* Create empty cursor for window */
631706f2543Smrg    pixmap = XCreateBitmapFromData(priv->display, priv->window,
632706f2543Smrg                                   noCursorData, 8, 8);
633706f2543Smrg    if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
634706f2543Smrg                          "black",
635706f2543Smrg                          &color,
636706f2543Smrg                          &tmpColor))
637706f2543Smrg        dmxLog(dmxFatal, "Cannot allocate color for cursor\n");
638706f2543Smrg    cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0);
639706f2543Smrg    XFreePixmap(dpy, pixmap);
640706f2543Smrg    return cursor;
641706f2543Smrg}
642706f2543Smrg
643706f2543Smrgstatic void dmxConsoleComputeWidthHeight(myPrivate *priv,
644706f2543Smrg                                         int *width, int *height,
645706f2543Smrg                                         double *xScale, double *yScale,
646706f2543Smrg                                         int *consWidth, int *consHeight)
647706f2543Smrg{
648706f2543Smrg    int     screen;
649706f2543Smrg    Display *dpy = priv->display;
650706f2543Smrg
651706f2543Smrg    *width      = 0;
652706f2543Smrg    *height     = 0;
653706f2543Smrg    *xScale     = 1.0;
654706f2543Smrg    *yScale     = 1.0;
655706f2543Smrg
656706f2543Smrg    screen      = DefaultScreen(dpy);
657706f2543Smrg    *consWidth  = DisplayWidth(dpy, screen)  * CONSOLE_NUM / CONSOLE_DEN;
658706f2543Smrg    *consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
659706f2543Smrg
660706f2543Smrg    if (*consWidth  < 1) *consWidth  = 1;
661706f2543Smrg    if (*consHeight < 1) *consHeight = 1;
662706f2543Smrg
663706f2543Smrg#if 1
664706f2543Smrg                                /* Always keep the console size similar
665706f2543Smrg                                 * to the global bounding box. */
666706f2543Smrg    *width  = dmxGlobalWidth;
667706f2543Smrg    *height = dmxGlobalHeight;
668706f2543Smrg#else
669706f2543Smrg                                /* Make the console window as big as
670706f2543Smrg                                 * possible by computing the visible
671706f2543Smrg                                 * bounding box. */
672706f2543Smrg    for (i = 0; i < dmxNumScreens; i++) {
673706f2543Smrg	if (screenInfo.screens[i]->x+screenInfo.screens[i]->width > *width)
674706f2543Smrg	    *width = screenInfo.screens[i]->x+screenInfo.screens[i]->width;
675706f2543Smrg
676706f2543Smrg	if (screenInfo.screens[i]->y+screenInfo.screens[i]->height > *height)
677706f2543Smrg	    *height = screenInfo.screens[i]->y+screenInfo.screens[i]->height;
678706f2543Smrg    }
679706f2543Smrg#endif
680706f2543Smrg
681706f2543Smrg    if ((double)*consWidth / *width < (double)*consHeight / *height)
682706f2543Smrg	*xScale = *yScale = (double)*consWidth / *width;
683706f2543Smrg    else
684706f2543Smrg	*xScale = *yScale = (double)*consHeight / *height;
685706f2543Smrg
686706f2543Smrg    *consWidth  = scalex(priv, *width);
687706f2543Smrg    *consHeight = scaley(priv, *height);
688706f2543Smrg    if (*consWidth  < 1) *consWidth  = 1;
689706f2543Smrg    if (*consHeight < 1) *consHeight = 1;
690706f2543Smrg}
691706f2543Smrg
692706f2543Smrg/** Re-initialized the console device described by \a pDev (after a
693706f2543Smrg * reconfig). */
694706f2543Smrgvoid dmxConsoleReInit(DevicePtr pDev)
695706f2543Smrg{
696706f2543Smrg    GETPRIVFROMPDEV;
697706f2543Smrg    Display *dpy;
698706f2543Smrg
699706f2543Smrg    if (!priv || !priv->initialized) return;
700706f2543Smrg    dpy = priv->display;
701706f2543Smrg
702706f2543Smrg    dmxConsoleComputeWidthHeight(priv,
703706f2543Smrg                                 &priv->width, &priv->height,
704706f2543Smrg                                 &priv->xScale, &priv->yScale,
705706f2543Smrg                                 &priv->consWidth, &priv->consHeight);
706706f2543Smrg    XResizeWindow(dpy, priv->window, priv->consWidth, priv->consHeight);
707706f2543Smrg    XFreePixmap(dpy, priv->pixmap);
708706f2543Smrg    priv->pixmap = XCreatePixmap(dpy,
709706f2543Smrg                                 RootWindow(dpy, DefaultScreen(dpy)),
710706f2543Smrg                                 priv->consWidth,
711706f2543Smrg                                 priv->consHeight,
712706f2543Smrg                                 DefaultDepth(dpy,DefaultScreen(dpy)));
713706f2543Smrg    dmxConsoleDraw(priv, 1, 1);
714706f2543Smrg}
715706f2543Smrg
716706f2543Smrg/** Initialized the console device described by \a pDev. */
717706f2543Smrgvoid dmxConsoleInit(DevicePtr pDev)
718706f2543Smrg{
719706f2543Smrg    GETPRIVFROMPDEV;
720706f2543Smrg    DMXInputInfo         *dmxInput = &dmxInputs[dmxLocal->inputIdx];
721706f2543Smrg    int                  screen;
722706f2543Smrg    unsigned long        mask;
723706f2543Smrg    XSetWindowAttributes attribs;
724706f2543Smrg    Display              *dpy;
725706f2543Smrg    Window               win;
726706f2543Smrg    XGCValues            gcvals;
727706f2543Smrg    XColor               color;
728706f2543Smrg    XClassHint           class_hints;
729706f2543Smrg    unsigned long        tmp;
730706f2543Smrg
731706f2543Smrg    if (dmxLocal->type == DMX_LOCAL_MOUSE)    priv->mou = pDev;
732706f2543Smrg    if (dmxLocal->type == DMX_LOCAL_KEYBOARD) priv->kbd = pDev;
733706f2543Smrg    if (priv->initialized++) return; /* Only do once for mouse/keyboard pair */
734706f2543Smrg
735706f2543Smrg    if (!(dpy = priv->display = XOpenDisplay(dmxInput->name)))
736706f2543Smrg        dmxLog(dmxFatal,
737706f2543Smrg               "dmxOpenConsole: cannot open console display %s\n",
738706f2543Smrg               dmxInput->name);
739706f2543Smrg
740706f2543Smrg    /* Set up defaults */
741706f2543Smrg    dmxConsoleComputeWidthHeight(priv,
742706f2543Smrg                                 &priv->width, &priv->height,
743706f2543Smrg                                 &priv->xScale, &priv->yScale,
744706f2543Smrg                                 &priv->consWidth, &priv->consHeight);
745706f2543Smrg
746706f2543Smrg    /* Private initialization using computed values or constants. */
747706f2543Smrg    screen                   = DefaultScreen(dpy);
748706f2543Smrg    priv->initPointerX       = scalex(priv, priv->width / 2);
749706f2543Smrg    priv->initPointerY       = scaley(priv, priv->height / 2);
750706f2543Smrg    priv->eventMask          = (ButtonPressMask
751706f2543Smrg                                | ButtonReleaseMask
752706f2543Smrg                                | PointerMotionMask
753706f2543Smrg                                | EnterWindowMask
754706f2543Smrg                                | LeaveWindowMask
755706f2543Smrg                                | KeyPressMask
756706f2543Smrg                                | KeyReleaseMask
757706f2543Smrg                                | ExposureMask
758706f2543Smrg                                | ResizeRedirectMask);
759706f2543Smrg
760706f2543Smrg    mask = CWBackPixel | CWEventMask | CWColormap | CWOverrideRedirect;
761706f2543Smrg    attribs.colormap = DefaultColormap(dpy, screen);
762706f2543Smrg    if (XParseColor(dpy, attribs.colormap, CONSOLE_BG_COLOR, &color)
763706f2543Smrg        && XAllocColor(dpy, attribs.colormap, &color)) {
764706f2543Smrg	attribs.background_pixel = color.pixel;
765706f2543Smrg    } else
766706f2543Smrg        attribs.background_pixel = WhitePixel(dpy, screen);
767706f2543Smrg
768706f2543Smrg    attribs.event_mask        = priv->eventMask;
769706f2543Smrg    attribs.override_redirect = False;
770706f2543Smrg
771706f2543Smrg    win = priv->window = XCreateWindow(dpy,
772706f2543Smrg                                       RootWindow(dpy, screen),
773706f2543Smrg                                       0, 0, priv->consWidth, priv->consHeight,
774706f2543Smrg                                       0,
775706f2543Smrg                                       DefaultDepth(dpy, screen),
776706f2543Smrg                                       InputOutput,
777706f2543Smrg                                       DefaultVisual(dpy, screen),
778706f2543Smrg                                       mask, &attribs);
779706f2543Smrg    priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
780706f2543Smrg                                 priv->consWidth, priv->consHeight,
781706f2543Smrg                                 DefaultDepth(dpy, screen));
782706f2543Smrg
783706f2543Smrg                                /* Set up properties */
784706f2543Smrg    XStoreName(dpy, win, DMX_CONSOLE_NAME);
785706f2543Smrg    class_hints.res_name  = DMX_RES_NAME;
786706f2543Smrg    class_hints.res_class = DMX_RES_CLASS;
787706f2543Smrg    XSetClassHint(dpy, win, &class_hints);
788706f2543Smrg
789706f2543Smrg
790706f2543Smrg                                /* Map the window */
791706f2543Smrg    XMapWindow(dpy, win);
792706f2543Smrg
793706f2543Smrg                                /* Create cursors */
794706f2543Smrg    priv->cursorNormal  = XCreateFontCursor(dpy, XC_circle);
795706f2543Smrg    priv->cursorGrabbed = XCreateFontCursor(dpy, XC_spider);
796706f2543Smrg    priv->cursorEmpty   = dmxConsoleCreateEmptyCursor(priv);
797706f2543Smrg    XDefineCursor(dpy, priv->window, priv->cursorNormal);
798706f2543Smrg
799706f2543Smrg                                /* Create GC */
800706f2543Smrg    mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground |
801706f2543Smrg	    GCBackground | GCLineWidth | GCLineStyle | GCCapStyle |
802706f2543Smrg	    GCFillStyle | GCGraphicsExposures);
803706f2543Smrg    gcvals.function = GXcopy;
804706f2543Smrg    gcvals.plane_mask = AllPlanes;
805706f2543Smrg    gcvals.clip_mask = None;
806706f2543Smrg    if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_FG_COLOR, &color)
807706f2543Smrg        && XAllocColor(dpy, attribs.colormap, &color)) {
808706f2543Smrg	gcvals.foreground = color.pixel;
809706f2543Smrg    } else
810706f2543Smrg	gcvals.foreground = BlackPixel(dpy, screen);
811706f2543Smrg    if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_BG_COLOR, &color)
812706f2543Smrg        && XAllocColor(dpy, attribs.colormap, &color)) {
813706f2543Smrg	gcvals.background = color.pixel;
814706f2543Smrg    } else
815706f2543Smrg	gcvals.background = WhitePixel(dpy, screen);
816706f2543Smrg    gcvals.line_width         = 0;
817706f2543Smrg    gcvals.line_style         = LineSolid;
818706f2543Smrg    gcvals.cap_style          = CapNotLast;
819706f2543Smrg    gcvals.fill_style         = FillSolid;
820706f2543Smrg    gcvals.graphics_exposures = False;
821706f2543Smrg
822706f2543Smrg    priv->gc = XCreateGC(dpy, win, mask, &gcvals);
823706f2543Smrg
824706f2543Smrg    tmp               = gcvals.foreground;
825706f2543Smrg    if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_DET_COLOR, &color)
826706f2543Smrg        && XAllocColor(dpy, attribs.colormap, &color)) {
827706f2543Smrg        gcvals.foreground = color.pixel;
828706f2543Smrg    } else
829706f2543Smrg        gcvals.foreground = BlackPixel(dpy, screen);
830706f2543Smrg    priv->gcDet = XCreateGC(dpy, win, mask, &gcvals);
831706f2543Smrg    gcvals.foreground = tmp;
832706f2543Smrg
833706f2543Smrg    tmp               = gcvals.background;
834706f2543Smrg    gcvals.background = gcvals.foreground;
835706f2543Smrg    gcvals.foreground = tmp;
836706f2543Smrg    priv->gcRev = XCreateGC(dpy, win, mask, &gcvals);
837706f2543Smrg
838706f2543Smrg    gcvals.background = gcvals.foreground;
839706f2543Smrg    if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_CUR_COLOR, &color)
840706f2543Smrg        && XAllocColor(dpy, attribs.colormap, &color)) {
841706f2543Smrg        gcvals.foreground = color.pixel;
842706f2543Smrg    } else
843706f2543Smrg        gcvals.foreground = BlackPixel(dpy, screen);
844706f2543Smrg    priv->gcCur = XCreateGC(dpy, win, mask, &gcvals);
845706f2543Smrg
846706f2543Smrg    dmxConsoleDraw(priv, 1, 1);
847706f2543Smrg
848706f2543Smrg    if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
849706f2543Smrg			 dmxScreenPrivateKey))
850706f2543Smrg        priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
851706f2543Smrg				      dmxScreenPrivateKey);
852706f2543Smrg    else
853706f2543Smrg        DMX_WRAP(CloseScreen, dmxCloseConsoleScreen,
854706f2543Smrg                 priv, screenInfo.screens[0]);
855706f2543Smrg    dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey,
856706f2543Smrg		  priv);
857706f2543Smrg}
858706f2543Smrg
859706f2543Smrg/** Fill in the \a info structure for the specified \a pDev.  Only used
860706f2543Smrg * for pointers. */
861706f2543Smrgvoid dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
862706f2543Smrg{
863706f2543Smrg    GETPRIVFROMPDEV;
864706f2543Smrg
865706f2543Smrg    info->buttonClass      = 1;
866706f2543Smrg    dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
867706f2543Smrg    info->valuatorClass    = 1;
868706f2543Smrg    info->numRelAxes       = 2;
869706f2543Smrg    info->minval[0] = 0;
870706f2543Smrg    info->minval[1] = 0;
871706f2543Smrg    /* max possible console window size: */
872706f2543Smrg    info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display));
873706f2543Smrg    info->maxval[1] = DisplayHeight(priv->display, DefaultScreen(priv->display));
874706f2543Smrg    info->res[0]           = 1;
875706f2543Smrg    info->minres[0]        = 0;
876706f2543Smrg    info->maxres[0]        = 1;
877706f2543Smrg    info->ptrFeedbackClass = 1;
878706f2543Smrg}
879706f2543Smrg
880706f2543Smrg/** Fill in the \a info structure for the specified \a pDev.  Only used
881706f2543Smrg * for keyboard. */
882706f2543Smrgvoid dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
883706f2543Smrg{
884706f2543Smrg    dmxCommonKbdGetInfo(pDev, info);
885706f2543Smrg    info->keyboard         = 1;
886706f2543Smrg    info->keyClass         = 1;
887706f2543Smrg    dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
888706f2543Smrg    info->freemap          = 1;
889706f2543Smrg    info->focusClass       = 1;
890706f2543Smrg    info->kbdFeedbackClass = 1;
891706f2543Smrg}
892706f2543Smrg
893706f2543Smrg/** Handle special console-only keys. */
894706f2543Smrgint dmxConsoleFunctions(pointer private, DMXFunctionType function)
895706f2543Smrg{
896706f2543Smrg    GETONLYPRIVFROMPRIVATE;
897706f2543Smrg    XRectangle rect;
898706f2543Smrg    Display    *dpy = priv->display;
899706f2543Smrg
900706f2543Smrg    switch (function) {
901706f2543Smrg    case DMX_FUNCTION_FINE:
902706f2543Smrg        if (priv->fine) {
903706f2543Smrg            priv->fine = 0;
904706f2543Smrg            dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rect);
905706f2543Smrg            XSetClipRectangles(dpy, priv->gc, 0, 0, &rect, 1, Unsorted);
906706f2543Smrg            XCopyArea(dpy, priv->pixmap, priv->window, priv->gc,
907706f2543Smrg                      0, 0, priv->consWidth, priv->consHeight, 0, 0);
908706f2543Smrg            XSetClipMask(dpy, priv->gc, None);
909706f2543Smrg
910706f2543Smrg            XDefineCursor(dpy, priv->window,
911706f2543Smrg                          priv->grabbed
912706f2543Smrg                          ? priv->cursorGrabbed
913706f2543Smrg                          : priv->cursorNormal);
914706f2543Smrg            XWarpPointer(dpy, priv->window, priv->window,
915706f2543Smrg                         0, 0, 0, 0,
916706f2543Smrg                         scalex(priv, priv->globalX),
917706f2543Smrg                         scaley(priv, priv->globalY));
918706f2543Smrg            XSync(dpy, False); /* Not a backend display */
919706f2543Smrg        } else {
920706f2543Smrg            priv->fine = 1;
921706f2543Smrg            XRaiseWindow(dpy, priv->window);
922706f2543Smrg            XDefineCursor(dpy, priv->window, priv->cursorEmpty);
923706f2543Smrg            dmxConsoleUpdateFineCursor(priv);
924706f2543Smrg        }
925706f2543Smrg        return 1;
926706f2543Smrg    case DMX_FUNCTION_GRAB:
927706f2543Smrg        if (priv->grabbed) {
928706f2543Smrg            XUngrabKeyboard(dpy, CurrentTime);
929706f2543Smrg            XUngrabPointer(dpy, CurrentTime);
930706f2543Smrg            XDefineCursor(dpy, priv->window,
931706f2543Smrg                          priv->fine
932706f2543Smrg                          ? priv->cursorEmpty
933706f2543Smrg                          : priv->cursorNormal);
934706f2543Smrg        } else {
935706f2543Smrg            if (XGrabPointer(dpy, priv->window, True,
936706f2543Smrg                             0, GrabModeAsync, GrabModeAsync, priv->window,
937706f2543Smrg                             None, CurrentTime)) {
938706f2543Smrg                dmxLog(dmxError, "XGrabPointer failed\n");
939706f2543Smrg                return 0;
940706f2543Smrg            }
941706f2543Smrg            if (XGrabKeyboard(dpy, priv->window, True,
942706f2543Smrg                              GrabModeAsync, GrabModeAsync, CurrentTime)) {
943706f2543Smrg                dmxLog(dmxError, "XGrabKeyboard failed\n");
944706f2543Smrg                XUngrabPointer(dpy, CurrentTime);
945706f2543Smrg                return 0;
946706f2543Smrg            }
947706f2543Smrg            XDefineCursor(dpy, priv->window,
948706f2543Smrg                          priv->fine
949706f2543Smrg                          ? priv->cursorEmpty
950706f2543Smrg                          : priv->cursorGrabbed);
951706f2543Smrg        }
952706f2543Smrg        priv->grabbed = !priv->grabbed;
953706f2543Smrg        if (priv->fine) dmxConsoleUpdateFineCursor(priv);
954706f2543Smrg        return 1;
955706f2543Smrg    case DMX_FUNCTION_TERMINATE:
956706f2543Smrg        return 1;
957706f2543Smrg    default:
958706f2543Smrg        return 0;
959706f2543Smrg    }
960706f2543Smrg}
961706f2543Smrg
962706f2543Smrgstatic void dmxDump(void)
963706f2543Smrg{
964706f2543Smrg    int          i, j;
965706f2543Smrg    DMXInputInfo *dmxInput;
966706f2543Smrg    XEvent       X;
967706f2543Smrg
968706f2543Smrg    for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
969706f2543Smrg        for (j = 0; j < dmxInput->numDevs; j++) {
970706f2543Smrg            DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
971706f2543Smrg            myPrivate            *priv    = dmxLocal->private;
972706f2543Smrg            while (priv
973706f2543Smrg                   && priv->display
974706f2543Smrg                   && XCheckTypedEvent(priv->display, MotionNotify, &X)) {
975706f2543Smrg                DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
976706f2543Smrg                        dmxInput->name, j, X.type, dmxEventName(X.type));
977706f2543Smrg            }
978706f2543Smrg        }
979706f2543Smrg    }
980706f2543Smrg}
981706f2543Smrg
982706f2543Smrg/** This routine is used to warp the pointer into the console window
983706f2543Smrg * from anywhere on the screen.  It is used when backend and console
984706f2543Smrg * input are both being taken from the same X display. */
985706f2543Smrgvoid dmxConsoleCapture(DMXInputInfo *dmxInput)
986706f2543Smrg{
987706f2543Smrg    int     i;
988706f2543Smrg    XEvent  X;
989706f2543Smrg
990706f2543Smrg    DMXDBG0("dmxConsoleCapture\n");
991706f2543Smrg    dmxSync(NULL, TRUE);
992706f2543Smrg    for (i = 0; i < dmxInput->numDevs; i++) {
993706f2543Smrg        DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
994706f2543Smrg        myPrivate            *priv    = dmxLocal->private;
995706f2543Smrg        if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue;
996706f2543Smrg        if (dmxLocal->type    != DMX_LOCAL_MOUSE)        continue;
997706f2543Smrg        if (priv->captured)                              continue;
998706f2543Smrg        priv->captured = 2;     /* Ungrab only after proximal events. */
999706f2543Smrg        XRaiseWindow(priv->display, priv->window);
1000706f2543Smrg        XSync(priv->display, False); /* Not a backend display */
1001706f2543Smrg        while (XCheckTypedEvent(priv->display, MotionNotify, &X)) {
1002706f2543Smrg            DMXDBG3("   Ignoring motion to %d %d after capture on %s\n",
1003706f2543Smrg                    X.xmotion.x, X.xmotion.y, dmxInput->name);
1004706f2543Smrg        }
1005706f2543Smrg        XWarpPointer(priv->display, None,
1006706f2543Smrg                     priv->window, 0, 0, 0, 0, priv->curX, priv->curY);
1007706f2543Smrg        XSync(priv->display, False); /* Not a backend display */
1008706f2543Smrg        dmxDump();
1009706f2543Smrg        if (priv->fine) dmxConsoleUpdateFineCursor(priv);
1010706f2543Smrg    }
1011706f2543Smrg}
1012706f2543Smrg
1013706f2543Smrg/** Undo the capture that was done by #dmxConsoleCapture. */
1014706f2543Smrgvoid dmxConsoleUncapture(DMXInputInfo *dmxInput)
1015706f2543Smrg{
1016706f2543Smrg    int i;
1017706f2543Smrg
1018706f2543Smrg    DMXDBG0("dmxConsoleUncapture\n");
1019706f2543Smrg    dmxSync(NULL, TRUE);
1020706f2543Smrg    for (i = 0; i < dmxInput->numDevs; i++) {
1021706f2543Smrg        DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
1022706f2543Smrg        myPrivate            *priv    = dmxLocal->private;
1023706f2543Smrg        if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue;
1024706f2543Smrg        if (dmxLocal->type    != DMX_LOCAL_MOUSE)        continue;
1025706f2543Smrg        if (!priv->captured)                             continue;
1026706f2543Smrg        priv->captured = 0;
1027706f2543Smrg        XSync(priv->display, False); /* Not a backend display */
1028706f2543Smrg    }
1029706f2543Smrg}
1030