dmxconsole.c revision 706f2543
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 scalex(myPrivate *priv, int x) 120{ 121 return (int)((x * priv->xScale) + .5); 122} 123 124static int scaley(myPrivate *priv, int y) 125{ 126 return (int)((y * priv->yScale) + .5); 127} 128 129static int unscalex(myPrivate *priv, int x) 130{ 131 return (int)((x / priv->xScale) + .5); 132} 133 134static int unscaley(myPrivate *priv, int y) 135{ 136 return (int)((y / priv->yScale) + .5); 137} 138 139/** Create the private area for \a pDevice. */ 140pointer dmxConsoleCreatePrivate(DeviceIntPtr pDevice) 141{ 142 GETDMXLOCALFROMPDEVICE; 143 myPrivate *priv = calloc(1, sizeof(*priv)); 144 priv->dmxLocal = dmxLocal; 145 return priv; 146} 147 148/** If \a private is non-NULL, free its associated memory. */ 149void dmxConsoleDestroyPrivate(pointer private) 150{ 151 free(private); 152} 153 154static void dmxConsoleDrawFineCursor(myPrivate *priv, XRectangle *rect) 155{ 156 int size = 6; 157 int x, y; 158 159 XDrawLine(priv->display, priv->pixmap, priv->gcCur, 160 x = scalex(priv, priv->globalX) - size, 161 scaley(priv, priv->globalY), 162 scalex(priv, priv->globalX) + size, 163 scaley(priv, priv->globalY)); 164 XDrawLine(priv->display, priv->pixmap, priv->gcCur, 165 scalex(priv, priv->globalX), 166 y = scaley(priv, priv->globalY) - size, 167 scalex(priv, priv->globalX), 168 scaley(priv, priv->globalY) + size); 169 if (priv->grabbed) { 170 XDrawLine(priv->display, priv->pixmap, priv->gcCur, 171 scalex(priv, priv->globalX) - (int)(size / 1.4), 172 scaley(priv, priv->globalY) - (int)(size / 1.4), 173 scalex(priv, priv->globalX) + (int)(size / 1.4), 174 scaley(priv, priv->globalY) + (int)(size / 1.4)); 175 XDrawLine(priv->display, priv->pixmap, priv->gcCur, 176 scalex(priv, priv->globalX) - (int)(size / 1.4), 177 scaley(priv, priv->globalY) + (int)(size / 1.4), 178 scalex(priv, priv->globalX) + (int)(size / 1.4), 179 scaley(priv, priv->globalY) - (int)(size / 1.4)); 180 } 181 if (rect) { 182 rect->x = x; 183 rect->y = y; 184 rect->width = 2 * size; 185 rect->height = 2 * size; 186 } 187} 188 189static void dmxConsoleDrawWindows(pointer private) 190{ 191 GETONLYPRIVFROMPRIVATE; 192 Display *dpy = priv->display; 193 int i; 194 Region whole, used, avail; 195 XRectangle rect; 196 197 whole = XCreateRegion(); 198 used = XCreateRegion(); 199 avail = XCreateRegion(); 200 rect.x = 0; 201 rect.y = 0; 202 rect.width = priv->consWidth; 203 rect.height = priv->consHeight; 204 XUnionRectWithRegion(&rect, whole, whole); 205 206 for (i = 0; i < dmxNumScreens; i++) { 207 ScreenPtr pScreen = screenInfo.screens[i]; 208 WindowPtr pRoot = pScreen->root; 209 WindowPtr pChild; 210 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 216 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 RegionNumRects(&pChild->clipList)); 230#endif 231 rect.x = scalex(priv, pChild->drawable.x + pScreen->x); 232 rect.y = scaley(priv, pChild->drawable.y + pScreen->y); 233 rect.width = scalex(priv, pChild->drawable.width); 234 rect.height = scaley(priv, pChild->drawable.height); 235 XDrawRectangle(dpy, priv->pixmap, priv->gc, 236 rect.x, rect.y, rect.width, rect.height); 237 XUnionRectWithRegion(&rect, used, used); 238 XSubtractRegion(whole, used, avail); 239 XSetRegion(dpy, priv->gc, avail); 240 } 241 } 242#ifdef PANORAMIX 243 if (!noPanoramiXExtension) break; /* Screen 0 valid with Xinerama */ 244#endif 245 } 246 XDestroyRegion(avail); 247 XDestroyRegion(used); 248 XDestroyRegion(whole); 249 XSetClipMask(dpy, priv->gc, None); 250} 251 252static void dmxConsoleDraw(myPrivate *priv, int updateCursor, int update) 253{ 254 GETDMXINPUTFROMPRIV; 255 Display *dpy = priv->display; 256 int i; 257 258 XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0, 259 priv->consWidth, priv->consHeight); 260 261 for (i = 0; i < dmxNumScreens; i++) { 262 DMXScreenInfo *dmxScreen = &dmxScreens[i]; 263 XFillRectangle(dpy, priv->pixmap, 264 dmxScreen->beDisplay ? priv->gcRev : priv->gcDet, 265 scalex(priv, screenInfo.screens[i]->x), 266 scaley(priv, screenInfo.screens[i]->y), 267 scalex(priv, screenInfo.screens[i]->width), 268 scaley(priv, screenInfo.screens[i]->height)); 269 } 270 for (i = 0; i < dmxNumScreens; i++) { 271 XDrawRectangle(dpy, priv->pixmap, priv->gc, 272 scalex(priv, screenInfo.screens[i]->x), 273 scaley(priv, screenInfo.screens[i]->y), 274 scalex(priv, screenInfo.screens[i]->width), 275 scaley(priv, screenInfo.screens[i]->height)); 276 } 277 if (dmxInput->windows) dmxConsoleDrawWindows(priv); 278 if (priv->fine && updateCursor) dmxConsoleDrawFineCursor(priv, 0); 279 if (update) { 280 XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc, 281 0, 0, priv->consWidth, priv->consHeight, 0, 0); 282 XSync(priv->display, False); /* Not a backend display */ 283 } 284} 285 286static void dmxConsoleClearCursor(myPrivate *priv, int x, int y, 287 XRectangle *rect) 288{ 289 int cw = 14, ch = 14; /* Clear width and height */ 290 291 rect->x = scalex(priv, x) - cw/2; 292 rect->y = scaley(priv, y) - ch/2; 293 rect->width = cw; 294 rect->height = ch; 295 XSetClipRectangles(priv->display, priv->gc, 0, 0, rect, 1, Unsorted); 296 XSetClipRectangles(priv->display, priv->gcDet, 0, 0, rect, 1, Unsorted); 297 XSetClipRectangles(priv->display, priv->gcRev, 0, 0, rect, 1, Unsorted); 298 dmxConsoleDraw(priv, 0, 0); 299 XSetClipMask(priv->display, priv->gc, None); 300 XSetClipMask(priv->display, priv->gcDet, None); 301 XSetClipMask(priv->display, priv->gcRev, None); 302} 303 304 305static void dmxConsoleUpdateFineCursor(myPrivate *priv) 306{ 307 int leave = 0; 308 XRectangle rects[2]; 309 310 dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]); 311 if (priv->dmxLocal->sendsCore) { 312 dmxGetGlobalPosition(&priv->globalX, &priv->globalY); 313 } else { 314 priv->globalX = priv->dmxLocal->lastX; 315 priv->globalY = priv->dmxLocal->lastY; 316 } 317 318 priv->lastX = scalex(priv, priv->width / 2); 319 priv->lastY = scaley(priv, priv->height / 2); 320 321 /* Compute new warp position, which may be 322 outside the window */ 323 if (priv->globalX < 1 || priv->globalX >= priv->width) { 324 if (priv->globalX < 1) priv->lastX = 0; 325 else priv->lastX = scalex(priv, priv->width); 326 priv->lastY = scaley(priv, priv->globalY); 327 ++leave; 328 } 329 if (priv->globalY < 1 || priv->globalY >= priv->height) { 330 if (priv->globalY < 1) priv->lastY = 0; 331 else priv->lastY = scaley(priv, priv->height); 332 priv->lastX = scalex(priv, priv->globalX); 333 ++leave; 334 } 335 336 /* Draw pseudo cursor in window */ 337 dmxConsoleDrawFineCursor(priv, &rects[1]); 338 339 XSetClipRectangles(priv->display, priv->gc, 0, 0, rects, 2, Unsorted); 340 XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc, 341 0, 0, priv->consWidth, priv->consHeight, 0, 0); 342 XSetClipMask(priv->display, priv->gc, None); 343 344 DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n", 345 priv->lastX, priv->lastY); 346 XWarpPointer(priv->display, priv->window, priv->window, 347 0, 0, 0, 0, priv->lastX, priv->lastY); 348 XSync(priv->display, False); /* Not a backend display */ 349 350 if (leave) { 351 XEvent X; 352 while (XCheckMaskEvent(priv->display, PointerMotionMask, &X)) { 353 if (X.type == MotionNotify) { 354 if (X.xmotion.x != priv->lastX || X.xmotion.y != priv->lastY) { 355 DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n", 356 X.xmotion.x, X.xmotion.y, 357 priv->lastX, priv->lastY); 358 } 359 } else { 360 dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n", 361 X.type, dmxEventName(X.type)); 362 } 363 } 364 } 365 DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n", 366 priv->lastX, priv->lastY, 367 scalex(priv, priv->width), 368 scaley(priv, priv->height), 369 priv->globalX, priv->globalY); 370} 371 372/** Whenever the window layout (size, position, stacking order) might be 373 * changed, this routine is called with the \a pWindow that changed and 374 * the \a type of change. This routine is called in a conservative 375 * fashion: the actual layout of the windows of the screen might not 376 * have had any human-visible changes. */ 377void dmxConsoleUpdateInfo(pointer private, DMXUpdateType type, 378 WindowPtr pWindow) 379{ 380 GETONLYPRIVFROMPRIVATE; 381 dmxConsoleDraw(priv, 1, 1); 382} 383 384static void dmxConsoleMoveAbsolute(myPrivate *priv, int x, int y, 385 DevicePtr pDev, dmxMotionProcPtr motion, 386 DMXBlockType block) 387{ 388 int tmpX, tmpY, v[2]; 389 390 tmpX = unscalex(priv, x); 391 tmpY = unscalex(priv, y); 392 DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n", 393 x, y, tmpX, tmpY, priv->curX, priv->curY); 394 if (tmpX == priv->curX && tmpY == priv->curY) return; 395 v[0] = unscalex(priv, x); 396 v[1] = unscaley(priv, y); 397 motion(pDev, v, 0, 2, DMX_ABSOLUTE_CONFINED, block); 398 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */ 399} 400 401static void dmxConsoleMoveRelative(myPrivate *priv, int x, int y, 402 DevicePtr pDev, dmxMotionProcPtr motion, 403 DMXBlockType block) 404{ 405 int v[2]; 406 /* Ignore the event generated from * warping back to middle */ 407 if (x == priv->lastX && y == priv->lastY) return; 408 v[0] = priv->lastX - x; 409 v[1] = priv->lastY - y; 410 motion(pDev, v, 0, 2, DMX_RELATIVE, block); 411 /* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */ 412} 413 414/** This routine gets called from #dmxCoreMotion for each motion. This 415 * allows the console's notion of the cursor postion to change when 416 * another input device actually caused the change. */ 417void dmxConsoleUpdatePosition(pointer private, int x, int y) 418{ 419 GETONLYPRIVFROMPRIVATE; 420 int tmpX, tmpY; 421 Display *dpy = priv->display; 422 static unsigned long dmxGeneration = 0; 423 424 425 tmpX = scalex(priv, x); 426 tmpY = scaley(priv, y); 427 DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n", 428 x, y, tmpX, tmpY, priv->consWidth, priv->consHeight); 429 430 if (priv->fine) dmxConsoleUpdateFineCursor(priv); 431 if (tmpX != priv->curX || tmpY != priv->curY) { 432 if (tmpX < 0) tmpX = 0; 433 if (tmpY < 0) tmpY = 0; 434 if (tmpX >= priv->consWidth) tmpX = priv->consWidth - 1; 435 if (tmpY >= priv->consHeight) tmpY = priv->consHeight - 1; 436 priv->curX = tmpX; 437 priv->curY = tmpY; 438 if (!priv->fine) { 439 DMXDBG2(" WARP B %d %d\n", priv->curX, priv->curY); 440 XWarpPointer(dpy, priv->window, 441 priv->window, 0, 0, 0, 0, tmpX, tmpY); 442 XSync(dpy, False); /* Not a backend display */ 443 } 444 } 445 446 if (dmxGeneration != serverGeneration) { 447 dmxGeneration = serverGeneration; 448 dmxConsoleDraw(priv, 1, 1); 449 } 450} 451 452/** Collect all pending events from the console's display. Plase these 453 * events on the server event queue using the \a motion and \a enqueue 454 * routines. The \a checkspecial routine is used to check for special 455 * keys that need handling. \a block tells if signals should be blocked 456 * when updating the event queue. */ 457void dmxConsoleCollectEvents(DevicePtr pDev, 458 dmxMotionProcPtr motion, 459 dmxEnqueueProcPtr enqueue, 460 dmxCheckSpecialProcPtr checkspecial, 461 DMXBlockType block) 462{ 463 GETPRIVFROMPDEV; 464 GETDMXINPUTFROMPRIV; 465 Display *dpy = priv->display; 466 Window win = priv->window; 467 int width = priv->width; 468 int height = priv->height; 469 XEvent X, N; 470 XSetWindowAttributes attribs; 471 static int rInitialized = 0; 472 static Region r; 473 XRectangle rect; 474 static int raising = 0, raiseX, raiseY; /* FIXME */ 475 476 while (XPending(dpy)) { 477 XNextEvent(dpy, &X); 478 switch(X.type) { 479 case VisibilityNotify: 480 break; 481 case Expose: 482 DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n", 483 X.xexpose.count, 484 X.xexpose.x, X.xexpose.y, 485 X.xexpose.width, X.xexpose.height); 486 if (!rInitialized++) r = XCreateRegion(); 487 rect.x = X.xexpose.x; 488 rect.y = X.xexpose.y; 489 rect.width = X.xexpose.width; 490 rect.height = X.xexpose.height; 491 XUnionRectWithRegion(&rect, r, r); 492 if (X.xexpose.count == 0) { 493 XSetRegion(dpy, priv->gc, r); 494 XSetRegion(dpy, priv->gcDet, r); 495 XSetRegion(dpy, priv->gcRev, r); 496 dmxConsoleDraw(priv, 1, 1); 497 XSetClipMask(dpy, priv->gc, None); 498 XSetClipMask(dpy, priv->gcDet, None); 499 XSetClipMask(dpy, priv->gcRev, None); 500 XDestroyRegion(r); 501 rInitialized = 0; 502 } 503 break; 504 case ResizeRequest: 505 DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n", 506 X.xresizerequest.width, X.xresizerequest.height); 507 priv->consWidth = X.xresizerequest.width; 508 priv->consHeight = X.xresizerequest.height; 509 priv->xScale = (double)priv->consWidth / width; 510 priv->yScale = (double)priv->consHeight / height; 511 attribs.override_redirect = True; 512 XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs); 513 XResizeWindow(dpy, win, priv->consWidth, priv->consHeight); 514 XFreePixmap(dpy, priv->pixmap); 515 priv->pixmap = XCreatePixmap(dpy, 516 RootWindow(dpy, DefaultScreen(dpy)), 517 priv->consWidth, 518 priv->consHeight, 519 DefaultDepth(dpy,DefaultScreen(dpy))); 520 dmxConsoleDraw(priv, 1, 1); 521 attribs.override_redirect = False; 522 XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs); 523 break; 524 case LeaveNotify: 525 DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n", 526 X.xcrossing.x, X.xcrossing.y, raising, priv->fine); 527 if (!priv->captured) dmxCommonRestoreState(priv); 528 else { 529 dmxConsoleUncapture(dmxInput); 530 dmxCommonRestoreState(priv); 531 } 532 break; 533 case EnterNotify: 534 DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n", 535 X.xcrossing.x, X.xcrossing.y, raising, priv->fine, 536 priv->curX, priv->curY); 537 dmxCommonSaveState(priv); 538 if (raising) { 539 raising = 0; 540 dmxConsoleMoveAbsolute(priv, raiseX, raiseY, 541 priv->mou, motion, block); 542 } else { 543 if (priv->fine) { 544 /* The raise will generate an event near the center, 545 * which is not where the cursor should be. So we 546 * save the real position, do the raise, and move 547 * the cursor here again after the raise generates 548 * the event. */ 549 raising = 1; 550 raiseX = X.xcrossing.x; 551 raiseY = X.xcrossing.y; 552 XRaiseWindow(dpy, priv->window); 553 } 554 XSync(dpy, False); /* Not a backend display */ 555 if (!X.xcrossing.x && !X.xcrossing.y) 556 dmxConsoleMoveAbsolute(priv, priv->curX, priv->curY, 557 priv->mou, motion, block); 558 } 559 break; 560 case MotionNotify: 561 if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y) 562 continue; 563 if (XPending(dpy)) { /* do motion compression */ 564 XPeekEvent(dpy, &N); 565 if (N.type == MotionNotify) continue; 566 } 567 DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n", 568 X.xmotion.x, X.xmotion.y); 569 if (raising) { 570 raising = 0; 571 dmxConsoleMoveAbsolute(priv, raiseX, raiseY, 572 priv->mou, motion, block); 573 } else { 574 if (priv->fine) 575 dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y, 576 priv->mou, motion, block); 577 else 578 dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y, 579 priv->mou, motion, block); 580 } 581 break; 582 case KeyPress: 583 case KeyRelease: 584 enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block); 585 break; 586 default: 587 /* Pass the whole event here, because 588 * this may be an extension event. */ 589 enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block); 590 break; 591 } 592 } 593} 594 595static void dmxCloseConsole(myPrivate *priv) 596{ 597 GETDMXINPUTFROMPRIV; 598 dmxCommonRestoreState(priv); 599 if (priv->display) { 600 XFreeGC(priv->display, priv->gc); 601 XFreeGC(priv->display, priv->gcDet); 602 XFreeGC(priv->display, priv->gcRev); 603 XFreeGC(priv->display, priv->gcCur); 604 if (!dmxInput->console) XCloseDisplay(priv->display); 605 } 606 priv->display = NULL; 607} 608 609static Bool dmxCloseConsoleScreen(int idx, ScreenPtr pScreen) 610{ 611 myPrivate *priv, *last; 612 613 for (last = priv = (myPrivate *)dixLookupPrivate(&pScreen->devPrivates, 614 dmxScreenPrivateKey); 615 priv; 616 priv = priv->next) dmxCloseConsole(last = priv); 617 618 DMX_UNWRAP(CloseScreen, last, pScreen); 619 return pScreen->CloseScreen(idx, pScreen); 620} 621 622static Cursor dmxConsoleCreateEmptyCursor(myPrivate *priv) 623{ 624 char noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 625 Pixmap pixmap; 626 Cursor cursor; 627 XColor color, tmpColor; 628 Display *dpy = priv->display; 629 630 /* Create empty cursor for window */ 631 pixmap = XCreateBitmapFromData(priv->display, priv->window, 632 noCursorData, 8, 8); 633 if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), 634 "black", 635 &color, 636 &tmpColor)) 637 dmxLog(dmxFatal, "Cannot allocate color for cursor\n"); 638 cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0); 639 XFreePixmap(dpy, pixmap); 640 return cursor; 641} 642 643static void dmxConsoleComputeWidthHeight(myPrivate *priv, 644 int *width, int *height, 645 double *xScale, double *yScale, 646 int *consWidth, int *consHeight) 647{ 648 int screen; 649 Display *dpy = priv->display; 650 651 *width = 0; 652 *height = 0; 653 *xScale = 1.0; 654 *yScale = 1.0; 655 656 screen = DefaultScreen(dpy); 657 *consWidth = DisplayWidth(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN; 658 *consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN; 659 660 if (*consWidth < 1) *consWidth = 1; 661 if (*consHeight < 1) *consHeight = 1; 662 663#if 1 664 /* Always keep the console size similar 665 * to the global bounding box. */ 666 *width = dmxGlobalWidth; 667 *height = dmxGlobalHeight; 668#else 669 /* Make the console window as big as 670 * possible by computing the visible 671 * bounding box. */ 672 for (i = 0; i < dmxNumScreens; i++) { 673 if (screenInfo.screens[i]->x+screenInfo.screens[i]->width > *width) 674 *width = screenInfo.screens[i]->x+screenInfo.screens[i]->width; 675 676 if (screenInfo.screens[i]->y+screenInfo.screens[i]->height > *height) 677 *height = screenInfo.screens[i]->y+screenInfo.screens[i]->height; 678 } 679#endif 680 681 if ((double)*consWidth / *width < (double)*consHeight / *height) 682 *xScale = *yScale = (double)*consWidth / *width; 683 else 684 *xScale = *yScale = (double)*consHeight / *height; 685 686 *consWidth = scalex(priv, *width); 687 *consHeight = scaley(priv, *height); 688 if (*consWidth < 1) *consWidth = 1; 689 if (*consHeight < 1) *consHeight = 1; 690} 691 692/** Re-initialized the console device described by \a pDev (after a 693 * reconfig). */ 694void dmxConsoleReInit(DevicePtr pDev) 695{ 696 GETPRIVFROMPDEV; 697 Display *dpy; 698 699 if (!priv || !priv->initialized) return; 700 dpy = priv->display; 701 702 dmxConsoleComputeWidthHeight(priv, 703 &priv->width, &priv->height, 704 &priv->xScale, &priv->yScale, 705 &priv->consWidth, &priv->consHeight); 706 XResizeWindow(dpy, priv->window, priv->consWidth, priv->consHeight); 707 XFreePixmap(dpy, priv->pixmap); 708 priv->pixmap = XCreatePixmap(dpy, 709 RootWindow(dpy, DefaultScreen(dpy)), 710 priv->consWidth, 711 priv->consHeight, 712 DefaultDepth(dpy,DefaultScreen(dpy))); 713 dmxConsoleDraw(priv, 1, 1); 714} 715 716/** Initialized the console device described by \a pDev. */ 717void dmxConsoleInit(DevicePtr pDev) 718{ 719 GETPRIVFROMPDEV; 720 DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx]; 721 int screen; 722 unsigned long mask; 723 XSetWindowAttributes attribs; 724 Display *dpy; 725 Window win; 726 XGCValues gcvals; 727 XColor color; 728 XClassHint class_hints; 729 unsigned long tmp; 730 731 if (dmxLocal->type == DMX_LOCAL_MOUSE) priv->mou = pDev; 732 if (dmxLocal->type == DMX_LOCAL_KEYBOARD) priv->kbd = pDev; 733 if (priv->initialized++) return; /* Only do once for mouse/keyboard pair */ 734 735 if (!(dpy = priv->display = XOpenDisplay(dmxInput->name))) 736 dmxLog(dmxFatal, 737 "dmxOpenConsole: cannot open console display %s\n", 738 dmxInput->name); 739 740 /* Set up defaults */ 741 dmxConsoleComputeWidthHeight(priv, 742 &priv->width, &priv->height, 743 &priv->xScale, &priv->yScale, 744 &priv->consWidth, &priv->consHeight); 745 746 /* Private initialization using computed values or constants. */ 747 screen = DefaultScreen(dpy); 748 priv->initPointerX = scalex(priv, priv->width / 2); 749 priv->initPointerY = scaley(priv, priv->height / 2); 750 priv->eventMask = (ButtonPressMask 751 | ButtonReleaseMask 752 | PointerMotionMask 753 | EnterWindowMask 754 | LeaveWindowMask 755 | KeyPressMask 756 | KeyReleaseMask 757 | ExposureMask 758 | ResizeRedirectMask); 759 760 mask = CWBackPixel | CWEventMask | CWColormap | CWOverrideRedirect; 761 attribs.colormap = DefaultColormap(dpy, screen); 762 if (XParseColor(dpy, attribs.colormap, CONSOLE_BG_COLOR, &color) 763 && XAllocColor(dpy, attribs.colormap, &color)) { 764 attribs.background_pixel = color.pixel; 765 } else 766 attribs.background_pixel = WhitePixel(dpy, screen); 767 768 attribs.event_mask = priv->eventMask; 769 attribs.override_redirect = False; 770 771 win = priv->window = XCreateWindow(dpy, 772 RootWindow(dpy, screen), 773 0, 0, priv->consWidth, priv->consHeight, 774 0, 775 DefaultDepth(dpy, screen), 776 InputOutput, 777 DefaultVisual(dpy, screen), 778 mask, &attribs); 779 priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen), 780 priv->consWidth, priv->consHeight, 781 DefaultDepth(dpy, screen)); 782 783 /* Set up properties */ 784 XStoreName(dpy, win, DMX_CONSOLE_NAME); 785 class_hints.res_name = DMX_RES_NAME; 786 class_hints.res_class = DMX_RES_CLASS; 787 XSetClassHint(dpy, win, &class_hints); 788 789 790 /* Map the window */ 791 XMapWindow(dpy, win); 792 793 /* Create cursors */ 794 priv->cursorNormal = XCreateFontCursor(dpy, XC_circle); 795 priv->cursorGrabbed = XCreateFontCursor(dpy, XC_spider); 796 priv->cursorEmpty = dmxConsoleCreateEmptyCursor(priv); 797 XDefineCursor(dpy, priv->window, priv->cursorNormal); 798 799 /* Create GC */ 800 mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground | 801 GCBackground | GCLineWidth | GCLineStyle | GCCapStyle | 802 GCFillStyle | GCGraphicsExposures); 803 gcvals.function = GXcopy; 804 gcvals.plane_mask = AllPlanes; 805 gcvals.clip_mask = None; 806 if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_FG_COLOR, &color) 807 && XAllocColor(dpy, attribs.colormap, &color)) { 808 gcvals.foreground = color.pixel; 809 } else 810 gcvals.foreground = BlackPixel(dpy, screen); 811 if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_BG_COLOR, &color) 812 && XAllocColor(dpy, attribs.colormap, &color)) { 813 gcvals.background = color.pixel; 814 } else 815 gcvals.background = WhitePixel(dpy, screen); 816 gcvals.line_width = 0; 817 gcvals.line_style = LineSolid; 818 gcvals.cap_style = CapNotLast; 819 gcvals.fill_style = FillSolid; 820 gcvals.graphics_exposures = False; 821 822 priv->gc = XCreateGC(dpy, win, mask, &gcvals); 823 824 tmp = gcvals.foreground; 825 if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_DET_COLOR, &color) 826 && XAllocColor(dpy, attribs.colormap, &color)) { 827 gcvals.foreground = color.pixel; 828 } else 829 gcvals.foreground = BlackPixel(dpy, screen); 830 priv->gcDet = XCreateGC(dpy, win, mask, &gcvals); 831 gcvals.foreground = tmp; 832 833 tmp = gcvals.background; 834 gcvals.background = gcvals.foreground; 835 gcvals.foreground = tmp; 836 priv->gcRev = XCreateGC(dpy, win, mask, &gcvals); 837 838 gcvals.background = gcvals.foreground; 839 if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_CUR_COLOR, &color) 840 && XAllocColor(dpy, attribs.colormap, &color)) { 841 gcvals.foreground = color.pixel; 842 } else 843 gcvals.foreground = BlackPixel(dpy, screen); 844 priv->gcCur = XCreateGC(dpy, win, mask, &gcvals); 845 846 dmxConsoleDraw(priv, 1, 1); 847 848 if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates, 849 dmxScreenPrivateKey)) 850 priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates, 851 dmxScreenPrivateKey); 852 else 853 DMX_WRAP(CloseScreen, dmxCloseConsoleScreen, 854 priv, screenInfo.screens[0]); 855 dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey, 856 priv); 857} 858 859/** Fill in the \a info structure for the specified \a pDev. Only used 860 * for pointers. */ 861void dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) 862{ 863 GETPRIVFROMPDEV; 864 865 info->buttonClass = 1; 866 dmxCommonMouGetMap(pDev, info->map, &info->numButtons); 867 info->valuatorClass = 1; 868 info->numRelAxes = 2; 869 info->minval[0] = 0; 870 info->minval[1] = 0; 871 /* max possible console window size: */ 872 info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display)); 873 info->maxval[1] = DisplayHeight(priv->display, DefaultScreen(priv->display)); 874 info->res[0] = 1; 875 info->minres[0] = 0; 876 info->maxres[0] = 1; 877 info->ptrFeedbackClass = 1; 878} 879 880/** Fill in the \a info structure for the specified \a pDev. Only used 881 * for keyboard. */ 882void dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) 883{ 884 dmxCommonKbdGetInfo(pDev, info); 885 info->keyboard = 1; 886 info->keyClass = 1; 887 dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap); 888 info->freemap = 1; 889 info->focusClass = 1; 890 info->kbdFeedbackClass = 1; 891} 892 893/** Handle special console-only keys. */ 894int dmxConsoleFunctions(pointer private, DMXFunctionType function) 895{ 896 GETONLYPRIVFROMPRIVATE; 897 XRectangle rect; 898 Display *dpy = priv->display; 899 900 switch (function) { 901 case DMX_FUNCTION_FINE: 902 if (priv->fine) { 903 priv->fine = 0; 904 dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rect); 905 XSetClipRectangles(dpy, priv->gc, 0, 0, &rect, 1, Unsorted); 906 XCopyArea(dpy, priv->pixmap, priv->window, priv->gc, 907 0, 0, priv->consWidth, priv->consHeight, 0, 0); 908 XSetClipMask(dpy, priv->gc, None); 909 910 XDefineCursor(dpy, priv->window, 911 priv->grabbed 912 ? priv->cursorGrabbed 913 : priv->cursorNormal); 914 XWarpPointer(dpy, priv->window, priv->window, 915 0, 0, 0, 0, 916 scalex(priv, priv->globalX), 917 scaley(priv, priv->globalY)); 918 XSync(dpy, False); /* Not a backend display */ 919 } else { 920 priv->fine = 1; 921 XRaiseWindow(dpy, priv->window); 922 XDefineCursor(dpy, priv->window, priv->cursorEmpty); 923 dmxConsoleUpdateFineCursor(priv); 924 } 925 return 1; 926 case DMX_FUNCTION_GRAB: 927 if (priv->grabbed) { 928 XUngrabKeyboard(dpy, CurrentTime); 929 XUngrabPointer(dpy, CurrentTime); 930 XDefineCursor(dpy, priv->window, 931 priv->fine 932 ? priv->cursorEmpty 933 : priv->cursorNormal); 934 } else { 935 if (XGrabPointer(dpy, priv->window, True, 936 0, GrabModeAsync, GrabModeAsync, priv->window, 937 None, CurrentTime)) { 938 dmxLog(dmxError, "XGrabPointer failed\n"); 939 return 0; 940 } 941 if (XGrabKeyboard(dpy, priv->window, True, 942 GrabModeAsync, GrabModeAsync, CurrentTime)) { 943 dmxLog(dmxError, "XGrabKeyboard failed\n"); 944 XUngrabPointer(dpy, CurrentTime); 945 return 0; 946 } 947 XDefineCursor(dpy, priv->window, 948 priv->fine 949 ? priv->cursorEmpty 950 : priv->cursorGrabbed); 951 } 952 priv->grabbed = !priv->grabbed; 953 if (priv->fine) dmxConsoleUpdateFineCursor(priv); 954 return 1; 955 case DMX_FUNCTION_TERMINATE: 956 return 1; 957 default: 958 return 0; 959 } 960} 961 962static void dmxDump(void) 963{ 964 int i, j; 965 DMXInputInfo *dmxInput; 966 XEvent X; 967 968 for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) { 969 for (j = 0; j < dmxInput->numDevs; j++) { 970 DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j]; 971 myPrivate *priv = dmxLocal->private; 972 while (priv 973 && priv->display 974 && XCheckTypedEvent(priv->display, MotionNotify, &X)) { 975 DMXDBG4("dmxDump: %s/%d threw event away %d %s\n", 976 dmxInput->name, j, X.type, dmxEventName(X.type)); 977 } 978 } 979 } 980} 981 982/** This routine is used to warp the pointer into the console window 983 * from anywhere on the screen. It is used when backend and console 984 * input are both being taken from the same X display. */ 985void dmxConsoleCapture(DMXInputInfo *dmxInput) 986{ 987 int i; 988 XEvent X; 989 990 DMXDBG0("dmxConsoleCapture\n"); 991 dmxSync(NULL, TRUE); 992 for (i = 0; i < dmxInput->numDevs; i++) { 993 DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i]; 994 myPrivate *priv = dmxLocal->private; 995 if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue; 996 if (dmxLocal->type != DMX_LOCAL_MOUSE) continue; 997 if (priv->captured) continue; 998 priv->captured = 2; /* Ungrab only after proximal events. */ 999 XRaiseWindow(priv->display, priv->window); 1000 XSync(priv->display, False); /* Not a backend display */ 1001 while (XCheckTypedEvent(priv->display, MotionNotify, &X)) { 1002 DMXDBG3(" Ignoring motion to %d %d after capture on %s\n", 1003 X.xmotion.x, X.xmotion.y, dmxInput->name); 1004 } 1005 XWarpPointer(priv->display, None, 1006 priv->window, 0, 0, 0, 0, priv->curX, priv->curY); 1007 XSync(priv->display, False); /* Not a backend display */ 1008 dmxDump(); 1009 if (priv->fine) dmxConsoleUpdateFineCursor(priv); 1010 } 1011} 1012 1013/** Undo the capture that was done by #dmxConsoleCapture. */ 1014void dmxConsoleUncapture(DMXInputInfo *dmxInput) 1015{ 1016 int i; 1017 1018 DMXDBG0("dmxConsoleUncapture\n"); 1019 dmxSync(NULL, TRUE); 1020 for (i = 0; i < dmxInput->numDevs; i++) { 1021 DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i]; 1022 myPrivate *priv = dmxLocal->private; 1023 if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue; 1024 if (dmxLocal->type != DMX_LOCAL_MOUSE) continue; 1025 if (!priv->captured) continue; 1026 priv->captured = 0; 1027 XSync(priv->display, False); /* Not a backend display */ 1028 } 1029} 1030