You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XDesktop.cxx 23KB

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