Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

XDesktop.cxx 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
  3. * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. #include <x0vncserver/XDesktop.h>
  21. #include <X11/XKBlib.h>
  22. #ifdef HAVE_XTEST
  23. #include <X11/extensions/XTest.h>
  24. #endif
  25. #ifdef HAVE_XDAMAGE
  26. #include <X11/extensions/Xdamage.h>
  27. #endif
  28. #ifdef HAVE_XFIXES
  29. #include <X11/extensions/Xfixes.h>
  30. #endif
  31. #ifdef HAVE_XRANDR
  32. #include <X11/extensions/Xrandr.h>
  33. #include <RandrGlue.h>
  34. extern "C" {
  35. void vncSetGlueContext(Display *dpy, void *res);
  36. }
  37. #endif
  38. #include <x0vncserver/Geometry.h>
  39. #include <x0vncserver/XPixelBuffer.h>
  40. using namespace rfb;
  41. extern const unsigned short code_map_qnum_to_xorgevdev[];
  42. extern const unsigned int code_map_qnum_to_xorgevdev_len;
  43. extern const unsigned short code_map_qnum_to_xorgkbd[];
  44. extern const unsigned int code_map_qnum_to_xorgkbd_len;
  45. BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
  46. BoolParameter rawKeyboard("RawKeyboard",
  47. "Send keyboard events straight through and "
  48. "avoid mapping them to the current keyboard "
  49. "layout", false);
  50. static rfb::LogWriter vlog("XDesktop");
  51. // order is important as it must match RFB extension
  52. static const char * ledNames[XDESKTOP_N_LEDS] = {
  53. "Scroll Lock", "Num Lock", "Caps Lock"
  54. };
  55. XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
  56. : dpy(dpy_), geometry(geometry_), pb(0), server(0),
  57. oldButtonMask(0), haveXtest(false), haveDamage(false),
  58. maxButtons(0), running(false), ledMasks(), ledState(0),
  59. codeMap(0), codeMapLen(0)
  60. {
  61. int major, minor;
  62. int xkbOpcode, xkbErrorBase;
  63. major = XkbMajorVersion;
  64. minor = XkbMinorVersion;
  65. if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
  66. &xkbErrorBase, &major, &minor)) {
  67. vlog.error("XKEYBOARD extension not present");
  68. throw Exception();
  69. }
  70. XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
  71. XkbIndicatorStateNotifyMask);
  72. // figure out bit masks for the indicators we are interested in
  73. for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
  74. Atom a;
  75. int shift;
  76. Bool on;
  77. a = XInternAtom(dpy, ledNames[i], True);
  78. if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
  79. continue;
  80. ledMasks[i] = 1u << shift;
  81. vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
  82. if (on)
  83. ledState |= 1u << i;
  84. }
  85. // X11 unfortunately uses keyboard driver specific keycodes and provides no
  86. // direct way to query this, so guess based on the keyboard mapping
  87. XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
  88. if (desc && desc->names) {
  89. char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
  90. if (keycodes) {
  91. if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
  92. codeMap = code_map_qnum_to_xorgevdev;
  93. codeMapLen = code_map_qnum_to_xorgevdev_len;
  94. vlog.info("Using evdev codemap\n");
  95. } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
  96. codeMap = code_map_qnum_to_xorgkbd;
  97. codeMapLen = code_map_qnum_to_xorgkbd_len;
  98. vlog.info("Using xorgkbd codemap\n");
  99. } else {
  100. vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
  101. }
  102. XFree(keycodes);
  103. } else {
  104. vlog.debug("Unable to get keycode map\n");
  105. }
  106. XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
  107. }
  108. #ifdef HAVE_XTEST
  109. int xtestEventBase;
  110. int xtestErrorBase;
  111. if (XTestQueryExtension(dpy, &xtestEventBase,
  112. &xtestErrorBase, &major, &minor)) {
  113. XTestGrabControl(dpy, True);
  114. vlog.info("XTest extension present - version %d.%d",major,minor);
  115. haveXtest = true;
  116. } else {
  117. #endif
  118. vlog.info("XTest extension not present");
  119. vlog.info("Unable to inject events or display while server is grabbed");
  120. #ifdef HAVE_XTEST
  121. }
  122. #endif
  123. #ifdef HAVE_XDAMAGE
  124. int xdamageErrorBase;
  125. if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
  126. haveDamage = true;
  127. } else {
  128. #endif
  129. vlog.info("DAMAGE extension not present");
  130. vlog.info("Will have to poll screen for changes");
  131. #ifdef HAVE_XDAMAGE
  132. }
  133. #endif
  134. #ifdef HAVE_XFIXES
  135. int xfixesErrorBase;
  136. if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
  137. XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
  138. XFixesDisplayCursorNotifyMask);
  139. } else {
  140. #endif
  141. vlog.info("XFIXES extension not present");
  142. vlog.info("Will not be able to display cursors");
  143. #ifdef HAVE_XFIXES
  144. }
  145. #endif
  146. #ifdef HAVE_XRANDR
  147. int xrandrErrorBase;
  148. randrSyncSerial = 0;
  149. if (XRRQueryExtension(dpy, &xrandrEventBase, &xrandrErrorBase)) {
  150. XRRSelectInput(dpy, DefaultRootWindow(dpy),
  151. RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
  152. /* Override TXWindow::init input mask */
  153. XSelectInput(dpy, DefaultRootWindow(dpy),
  154. PropertyChangeMask | StructureNotifyMask | ExposureMask);
  155. } else {
  156. #endif
  157. vlog.info("RANDR extension not present");
  158. vlog.info("Will not be able to handle session resize");
  159. #ifdef HAVE_XRANDR
  160. }
  161. #endif
  162. TXWindow::setGlobalEventHandler(this);
  163. }
  164. XDesktop::~XDesktop() {
  165. if (running)
  166. stop();
  167. }
  168. void XDesktop::poll() {
  169. if (pb and not haveDamage)
  170. pb->poll(server);
  171. if (running) {
  172. Window root, child;
  173. int x, y, wx, wy;
  174. unsigned int mask;
  175. XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
  176. &x, &y, &wx, &wy, &mask);
  177. x -= geometry->offsetLeft();
  178. y -= geometry->offsetTop();
  179. server->setCursorPos(rfb::Point(x, y));
  180. }
  181. }
  182. void XDesktop::start(VNCServer* vs) {
  183. // Determine actual number of buttons of the X pointer device.
  184. unsigned char btnMap[8];
  185. int numButtons = XGetPointerMapping(dpy, btnMap, 8);
  186. maxButtons = (numButtons > 8) ? 8 : numButtons;
  187. vlog.info("Enabling %d button%s of X pointer device",
  188. maxButtons, (maxButtons != 1) ? "s" : "");
  189. // Create an ImageFactory instance for producing Image objects.
  190. ImageFactory factory((bool)useShm);
  191. // Create pixel buffer and provide it to the server object.
  192. pb = new XPixelBuffer(dpy, factory, geometry->getRect());
  193. vlog.info("Allocated %s", pb->getImage()->classDesc());
  194. server = (VNCServerST *)vs;
  195. server->setPixelBuffer(pb, computeScreenLayout());
  196. #ifdef HAVE_XDAMAGE
  197. if (haveDamage) {
  198. damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
  199. XDamageReportRawRectangles);
  200. }
  201. #endif
  202. #ifdef HAVE_XFIXES
  203. setCursor();
  204. #endif
  205. server->setLEDState(ledState);
  206. running = true;
  207. }
  208. void XDesktop::stop() {
  209. running = false;
  210. #ifdef HAVE_XDAMAGE
  211. if (haveDamage)
  212. XDamageDestroy(dpy, damage);
  213. #endif
  214. server->setPixelBuffer(0);
  215. server = 0;
  216. delete pb;
  217. pb = 0;
  218. }
  219. bool XDesktop::isRunning() {
  220. return running;
  221. }
  222. void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
  223. #ifdef HAVE_XTEST
  224. if (!haveXtest) return;
  225. XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
  226. geometry->offsetLeft() + pos.x,
  227. geometry->offsetTop() + pos.y,
  228. CurrentTime);
  229. if (buttonMask != oldButtonMask) {
  230. for (int i = 0; i < maxButtons; i++) {
  231. if ((buttonMask ^ oldButtonMask) & (1<<i)) {
  232. if (buttonMask & (1<<i)) {
  233. XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
  234. } else {
  235. XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
  236. }
  237. }
  238. }
  239. }
  240. oldButtonMask = buttonMask;
  241. #endif
  242. }
  243. #ifdef HAVE_XTEST
  244. KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
  245. XkbDescPtr xkb;
  246. XkbStateRec state;
  247. unsigned int mods;
  248. unsigned keycode;
  249. xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
  250. if (!xkb)
  251. return 0;
  252. XkbGetState(dpy, XkbUseCoreKbd, &state);
  253. // XkbStateFieldFromRec() doesn't work properly because
  254. // state.lookup_mods isn't properly updated, so we do this manually
  255. mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
  256. for (keycode = xkb->min_key_code;
  257. keycode <= xkb->max_key_code;
  258. keycode++) {
  259. KeySym cursym;
  260. unsigned int out_mods;
  261. XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
  262. if (cursym == keysym)
  263. break;
  264. }
  265. if (keycode > xkb->max_key_code)
  266. keycode = 0;
  267. XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
  268. // Shift+Tab is usually ISO_Left_Tab, but RFB hides this fact. Do
  269. // another attempt if we failed the initial lookup
  270. if ((keycode == 0) && (keysym == XK_Tab) && (mods & ShiftMask))
  271. return XkbKeysymToKeycode(dpy, XK_ISO_Left_Tab);
  272. return keycode;
  273. }
  274. #endif
  275. void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
  276. #ifdef HAVE_XTEST
  277. int keycode = 0;
  278. if (!haveXtest)
  279. return;
  280. // Use scan code if provided and mapping exists
  281. if (codeMap && rawKeyboard && xtcode < codeMapLen)
  282. keycode = codeMap[xtcode];
  283. if (!keycode) {
  284. if (pressedKeys.find(keysym) != pressedKeys.end())
  285. keycode = pressedKeys[keysym];
  286. else {
  287. // XKeysymToKeycode() doesn't respect state, so we have to use
  288. // something slightly more complex
  289. keycode = XkbKeysymToKeycode(dpy, keysym);
  290. }
  291. }
  292. if (!keycode) {
  293. vlog.error("Could not map key event to X11 key code");
  294. return;
  295. }
  296. if (down)
  297. pressedKeys[keysym] = keycode;
  298. else
  299. pressedKeys.erase(keysym);
  300. vlog.debug("%d %s", keycode, down ? "down" : "up");
  301. XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
  302. #endif
  303. }
  304. void XDesktop::clientCutText(const char* str, int len) {
  305. }
  306. ScreenSet XDesktop::computeScreenLayout()
  307. {
  308. ScreenSet layout;
  309. #ifdef HAVE_XRANDR
  310. XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
  311. if (!res) {
  312. vlog.error("XRRGetScreenResources failed");
  313. return layout;
  314. }
  315. vncSetGlueContext(dpy, res);
  316. layout = ::computeScreenLayout(&outputIdMap);
  317. XRRFreeScreenResources(res);
  318. // Adjust the layout relative to the geometry
  319. ScreenSet::iterator iter, iter_next;
  320. Point offset(-geometry->offsetLeft(), -geometry->offsetTop());
  321. for (iter = layout.begin();iter != layout.end();iter = iter_next) {
  322. iter_next = iter; ++iter_next;
  323. iter->dimensions = iter->dimensions.translate(offset);
  324. if (iter->dimensions.enclosed_by(geometry->getRect()))
  325. continue;
  326. iter->dimensions = iter->dimensions.intersect(geometry->getRect());
  327. if (iter->dimensions.is_empty()) {
  328. layout.remove_screen(iter->id);
  329. }
  330. }
  331. #endif
  332. // Make sure that we have at least one screen
  333. if (layout.num_screens() == 0)
  334. layout.add_screen(rfb::Screen(0, 0, 0, geometry->width(),
  335. geometry->height(), 0));
  336. return layout;
  337. }
  338. #ifdef HAVE_XRANDR
  339. /* Get the biggest mode which is equal or smaller to requested
  340. size. If no such mode exists, return the smallest. */
  341. static void GetSmallerMode(XRRScreenResources *res,
  342. XRROutputInfo *output,
  343. unsigned int *width, unsigned int *height)
  344. {
  345. XRRModeInfo best = {};
  346. XRRModeInfo smallest = {};
  347. smallest.width = -1;
  348. smallest.height = -1;
  349. for (int i = 0; i < res->nmode; i++) {
  350. for (int j = 0; j < output->nmode; j++) {
  351. if (output->modes[j] == res->modes[i].id) {
  352. if ((res->modes[i].width > best.width && res->modes[i].width <= *width) &&
  353. (res->modes[i].height > best.height && res->modes[i].height <= *height)) {
  354. best = res->modes[i];
  355. }
  356. if ((res->modes[i].width < smallest.width) && res->modes[i].height < smallest.height) {
  357. smallest = res->modes[i];
  358. }
  359. }
  360. }
  361. }
  362. if (best.id == 0 && smallest.id != 0) {
  363. best = smallest;
  364. }
  365. *width = best.width;
  366. *height = best.height;
  367. }
  368. #endif /* HAVE_XRANDR */
  369. unsigned int XDesktop::setScreenLayout(int fb_width, int fb_height,
  370. const rfb::ScreenSet& layout)
  371. {
  372. #ifdef HAVE_XRANDR
  373. char buffer[2048];
  374. vlog.debug("Got request for framebuffer resize to %dx%d",
  375. fb_width, fb_height);
  376. layout.print(buffer, sizeof(buffer));
  377. vlog.debug("%s", buffer);
  378. XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
  379. if (!res) {
  380. vlog.error("XRRGetScreenResources failed");
  381. return rfb::resultProhibited;
  382. }
  383. vncSetGlueContext(dpy, res);
  384. /* The client may request a screen layout which is not supported by
  385. the Xserver. This happens, for example, when adjusting the size
  386. of a non-fullscreen vncviewer window. To handle this and other
  387. cases, we first call tryScreenLayout. If this fails, we try to
  388. adjust the request to one screen with a smaller mode. */
  389. vlog.debug("Testing screen layout");
  390. unsigned int tryresult = ::tryScreenLayout(fb_width, fb_height, layout, &outputIdMap);
  391. rfb::ScreenSet adjustedLayout;
  392. if (tryresult == rfb::resultSuccess) {
  393. adjustedLayout = layout;
  394. } else {
  395. vlog.debug("Impossible layout - trying to adjust");
  396. ScreenSet::const_iterator firstscreen = layout.begin();
  397. adjustedLayout.add_screen(*firstscreen);
  398. ScreenSet::iterator iter = adjustedLayout.begin();
  399. RROutput outputId = None;
  400. for (int i = 0;i < vncRandRGetOutputCount();i++) {
  401. unsigned int oi = vncRandRGetOutputId(i);
  402. /* Known? */
  403. if (outputIdMap.count(oi) == 0)
  404. continue;
  405. /* Find the corresponding screen... */
  406. if (iter->id == outputIdMap[oi]) {
  407. outputId = oi;
  408. } else {
  409. outputIdMap.erase(oi);
  410. }
  411. }
  412. /* New screen */
  413. if (outputId == None) {
  414. int i = getPreferredScreenOutput(&outputIdMap, std::set<unsigned int>());
  415. if (i != -1) {
  416. outputId = vncRandRGetOutputId(i);
  417. }
  418. }
  419. if (outputId == None) {
  420. vlog.debug("Resize adjust: Could not find corresponding screen");
  421. XRRFreeScreenResources(res);
  422. return rfb::resultInvalid;
  423. }
  424. XRROutputInfo *output = XRRGetOutputInfo(dpy, res, outputId);
  425. if (!output) {
  426. vlog.debug("Resize adjust: XRRGetOutputInfo failed");
  427. XRRFreeScreenResources(res);
  428. return rfb::resultInvalid;
  429. }
  430. if (!output->crtc) {
  431. vlog.debug("Resize adjust: Selected output has no CRTC");
  432. XRRFreeScreenResources(res);
  433. XRRFreeOutputInfo(output);
  434. return rfb::resultInvalid;
  435. }
  436. XRRCrtcInfo *crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
  437. if (!crtc) {
  438. vlog.debug("Resize adjust: XRRGetCrtcInfo failed");
  439. XRRFreeScreenResources(res);
  440. XRRFreeOutputInfo(output);
  441. return rfb::resultInvalid;
  442. }
  443. unsigned int swidth = iter->dimensions.width();
  444. unsigned int sheight = iter->dimensions.height();
  445. switch (crtc->rotation) {
  446. case RR_Rotate_90:
  447. case RR_Rotate_270:
  448. unsigned int swap = swidth;
  449. swidth = sheight;
  450. sheight = swap;
  451. break;
  452. }
  453. GetSmallerMode(res, output, &swidth, &sheight);
  454. XRRFreeOutputInfo(output);
  455. switch (crtc->rotation) {
  456. case RR_Rotate_90:
  457. case RR_Rotate_270:
  458. unsigned int swap = swidth;
  459. swidth = sheight;
  460. sheight = swap;
  461. break;
  462. }
  463. XRRFreeCrtcInfo(crtc);
  464. if (sheight != 0 && swidth != 0) {
  465. vlog.debug("Adjusted resize request to %dx%d", swidth, sheight);
  466. iter->dimensions.setXYWH(0, 0, swidth, sheight);
  467. fb_width = swidth;
  468. fb_height = sheight;
  469. } else {
  470. vlog.error("Failed to find smaller or equal screen size");
  471. XRRFreeScreenResources(res);
  472. return rfb::resultInvalid;
  473. }
  474. }
  475. vlog.debug("Changing screen layout");
  476. unsigned int ret = ::setScreenLayout(fb_width, fb_height, adjustedLayout, &outputIdMap);
  477. XRRFreeScreenResources(res);
  478. /* Send a dummy event to the root window. When this event is seen,
  479. earlier change events (ConfigureNotify and/or CrtcChange) have
  480. been processed. An Expose event is used for simplicity; does not
  481. require any Atoms, and will not affect other applications. */
  482. unsigned long serial = XNextRequest(dpy);
  483. XExposeEvent ev = {}; /* zero x, y, width, height, count */
  484. ev.type = Expose;
  485. ev.display = dpy;
  486. ev.window = DefaultRootWindow(dpy);
  487. if (XSendEvent(dpy, DefaultRootWindow(dpy), False, ExposureMask, (XEvent*)&ev)) {
  488. while (randrSyncSerial < serial) {
  489. TXWindow::handleXEvents(dpy);
  490. }
  491. } else {
  492. vlog.error("XSendEvent failed");
  493. }
  494. /* The protocol requires that an error is returned if the requested
  495. layout could not be set. This is checked by
  496. VNCSConnectionST::setDesktopSize. Another ExtendedDesktopSize
  497. with reason=0 will be sent in response to the changes seen by the
  498. event handler. */
  499. if (adjustedLayout != layout)
  500. return rfb::resultInvalid;
  501. // Explicitly update the server state with the result as there
  502. // can be corner cases where we don't get feedback from the X server
  503. server->setScreenLayout(computeScreenLayout());
  504. return ret;
  505. #else
  506. return rfb::resultProhibited;
  507. #endif /* HAVE_XRANDR */
  508. }
  509. bool XDesktop::handleGlobalEvent(XEvent* ev) {
  510. if (ev->type == xkbEventBase + XkbEventCode) {
  511. XkbEvent *kb = (XkbEvent *)ev;
  512. if (kb->any.xkb_type != XkbIndicatorStateNotify)
  513. return false;
  514. vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
  515. ledState = 0;
  516. for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
  517. if (kb->indicators.state & ledMasks[i])
  518. ledState |= 1u << i;
  519. }
  520. if (running)
  521. server->setLEDState(ledState);
  522. return true;
  523. #ifdef HAVE_XDAMAGE
  524. } else if (ev->type == xdamageEventBase) {
  525. XDamageNotifyEvent* dev;
  526. Rect rect;
  527. if (!running)
  528. return true;
  529. dev = (XDamageNotifyEvent*)ev;
  530. rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
  531. server->add_changed(rect);
  532. return true;
  533. #endif
  534. #ifdef HAVE_XFIXES
  535. } else if (ev->type == xfixesEventBase + XFixesCursorNotify) {
  536. XFixesCursorNotifyEvent* cev;
  537. if (!running)
  538. return true;
  539. cev = (XFixesCursorNotifyEvent*)ev;
  540. if (cev->subtype != XFixesDisplayCursorNotify)
  541. return false;
  542. return setCursor();
  543. #endif
  544. #ifdef HAVE_XRANDR
  545. } else if (ev->type == Expose) {
  546. XExposeEvent* eev = (XExposeEvent*)ev;
  547. randrSyncSerial = eev->serial;
  548. return false;
  549. } else if (ev->type == ConfigureNotify) {
  550. XConfigureEvent* cev = (XConfigureEvent*)ev;
  551. if (cev->window != DefaultRootWindow(dpy)) {
  552. return false;
  553. }
  554. XRRUpdateConfiguration(ev);
  555. geometry->recalc(cev->width, cev->height);
  556. if (!running) {
  557. return false;
  558. }
  559. if ((cev->width != pb->width() || (cev->height != pb->height()))) {
  560. // Recreate pixel buffer
  561. ImageFactory factory((bool)useShm);
  562. delete pb;
  563. pb = new XPixelBuffer(dpy, factory, geometry->getRect());
  564. server->setPixelBuffer(pb, computeScreenLayout());
  565. // Mark entire screen as changed
  566. server->add_changed(rfb::Region(Rect(0, 0, cev->width, cev->height)));
  567. }
  568. return true;
  569. } else if (ev->type == xrandrEventBase + RRNotify) {
  570. XRRNotifyEvent* rev = (XRRNotifyEvent*)ev;
  571. if (rev->window != DefaultRootWindow(dpy)) {
  572. return false;
  573. }
  574. if (!running)
  575. return false;
  576. if (rev->subtype == RRNotify_CrtcChange) {
  577. server->setScreenLayout(computeScreenLayout());
  578. }
  579. return true;
  580. #endif
  581. }
  582. return false;
  583. }
  584. bool XDesktop::setCursor()
  585. {
  586. XFixesCursorImage *cim;
  587. cim = XFixesGetCursorImage(dpy);
  588. if (cim == NULL)
  589. return false;
  590. // Copied from XserverDesktop::setCursor() in
  591. // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
  592. // handle long -> U32 conversion for 64-bit Xlib
  593. rdr::U8* cursorData;
  594. rdr::U8 *out;
  595. const unsigned long *pixels;
  596. cursorData = new rdr::U8[cim->width * cim->height * 4];
  597. // Un-premultiply alpha
  598. pixels = cim->pixels;
  599. out = cursorData;
  600. for (int y = 0; y < cim->height; y++) {
  601. for (int x = 0; x < cim->width; x++) {
  602. rdr::U8 alpha;
  603. rdr::U32 pixel = *pixels++;
  604. alpha = (pixel >> 24) & 0xff;
  605. if (alpha == 0)
  606. alpha = 1; // Avoid division by zero
  607. *out++ = ((pixel >> 16) & 0xff) * 255/alpha;
  608. *out++ = ((pixel >> 8) & 0xff) * 255/alpha;
  609. *out++ = ((pixel >> 0) & 0xff) * 255/alpha;
  610. *out++ = ((pixel >> 24) & 0xff);
  611. }
  612. }
  613. try {
  614. server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
  615. cursorData);
  616. } catch (rdr::Exception& e) {
  617. vlog.error("XserverDesktop::setCursor: %s",e.str());
  618. }
  619. delete [] cursorData;
  620. XFree(cim);
  621. return true;
  622. }