Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / dmx / input / dmxconsole.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
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:
13 *
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.
17 *
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.
26 */
27
28/*
29 * Authors:
30 * David H. Dawes <dawes@xfree86.org>
31 * Kevin E. Martin <kem@redhat.com>
32 * Rickard E. (Rik) Faith <faith@redhat.com>
33 *
34 */
35
36/** \file
37 *
38 * This file implements the console input devices.
39 */
40
41#ifdef HAVE_DMX_CONFIG_H
42#include <dmx-config.h>
43#endif
44
45#define DMX_CONSOLE_DEBUG 0
46#define DMX_WINDOW_DEBUG 0
47
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"
55
56#include "inputstr.h"
57#include "input.h"
58#include "mipointer.h"
59#include "windowstr.h"
60
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"
72
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
92
93/* Private area for consoles. */
94typedef 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;
112
113 CloseScreenProcPtr CloseScreen;
114 struct _myPrivate *next; /* for closing multiple consoles */
115 int initialized;
116 DevicePtr mou, kbd;
117} myPrivate;
118
119static int
120scalex(myPrivate * priv, int x)
121{
122 return (int) ((x * priv->xScale) + .5);
123}
124
125static int
126scaley(myPrivate * priv, int y)
127{
128 return (int) ((y * priv->yScale) + .5);
129}
130
131static int
132unscalex(myPrivate * priv, int x)
133{
134 return (int) ((x / priv->xScale) + .5);
135}
136
137static int
138unscaley(myPrivate * priv, int y)
139{
140 return (int) ((y / priv->yScale) + .5);
141}
142
143/** Create the private area for \a pDevice. */
144pointer
145dmxConsoleCreatePrivate(DeviceIntPtr pDevice)
146{
147 GETDMXLOCALFROMPDEVICE;
148 myPrivate *priv = calloc(1, sizeof(*priv));
149
150 priv->dmxLocal = dmxLocal;
151 return priv;
152}
153
154/** If \a private is non-NULL, free its associated memory. */
155void
156dmxConsoleDestroyPrivate(pointer private)
157{
158 free(private);
159}
160
161static void
162dmxConsoleDrawFineCursor(myPrivate * priv, XRectangle * rect)
163{
164 int size = 6;
165 int x, y;
166
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);
175 if (priv->grabbed) {
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));
186 }
187 if (rect) {
188 rect->x = x;
189 rect->y = y;
190 rect->width = 2 * size;
191 rect->height = 2 * size;
192 }
193}
194
195static void
196dmxConsoleDrawWindows(pointer private)
197{
198 GETONLYPRIVFROMPRIVATE;
199 Display *dpy = priv->display;
200 int i;
201 Region whole, used, avail;
202 XRectangle rect;
203
204 whole = XCreateRegion();
205 used = XCreateRegion();
206 avail = XCreateRegion();
207 rect.x = 0;
208 rect.y = 0;
209 rect.width = priv->consWidth;
210 rect.height = priv->consHeight;
211 XUnionRectWithRegion(&rect, whole, whole);
212
213 for (i = 0; i < dmxNumScreens; i++) {
214 ScreenPtr pScreen = screenInfo.screens[i];
215 WindowPtr pRoot = pScreen->root;
216 WindowPtr pChild;
217
218#if DMX_WINDOW_DEBUG
219 dmxLog(dmxDebug, "%lu %p %p %p 2\n",
220 pRoot->drawable.id,
221 pRoot->parent, pRoot->firstChild, pRoot->lastChild);
222#endif
223
224 for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
225 if (pChild->mapped && pChild->realized) {
226#if DMX_WINDOW_DEBUG
227 dmxLog(dmxDebug, " %p %d,%d %dx%d %d %d %d RECTS\n",
228 pChild,
229 pChild->drawable.x,
230 pChild->drawable.y,
231 pChild->drawable.width,
232 pChild->drawable.height,
233 pChild->visibility,
234 pChild->overrideRedirect,
235 RegionNumRects(&pChild->clipList));
236#endif
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);
246 }
247 }
248#ifdef PANORAMIX
249 if (!noPanoramiXExtension)
250 break; /* Screen 0 valid with Xinerama */
251#endif
252 }
253 XDestroyRegion(avail);
254 XDestroyRegion(used);
255 XDestroyRegion(whole);
256 XSetClipMask(dpy, priv->gc, None);
257}
258
259static void
260dmxConsoleDraw(myPrivate * priv, int updateCursor, int update)
261{
262 GETDMXINPUTFROMPRIV;
263 Display *dpy = priv->display;
264 int i;
265
266 XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0,
267 priv->consWidth, priv->consHeight);
268
269 for (i = 0; i < dmxNumScreens; i++) {
270 DMXScreenInfo *dmxScreen = &dmxScreens[i];
271
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));
278 }
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));
285 }
286 if (dmxInput->windows)
287 dmxConsoleDrawWindows(priv);
288 if (priv->fine && updateCursor)
289 dmxConsoleDrawFineCursor(priv, 0);
290 if (update) {
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 */
294 }
295}
296
297static void
298dmxConsoleClearCursor(myPrivate * priv, int x, int y, XRectangle * rect)
299{
300 int cw = 14, ch = 14; /* Clear width and height */
301
302 rect->x = scalex(priv, x) - cw / 2;
303 rect->y = scaley(priv, y) - ch / 2;
304 rect->width = cw;
305 rect->height = ch;
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);
313}
314
315static void
316dmxConsoleUpdateFineCursor(myPrivate * priv)
317{
318 int leave = 0;
319 XRectangle rects[2];
320
321 dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
322 if (priv->dmxLocal->sendsCore) {
323 dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
324 }
325 else {
326 priv->globalX = priv->dmxLocal->lastX;
327 priv->globalY = priv->dmxLocal->lastY;
328 }
329
330 priv->lastX = scalex(priv, priv->width / 2);
331 priv->lastY = scaley(priv, priv->height / 2);
332
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)
337 priv->lastX = 0;
338 else
339 priv->lastX = scalex(priv, priv->width);
340 priv->lastY = scaley(priv, priv->globalY);
341 ++leave;
342 }
343 if (priv->globalY < 1 || priv->globalY >= priv->height) {
344 if (priv->globalY < 1)
345 priv->lastY = 0;
346 else
347 priv->lastY = scaley(priv, priv->height);
348 priv->lastX = scalex(priv, priv->globalX);
349 ++leave;
350 }
351
352 /* Draw pseudo cursor in window */
353 dmxConsoleDrawFineCursor(priv, &rects[1]);
354
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);
359
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 */
365
366 if (leave) {
367 XEvent X;
368
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);
374 }
375 }
376 else {
377 dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n",
378 X.type, dmxEventName(X.type));
379 }
380 }
381 }
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);
386}
387
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. */
393void
394dmxConsoleUpdateInfo(pointer private, DMXUpdateType type, WindowPtr pWindow)
395{
396 GETONLYPRIVFROMPRIVATE;
397 dmxConsoleDraw(priv, 1, 1);
398}
399
400static void
401dmxConsoleMoveAbsolute(myPrivate * priv, int x, int y,
402 DevicePtr pDev, dmxMotionProcPtr motion,
403 DMXBlockType block)
404{
405 int tmpX, tmpY, v[2];
406
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)
412 return;
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 */
417}
418
419static void
420dmxConsoleMoveRelative(myPrivate * priv, int x, int y,
421 DevicePtr pDev, dmxMotionProcPtr motion,
422 DMXBlockType block)
423{
424 int v[2];
425
426 /* Ignore the event generated from * warping back to middle */
427 if (x == priv->lastX && y == priv->lastY)
428 return;
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 */
433}
434
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. */
438void
439dmxConsoleUpdatePosition(pointer private, int x, int y)
440{
441 GETONLYPRIVFROMPRIVATE;
442 int tmpX, tmpY;
443 Display *dpy = priv->display;
444 static unsigned long dmxGeneration = 0;
445
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);
450
451 if (priv->fine)
452 dmxConsoleUpdateFineCursor(priv);
453 if (tmpX != priv->curX || tmpY != priv->curY) {
454 if (tmpX < 0)
455 tmpX = 0;
456 if (tmpY < 0)
457 tmpY = 0;
458 if (tmpX >= priv->consWidth)
459 tmpX = priv->consWidth - 1;
460 if (tmpY >= priv->consHeight)
461 tmpY = priv->consHeight - 1;
462 priv->curX = tmpX;
463 priv->curY = tmpY;
464 if (!priv->fine) {
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 */
469 }
470 }
471
472 if (dmxGeneration != serverGeneration) {
473 dmxGeneration = serverGeneration;
474 dmxConsoleDraw(priv, 1, 1);
475 }
476}
477
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. */
483void
484dmxConsoleCollectEvents(DevicePtr pDev,
485 dmxMotionProcPtr motion,
486 dmxEnqueueProcPtr enqueue,
487 dmxCheckSpecialProcPtr checkspecial, DMXBlockType block)
488{
489 GETPRIVFROMPDEV;
490 GETDMXINPUTFROMPRIV;
491 Display *dpy = priv->display;
492 Window win = priv->window;
493 int width = priv->width;
494 int height = priv->height;
495 XEvent X, N;
496 XSetWindowAttributes attribs;
497 static int rInitialized = 0;
498 static Region r;
499 XRectangle rect;
500 static int raising = 0, raiseX, raiseY; /* FIXME */
501
502 while (XPending(dpy)) {
503 XNextEvent(dpy, &X);
504 switch (X.type) {
505 case VisibilityNotify:
506 break;
507 case Expose:
508 DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
509 X.xexpose.count,
510 X.xexpose.x, X.xexpose.y,
511 X.xexpose.width, X.xexpose.height);
512 if (!rInitialized++)
513 r = XCreateRegion();
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);
527 XDestroyRegion(r);
528 rInitialized = 0;
529 }
530 break;
531 case ResizeRequest:
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)),
544 priv->consWidth,
545 priv->consHeight,
546 DefaultDepth(dpy, DefaultScreen(dpy)));
547 dmxConsoleDraw(priv, 1, 1);
548 attribs.override_redirect = False;
549 XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
550 break;
551 case LeaveNotify:
552 DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
553 X.xcrossing.x, X.xcrossing.y, raising, priv->fine);
554 if (!priv->captured)
555 dmxCommonRestoreState(priv);
556 else {
557 dmxConsoleUncapture(dmxInput);
558 dmxCommonRestoreState(priv);
559 }
560 break;
561 case EnterNotify:
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);
566 if (raising) {
567 raising = 0;
568 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
569 priv->mou, motion, block);
570 }
571 else {
572 if (priv->fine) {
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
577 * the event. */
578 raising = 1;
579 raiseX = X.xcrossing.x;
580 raiseY = X.xcrossing.y;
581 XRaiseWindow(dpy, priv->window);
582 }
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);
587 }
588 break;
589 case MotionNotify:
590 if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
591 continue;
592 if (XPending(dpy)) { /* do motion compression */
593 XPeekEvent(dpy, &N);
594 if (N.type == MotionNotify)
595 continue;
596 }
597 DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
598 X.xmotion.x, X.xmotion.y);
599 if (raising) {
600 raising = 0;
601 dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
602 priv->mou, motion, block);
603 }
604 else {
605 if (priv->fine)
606 dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
607 priv->mou, motion, block);
608 else
609 dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
610 priv->mou, motion, block);
611 }
612 break;
613 case KeyPress:
614 case KeyRelease:
615 enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
616 break;
617 default:
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);
621 break;
622 }
623 }
624}
625
626static void
627dmxCloseConsole(myPrivate * priv)
628{
629 GETDMXINPUTFROMPRIV;
630 dmxCommonRestoreState(priv);
631 if (priv->display) {
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);
638 }
639 priv->display = NULL;
640}
641
642static Bool
643dmxCloseConsoleScreen(ScreenPtr pScreen)
644{
645 myPrivate *priv, *last;
646
647 for (last = priv = (myPrivate *) dixLookupPrivate(&pScreen->devPrivates,
648 dmxScreenPrivateKey);
649 priv; priv = priv->next)
650 dmxCloseConsole(last = priv);
651
652 DMX_UNWRAP(CloseScreen, last, pScreen);
653 return pScreen->CloseScreen(pScreen);
654}
655
656static Cursor
657dmxConsoleCreateEmptyCursor(myPrivate * priv)
658{
659 char noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
660 Pixmap pixmap;
661 Cursor cursor;
662 XColor color, tmpColor;
663 Display *dpy = priv->display;
664
665 /* Create empty cursor for window */
666 pixmap = XCreateBitmapFromData(priv->display, priv->window,
667 noCursorData, 8, 8);
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);
673 return cursor;
674}
675
676static void
677dmxConsoleComputeWidthHeight(myPrivate * priv,
678 int *width, int *height,
679 double *xScale, double *yScale,
680 int *consWidth, int *consHeight)
681{
682 int screen;
683 Display *dpy = priv->display;
684
685 *width = 0;
686 *height = 0;
687 *xScale = 1.0;
688 *yScale = 1.0;
689
690 screen = DefaultScreen(dpy);
691 *consWidth = DisplayWidth(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
692 *consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
693
694 if (*consWidth < 1)
695 *consWidth = 1;
696 if (*consHeight < 1)
697 *consHeight = 1;
698
699#if 1
700 /* Always keep the console size similar
701 * to the global bounding box. */
702 *width = dmxGlobalWidth;
703 *height = dmxGlobalHeight;
704#else
705 /* Make the console window as big as
706 * possible by computing the visible
707 * bounding box. */
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;
711
712 if (screenInfo.screens[i]->y + screenInfo.screens[i]->height > *height)
713 *height = screenInfo.screens[i]->y + screenInfo.screens[i]->height;
714 }
715#endif
716
717 if ((double) *consWidth / *width < (double) *consHeight / *height)
718 *xScale = *yScale = (double) *consWidth / *width;
719 else
720 *xScale = *yScale = (double) *consHeight / *height;
721
722 *consWidth = scalex(priv, *width);
723 *consHeight = scaley(priv, *height);
724 if (*consWidth < 1)
725 *consWidth = 1;
726 if (*consHeight < 1)
727 *consHeight = 1;
728}
729
730/** Re-initialized the console device described by \a pDev (after a
731 * reconfig). */
732void
733dmxConsoleReInit(DevicePtr pDev)
734{
735 GETPRIVFROMPDEV;
736 Display *dpy;
737
738 if (!priv || !priv->initialized)
739 return;
740 dpy = priv->display;
741
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)),
750 priv->consWidth,
751 priv->consHeight,
752 DefaultDepth(dpy, DefaultScreen(dpy)));
753 dmxConsoleDraw(priv, 1, 1);
754}
755
756/** Initialized the console device described by \a pDev. */
757void
758dmxConsoleInit(DevicePtr pDev)
759{
760 GETPRIVFROMPDEV;
761 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
762 int screen;
763 unsigned long mask;
764 XSetWindowAttributes attribs;
765 Display *dpy;
766 Window win;
767 XGCValues gcvals;
768 XColor color;
769 XClassHint class_hints;
770 unsigned long tmp;
771
772 if (dmxLocal->type == DMX_LOCAL_MOUSE)
773 priv->mou = pDev;
774 if (dmxLocal->type == DMX_LOCAL_KEYBOARD)
775 priv->kbd = pDev;
776 if (priv->initialized++)
777 return; /* Only do once for mouse/keyboard pair */
778
779 if (!(dpy = priv->display = XOpenDisplay(dmxInput->name)))
780 dmxLog(dmxFatal,
781 "dmxOpenConsole: cannot open console display %s\n",
782 dmxInput->name);
783
784 /* Set up defaults */
785 dmxConsoleComputeWidthHeight(priv,
786 &priv->width, &priv->height,
787 &priv->xScale, &priv->yScale,
788 &priv->consWidth, &priv->consHeight);
789
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
795 | ButtonReleaseMask
796 | PointerMotionMask
797 | EnterWindowMask
798 | LeaveWindowMask
799 | KeyPressMask
800 | KeyReleaseMask | ExposureMask | ResizeRedirectMask);
801
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;
807 }
808 else
809 attribs.background_pixel = WhitePixel(dpy, screen);
810
811 attribs.event_mask = priv->eventMask;
812 attribs.override_redirect = False;
813
814 win = priv->window = XCreateWindow(dpy,
815 RootWindow(dpy, screen),
816 0, 0, priv->consWidth, priv->consHeight,
817 0,
818 DefaultDepth(dpy, screen),
819 InputOutput,
820 DefaultVisual(dpy, screen),
821 mask, &attribs);
822 priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
823 priv->consWidth, priv->consHeight,
824 DefaultDepth(dpy, screen));
825
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);
831
832 /* Map the window */
833 XMapWindow(dpy, win);
834
835 /* Create cursors */
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);
840
841 /* Create GC */
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;
851 }
852 else
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;
857 }
858 else
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;
865
866 priv->gc = XCreateGC(dpy, win, mask, &gcvals);
867
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;
872 }
873 else
874 gcvals.foreground = BlackPixel(dpy, screen);
875 priv->gcDet = XCreateGC(dpy, win, mask, &gcvals);
876 gcvals.foreground = tmp;
877
878 tmp = gcvals.background;
879 gcvals.background = gcvals.foreground;
880 gcvals.foreground = tmp;
881 priv->gcRev = XCreateGC(dpy, win, mask, &gcvals);
882
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;
887 }
888 else
889 gcvals.foreground = BlackPixel(dpy, screen);
890 priv->gcCur = XCreateGC(dpy, win, mask, &gcvals);
891
892 dmxConsoleDraw(priv, 1, 1);
893
894 if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
895 dmxScreenPrivateKey))
896 priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
897 dmxScreenPrivateKey);
898 else
899 DMX_WRAP(CloseScreen, dmxCloseConsoleScreen,
900 priv, screenInfo.screens[0]);
901 dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey,
902 priv);
903}
904
905/** Fill in the \a info structure for the specified \a pDev. Only used
906 * for pointers. */
907void
908dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
909{
910 GETPRIVFROMPDEV;
911
912 info->buttonClass = 1;
913 dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
914 info->valuatorClass = 1;
915 info->numRelAxes = 2;
916 info->minval[0] = 0;
917 info->minval[1] = 0;
918 /* max possible console window size: */
919 info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display));
920 info->maxval[1] =
921 DisplayHeight(priv->display, DefaultScreen(priv->display));
922 info->res[0] = 1;
923 info->minres[0] = 0;
924 info->maxres[0] = 1;
925 info->ptrFeedbackClass = 1;
926}
927
928/** Fill in the \a info structure for the specified \a pDev. Only used
929 * for keyboard. */
930void
931dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
932{
933 dmxCommonKbdGetInfo(pDev, info);
934 info->keyboard = 1;
935 info->keyClass = 1;
936 dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
937 info->freemap = 1;
938 info->focusClass = 1;
939 info->kbdFeedbackClass = 1;
940}
941
942/** Handle special console-only keys. */
943int
944dmxConsoleFunctions(pointer private, DMXFunctionType function)
945{
946 GETONLYPRIVFROMPRIVATE;
947 XRectangle rect;
948 Display *dpy = priv->display;
949
950 switch (function) {
951 case DMX_FUNCTION_FINE:
952 if (priv->fine) {
953 priv->fine = 0;
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);
959
960 XDefineCursor(dpy, priv->window,
961 priv->grabbed
962 ? priv->cursorGrabbed : priv->cursorNormal);
963 XWarpPointer(dpy, priv->window, priv->window,
964 0, 0, 0, 0,
965 scalex(priv, priv->globalX),
966 scaley(priv, priv->globalY));
967 XSync(dpy, False); /* Not a backend display */
968 }
969 else {
970 priv->fine = 1;
971 XRaiseWindow(dpy, priv->window);
972 XDefineCursor(dpy, priv->window, priv->cursorEmpty);
973 dmxConsoleUpdateFineCursor(priv);
974 }
975 return 1;
976 case DMX_FUNCTION_GRAB:
977 if (priv->grabbed) {
978 XUngrabKeyboard(dpy, CurrentTime);
979 XUngrabPointer(dpy, CurrentTime);
980 XDefineCursor(dpy, priv->window,
981 priv->fine ? priv->cursorEmpty : priv->cursorNormal);
982 }
983 else {
984 if (XGrabPointer(dpy, priv->window, True,
985 0, GrabModeAsync, GrabModeAsync, priv->window,
986 None, CurrentTime)) {
987 dmxLog(dmxError, "XGrabPointer failed\n");
988 return 0;
989 }
990 if (XGrabKeyboard(dpy, priv->window, True,
991 GrabModeAsync, GrabModeAsync, CurrentTime)) {
992 dmxLog(dmxError, "XGrabKeyboard failed\n");
993 XUngrabPointer(dpy, CurrentTime);
994 return 0;
995 }
996 XDefineCursor(dpy, priv->window,
997 priv->fine ? priv->cursorEmpty : priv->cursorGrabbed);
998 }
999 priv->grabbed = !priv->grabbed;
1000 if (priv->fine)
1001 dmxConsoleUpdateFineCursor(priv);
1002 return 1;
1003 case DMX_FUNCTION_TERMINATE:
1004 return 1;
1005 default:
1006 return 0;
1007 }
1008}
1009
1010static void
1011dmxDump(void)
1012{
1013 int i, j;
1014 DMXInputInfo *dmxInput;
1015 XEvent X;
1016
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;
1021
1022 while (priv
1023 && priv->display
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));
1027 }
1028 }
1029 }
1030}
1031
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. */
1035void
1036dmxConsoleCapture(DMXInputInfo * dmxInput)
1037{
1038 int i;
1039 XEvent X;
1040
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;
1046
1047 if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
1048 continue;
1049 if (dmxLocal->type != DMX_LOCAL_MOUSE)
1050 continue;
1051 if (priv->captured)
1052 continue;
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);
1059 }
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 */
1063 dmxDump();
1064 if (priv->fine)
1065 dmxConsoleUpdateFineCursor(priv);
1066 }
1067}
1068
1069/** Undo the capture that was done by #dmxConsoleCapture. */
1070void
1071dmxConsoleUncapture(DMXInputInfo * dmxInput)
1072{
1073 int i;
1074
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;
1080
1081 if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE)
1082 continue;
1083 if (dmxLocal->type != DMX_LOCAL_MOUSE)
1084 continue;
1085 if (!priv->captured)
1086 continue;
1087 priv->captured = 0;
1088 XSync(priv->display, False); /* Not a backend display */
1089 }
1090}