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 */
119 static int scalex(myPrivate
*priv
, int x
)
121 return (int)((x
* priv
->xScale
) + .5);
124 static int scaley(myPrivate
*priv
, int y
)
126 return (int)((y
* priv
->yScale
) + .5);
129 static int unscalex(myPrivate
*priv
, int x
)
131 return (int)((x
/ priv
->xScale
) + .5);
134 static int unscaley(myPrivate
*priv
, int y
)
136 return (int)((y
/ priv
->yScale
) + .5);
139 /** Create the private area for \a pDevice. */
140 pointer
dmxConsoleCreatePrivate(DeviceIntPtr pDevice
)
142 GETDMXLOCALFROMPDEVICE
;
143 myPrivate
*priv
= xalloc(sizeof(*priv
));
144 memset(priv
, 0, sizeof(*priv
));
145 priv
->dmxLocal
= dmxLocal
;
149 /** If \a private is non-NULL, free its associated memory. */
150 void dmxConsoleDestroyPrivate(pointer
private)
152 if (private) xfree(private);
155 static void dmxConsoleDrawFineCursor(myPrivate
*priv
, XRectangle
*rect
)
160 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
161 x
= scalex(priv
, priv
->globalX
) - size
,
162 scaley(priv
, priv
->globalY
),
163 scalex(priv
, priv
->globalX
) + size
,
164 scaley(priv
, priv
->globalY
));
165 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
166 scalex(priv
, priv
->globalX
),
167 y
= scaley(priv
, priv
->globalY
) - size
,
168 scalex(priv
, priv
->globalX
),
169 scaley(priv
, priv
->globalY
) + size
);
171 XDrawLine(priv
->display
, priv
->pixmap
, priv
->gcCur
,
172 scalex(priv
, priv
->globalX
) - (int)(size
/ 1.4),
173 scaley(priv
, priv
->globalY
) - (int)(size
/ 1.4),
174 scalex(priv
, priv
->globalX
) + (int)(size
/ 1.4),
175 scaley(priv
, priv
->globalY
) + (int)(size
/ 1.4));
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));
185 rect
->width
= 2 * size
;
186 rect
->height
= 2 * size
;
190 static void dmxConsoleDrawWindows(pointer
private)
192 GETONLYPRIVFROMPRIVATE
;
193 Display
*dpy
= priv
->display
;
195 Region whole
, used
, avail
;
198 whole
= XCreateRegion();
199 used
= XCreateRegion();
200 avail
= XCreateRegion();
203 rect
.width
= priv
->consWidth
;
204 rect
.height
= priv
->consHeight
;
205 XUnionRectWithRegion(&rect
, whole
, whole
);
207 for (i
= 0; i
< dmxNumScreens
; i
++) {
208 WindowPtr pRoot
= WindowTable
[i
];
212 dmxLog(dmxDebug
, "%lu %p %p %p 2\n",
214 pRoot
->parent
, pRoot
->firstChild
, pRoot
->lastChild
);
217 for (pChild
= pRoot
->firstChild
; pChild
; pChild
= pChild
->nextSib
) {
219 && pChild
->realized
) {
221 dmxLog(dmxDebug
, " %p %d,%d %dx%d %d %d %d RECTS\n",
225 pChild
->drawable
.width
,
226 pChild
->drawable
.height
,
228 pChild
->overrideRedirect
,
229 REGION_NUM_RECTS(&pChild
->clipList
));
231 rect
.x
= scalex(priv
, pChild
->drawable
.x
232 + dixScreenOrigins
[i
].x
);
233 rect
.y
= scaley(priv
, pChild
->drawable
.y
234 + dixScreenOrigins
[i
].y
);
235 rect
.width
= scalex(priv
, pChild
->drawable
.width
);
236 rect
.height
= scaley(priv
, pChild
->drawable
.height
);
237 XDrawRectangle(dpy
, priv
->pixmap
, priv
->gc
,
238 rect
.x
, rect
.y
, rect
.width
, rect
.height
);
239 XUnionRectWithRegion(&rect
, used
, used
);
240 XSubtractRegion(whole
, used
, avail
);
241 XSetRegion(dpy
, priv
->gc
, avail
);
245 if (!noPanoramiXExtension
) break; /* Screen 0 valid with Xinerama */
248 XDestroyRegion(avail
);
249 XDestroyRegion(used
);
250 XDestroyRegion(whole
);
251 XSetClipMask(dpy
, priv
->gc
, None
);
254 static void dmxConsoleDraw(myPrivate
*priv
, int updateCursor
, int update
)
257 Display
*dpy
= priv
->display
;
260 XFillRectangle(dpy
, priv
->pixmap
, priv
->gc
, 0, 0,
261 priv
->consWidth
, priv
->consHeight
);
263 for (i
= 0; i
< dmxNumScreens
; i
++) {
264 DMXScreenInfo
*dmxScreen
= &dmxScreens
[i
];
265 XFillRectangle(dpy
, priv
->pixmap
,
266 dmxScreen
->beDisplay
? priv
->gcRev
: priv
->gcDet
,
267 scalex(priv
, dixScreenOrigins
[i
].x
),
268 scaley(priv
, dixScreenOrigins
[i
].y
),
269 scalex(priv
, screenInfo
.screens
[i
]->width
),
270 scaley(priv
, screenInfo
.screens
[i
]->height
));
272 for (i
= 0; i
< dmxNumScreens
; i
++) {
273 XDrawRectangle(dpy
, priv
->pixmap
, priv
->gc
,
274 scalex(priv
, dixScreenOrigins
[i
].x
),
275 scaley(priv
, dixScreenOrigins
[i
].y
),
276 scalex(priv
, screenInfo
.screens
[i
]->width
),
277 scaley(priv
, screenInfo
.screens
[i
]->height
));
279 if (dmxInput
->windows
) dmxConsoleDrawWindows(priv
);
280 if (priv
->fine
&& updateCursor
) dmxConsoleDrawFineCursor(priv
, 0);
282 XCopyArea(priv
->display
, priv
->pixmap
, priv
->window
, priv
->gc
,
283 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
284 XSync(priv
->display
, False
); /* Not a backend display */
288 static void dmxConsoleClearCursor(myPrivate
*priv
, int x
, int y
,
291 int cw
= 14, ch
= 14; /* Clear width and height */
293 rect
->x
= scalex(priv
, x
) - cw
/2;
294 rect
->y
= scaley(priv
, y
) - ch
/2;
297 XSetClipRectangles(priv
->display
, priv
->gc
, 0, 0, rect
, 1, Unsorted
);
298 XSetClipRectangles(priv
->display
, priv
->gcDet
, 0, 0, rect
, 1, Unsorted
);
299 XSetClipRectangles(priv
->display
, priv
->gcRev
, 0, 0, rect
, 1, Unsorted
);
300 dmxConsoleDraw(priv
, 0, 0);
301 XSetClipMask(priv
->display
, priv
->gc
, None
);
302 XSetClipMask(priv
->display
, priv
->gcDet
, None
);
303 XSetClipMask(priv
->display
, priv
->gcRev
, None
);
307 static void dmxConsoleUpdateFineCursor(myPrivate
*priv
)
312 dmxConsoleClearCursor(priv
, priv
->globalX
, priv
->globalY
, &rects
[0]);
313 if (priv
->dmxLocal
->sendsCore
) {
314 dmxGetGlobalPosition(&priv
->globalX
, &priv
->globalY
);
316 priv
->globalX
= priv
->dmxLocal
->lastX
;
317 priv
->globalY
= priv
->dmxLocal
->lastY
;
320 priv
->lastX
= scalex(priv
, priv
->width
/ 2);
321 priv
->lastY
= scaley(priv
, priv
->height
/ 2);
323 /* Compute new warp position, which may be
324 outside the window */
325 if (priv
->globalX
< 1 || priv
->globalX
>= priv
->width
) {
326 if (priv
->globalX
< 1) priv
->lastX
= 0;
327 else priv
->lastX
= scalex(priv
, priv
->width
);
328 priv
->lastY
= scaley(priv
, priv
->globalY
);
331 if (priv
->globalY
< 1 || priv
->globalY
>= priv
->height
) {
332 if (priv
->globalY
< 1) priv
->lastY
= 0;
333 else priv
->lastY
= scaley(priv
, priv
->height
);
334 priv
->lastX
= scalex(priv
, priv
->globalX
);
338 /* Draw pseudo cursor in window */
339 dmxConsoleDrawFineCursor(priv
, &rects
[1]);
341 XSetClipRectangles(priv
->display
, priv
->gc
, 0, 0, rects
, 2, Unsorted
);
342 XCopyArea(priv
->display
, priv
->pixmap
, priv
->window
, priv
->gc
,
343 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
344 XSetClipMask(priv
->display
, priv
->gc
, None
);
346 DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
347 priv
->lastX
, priv
->lastY
);
348 XWarpPointer(priv
->display
, priv
->window
, priv
->window
,
349 0, 0, 0, 0, priv
->lastX
, priv
->lastY
);
350 XSync(priv
->display
, False
); /* Not a backend display */
354 while (XCheckMaskEvent(priv
->display
, PointerMotionMask
, &X
)) {
355 if (X
.type
== MotionNotify
) {
356 if (X
.xmotion
.x
!= priv
->lastX
|| X
.xmotion
.y
!= priv
->lastY
) {
357 DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
358 X
.xmotion
.x
, X
.xmotion
.y
,
359 priv
->lastX
, priv
->lastY
);
362 dmxLog(dmxInfo
, "Ignoring event (%d): %s ****************\n",
363 X
.type
, dmxEventName(X
.type
));
367 DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
368 priv
->lastX
, priv
->lastY
,
369 scalex(priv
, priv
->width
),
370 scaley(priv
, priv
->height
),
371 priv
->globalX
, priv
->globalY
);
374 /** Whenever the window layout (size, position, stacking order) might be
375 * changed, this routine is called with the \a pWindow that changed and
376 * the \a type of change. This routine is called in a conservative
377 * fashion: the actual layout of the windows of the screen might not
378 * have had any human-visible changes. */
379 void dmxConsoleUpdateInfo(pointer
private, DMXUpdateType type
,
382 GETONLYPRIVFROMPRIVATE
;
383 dmxConsoleDraw(priv
, 1, 1);
386 static void dmxConsoleMoveAbsolute(myPrivate
*priv
, int x
, int y
,
387 DevicePtr pDev
, dmxMotionProcPtr motion
,
390 int tmpX
, tmpY
, v
[2];
392 tmpX
= unscalex(priv
, x
);
393 tmpY
= unscalex(priv
, y
);
394 DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
395 x
, y
, tmpX
, tmpY
, priv
->curX
, priv
->curY
);
396 if (tmpX
== priv
->curX
&& tmpY
== priv
->curY
) return;
397 v
[0] = unscalex(priv
, x
);
398 v
[1] = unscaley(priv
, y
);
399 motion(pDev
, v
, 0, 2, DMX_ABSOLUTE_CONFINED
, block
);
400 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
403 static void dmxConsoleMoveRelative(myPrivate
*priv
, int x
, int y
,
404 DevicePtr pDev
, dmxMotionProcPtr motion
,
408 /* Ignore the event generated from * warping back to middle */
409 if (x
== priv
->lastX
&& y
== priv
->lastY
) return;
410 v
[0] = priv
->lastX
- x
;
411 v
[1] = priv
->lastY
- y
;
412 motion(pDev
, v
, 0, 2, DMX_RELATIVE
, block
);
413 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
416 /** This routine gets called from #dmxCoreMotion for each motion. This
417 * allows the console's notion of the cursor postion to change when
418 * another input device actually caused the change. */
419 void dmxConsoleUpdatePosition(pointer
private, int x
, int y
)
421 GETONLYPRIVFROMPRIVATE
;
423 Display
*dpy
= priv
->display
;
424 static unsigned long dmxGeneration
= 0;
427 tmpX
= scalex(priv
, x
);
428 tmpY
= scaley(priv
, y
);
429 DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
430 x
, y
, tmpX
, tmpY
, priv
->consWidth
, priv
->consHeight
);
432 if (priv
->fine
) dmxConsoleUpdateFineCursor(priv
);
433 if (tmpX
!= priv
->curX
|| tmpY
!= priv
->curY
) {
434 if (tmpX
< 0) tmpX
= 0;
435 if (tmpY
< 0) tmpY
= 0;
436 if (tmpX
>= priv
->consWidth
) tmpX
= priv
->consWidth
- 1;
437 if (tmpY
>= priv
->consHeight
) tmpY
= priv
->consHeight
- 1;
441 DMXDBG2(" WARP B %d %d\n", priv
->curX
, priv
->curY
);
442 XWarpPointer(dpy
, priv
->window
,
443 priv
->window
, 0, 0, 0, 0, tmpX
, tmpY
);
444 XSync(dpy
, False
); /* Not a backend display */
448 if (dmxGeneration
!= serverGeneration
) {
449 dmxGeneration
= serverGeneration
;
450 dmxConsoleDraw(priv
, 1, 1);
454 /** Collect all pending events from the console's display. Plase these
455 * events on the server event queue using the \a motion and \a enqueue
456 * routines. The \a checkspecial routine is used to check for special
457 * keys that need handling. \a block tells if signals should be blocked
458 * when updating the event queue. */
459 void dmxConsoleCollectEvents(DevicePtr pDev
,
460 dmxMotionProcPtr motion
,
461 dmxEnqueueProcPtr enqueue
,
462 dmxCheckSpecialProcPtr checkspecial
,
467 Display
*dpy
= priv
->display
;
468 Window win
= priv
->window
;
469 int width
= priv
->width
;
470 int height
= priv
->height
;
472 XSetWindowAttributes attribs
;
473 static int rInitialized
= 0;
476 static int raising
= 0, raiseX
, raiseY
; /* FIXME */
478 while (XPending(dpy
)) {
481 case VisibilityNotify
:
484 DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
486 X
.xexpose
.x
, X
.xexpose
.y
,
487 X
.xexpose
.width
, X
.xexpose
.height
);
488 if (!rInitialized
++) r
= XCreateRegion();
489 rect
.x
= X
.xexpose
.x
;
490 rect
.y
= X
.xexpose
.y
;
491 rect
.width
= X
.xexpose
.width
;
492 rect
.height
= X
.xexpose
.height
;
493 XUnionRectWithRegion(&rect
, r
, r
);
494 if (X
.xexpose
.count
== 0) {
495 XSetRegion(dpy
, priv
->gc
, r
);
496 XSetRegion(dpy
, priv
->gcDet
, r
);
497 XSetRegion(dpy
, priv
->gcRev
, r
);
498 dmxConsoleDraw(priv
, 1, 1);
499 XSetClipMask(dpy
, priv
->gc
, None
);
500 XSetClipMask(dpy
, priv
->gcDet
, None
);
501 XSetClipMask(dpy
, priv
->gcRev
, None
);
507 DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
508 X
.xresizerequest
.width
, X
.xresizerequest
.height
);
509 priv
->consWidth
= X
.xresizerequest
.width
;
510 priv
->consHeight
= X
.xresizerequest
.height
;
511 priv
->xScale
= (double)priv
->consWidth
/ width
;
512 priv
->yScale
= (double)priv
->consHeight
/ height
;
513 attribs
.override_redirect
= True
;
514 XChangeWindowAttributes(dpy
, win
, CWOverrideRedirect
, &attribs
);
515 XResizeWindow(dpy
, win
, priv
->consWidth
, priv
->consHeight
);
516 XFreePixmap(dpy
, priv
->pixmap
);
517 priv
->pixmap
= XCreatePixmap(dpy
,
518 RootWindow(dpy
, DefaultScreen(dpy
)),
521 DefaultDepth(dpy
,DefaultScreen(dpy
)));
522 dmxConsoleDraw(priv
, 1, 1);
523 attribs
.override_redirect
= False
;
524 XChangeWindowAttributes(dpy
, win
, CWOverrideRedirect
, &attribs
);
527 DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
528 X
.xcrossing
.x
, X
.xcrossing
.y
, raising
, priv
->fine
);
529 if (!priv
->captured
) dmxCommonRestoreState(priv
);
531 dmxConsoleUncapture(dmxInput
);
532 dmxCommonRestoreState(priv
);
536 DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
537 X
.xcrossing
.x
, X
.xcrossing
.y
, raising
, priv
->fine
,
538 priv
->curX
, priv
->curY
);
539 dmxCommonSaveState(priv
);
542 dmxConsoleMoveAbsolute(priv
, raiseX
, raiseY
,
543 priv
->mou
, motion
, block
);
546 /* The raise will generate an event near the center,
547 * which is not where the cursor should be. So we
548 * save the real position, do the raise, and move
549 * the cursor here again after the raise generates
552 raiseX
= X
.xcrossing
.x
;
553 raiseY
= X
.xcrossing
.y
;
554 XRaiseWindow(dpy
, priv
->window
);
556 XSync(dpy
, False
); /* Not a backend display */
557 if (!X
.xcrossing
.x
&& !X
.xcrossing
.y
)
558 dmxConsoleMoveAbsolute(priv
, priv
->curX
, priv
->curY
,
559 priv
->mou
, motion
, block
);
563 if (priv
->curX
== X
.xmotion
.x
&& priv
->curY
== X
.xmotion
.y
)
565 if (XPending(dpy
)) { /* do motion compression */
567 if (N
.type
== MotionNotify
) continue;
569 DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
570 X
.xmotion
.x
, X
.xmotion
.y
);
573 dmxConsoleMoveAbsolute(priv
, raiseX
, raiseY
,
574 priv
->mou
, motion
, block
);
577 dmxConsoleMoveRelative(priv
, X
.xmotion
.x
, X
.xmotion
.y
,
578 priv
->mou
, motion
, block
);
580 dmxConsoleMoveAbsolute(priv
, X
.xmotion
.x
, X
.xmotion
.y
,
581 priv
->mou
, motion
, block
);
586 enqueue(priv
->kbd
, X
.type
, X
.xkey
.keycode
, 0, NULL
, block
);
589 /* Pass the whole event here, because
590 * this may be an extension event. */
591 enqueue(priv
->mou
, X
.type
, X
.xbutton
.button
, 0, &X
, block
);
597 static void dmxCloseConsole(myPrivate
*priv
)
600 dmxCommonRestoreState(priv
);
602 XFreeGC(priv
->display
, priv
->gc
);
603 XFreeGC(priv
->display
, priv
->gcDet
);
604 XFreeGC(priv
->display
, priv
->gcRev
);
605 XFreeGC(priv
->display
, priv
->gcCur
);
606 if (!dmxInput
->console
) XCloseDisplay(priv
->display
);
608 priv
->display
= NULL
;
611 static Bool
dmxCloseConsoleScreen(int idx
, ScreenPtr pScreen
)
613 myPrivate
*priv
, *last
;
615 for (last
= priv
= pScreen
->devPrivates
[dmxScreenPrivateIndex
].ptr
;
617 priv
= priv
->next
) dmxCloseConsole(last
= priv
);
619 DMX_UNWRAP(CloseScreen
, last
, pScreen
);
620 return pScreen
->CloseScreen(idx
, pScreen
);
623 static Cursor
dmxConsoleCreateEmptyCursor(myPrivate
*priv
)
625 char noCursorData
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
628 XColor color
, tmpColor
;
629 Display
*dpy
= priv
->display
;
631 /* Create empty cursor for window */
632 pixmap
= XCreateBitmapFromData(priv
->display
, priv
->window
,
634 if (!XAllocNamedColor(dpy
, DefaultColormap(dpy
, DefaultScreen(dpy
)),
638 dmxLog(dmxFatal
, "Cannot allocate color for cursor\n");
639 cursor
= XCreatePixmapCursor(dpy
, pixmap
, pixmap
, &color
, &color
, 0, 0);
640 XFreePixmap(dpy
, pixmap
);
644 static void dmxConsoleComputeWidthHeight(myPrivate
*priv
,
645 int *width
, int *height
,
646 double *xScale
, double *yScale
,
647 int *consWidth
, int *consHeight
)
650 Display
*dpy
= priv
->display
;
657 screen
= DefaultScreen(dpy
);
658 *consWidth
= DisplayWidth(dpy
, screen
) * CONSOLE_NUM
/ CONSOLE_DEN
;
659 *consHeight
= DisplayHeight(dpy
, screen
) * CONSOLE_NUM
/ CONSOLE_DEN
;
661 if (*consWidth
< 1) *consWidth
= 1;
662 if (*consHeight
< 1) *consHeight
= 1;
665 /* Always keep the console size similar
666 * to the global bounding box. */
667 *width
= dmxGlobalWidth
;
668 *height
= dmxGlobalHeight
;
670 /* Make the console window as big as
671 * possible by computing the visible
673 for (i
= 0; i
< dmxNumScreens
; i
++) {
674 if (dixScreenOrigins
[i
].x
+screenInfo
.screens
[i
]->width
> *width
)
675 *width
= dixScreenOrigins
[i
].x
+screenInfo
.screens
[i
]->width
;
677 if (dixScreenOrigins
[i
].y
+screenInfo
.screens
[i
]->height
> *height
)
678 *height
= dixScreenOrigins
[i
].y
+screenInfo
.screens
[i
]->height
;
682 if ((double)*consWidth
/ *width
< (double)*consHeight
/ *height
)
683 *xScale
= *yScale
= (double)*consWidth
/ *width
;
685 *xScale
= *yScale
= (double)*consHeight
/ *height
;
687 *consWidth
= scalex(priv
, *width
);
688 *consHeight
= scaley(priv
, *height
);
689 if (*consWidth
< 1) *consWidth
= 1;
690 if (*consHeight
< 1) *consHeight
= 1;
693 /** Re-initialized the console device described by \a pDev (after a
695 void dmxConsoleReInit(DevicePtr pDev
)
700 if (!priv
|| !priv
->initialized
) return;
703 dmxConsoleComputeWidthHeight(priv
,
704 &priv
->width
, &priv
->height
,
705 &priv
->xScale
, &priv
->yScale
,
706 &priv
->consWidth
, &priv
->consHeight
);
707 XResizeWindow(dpy
, priv
->window
, priv
->consWidth
, priv
->consHeight
);
708 XFreePixmap(dpy
, priv
->pixmap
);
709 priv
->pixmap
= XCreatePixmap(dpy
,
710 RootWindow(dpy
, DefaultScreen(dpy
)),
713 DefaultDepth(dpy
,DefaultScreen(dpy
)));
714 dmxConsoleDraw(priv
, 1, 1);
717 /** Initialized the console device described by \a pDev. */
718 void dmxConsoleInit(DevicePtr pDev
)
721 DMXInputInfo
*dmxInput
= &dmxInputs
[dmxLocal
->inputIdx
];
724 XSetWindowAttributes attribs
;
729 XClassHint class_hints
;
732 if (dmxLocal
->type
== DMX_LOCAL_MOUSE
) priv
->mou
= pDev
;
733 if (dmxLocal
->type
== DMX_LOCAL_KEYBOARD
) priv
->kbd
= pDev
;
734 if (priv
->initialized
++) return; /* Only do once for mouse/keyboard pair */
736 if (!(dpy
= priv
->display
= XOpenDisplay(dmxInput
->name
)))
738 "dmxOpenConsole: cannot open console display %s\n",
741 /* Set up defaults */
742 dmxConsoleComputeWidthHeight(priv
,
743 &priv
->width
, &priv
->height
,
744 &priv
->xScale
, &priv
->yScale
,
745 &priv
->consWidth
, &priv
->consHeight
);
747 /* Private initialization using computed values or constants. */
748 screen
= DefaultScreen(dpy
);
749 priv
->initPointerX
= scalex(priv
, priv
->width
/ 2);
750 priv
->initPointerY
= scaley(priv
, priv
->height
/ 2);
751 priv
->eventMask
= (ButtonPressMask
759 | ResizeRedirectMask
);
761 mask
= CWBackPixel
| CWEventMask
| CWColormap
| CWOverrideRedirect
;
762 attribs
.colormap
= DefaultColormap(dpy
, screen
);
763 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_BG_COLOR
, &color
)
764 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
765 attribs
.background_pixel
= color
.pixel
;
767 attribs
.background_pixel
= WhitePixel(dpy
, screen
);
769 attribs
.event_mask
= priv
->eventMask
;
770 attribs
.override_redirect
= False
;
772 win
= priv
->window
= XCreateWindow(dpy
,
773 RootWindow(dpy
, screen
),
774 0, 0, priv
->consWidth
, priv
->consHeight
,
776 DefaultDepth(dpy
, screen
),
778 DefaultVisual(dpy
, screen
),
780 priv
->pixmap
= XCreatePixmap(dpy
, RootWindow(dpy
, screen
),
781 priv
->consWidth
, priv
->consHeight
,
782 DefaultDepth(dpy
, screen
));
784 /* Set up properties */
785 XStoreName(dpy
, win
, DMX_CONSOLE_NAME
);
786 class_hints
.res_name
= DMX_RES_NAME
;
787 class_hints
.res_class
= DMX_RES_CLASS
;
788 XSetClassHint(dpy
, win
, &class_hints
);
792 XMapWindow(dpy
, win
);
795 priv
->cursorNormal
= XCreateFontCursor(dpy
, XC_circle
);
796 priv
->cursorGrabbed
= XCreateFontCursor(dpy
, XC_spider
);
797 priv
->cursorEmpty
= dmxConsoleCreateEmptyCursor(priv
);
798 XDefineCursor(dpy
, priv
->window
, priv
->cursorNormal
);
801 mask
= (GCFunction
| GCPlaneMask
| GCClipMask
| GCForeground
|
802 GCBackground
| GCLineWidth
| GCLineStyle
| GCCapStyle
|
803 GCFillStyle
| GCGraphicsExposures
);
804 gcvals
.function
= GXcopy
;
805 gcvals
.plane_mask
= AllPlanes
;
806 gcvals
.clip_mask
= None
;
807 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_FG_COLOR
, &color
)
808 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
809 gcvals
.foreground
= color
.pixel
;
811 gcvals
.foreground
= BlackPixel(dpy
, screen
);
812 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_BG_COLOR
, &color
)
813 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
814 gcvals
.background
= color
.pixel
;
816 gcvals
.background
= WhitePixel(dpy
, screen
);
817 gcvals
.line_width
= 0;
818 gcvals
.line_style
= LineSolid
;
819 gcvals
.cap_style
= CapNotLast
;
820 gcvals
.fill_style
= FillSolid
;
821 gcvals
.graphics_exposures
= False
;
823 priv
->gc
= XCreateGC(dpy
, win
, mask
, &gcvals
);
825 tmp
= gcvals
.foreground
;
826 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_DET_COLOR
, &color
)
827 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
828 gcvals
.foreground
= color
.pixel
;
830 gcvals
.foreground
= BlackPixel(dpy
, screen
);
831 priv
->gcDet
= XCreateGC(dpy
, win
, mask
, &gcvals
);
832 gcvals
.foreground
= tmp
;
834 tmp
= gcvals
.background
;
835 gcvals
.background
= gcvals
.foreground
;
836 gcvals
.foreground
= tmp
;
837 priv
->gcRev
= XCreateGC(dpy
, win
, mask
, &gcvals
);
839 gcvals
.background
= gcvals
.foreground
;
840 if (XParseColor(dpy
, attribs
.colormap
, CONSOLE_SCREEN_CUR_COLOR
, &color
)
841 && XAllocColor(dpy
, attribs
.colormap
, &color
)) {
842 gcvals
.foreground
= color
.pixel
;
844 gcvals
.foreground
= BlackPixel(dpy
, screen
);
845 priv
->gcCur
= XCreateGC(dpy
, win
, mask
, &gcvals
);
847 dmxConsoleDraw(priv
, 1, 1);
849 if (screenInfo
.screens
[0]->devPrivates
[dmxScreenPrivateIndex
].ptr
)
850 priv
->next
= (screenInfo
.screens
[0]
851 ->devPrivates
[dmxScreenPrivateIndex
].ptr
);
853 DMX_WRAP(CloseScreen
, dmxCloseConsoleScreen
,
854 priv
, screenInfo
.screens
[0]);
855 screenInfo
.screens
[0]->devPrivates
[dmxScreenPrivateIndex
].ptr
= priv
;
858 /** Fill in the \a info structure for the specified \a pDev. Only used
860 void dmxConsoleMouGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
864 info
->buttonClass
= 1;
865 dmxCommonMouGetMap(pDev
, info
->map
, &info
->numButtons
);
866 info
->valuatorClass
= 1;
867 info
->numRelAxes
= 2;
870 /* max possible console window size: */
871 info
->maxval
[0] = DisplayWidth(priv
->display
, DefaultScreen(priv
->display
));
872 info
->maxval
[1] = DisplayHeight(priv
->display
, DefaultScreen(priv
->display
));
876 info
->ptrFeedbackClass
= 1;
879 /** Fill in the \a info structure for the specified \a pDev. Only used
881 void dmxConsoleKbdGetInfo(DevicePtr pDev
, DMXLocalInitInfoPtr info
)
883 dmxCommonKbdGetInfo(pDev
, info
);
886 dmxCommonKbdGetMap(pDev
, &info
->keySyms
, info
->modMap
);
888 info
->focusClass
= 1;
889 info
->kbdFeedbackClass
= 1;
892 /** Handle special console-only keys. */
893 int dmxConsoleFunctions(pointer
private, DMXFunctionType function
)
895 GETONLYPRIVFROMPRIVATE
;
897 Display
*dpy
= priv
->display
;
900 case DMX_FUNCTION_FINE
:
903 dmxConsoleClearCursor(priv
, priv
->globalX
, priv
->globalY
, &rect
);
904 XSetClipRectangles(dpy
, priv
->gc
, 0, 0, &rect
, 1, Unsorted
);
905 XCopyArea(dpy
, priv
->pixmap
, priv
->window
, priv
->gc
,
906 0, 0, priv
->consWidth
, priv
->consHeight
, 0, 0);
907 XSetClipMask(dpy
, priv
->gc
, None
);
909 XDefineCursor(dpy
, priv
->window
,
911 ? priv
->cursorGrabbed
912 : priv
->cursorNormal
);
913 XWarpPointer(dpy
, priv
->window
, priv
->window
,
915 scalex(priv
, priv
->globalX
),
916 scaley(priv
, priv
->globalY
));
917 XSync(dpy
, False
); /* Not a backend display */
920 XRaiseWindow(dpy
, priv
->window
);
921 XDefineCursor(dpy
, priv
->window
, priv
->cursorEmpty
);
922 dmxConsoleUpdateFineCursor(priv
);
925 case DMX_FUNCTION_GRAB
:
927 XUngrabKeyboard(dpy
, CurrentTime
);
928 XUngrabPointer(dpy
, CurrentTime
);
929 XDefineCursor(dpy
, priv
->window
,
932 : priv
->cursorNormal
);
934 if (XGrabPointer(dpy
, priv
->window
, True
,
935 0, GrabModeAsync
, GrabModeAsync
, priv
->window
,
936 None
, CurrentTime
)) {
937 dmxLog(dmxError
, "XGrabPointer failed\n");
940 if (XGrabKeyboard(dpy
, priv
->window
, True
,
941 GrabModeAsync
, GrabModeAsync
, CurrentTime
)) {
942 dmxLog(dmxError
, "XGrabKeyboard failed\n");
943 XUngrabPointer(dpy
, CurrentTime
);
946 XDefineCursor(dpy
, priv
->window
,
949 : priv
->cursorGrabbed
);
951 priv
->grabbed
= !priv
->grabbed
;
952 if (priv
->fine
) dmxConsoleUpdateFineCursor(priv
);
954 case DMX_FUNCTION_TERMINATE
:
961 static void dmxDump(void)
964 DMXInputInfo
*dmxInput
;
967 for (i
= 0, dmxInput
= &dmxInputs
[0]; i
< dmxNumInputs
; i
++, dmxInput
++) {
968 for (j
= 0; j
< dmxInput
->numDevs
; j
++) {
969 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[j
];
970 myPrivate
*priv
= dmxLocal
->private;
973 && XCheckTypedEvent(priv
->display
, MotionNotify
, &X
)) {
974 DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
975 dmxInput
->name
, j
, X
.type
, dmxEventName(X
.type
));
981 /** This routine is used to warp the pointer into the console window
982 * from anywhere on the screen. It is used when backend and console
983 * input are both being taken from the same X display. */
984 void dmxConsoleCapture(DMXInputInfo
*dmxInput
)
989 DMXDBG0("dmxConsoleCapture\n");
991 for (i
= 0; i
< dmxInput
->numDevs
; i
++) {
992 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[i
];
993 myPrivate
*priv
= dmxLocal
->private;
994 if (dmxLocal
->extType
!= DMX_LOCAL_TYPE_CONSOLE
) continue;
995 if (dmxLocal
->type
!= DMX_LOCAL_MOUSE
) continue;
996 if (priv
->captured
) continue;
997 priv
->captured
= 2; /* Ungrab only after proximal events. */
998 XRaiseWindow(priv
->display
, priv
->window
);
999 XSync(priv
->display
, False
); /* Not a backend display */
1000 while (XCheckTypedEvent(priv
->display
, MotionNotify
, &X
)) {
1001 DMXDBG3(" Ignoring motion to %d %d after capture on %s\n",
1002 X
.xmotion
.x
, X
.xmotion
.y
, dmxInput
->name
);
1004 XWarpPointer(priv
->display
, None
,
1005 priv
->window
, 0, 0, 0, 0, priv
->curX
, priv
->curY
);
1006 XSync(priv
->display
, False
); /* Not a backend display */
1008 if (priv
->fine
) dmxConsoleUpdateFineCursor(priv
);
1012 /** Undo the capture that was done by #dmxConsoleCapture. */
1013 void dmxConsoleUncapture(DMXInputInfo
*dmxInput
)
1017 DMXDBG0("dmxConsoleUncapture\n");
1018 dmxSync(NULL
, TRUE
);
1019 for (i
= 0; i
< dmxInput
->numDevs
; i
++) {
1020 DMXLocalInputInfoPtr dmxLocal
= dmxInput
->devs
[i
];
1021 myPrivate
*priv
= dmxLocal
->private;
1022 if (dmxLocal
->extType
!= DMX_LOCAL_TYPE_CONSOLE
) continue;
1023 if (dmxLocal
->type
!= DMX_LOCAL_MOUSE
) continue;
1024 if (!priv
->captured
) continue;
1026 XSync(priv
->display
, False
); /* Not a backend display */