PageRenderTime 189ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/hw/dmx/input/dmxconsole.c

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