First import
[xorg_rtime.git] / xorg-server-1.4 / hw / dmx / input / dmxconsole.c
blobcc820a204f82d41f13cd0905a6acdb10e5fe8a53
1 /*
2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved.
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
25 * SOFTWARE.
29 * Authors:
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
36 /** \file
38 * This file implements the console input devices.
41 #ifdef HAVE_DMX_CONFIG_H
42 #include <dmx-config.h>
43 #endif
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"
53 #include "dmxcb.h"
54 #include "dmxsync.h"
56 #include "inputstr.h"
57 #include "input.h"
58 #include "mipointer.h"
59 #include "windowstr.h"
61 #define CONSOLE_NUM 3
62 #define CONSOLE_DEN 4
63 #define DMX_CONSOLE_NAME "DMX Console"
64 #define DMX_RES_NAME "Xdmx"
65 #define DMX_RES_CLASS "XDmx"
66 #define CONSOLE_BG_COLOR "gray75"
67 #define CONSOLE_FG_COLOR "black"
68 #define CONSOLE_SCREEN_BG_COLOR "white"
69 #define CONSOLE_SCREEN_FG_COLOR "black"
70 #define CONSOLE_SCREEN_DET_COLOR "gray75"
71 #define CONSOLE_SCREEN_CUR_COLOR "red"
73 #if DMX_CONSOLE_DEBUG
74 #define DMXDBG0(f) dmxLog(dmxDebug,f)
75 #define DMXDBG1(f,a) dmxLog(dmxDebug,f,a)
76 #define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
77 #define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c)
78 #define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d)
79 #define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
80 #define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g)
81 #define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
82 #else
83 #define DMXDBG0(f)
84 #define DMXDBG1(f,a)
85 #define DMXDBG2(f,a,b)
86 #define DMXDBG3(f,a,b,c)
87 #define DMXDBG4(f,a,b,c,d)
88 #define DMXDBG5(f,a,b,c,d,e)
89 #define DMXDBG6(f,a,b,c,d,e,g)
90 #define DMXDBG7(f,a,b,c,d,e,g,h)
91 #endif
93 /* Private area for consoles. */
94 typedef struct _myPrivate {
95 DMX_COMMON_PRIVATE;
96 int lastX;
97 int lastY;
98 int globalX;
99 int globalY;
100 int curX;
101 int curY;
102 int width;
103 int height;
104 int consWidth;
105 int consHeight;
106 double xScale;
107 double yScale;
108 XlibGC gc, gcDet, gcRev, gcCur;
109 int grabbed, fine, captured;
110 Cursor cursorNormal, cursorGrabbed, cursorEmpty;
111 Pixmap pixmap;
113 CloseScreenProcPtr CloseScreen;
114 struct _myPrivate *next; /* for closing multiple consoles */
115 int initialized;
116 DevicePtr mou, kbd;
117 } myPrivate;
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;
146 return priv;
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)
157 int size = 6;
158 int x, y;
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);
170 if (priv->grabbed) {
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));
182 if (rect) {
183 rect->x = x;
184 rect->y = y;
185 rect->width = 2 * size;
186 rect->height = 2 * size;
190 static void dmxConsoleDrawWindows(pointer private)
192 GETONLYPRIVFROMPRIVATE;
193 Display *dpy = priv->display;
194 int i;
195 Region whole, used, avail;
196 XRectangle rect;
198 whole = XCreateRegion();
199 used = XCreateRegion();
200 avail = XCreateRegion();
201 rect.x = 0;
202 rect.y = 0;
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];
209 WindowPtr pChild;
211 #if DMX_WINDOW_DEBUG
212 dmxLog(dmxDebug, "%lu %p %p %p 2\n",
213 pRoot->drawable.id,
214 pRoot->parent, pRoot->firstChild, pRoot->lastChild);
215 #endif
217 for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
218 if (pChild->mapped
219 && pChild->realized) {
220 #if DMX_WINDOW_DEBUG
221 dmxLog(dmxDebug, " %p %d,%d %dx%d %d %d %d RECTS\n",
222 pChild,
223 pChild->drawable.x,
224 pChild->drawable.y,
225 pChild->drawable.width,
226 pChild->drawable.height,
227 pChild->visibility,
228 pChild->overrideRedirect,
229 REGION_NUM_RECTS(&pChild->clipList));
230 #endif
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);
244 #ifdef PANORAMIX
245 if (!noPanoramiXExtension) break; /* Screen 0 valid with Xinerama */
246 #endif
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)
256 GETDMXINPUTFROMPRIV;
257 Display *dpy = priv->display;
258 int i;
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);
281 if (update) {
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,
289 XRectangle *rect)
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;
295 rect->width = cw;
296 rect->height = ch;
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)
309 int leave = 0;
310 XRectangle rects[2];
312 dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
313 if (priv->dmxLocal->sendsCore) {
314 dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
315 } else {
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);
329 ++leave;
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);
335 ++leave;
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 */
352 if (leave) {
353 XEvent X;
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);
361 } else {
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,
380 WindowPtr pWindow)
382 GETONLYPRIVFROMPRIVATE;
383 dmxConsoleDraw(priv, 1, 1);
386 static void dmxConsoleMoveAbsolute(myPrivate *priv, int x, int y,
387 DevicePtr pDev, dmxMotionProcPtr motion,
388 DMXBlockType block)
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,
405 DMXBlockType block)
407 int v[2];
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;
422 int tmpX, tmpY;
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;
438 priv->curX = tmpX;
439 priv->curY = tmpY;
440 if (!priv->fine) {
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,
463 DMXBlockType block)
465 GETPRIVFROMPDEV;
466 GETDMXINPUTFROMPRIV;
467 Display *dpy = priv->display;
468 Window win = priv->window;
469 int width = priv->width;
470 int height = priv->height;
471 XEvent X, N;
472 XSetWindowAttributes attribs;
473 static int rInitialized = 0;
474 static Region r;
475 XRectangle rect;
476 static int raising = 0, raiseX, raiseY; /* FIXME */
478 while (XPending(dpy)) {
479 XNextEvent(dpy, &X);
480 switch(X.type) {
481 case VisibilityNotify:
482 break;
483 case Expose:
484 DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
485 X.xexpose.count,
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);
502 XDestroyRegion(r);
503 rInitialized = 0;
505 break;
506 case ResizeRequest:
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)),
519 priv->consWidth,
520 priv->consHeight,
521 DefaultDepth(dpy,DefaultScreen(dpy)));
522 dmxConsoleDraw(priv, 1, 1);
523 attribs.override_redirect = False;
524 XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
525 break;
526 case LeaveNotify:
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);
530 else {
531 dmxConsoleUncapture(dmxInput);
532 dmxCommonRestoreState(priv);
534 break;
535 case EnterNotify:
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);
540 if (raising) {
541 raising = 0;
542 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
543 priv->mou, motion, block);
544 } else {
545 if (priv->fine) {
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
550 * the event. */
551 raising = 1;
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);
561 break;
562 case MotionNotify:
563 if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
564 continue;
565 if (XPending(dpy)) { /* do motion compression */
566 XPeekEvent(dpy, &N);
567 if (N.type == MotionNotify) continue;
569 DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
570 X.xmotion.x, X.xmotion.y);
571 if (raising) {
572 raising = 0;
573 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
574 priv->mou, motion, block);
575 } else {
576 if (priv->fine)
577 dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
578 priv->mou, motion, block);
579 else
580 dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
581 priv->mou, motion, block);
583 break;
584 case KeyPress:
585 case KeyRelease:
586 enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
587 break;
588 default:
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);
592 break;
597 static void dmxCloseConsole(myPrivate *priv)
599 GETDMXINPUTFROMPRIV;
600 dmxCommonRestoreState(priv);
601 if (priv->display) {
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;
616 priv;
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 };
626 Pixmap pixmap;
627 Cursor cursor;
628 XColor color, tmpColor;
629 Display *dpy = priv->display;
631 /* Create empty cursor for window */
632 pixmap = XCreateBitmapFromData(priv->display, priv->window,
633 noCursorData, 8, 8);
634 if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
635 "black",
636 &color,
637 &tmpColor))
638 dmxLog(dmxFatal, "Cannot allocate color for cursor\n");
639 cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0);
640 XFreePixmap(dpy, pixmap);
641 return cursor;
644 static void dmxConsoleComputeWidthHeight(myPrivate *priv,
645 int *width, int *height,
646 double *xScale, double *yScale,
647 int *consWidth, int *consHeight)
649 int screen;
650 Display *dpy = priv->display;
652 *width = 0;
653 *height = 0;
654 *xScale = 1.0;
655 *yScale = 1.0;
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;
664 #if 1
665 /* Always keep the console size similar
666 * to the global bounding box. */
667 *width = dmxGlobalWidth;
668 *height = dmxGlobalHeight;
669 #else
670 /* Make the console window as big as
671 * possible by computing the visible
672 * bounding box. */
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;
680 #endif
682 if ((double)*consWidth / *width < (double)*consHeight / *height)
683 *xScale = *yScale = (double)*consWidth / *width;
684 else
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
694 * reconfig). */
695 void dmxConsoleReInit(DevicePtr pDev)
697 GETPRIVFROMPDEV;
698 Display *dpy;
700 if (!priv || !priv->initialized) return;
701 dpy = priv->display;
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)),
711 priv->consWidth,
712 priv->consHeight,
713 DefaultDepth(dpy,DefaultScreen(dpy)));
714 dmxConsoleDraw(priv, 1, 1);
717 /** Initialized the console device described by \a pDev. */
718 void dmxConsoleInit(DevicePtr pDev)
720 GETPRIVFROMPDEV;
721 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
722 int screen;
723 unsigned long mask;
724 XSetWindowAttributes attribs;
725 Display *dpy;
726 Window win;
727 XGCValues gcvals;
728 XColor color;
729 XClassHint class_hints;
730 unsigned long tmp;
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)))
737 dmxLog(dmxFatal,
738 "dmxOpenConsole: cannot open console display %s\n",
739 dmxInput->name);
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
752 | ButtonReleaseMask
753 | PointerMotionMask
754 | EnterWindowMask
755 | LeaveWindowMask
756 | KeyPressMask
757 | KeyReleaseMask
758 | ExposureMask
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;
766 } else
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),
777 InputOutput,
778 DefaultVisual(dpy, screen),
779 mask, &attribs);
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);
791 /* Map the window */
792 XMapWindow(dpy, win);
794 /* Create cursors */
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);
800 /* Create GC */
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;
810 } else
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;
815 } else
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;
829 } else
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;
843 } else
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);
852 else
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
859 * for pointers. */
860 void dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
862 GETPRIVFROMPDEV;
864 info->buttonClass = 1;
865 dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
866 info->valuatorClass = 1;
867 info->numRelAxes = 2;
868 info->minval[0] = 0;
869 info->minval[1] = 0;
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));
873 info->res[0] = 1;
874 info->minres[0] = 0;
875 info->maxres[0] = 1;
876 info->ptrFeedbackClass = 1;
879 /** Fill in the \a info structure for the specified \a pDev. Only used
880 * for keyboard. */
881 void dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
883 dmxCommonKbdGetInfo(pDev, info);
884 info->keyboard = 1;
885 info->keyClass = 1;
886 dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
887 info->freemap = 1;
888 info->focusClass = 1;
889 info->kbdFeedbackClass = 1;
892 /** Handle special console-only keys. */
893 int dmxConsoleFunctions(pointer private, DMXFunctionType function)
895 GETONLYPRIVFROMPRIVATE;
896 XRectangle rect;
897 Display *dpy = priv->display;
899 switch (function) {
900 case DMX_FUNCTION_FINE:
901 if (priv->fine) {
902 priv->fine = 0;
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,
910 priv->grabbed
911 ? priv->cursorGrabbed
912 : priv->cursorNormal);
913 XWarpPointer(dpy, priv->window, priv->window,
914 0, 0, 0, 0,
915 scalex(priv, priv->globalX),
916 scaley(priv, priv->globalY));
917 XSync(dpy, False); /* Not a backend display */
918 } else {
919 priv->fine = 1;
920 XRaiseWindow(dpy, priv->window);
921 XDefineCursor(dpy, priv->window, priv->cursorEmpty);
922 dmxConsoleUpdateFineCursor(priv);
924 return 1;
925 case DMX_FUNCTION_GRAB:
926 if (priv->grabbed) {
927 XUngrabKeyboard(dpy, CurrentTime);
928 XUngrabPointer(dpy, CurrentTime);
929 XDefineCursor(dpy, priv->window,
930 priv->fine
931 ? priv->cursorEmpty
932 : priv->cursorNormal);
933 } else {
934 if (XGrabPointer(dpy, priv->window, True,
935 0, GrabModeAsync, GrabModeAsync, priv->window,
936 None, CurrentTime)) {
937 dmxLog(dmxError, "XGrabPointer failed\n");
938 return 0;
940 if (XGrabKeyboard(dpy, priv->window, True,
941 GrabModeAsync, GrabModeAsync, CurrentTime)) {
942 dmxLog(dmxError, "XGrabKeyboard failed\n");
943 XUngrabPointer(dpy, CurrentTime);
944 return 0;
946 XDefineCursor(dpy, priv->window,
947 priv->fine
948 ? priv->cursorEmpty
949 : priv->cursorGrabbed);
951 priv->grabbed = !priv->grabbed;
952 if (priv->fine) dmxConsoleUpdateFineCursor(priv);
953 return 1;
954 case DMX_FUNCTION_TERMINATE:
955 return 1;
956 default:
957 return 0;
961 static void dmxDump(void)
963 int i, j;
964 DMXInputInfo *dmxInput;
965 XEvent X;
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;
971 while (priv
972 && priv->display
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)
986 int i;
987 XEvent X;
989 DMXDBG0("dmxConsoleCapture\n");
990 dmxSync(NULL, TRUE);
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 */
1007 dmxDump();
1008 if (priv->fine) dmxConsoleUpdateFineCursor(priv);
1012 /** Undo the capture that was done by #dmxConsoleCapture. */
1013 void dmxConsoleUncapture(DMXInputInfo *dmxInput)
1015 int i;
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;
1025 priv->captured = 0;
1026 XSync(priv->display, False); /* Not a backend display */