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 26KB

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