2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
38 * This file implements the console input devices.
41 #ifdef HAVE_DMX_CONFIG_H
42 #include <dmx-config.h>
45 #define DMX_CONSOLE_DEBUG 0
46 #define DMX_WINDOW_DEBUG 0
48 #include "dmxinputinit.h"
49 #include "dmxevents.h"
50 #include "dmxconsole.h"
51 #include "dmxcommon.h"
52 #include "dmxscrinit.h"
58 #include "mipointer.h"
59 #include "windowstr.h"
63 #define DMX_CONSOLE_NAME "DMX Console"
64 #define DMX_RES_NAME "Xdmx"
65 #define DMX_RES_CLASS "XDmx"
66 #define CONSOLE_BG_COLOR "gray75"
67 #define CONSOLE_FG_COLOR "black"
68 #define CONSOLE_SCREEN_BG_COLOR "white"
69 #define CONSOLE_SCREEN_FG_COLOR "black"
70 #define CONSOLE_SCREEN_DET_COLOR "gray75"
71 #define CONSOLE_SCREEN_CUR_COLOR "red"
74 #define DMXDBG0(f) dmxLog(dmxDebug,f)
75 #define DMXDBG1(f,a) dmxLog(dmxDebug,f,a)
76 #define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
77 #define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c)
78 #define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d)
79 #define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
80 #define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g)
81 #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
85 #define DMXDBG2(f,a,b)
86 #define DMXDBG3(f,a,b,c)
87 #define DMXDBG4(f,a,b,c,d)
88 #define DMXDBG5(f,a,b,c,d,e)
89 #define DMXDBG6(f,a,b,c,d,e,g)
90 #define DMXDBG7(f,a,b,c,d,e,g,h)
93 /* Private area for consoles. */
94 typedef struct _myPrivate
{
108 XlibGC gc
, gcDet
, gcRev
, gcCur
;
109 int grabbed
, fine
, captured
;
110 Cursor cursorNormal
, cursorGrabbed
, cursorEmpty
;
113 CloseScreenProcPtr CloseScreen
;
114 struct _myPrivate
*next
; /* for closing multiple consoles */
120 scalex(myPrivate
* priv
, int x
)
122 return (int) ((x
* priv
->xScale
) + .5);
126 scaley(myPrivate
* priv
, int y
)
128 return (int) ((y
* priv
->yScale
) + .5);
132 unscalex(myPrivate
* priv
, int x
)
134 return (int) ((x
/ priv
->xScale
) + .5);
138 unscaley(myPrivate
* priv
, int y
)
140 return (int) ((y
/ priv
->yScale
) + .5);
143 /** Create the private area for \a pDevice. */
145 dmxConsoleCreatePrivate(DeviceIntPtr pDevice
)
147 GETDMXLOCALFROMPDEVICE
;
148 myPrivate
*priv
= calloc(1, sizeof(*priv
));
150 priv
->dmxLocal
= dmxLocal
;
154 /** If \a private is non-NULL, free its associated memory. */
156 dmxConsoleDestroyPrivate(pointer
private)
162 dmxConsoleDrawFineCursor(myPrivate
* priv
, XRectangle
* rect
)
167 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
168 x
= scalex(priv
, priv
->globalX
) - size
,
169 scaley(priv
, priv
->globalY
),
170 scalex(priv
, priv
->globalX
) + size
, scaley(priv
, priv
->globalY
));
171 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
172 scalex(priv
, priv
->globalX
),
173 y
= scaley(priv
, priv
->globalY
) - size
,
174 scalex(priv
, priv
->globalX
), scaley(priv
, priv
->globalY
) + size
);
176 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
177 scalex(priv
, priv
->globalX
) - (int) (size
/ 1.4),
178 scaley(priv
, priv
->globalY
) - (int) (size
/ 1.4),
179 scalex(priv
, priv
->globalX
) + (int) (size
/ 1.4),
180 scaley(priv
, priv
->globalY
) + (int) (size
/ 1.4));
181 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
182 scalex(priv
, priv
->globalX
) - (int) (size
/ 1.4),
183 scaley(priv
, priv
->globalY
) + (int) (size
/ 1.4),
184 scalex(priv
, priv
->globalX
) + (int) (size
/ 1.4),
185 scaley(priv
, priv
->globalY
) - (int) (size
/ 1.4));
190 rect
->width
= 2 * size
;
191 rect
->height
= 2 * size
;
196 dmxConsoleDrawWindows(pointer
private)
198 GETONLYPRIVFROMPRIVATE
;
199 Display
*dpy
= priv
->display
;
201 Region whole
, used
, avail
;
204 whole
= XCreateRegion();
205 used
= XCreateRegion();
206 avail
= XCreateRegion();
209 rect
.width
= priv
->consWidth
;
210 rect
.height
= priv
->consHeight
;
211 XUnionRectWithRegion(&rect
, whole
, whole
);
213 for (i
= 0; i
< dmxNumScreens
; i
++) {
214 ScreenPtr pScreen
= screenInfo
.screens
[i
];
215 WindowPtr pRoot
= pScreen
->root
;
219 dmxLog(dmxDebug
, "%lu %p %p %p 2\n",
221 pRoot
->parent
, pRoot
->firstChild
, pRoot
->lastChild
);
224 for (pChild
= pRoot
->firstChild
; pChild
; pChild
= pChild
->nextSib
) {
225 if (pChild
->mapped
&& pChild
->realized
) {
227 dmxLog(dmxDebug
, " %p %d,%d %dx%d %d %d %d RECTS\n",
231 pChild
->drawable
.width
,
232 pChild
->drawable
.height
,
234 pChild
->overrideRedirect
,
235 RegionNumRects(&pChild
->clipList
));
237 rect
.x
= scalex(priv
, pChild
->drawable
.x
+ pScreen
->x
);
238 rect
.y
= scaley(priv
, pChild
->drawable
.y
+ pScreen
->y
);
239 rect
.width
= scalex(priv
, pChild
->drawable
.width
);
240 rect
.height
= scaley(priv
, pChild
->drawable
.height
);
241 XDrawRectangle(dpy
, priv
->pixmap
, priv
->gc
,
242 rect
.x
, rect
.y
, rect
.width
, rect
.height
);
243 XUnionRectWithRegion(&rect
, used
, used
);
244 XSubtractRegion(whole
, used
, avail
);
245 XSetRegion(dpy
, priv
->gc
, avail
);
249 if (!noPanoramiXExtension
)
250 break; /* Screen 0 valid with Xinerama */
253 XDestroyRegion(avail
);
254 XDestroyRegion(used
);
255 XDestroyRegion(whole
);
256 XSetClipMask(dpy
, priv
->gc
, None
);
260 dmxConsoleDraw(myPrivate
* priv
, int updateCursor
, int update
)
263 Display
*dpy
= priv
->display
;
266 XFillRectangle(dpy
, priv
->pixmap
, priv
->gc
, 0, 0,
267 priv
->consWidth
, priv
->consHeight
);
269 for (i
= 0; i
< dmxNumScreens
; i
++) {
270 DMXScreenInfo
*dmxScreen
= &dmxScreens
[i
];
272 XFillRectangle(dpy
, priv
->pixmap
,
273 dmxScreen
->beDisplay
? priv
->gcRev
: priv
->gcDet
,
274 scalex(priv
, screenInfo
.screens
[i
]->x
),
275 scaley(priv
, screenInfo
.screens
[i
]->y
),
276 scalex(priv
, screenInfo
.screens
[i
]->width
),
277 scaley(priv
, screenInfo
.screens
[i
]->height
));
279 for (i
= 0; i
< dmxNumScreens
; i
++) {
280 XDrawRectangle(dpy
, priv
->pixmap
, priv
->gc
,
281 scalex(priv
, screenInfo
.screens
[i
]->x
),
282 scaley(priv
, screenInfo
.screens
[i
]->y
),
283 scalex(priv
, screenInfo
.screens
[i
]->width
),
284 scaley(priv
, screenInfo
.screens
[i
]->height
));
286 if (dmxInput
->windows
)
287 dmxConsoleDrawWindows(priv
);
288 if (priv
->fine
&& updateCursor
)
289 dmxConsoleDrawFineCursor(priv
, 0);
291 XCopyArea(priv
->display
, priv
->pixmap
, priv
->window
, priv
->gc
,
292 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
293 XSync(priv
->display
, False
); /* Not a backend display */
298 dmxConsoleClearCursor(myPrivate
* priv
, int x
, int y
, XRectangle
* rect
)
300 int cw
= 14, ch
= 14; /* Clear width and height */
302 rect
->x
= scalex(priv
, x
) - cw
/ 2;
303 rect
->y
= scaley(priv
, y
) - ch
/ 2;
306 XSetClipRectangles(priv
->display
, priv
->gc
, 0, 0, rect
, 1, Unsorted
);
307 XSetClipRectangles(priv
->display
, priv
->gcDet
, 0, 0, rect
, 1, Unsorted
);
308 XSetClipRectangles(priv
->display
, priv
->gcRev
, 0, 0, rect
, 1, Unsorted
);
309 dmxConsoleDraw(priv
, 0, 0);
310 XSetClipMask(priv
->display
, priv
->gc
, None
);
311 XSetClipMask(priv
->display
, priv
->gcDet
, None
);
312 XSetClipMask(priv
->display
, priv
->gcRev
, None
);
316 dmxConsoleUpdateFineCursor(myPrivate
* priv
)
321 dmxConsoleClearCursor(priv
, priv
->globalX
, priv
->globalY
, &rects
[0]);
322 if (priv
->dmxLocal
->sendsCore
) {
323 dmxGetGlobalPosition(&priv
->globalX
, &priv
->globalY
);
326 priv
->globalX
= priv
->dmxLocal
->lastX
;
327 priv
->globalY
= priv
->dmxLocal
->lastY
;
330 priv
->lastX
= scalex(priv
, priv
->width
/ 2);
331 priv
->lastY
= scaley(priv
, priv
->height
/ 2);
333 /* Compute new warp position, which may be
334 outside the window */
335 if (priv
->globalX
< 1 || priv
->globalX
>= priv
->width
) {
336 if (priv
->globalX
< 1)
339 priv
->lastX
= scalex(priv
, priv
->width
);
340 priv
->lastY
= scaley(priv
, priv
->globalY
);
343 if (priv
->globalY
< 1 || priv
->globalY
>= priv
->height
) {
344 if (priv
->globalY
< 1)
347 priv
->lastY
= scaley(priv
, priv
->height
);
348 priv
->lastX
= scalex(priv
, priv
->globalX
);
352 /* Draw pseudo cursor in window */
353 dmxConsoleDrawFineCursor(priv
, &rects
[1]);
355 XSetClipRectangles(priv
->display
, priv
->gc
, 0, 0, rects
, 2, Unsorted
);
356 XCopyArea(priv
->display
, priv
->pixmap
, priv
->window
, priv
->gc
,
357 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
358 XSetClipMask(priv
->display
, priv
->gc
, None
);
360 DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
361 priv
->lastX
, priv
->lastY
);
362 XWarpPointer(priv
->display
, priv
->window
, priv
->window
,
363 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
364 XSync(priv
->display
, False
); /* Not a backend display */
369 while (XCheckMaskEvent(priv
->display
, PointerMotionMask
, &X
)) {
370 if (X
.type
== MotionNotify
) {
371 if (X
.xmotion
.x
!= priv
->lastX
|| X
.xmotion
.y
!= priv
->lastY
) {
372 DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
373 X
.xmotion
.x
, X
.xmotion
.y
, priv
->lastX
, priv
->lastY
);
377 dmxLog(dmxInfo
, "Ignoring event (%d): %s ****************\n",
378 X
.type
, dmxEventName(X
.type
));
382 DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
383 priv
->lastX
, priv
->lastY
,
384 scalex(priv
, priv
->width
),
385 scaley(priv
, priv
->height
), priv
->globalX
, priv
->globalY
);
388 /** Whenever the window layout (size, position, stacking order) might be
389 * changed, this routine is called with the \a pWindow that changed and
390 * the \a type of change. This routine is called in a conservative
391 * fashion: the actual layout of the windows of the screen might not
392 * have had any human-visible changes. */
394 dmxConsoleUpdateInfo(pointer
private, DMXUpdateType type
, WindowPtr pWindow
)
396 GETONLYPRIVFROMPRIVATE
;
397 dmxConsoleDraw(priv
, 1, 1);
401 dmxConsoleMoveAbsolute(myPrivate
* priv
, int x
, int y
,
402 DevicePtr pDev
, dmxMotionProcPtr motion
,
405 int tmpX
, tmpY
, v
[2];
407 tmpX
= unscalex(priv
, x
);
408 tmpY
= unscalex(priv
, y
);
409 DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
410 x
, y
, tmpX
, tmpY
, priv
->curX
, priv
->curY
);
411 if (tmpX
== priv
->curX
&& tmpY
== priv
->curY
)
413 v
[0] = unscalex(priv
, x
);
414 v
[1] = unscaley(priv
, y
);
415 motion(pDev
, v
, 0, 2, DMX_ABSOLUTE_CONFINED
, block
);
416 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
420 dmxConsoleMoveRelative(myPrivate
* priv
, int x
, int y
,
421 DevicePtr pDev
, dmxMotionProcPtr motion
,
426 /* Ignore the event generated from * warping back to middle */
427 if (x
== priv
->lastX
&& y
== priv
->lastY
)
429 v
[0] = priv
->lastX
- x
;
430 v
[1] = priv
->lastY
- y
;
431 motion(pDev
, v
, 0, 2, DMX_RELATIVE
, block
);
432 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
435 /** This routine gets called from #dmxCoreMotion for each motion. This
436 * allows the console's notion of the cursor postion to change when
437 * another input device actually caused the change. */
439 dmxConsoleUpdatePosition(pointer
private, int x
, int y
)
441 GETONLYPRIVFROMPRIVATE
;
443 Display
*dpy
= priv
->display
;
444 static unsigned long dmxGeneration
= 0;
446 tmpX
= scalex(priv
, x
);
447 tmpY
= scaley(priv
, y
);
448 DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
449 x
, y
, tmpX
, tmpY
, priv
->consWidth
, priv
->consHeight
);
452 dmxConsoleUpdateFineCursor(priv
);
453 if (tmpX
!= priv
->curX
|| tmpY
!= priv
->curY
) {
458 if (tmpX
>= priv
->consWidth
)
459 tmpX
= priv
->consWidth
- 1;
460 if (tmpY
>= priv
->consHeight
)
461 tmpY
= priv
->consHeight
- 1;
465 DMXDBG2(" WARP B %d %d\n", priv
->curX
, priv
->curY
);
466 XWarpPointer(dpy
, priv
->window
,
467 priv
->window
, 0, 0, 0, 0, tmpX
, tmpY
);
468 XSync(dpy
, False
); /* Not a backend display */
472 if (dmxGeneration
!= serverGeneration
) {
473 dmxGeneration
= serverGeneration
;
474 dmxConsoleDraw(priv
, 1, 1);
478 /** Collect all pending events from the console's display. Plase these
479 * events on the server event queue using the \a motion and \a enqueue
480 * routines. The \a checkspecial routine is used to check for special
481 * keys that need handling. \a block tells if signals should be blocked
482 * when updating the event queue. */
484 dmxConsoleCollectEvents(DevicePtr pDev
,
485 dmxMotionProcPtr motion
,
486 dmxEnqueueProcPtr enqueue
,
487 dmxCheckSpecialProcPtr checkspecial
, DMXBlockType block
)
491 Display
*dpy
= priv
->display
;
492 Window win
= priv
->window
;
493 int width
= priv
->width
;
494 int height
= priv
->height
;
496 XSetWindowAttributes attribs
;
497 static int rInitialized
= 0;
500 static int raising
= 0, raiseX
, raiseY
; /* FIXME */
502 while (XPending(dpy
)) {
505 case VisibilityNotify
:
508 DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
510 X
.xexpose
.x
, X
.xexpose
.y
,
511 X
.xexpose
.width
, X
.xexpose
.height
);
514 rect
.x
= X
.xexpose
.x
;
515 rect
.y
= X
.xexpose
.y
;
516 rect
.width
= X
.xexpose
.width
;
517 rect
.height
= X
.xexpose
.height
;
518 XUnionRectWithRegion(&rect
, r
, r
);
519 if (X
.xexpose
.count
== 0) {
520 XSetRegion(dpy
, priv
->gc
, r
);
521 XSetRegion(dpy
, priv
->gcDet
, r
);
522 XSetRegion(dpy
, priv
->gcRev
, r
);
523 dmxConsoleDraw(priv
, 1, 1);
524 XSetClipMask(dpy
, priv
->gc
, None
);
525 XSetClipMask(dpy
, priv
->gcDet
, None
);
526 XSetClipMask(dpy
, priv
->gcRev
, None
);
532 DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
533 X
.xresizerequest
.width
, X
.xresizerequest
.height
);
534 priv
->consWidth
= X
.xresizerequest
.width
;
535 priv
->consHeight
= X
.xresizerequest
.height
;
536 priv
->xScale
= (double) priv
->consWidth
/ width
;
537 priv
->yScale
= (double) priv
->consHeight
/ height
;
538 attribs
.override_redirect
= True
;
539 XChangeWindowAttributes(dpy
, win
, CWOverrideRedirect
, &attribs
);
540 XResizeWindow(dpy
, win
, priv
->consWidth
, priv
->consHeight
);
541 XFreePixmap(dpy
, priv
->pixmap
);
542 priv
->pixmap
= XCreatePixmap(dpy
,
543 RootWindow(dpy
, DefaultScreen(dpy
)),
546 DefaultDepth(dpy
, DefaultScreen(dpy
)));
547 dmxConsoleDraw(priv
, 1, 1);
548 attribs
.override_redirect
= False
;
549 XChangeWindowAttributes(dpy
, win
, CWOverrideRedirect
, &attribs
);
552 DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
553 X
.xcrossing
.x
, X
.xcrossing
.y
, raising
, priv
->fine
);
555 dmxCommonRestoreState(priv
);
557 dmxConsoleUncapture(dmxInput
);
558 dmxCommonRestoreState(priv
);
562 DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
563 X
.xcrossing
.x
, X
.xcrossing
.y
, raising
, priv
->fine
,
564 priv
->curX
, priv
->curY
);
565 dmxCommonSaveState(priv
);
568 dmxConsoleMoveAbsolute(priv
, raiseX
, raiseY
,
569 priv
->mou
, motion
, block
);
573 /* The raise will generate an event near the center,
574 * which is not where the cursor should be. So we
575 * save the real position, do the raise, and move
576 * the cursor here again after the raise generates
579 raiseX
= X
.xcrossing
.x
;
580 raiseY
= X
.xcrossing
.y
;
581 XRaiseWindow(dpy
, priv
->window
);
583 XSync(dpy
, False
); /* Not a backend display */
584 if (!X
.xcrossing
.x
&& !X
.xcrossing
.y
)
585 dmxConsoleMoveAbsolute(priv
, priv
->curX
, priv
->curY
,
586 priv
->mou
, motion
, block
);
590 if (priv
->curX
== X
.xmotion
.x
&& priv
->curY
== X
.xmotion
.y
)
592 if (XPending(dpy
)) { /* do motion compression */
594 if (N
.type
== MotionNotify
)
597 DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
598 X
.xmotion
.x
, X
.xmotion
.y
);
601 dmxConsoleMoveAbsolute(priv
, raiseX
, raiseY
,
602 priv
->mou
, motion
, block
);
606 dmxConsoleMoveRelative(priv
, X
.xmotion
.x
, X
.xmotion
.y
,
607 priv
->mou
, motion
, block
);
609 dmxConsoleMoveAbsolute(priv
, X
.xmotion
.x
, X
.xmotion
.y
,
610 priv
->mou
, motion
, block
);
615 enqueue(priv
->kbd
, X
.type
, X
.xkey
.keycode
, 0, NULL
, block
);
618 /* Pass the whole event here, because
619 * this may be an extension event. */
620 enqueue(priv
->mou
, X
.type
, X
.xbutton
.button
, 0, &X
, block
);
627 dmxCloseConsole(myPrivate
* priv
)
630 dmxCommonRestoreState(priv
);
632 XFreeGC(priv
->display
, priv
->gc
);
633 XFreeGC(priv
->display
, priv
->gcDet
);
634 XFreeGC(priv
->display
, priv
->gcRev
);
635 XFreeGC(priv
->display
, priv
->gcCur
);
636 if (!dmxInput
->console
)
637 XCloseDisplay(priv
->display
);
639 priv
->display
= NULL
;
643 dmxCloseConsoleScreen(ScreenPtr pScreen
)
645 myPrivate
*priv
, *last
;
647 for (last
= priv
= (myPrivate
*) dixLookupPrivate(&pScreen
->devPrivates
,
648 dmxScreenPrivateKey
);
649 priv
; priv
= priv
->next
)
650 dmxCloseConsole(last
= priv
);
652 DMX_UNWRAP(CloseScreen
, last
, pScreen
);
653 return pScreen
->CloseScreen(pScreen
);
657 dmxConsoleCreateEmptyCursor(myPrivate
* priv
)
659 char noCursorData
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
662 XColor color
, tmpColor
;
663 Display
*dpy
= priv
->display
;
665 /* Create empty cursor for window */
666 pixmap
= XCreateBitmapFromData(priv
->display
, priv
->window
,
668 if (!XAllocNamedColor(dpy
, DefaultColormap(dpy
, DefaultScreen(dpy
)),
669 "black", &color
, &tmpColor
))
670 dmxLog(dmxFatal
, "Cannot allocate color for cursor\n");
671 cursor
= XCreatePixmapCursor(dpy
, pixmap
, pixmap
, &color
, &color
, 0, 0);
672 XFreePixmap(dpy
, pixmap
);
677 dmxConsoleComputeWidthHeight(myPrivate
* priv
,
678 int *width
, int *height
,
679 double *xScale
, double *yScale
,
680 int *consWidth
, int *consHeight
)
683 Display
*dpy
= priv
->display
;
690 screen
= DefaultScreen(dpy
);
691 *consWidth
= DisplayWidth(dpy
, screen
) * CONSOLE_NUM
/ CONSOLE_DEN
;
692 *consHeight
= DisplayHeight(dpy
, screen
) * CONSOLE_NUM
/ CONSOLE_DEN
;
700 /* Always keep the console size similar
701 * to the global bounding box. */
702 *width
= dmxGlobalWidth
;
703 *height
= dmxGlobalHeight
;
705 /* Make the console window as big as
706 * possible by computing the visible
708 for (i
= 0; i
< dmxNumScreens
; i
++) {
709 if (screenInfo
.screens
[i
]->x
+ screenInfo
.screens
[i
]->width
> *width
)
710 *width
= screenInfo
.screens
[i
]->x
+ screenInfo
.screens
[i
]->width
;
712 if (screenInfo
.screens
[i
]->y
+ screenInfo
.screens
[i
]->height
> *height
)
713 *height
= screenInfo
.screens
[i
]->y
+ screenInfo
.screens
[i
]->height
;
717 if ((double) *consWidth
/ *width
< (double) *consHeight
/ *height
)
718 *xScale
= *yScale
= (double) *consWidth
/ *width
;
720 *xScale
= *yScale
= (double) *consHeight
/ *height
;
722 *consWidth
= scalex(priv
, *width
);
723 *consHeight
= scaley(priv
, *height
);
730 /** Re-initialized the console device described by \a pDev (after a
733 dmxConsoleReInit(DevicePtr pDev
)
738 if (!priv
|| !priv
->initialized
)
742 dmxConsoleComputeWidthHeight(priv
,
743 &priv
->width
, &priv
->height
,
744 &priv
->xScale
, &priv
->yScale
,
745 &priv
->consWidth
, &priv
->consHeight
);
746 XResizeWindow(dpy
, priv
->window
, priv
->consWidth
, priv
->consHeight
);
747 XFreePixmap(dpy
, priv
->pixmap
);
748 priv
->pixmap
= XCreatePixmap(dpy
,
749 RootWindow(dpy
, DefaultScreen(dpy
)),
752 DefaultDepth(dpy
, DefaultScreen(dpy
)));
753 dmxConsoleDraw(priv
, 1, 1);
756 /** Initialized the console device described by \a pDev. */
758 dmxConsoleInit(DevicePtr pDev
)
761 DMXInputInfo
*dmxInput
= &dmxInputs
[dmxLocal
->inputIdx
];
764 XSetWindowAttributes attribs
;
769 XClassHint class_hints
;
772 if (dmxLocal
->type
== DMX_LOCAL_MOUSE
)
774 if (dmxLocal
->type
== DMX_LOCAL_KEYBOARD
)
776 if (priv
->initialized
++)
777 return; /* Only do once for mouse/keyboard pair */
779 if (!(dpy
= priv
->display
= XOpenDisplay(dmxInput
->name
)))
781 "dmxOpenConsole: cannot open console display %s\n",
784 /* Set up defaults */
785 dmxConsoleComputeWidthHeight(priv
,
786 &priv
->width
, &priv
->height
,
787 &priv
->xScale
, &priv
->yScale
,
788 &priv
->consWidth
, &priv
->consHeight
);
790 /* Private initialization using computed values or constants. */
791 screen
= DefaultScreen(dpy
);
792 priv
->initPointerX
= scalex(priv
, priv
->width
/ 2);
793 priv
->initPointerY
= scaley(priv
, priv
->height
/ 2);
794 priv
->eventMask
= (ButtonPressMask
800 | KeyReleaseMask
| ExposureMask
| ResizeRedirectMask
);
802 mask
= CWBackPixel
| CWEventMask
| CWColormap
| CWOverrideRedirect
;
803 attribs
.colormap
= DefaultColormap(dpy
, screen
);
804 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_BG_COLOR
, &color
)
805 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
806 attribs
.background_pixel
= color
.pixel
;
809 attribs
.background_pixel
= WhitePixel(dpy
, screen
);
811 attribs
.event_mask
= priv
->eventMask
;
812 attribs
.override_redirect
= False
;
814 win
= priv
->window
= XCreateWindow(dpy
,
815 RootWindow(dpy
, screen
),
816 0, 0, priv
->consWidth
, priv
->consHeight
,
818 DefaultDepth(dpy
, screen
),
820 DefaultVisual(dpy
, screen
),
822 priv
->pixmap
= XCreatePixmap(dpy
, RootWindow(dpy
, screen
),
823 priv
->consWidth
, priv
->consHeight
,
824 DefaultDepth(dpy
, screen
));
826 /* Set up properties */
827 XStoreName(dpy
, win
, DMX_CONSOLE_NAME
);
828 class_hints
.res_name
= DMX_RES_NAME
;
829 class_hints
.res_class
= DMX_RES_CLASS
;
830 XSetClassHint(dpy
, win
, &class_hints
);
833 XMapWindow(dpy
, win
);
836 priv
->cursorNormal
= XCreateFontCursor(dpy
, XC_circle
);
837 priv
->cursorGrabbed
= XCreateFontCursor(dpy
, XC_spider
);
838 priv
->cursorEmpty
= dmxConsoleCreateEmptyCursor(priv
);
839 XDefineCursor(dpy
, priv
->window
, priv
->cursorNormal
);
842 mask
= (GCFunction
| GCPlaneMask
| GCClipMask
| GCForeground
|
843 GCBackground
| GCLineWidth
| GCLineStyle
| GCCapStyle
|
844 GCFillStyle
| GCGraphicsExposures
);
845 gcvals
.function
= GXcopy
;
846 gcvals
.plane_mask
= AllPlanes
;
847 gcvals
.clip_mask
= None
;
848 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_FG_COLOR
, &color
)
849 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
850 gcvals
.foreground
= color
.pixel
;
853 gcvals
.foreground
= BlackPixel(dpy
, screen
);
854 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_BG_COLOR
, &color
)
855 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
856 gcvals
.background
= color
.pixel
;
859 gcvals
.background
= WhitePixel(dpy
, screen
);
860 gcvals
.line_width
= 0;
861 gcvals
.line_style
= LineSolid
;
862 gcvals
.cap_style
= CapNotLast
;
863 gcvals
.fill_style
= FillSolid
;
864 gcvals
.graphics_exposures
= False
;
866 priv
->gc
= XCreateGC(dpy
, win
, mask
, &gcvals
);
868 tmp
= gcvals
.foreground
;
869 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_DET_COLOR
, &color
)
870 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
871 gcvals
.foreground
= color
.pixel
;
874 gcvals
.foreground
= BlackPixel(dpy
, screen
);
875 priv
->gcDet
= XCreateGC(dpy
, win
, mask
, &gcvals
);
876 gcvals
.foreground
= tmp
;
878 tmp
= gcvals
.background
;
879 gcvals
.background
= gcvals
.foreground
;
880 gcvals
.foreground
= tmp
;
881 priv
->gcRev
= XCreateGC(dpy
, win
, mask
, &gcvals
);
883 gcvals
.background
= gcvals
.foreground
;
884 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_CUR_COLOR
, &color
)
885 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
886 gcvals
.foreground
= color
.pixel
;
889 gcvals
.foreground
= BlackPixel(dpy
, screen
);
890 priv
->gcCur
= XCreateGC(dpy
, win
, mask
, &gcvals
);
892 dmxConsoleDraw(priv
, 1, 1);
894 if (dixLookupPrivate(&screenInfo
.screens
[0]->devPrivates
,
895 dmxScreenPrivateKey
))
896 priv
->next
= dixLookupPrivate(&screenInfo
.screens
[0]->devPrivates
,
897 dmxScreenPrivateKey
);
899 DMX_WRAP(CloseScreen
, dmxCloseConsoleScreen
,
900 priv
, screenInfo
.screens
[0]);
901 dixSetPrivate(&screenInfo
.screens
[0]->devPrivates
, dmxScreenPrivateKey
,
905 /** Fill in the \a info structure for the specified \a pDev. Only used
908 dmxConsoleMouGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
912 info
->buttonClass
= 1;
913 dmxCommonMouGetMap(pDev
, info
->map
, &info
->numButtons
);
914 info
->valuatorClass
= 1;
915 info
->numRelAxes
= 2;
918 /* max possible console window size: */
919 info
->maxval
[0] = DisplayWidth(priv
->display
, DefaultScreen(priv
->display
));
921 DisplayHeight(priv
->display
, DefaultScreen(priv
->display
));
925 info
->ptrFeedbackClass
= 1;
928 /** Fill in the \a info structure for the specified \a pDev. Only used
931 dmxConsoleKbdGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
933 dmxCommonKbdGetInfo(pDev
, info
);
936 dmxCommonKbdGetMap(pDev
, &info
->keySyms
, info
->modMap
);
938 info
->focusClass
= 1;
939 info
->kbdFeedbackClass
= 1;
942 /** Handle special console-only keys. */
944 dmxConsoleFunctions(pointer
private, DMXFunctionType function
)
946 GETONLYPRIVFROMPRIVATE
;
948 Display
*dpy
= priv
->display
;
951 case DMX_FUNCTION_FINE
:
954 dmxConsoleClearCursor(priv
, priv
->globalX
, priv
->globalY
, &rect
);
955 XSetClipRectangles(dpy
, priv
->gc
, 0, 0, &rect
, 1, Unsorted
);
956 XCopyArea(dpy
, priv
->pixmap
, priv
->window
, priv
->gc
,
957 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
958 XSetClipMask(dpy
, priv
->gc
, None
);
960 XDefineCursor(dpy
, priv
->window
,
962 ? priv
->cursorGrabbed
: priv
->cursorNormal
);
963 XWarpPointer(dpy
, priv
->window
, priv
->window
,
965 scalex(priv
, priv
->globalX
),
966 scaley(priv
, priv
->globalY
));
967 XSync(dpy
, False
); /* Not a backend display */
971 XRaiseWindow(dpy
, priv
->window
);
972 XDefineCursor(dpy
, priv
->window
, priv
->cursorEmpty
);
973 dmxConsoleUpdateFineCursor(priv
);
976 case DMX_FUNCTION_GRAB
:
978 XUngrabKeyboard(dpy
, CurrentTime
);
979 XUngrabPointer(dpy
, CurrentTime
);
980 XDefineCursor(dpy
, priv
->window
,
981 priv
->fine
? priv
->cursorEmpty
: priv
->cursorNormal
);
984 if (XGrabPointer(dpy
, priv
->window
, True
,
985 0, GrabModeAsync
, GrabModeAsync
, priv
->window
,
986 None
, CurrentTime
)) {
987 dmxLog(dmxError
, "XGrabPointer failed\n");
990 if (XGrabKeyboard(dpy
, priv
->window
, True
,
991 GrabModeAsync
, GrabModeAsync
, CurrentTime
)) {
992 dmxLog(dmxError
, "XGrabKeyboard failed\n");
993 XUngrabPointer(dpy
, CurrentTime
);
996 XDefineCursor(dpy
, priv
->window
,
997 priv
->fine
? priv
->cursorEmpty
: priv
->cursorGrabbed
);
999 priv
->grabbed
= !priv
->grabbed
;
1001 dmxConsoleUpdateFineCursor(priv
);
1003 case DMX_FUNCTION_TERMINATE
:
1014 DMXInputInfo
*dmxInput
;
1017 for (i
= 0, dmxInput
= &dmxInputs
[0]; i
< dmxNumInputs
; i
++, dmxInput
++) {
1018 for (j
= 0; j
< dmxInput
->numDevs
; j
++) {
1019 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[j
];
1020 myPrivate
*priv
= dmxLocal
->private;
1024 && XCheckTypedEvent(priv
->display
, MotionNotify
, &X
)) {
1025 DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
1026 dmxInput
->name
, j
, X
.type
, dmxEventName(X
.type
));
1032 /** This routine is used to warp the pointer into the console window
1033 * from anywhere on the screen. It is used when backend and console
1034 * input are both being taken from the same X display. */
1036 dmxConsoleCapture(DMXInputInfo
* dmxInput
)
1041 DMXDBG0("dmxConsoleCapture\n");
1042 dmxSync(NULL
, TRUE
);
1043 for (i
= 0; i
< dmxInput
->numDevs
; i
++) {
1044 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[i
];
1045 myPrivate
*priv
= dmxLocal
->private;
1047 if (dmxLocal
->extType
!= DMX_LOCAL_TYPE_CONSOLE
)
1049 if (dmxLocal
->type
!= DMX_LOCAL_MOUSE
)
1053 priv
->captured
= 2; /* Ungrab only after proximal events. */
1054 XRaiseWindow(priv
->display
, priv
->window
);
1055 XSync(priv
->display
, False
); /* Not a backend display */
1056 while (XCheckTypedEvent(priv
->display
, MotionNotify
, &X
)) {
1057 DMXDBG3(" Ignoring motion to %d %d after capture on %s\n",
1058 X
.xmotion
.x
, X
.xmotion
.y
, dmxInput
->name
);
1060 XWarpPointer(priv
->display
, None
,
1061 priv
->window
, 0, 0, 0, 0, priv
->curX
, priv
->curY
);
1062 XSync(priv
->display
, False
); /* Not a backend display */
1065 dmxConsoleUpdateFineCursor(priv
);
1069 /** Undo the capture that was done by #dmxConsoleCapture. */
1071 dmxConsoleUncapture(DMXInputInfo
* dmxInput
)
1075 DMXDBG0("dmxConsoleUncapture\n");
1076 dmxSync(NULL
, TRUE
);
1077 for (i
= 0; i
< dmxInput
->numDevs
; i
++) {
1078 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[i
];
1079 myPrivate
*priv
= dmxLocal
->private;
1081 if (dmxLocal
->extType
!= DMX_LOCAL_TYPE_CONSOLE
)
1083 if (dmxLocal
->type
!= DMX_LOCAL_MOUSE
)
1085 if (!priv
->captured
)
1088 XSync(priv
->display
, False
); /* Not a backend display */