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.

XserverDesktop.cc 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2011 Pierre Ossman for Cendio AB
  3. * Copyright 2014 Brian P. Hinz
  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. //
  21. // XserverDesktop.cxx
  22. //
  23. #ifdef HAVE_DIX_CONFIG_H
  24. #include <dix-config.h>
  25. #endif
  26. #include <assert.h>
  27. #include <stdio.h>
  28. #include <strings.h>
  29. #include <unistd.h>
  30. #include <pwd.h>
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. #include <fcntl.h>
  34. #include <sys/utsname.h>
  35. #include <network/TcpSocket.h>
  36. #include <rfb/Exception.h>
  37. #include <rfb/VNCServerST.h>
  38. #include <rfb/HTTPServer.h>
  39. #include <rfb/LogWriter.h>
  40. #include <rfb/Configuration.h>
  41. #include "XserverDesktop.h"
  42. #include "vncExtInit.h"
  43. #include "xorg-version.h"
  44. #include "Input.h"
  45. extern "C" {
  46. #define public c_public
  47. #define class c_class
  48. extern char *display;
  49. #ifdef RANDR
  50. #include "randrstr.h"
  51. #endif
  52. #include "cursorstr.h"
  53. #undef public
  54. #undef class
  55. }
  56. using namespace rfb;
  57. using namespace network;
  58. static LogWriter vlog("XserverDesktop");
  59. IntParameter queryConnectTimeout("QueryConnectTimeout",
  60. "Number of seconds to show the Accept Connection dialog before "
  61. "rejecting the connection",
  62. 10);
  63. static rdr::U8 reverseBits[] = {
  64. 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
  65. 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
  66. 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
  67. 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  68. 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
  69. 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
  70. 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
  71. 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  72. 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
  73. 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
  74. 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
  75. 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  76. 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
  77. 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
  78. 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
  79. 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  80. 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
  81. 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
  82. 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
  83. 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  84. 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
  85. 0x3f, 0xbf, 0x7f, 0xff
  86. };
  87. class FileHTTPServer : public rfb::HTTPServer {
  88. public:
  89. FileHTTPServer(XserverDesktop* d) : desktop(d) {}
  90. virtual ~FileHTTPServer() {}
  91. virtual rdr::InStream* getFile(const char* name, const char** contentType,
  92. int* contentLength, time_t* lastModified)
  93. {
  94. if (name[0] != '/' || strstr(name, "..") != 0) {
  95. vlog.info("http request was for invalid file name");
  96. return 0;
  97. }
  98. if (strcmp(name, "/") == 0) name = "/index.vnc";
  99. CharArray httpDirStr(httpDir.getData());
  100. CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
  101. sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
  102. int fd = open(fname.buf, O_RDONLY);
  103. if (fd < 0) return 0;
  104. rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
  105. *contentType = guessContentType(name, *contentType);
  106. if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
  107. is = new rdr::SubstitutingInStream(is, desktop, 20);
  108. *contentType = "text/html";
  109. } else {
  110. struct stat st;
  111. if (fstat(fd, &st) == 0) {
  112. *contentLength = st.st_size;
  113. *lastModified = st.st_mtime;
  114. }
  115. }
  116. return is;
  117. }
  118. XserverDesktop* desktop;
  119. };
  120. XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
  121. network::TcpListener* listener_,
  122. network::TcpListener* httpListener_,
  123. const char* name, const rfb::PixelFormat &pf,
  124. void* fbptr, int stride)
  125. : pScreen(pScreen_),
  126. server(0), httpServer(0),
  127. listener(listener_), httpListener(httpListener_),
  128. deferredUpdateTimerSet(false),
  129. grabbing(false), ignoreHooks_(false), directFbptr(true),
  130. queryConnectId(0)
  131. {
  132. format = pf;
  133. server = new VNCServerST(name, this);
  134. setFramebuffer(pScreen->width, pScreen->height, fbptr, stride);
  135. server->setQueryConnectionHandler(this);
  136. if (httpListener)
  137. httpServer = new FileHTTPServer(this);
  138. inputDevice = new InputDevice(server);
  139. }
  140. XserverDesktop::~XserverDesktop()
  141. {
  142. if (!directFbptr)
  143. delete [] data;
  144. delete inputDevice;
  145. delete httpServer;
  146. delete server;
  147. }
  148. void XserverDesktop::blockUpdates()
  149. {
  150. server->blockUpdates();
  151. }
  152. void XserverDesktop::unblockUpdates()
  153. {
  154. server->unblockUpdates();
  155. }
  156. void XserverDesktop::setFramebuffer(int w, int h, void* fbptr, int stride_)
  157. {
  158. ScreenSet layout;
  159. width_ = w;
  160. height_ = h;
  161. if (!directFbptr) {
  162. delete [] data;
  163. directFbptr = true;
  164. }
  165. if (!fbptr) {
  166. fbptr = new rdr::U8[w * h * (format.bpp/8)];
  167. stride_ = w;
  168. directFbptr = false;
  169. }
  170. data = (rdr::U8*)fbptr;
  171. stride = stride_;
  172. layout = computeScreenLayout();
  173. server->setPixelBuffer(this, layout);
  174. }
  175. void XserverDesktop::refreshScreenLayout()
  176. {
  177. server->setScreenLayout(computeScreenLayout());
  178. }
  179. ScreenSet XserverDesktop::computeScreenLayout()
  180. {
  181. ScreenSet layout;
  182. #ifdef RANDR
  183. rrScrPrivPtr rp = rrGetScrPriv(pScreen);
  184. OutputIdMap newIdMap;
  185. for (int i = 0;i < rp->numOutputs;i++) {
  186. RROutputPtr output;
  187. RRCrtcPtr crtc;
  188. output = rp->outputs[i];
  189. crtc = output->crtc;
  190. /* Disabled? */
  191. if ((crtc == NULL) || (crtc->mode == NULL))
  192. continue;
  193. /* Known output? */
  194. if (outputIdMap.count(output) == 1)
  195. newIdMap[output] = outputIdMap[output];
  196. else {
  197. rdr::U32 id;
  198. OutputIdMap::const_iterator iter;
  199. while (true) {
  200. id = rand();
  201. for (iter = outputIdMap.begin();iter != outputIdMap.end();++iter) {
  202. if (iter->second == id)
  203. break;
  204. }
  205. if (iter == outputIdMap.end())
  206. break;
  207. }
  208. newIdMap[output] = id;
  209. }
  210. layout.add_screen(Screen(newIdMap[output], crtc->x, crtc->y,
  211. crtc->mode->mode.width,
  212. crtc->mode->mode.height,
  213. 0));
  214. }
  215. /* Only keep the entries that are currently active */
  216. outputIdMap = newIdMap;
  217. #endif
  218. /*
  219. * Make sure we have something to display. Hopefully it's just temporary
  220. * that we have no active outputs...
  221. */
  222. if (layout.num_screens() == 0)
  223. layout.add_screen(Screen(0, 0, 0, pScreen->width, pScreen->height, 0));
  224. return layout;
  225. }
  226. #ifdef RANDR
  227. extern RRModePtr vncRandRModeGet(int width, int height);
  228. RRModePtr XserverDesktop::findRandRMode(RROutputPtr output, int width, int height)
  229. {
  230. RRModePtr mode;
  231. for (int i = 0;i < output->numModes;i++) {
  232. if ((output->modes[i]->mode.width == width) &&
  233. (output->modes[i]->mode.height == height))
  234. return output->modes[i];
  235. }
  236. mode = vncRandRModeGet(width, height);
  237. if (mode != NULL)
  238. return mode;
  239. return NULL;
  240. }
  241. #endif
  242. char* XserverDesktop::substitute(const char* varName)
  243. {
  244. if (strcmp(varName, "$$") == 0) {
  245. return rfb::strDup("$");
  246. }
  247. if (strcmp(varName, "$PORT") == 0) {
  248. char* str = new char[10];
  249. sprintf(str, "%d", listener ? listener->getMyPort() : 0);
  250. return str;
  251. }
  252. if (strcmp(varName, "$WIDTH") == 0) {
  253. char* str = new char[10];
  254. sprintf(str, "%d", width());
  255. return str;
  256. }
  257. if (strcmp(varName, "$HEIGHT") == 0) {
  258. char* str = new char[10];
  259. sprintf(str, "%d", height());
  260. return str;
  261. }
  262. if (strcmp(varName, "$APPLETWIDTH") == 0) {
  263. char* str = new char[10];
  264. sprintf(str, "%d", width());
  265. return str;
  266. }
  267. if (strcmp(varName, "$APPLETHEIGHT") == 0) {
  268. char* str = new char[10];
  269. sprintf(str, "%d", height());
  270. return str;
  271. }
  272. if (strcmp(varName, "$DESKTOP") == 0) {
  273. return rfb::strDup(server->getName());
  274. }
  275. if (strcmp(varName, "$DISPLAY") == 0) {
  276. struct utsname uts;
  277. uname(&uts);
  278. char* str = new char[256];
  279. strncpy(str, uts.nodename, 240);
  280. str[239] = '\0'; /* Ensure string is zero-terminated */
  281. strcat(str, ":");
  282. strncat(str, display, 10);
  283. return str;
  284. }
  285. if (strcmp(varName, "$USER") == 0) {
  286. struct passwd* user = getpwuid(getuid());
  287. return rfb::strDup(user ? user->pw_name : "?");
  288. }
  289. return 0;
  290. }
  291. rfb::VNCServerST::queryResult
  292. XserverDesktop::queryConnection(network::Socket* sock,
  293. const char* userName,
  294. char** reason) {
  295. if (queryConnectId) {
  296. *reason = strDup("Another connection is currently being queried.");
  297. return rfb::VNCServerST::REJECT;
  298. }
  299. queryConnectAddress.replaceBuf(sock->getPeerAddress());
  300. if (!userName)
  301. userName = "(anonymous)";
  302. queryConnectUsername.replaceBuf(strDup(userName));
  303. queryConnectId = sock;
  304. vncQueryConnect(this, sock);
  305. return rfb::VNCServerST::PENDING;
  306. }
  307. void XserverDesktop::bell()
  308. {
  309. server->bell();
  310. }
  311. void XserverDesktop::serverCutText(const char* str, int len)
  312. {
  313. try {
  314. server->serverCutText(str, len);
  315. } catch (rdr::Exception& e) {
  316. vlog.error("XserverDesktop::serverCutText: %s",e.str());
  317. }
  318. }
  319. void XserverDesktop::setDesktopName(const char* name)
  320. {
  321. try {
  322. server->setName(name);
  323. } catch (rdr::Exception& e) {
  324. vlog.error("XserverDesktop::setDesktopName: %s",e.str());
  325. }
  326. }
  327. void XserverDesktop::setCursor(CursorPtr cursor)
  328. {
  329. try {
  330. int w = cursor->bits->width;
  331. int h = cursor->bits->height;
  332. rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)];
  333. int rfbMaskBytesPerRow = (w + 7) / 8;
  334. rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h];
  335. #ifdef ARGB_CURSOR
  336. if (cursor->bits->argb) {
  337. rdr::U8 *out;
  338. CARD32 *in;
  339. rdr::U8 rgb[3];
  340. memset(cursorMask, 0, rfbMaskBytesPerRow * h);
  341. in = cursor->bits->argb;
  342. out = cursorData;
  343. for (int y = 0; y < h; y++) {
  344. for (int x = 0; x < w; x++) {
  345. rgb[0] = (*in >> 16) & 0xff;
  346. rgb[1] = (*in >> 8) & 0xff;
  347. rgb[2] = (*in >> 0) & 0xff;
  348. getPF().bufferFromRGB(out, rgb, 1);
  349. if (((*in >> 24) & 0xff) > 127)
  350. cursorMask[y * rfbMaskBytesPerRow + x/8] |= 0x80>>(x%8);
  351. in++;
  352. out += getPF().bpp/8;
  353. }
  354. }
  355. } else {
  356. #endif
  357. rdr::U8 rgb[3];
  358. rdr::U8 fg[4], bg[4];
  359. rdr::U8* buffer;
  360. rgb[0] = cursor->foreRed;
  361. rgb[1] = cursor->foreGreen;
  362. rgb[2] = cursor->foreBlue;
  363. getPF().bufferFromRGB(fg, rgb, 1);
  364. rgb[0] = cursor->backRed;
  365. rgb[1] = cursor->backGreen;
  366. rgb[2] = cursor->backBlue;
  367. getPF().bufferFromRGB(bg, rgb, 1);
  368. int xMaskBytesPerRow = BitmapBytePad(w);
  369. buffer = cursorData;
  370. for (int y = 0; y < h; y++) {
  371. for (int x = 0; x < w; x++) {
  372. rdr::U8 *pixel;
  373. int byte = y * xMaskBytesPerRow + x / 8;
  374. #if (BITMAP_BIT_ORDER == MSBFirst)
  375. int bit = 7 - x % 8;
  376. #else
  377. int bit = x % 8;
  378. #endif
  379. if (cursor->bits->source[byte] & (1 << bit))
  380. pixel = fg;
  381. else
  382. pixel = bg;
  383. memcpy(buffer, pixel, getPF().bpp/8);
  384. buffer += getPF().bpp/8;
  385. }
  386. }
  387. for (int j = 0; j < h; j++) {
  388. for (int i = 0; i < rfbMaskBytesPerRow; i++)
  389. #if (BITMAP_BIT_ORDER == MSBFirst)
  390. cursorMask[j * rfbMaskBytesPerRow + i]
  391. = cursor->bits->mask[j * xMaskBytesPerRow + i];
  392. #else
  393. cursorMask[j * rfbMaskBytesPerRow + i]
  394. = reverseBits[cursor->bits->mask[j * xMaskBytesPerRow + i]];
  395. #endif
  396. }
  397. #ifdef ARGB_CURSOR
  398. }
  399. #endif
  400. server->setCursor(cursor->bits->width, cursor->bits->height,
  401. Point(cursor->bits->xhot, cursor->bits->yhot),
  402. cursorData, cursorMask);
  403. delete [] cursorData;
  404. delete [] cursorMask;
  405. } catch (rdr::Exception& e) {
  406. vlog.error("XserverDesktop::setCursor: %s",e.str());
  407. }
  408. }
  409. void XserverDesktop::add_changed(RegionPtr reg)
  410. {
  411. if (ignoreHooks_) return;
  412. if (grabbing) return;
  413. try {
  414. rfb::Region rfbReg;
  415. rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, reg),
  416. REGION_NUM_RECTS(reg),
  417. (ShortRect*)REGION_RECTS(reg));
  418. server->add_changed(rfbReg);
  419. } catch (rdr::Exception& e) {
  420. vlog.error("XserverDesktop::add_changed: %s",e.str());
  421. }
  422. }
  423. void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy)
  424. {
  425. if (ignoreHooks_) return;
  426. if (grabbing) return;
  427. try {
  428. rfb::Region rfbReg;
  429. rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, dst),
  430. REGION_NUM_RECTS(dst),
  431. (ShortRect*)REGION_RECTS(dst));
  432. server->add_copied(rfbReg, rfb::Point(dx, dy));
  433. } catch (rdr::Exception& e) {
  434. vlog.error("XserverDesktop::add_copied: %s",e.str());
  435. }
  436. }
  437. static struct timeval XserverDesktopTimeout;
  438. void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout)
  439. {
  440. // We don't have a good callback for when we can init input devices[1],
  441. // so we abuse the fact that this routine will be called first thing
  442. // once the dix is done initialising.
  443. // [1] Technically Xvnc has InitInput(), but libvnc.so has nothing.
  444. inputDevice->InitInputDevice();
  445. try {
  446. int nextTimeout;
  447. // Add all sockets we want read events for, after purging
  448. // any closed sockets.
  449. if (listener)
  450. FD_SET(listener->getFd(), fds);
  451. if (httpListener)
  452. FD_SET(httpListener->getFd(), fds);
  453. std::list<Socket*> sockets;
  454. server->getSockets(&sockets);
  455. std::list<Socket*>::iterator i;
  456. for (i = sockets.begin(); i != sockets.end(); i++) {
  457. int fd = (*i)->getFd();
  458. if ((*i)->isShutdown()) {
  459. vlog.debug("client gone, sock %d",fd);
  460. server->removeSocket(*i);
  461. vncClientGone(fd);
  462. delete (*i);
  463. } else {
  464. FD_SET(fd, fds);
  465. }
  466. }
  467. if (httpServer) {
  468. httpServer->getSockets(&sockets);
  469. for (i = sockets.begin(); i != sockets.end(); i++) {
  470. int fd = (*i)->getFd();
  471. if ((*i)->isShutdown()) {
  472. vlog.debug("http client gone, sock %d",fd);
  473. httpServer->removeSocket(*i);
  474. delete (*i);
  475. } else {
  476. FD_SET(fd, fds);
  477. }
  478. }
  479. }
  480. // Then check when the next timer will expire.
  481. // (this unfortunately also triggers any already expired timers)
  482. nextTimeout = server->checkTimeouts();
  483. if (nextTimeout > 0) {
  484. // No timeout specified? Or later timeout than we need?
  485. if ((*timeout == NULL) ||
  486. ((*timeout)->tv_sec > (nextTimeout/1000)) ||
  487. (((*timeout)->tv_sec == (nextTimeout/1000)) &&
  488. ((*timeout)->tv_usec > ((nextTimeout%1000)*1000)))) {
  489. XserverDesktopTimeout.tv_sec = nextTimeout/1000;
  490. XserverDesktopTimeout.tv_usec = (nextTimeout%1000)*1000;
  491. *timeout = &XserverDesktopTimeout;
  492. }
  493. }
  494. } catch (rdr::Exception& e) {
  495. vlog.error("XserverDesktop::blockHandler: %s",e.str());
  496. }
  497. }
  498. void XserverDesktop::wakeupHandler(fd_set* fds, int nfds)
  499. {
  500. try {
  501. // First check for file descriptors with something to do
  502. if (nfds >= 1) {
  503. if (listener) {
  504. if (FD_ISSET(listener->getFd(), fds)) {
  505. FD_CLR(listener->getFd(), fds);
  506. Socket* sock = listener->accept();
  507. sock->outStream().setBlocking(false);
  508. server->addSocket(sock);
  509. vlog.debug("new client, sock %d",sock->getFd());
  510. }
  511. }
  512. if (httpListener) {
  513. if (FD_ISSET(httpListener->getFd(), fds)) {
  514. FD_CLR(httpListener->getFd(), fds);
  515. Socket* sock = httpListener->accept();
  516. sock->outStream().setBlocking(false);
  517. httpServer->addSocket(sock);
  518. vlog.debug("new http client, sock %d",sock->getFd());
  519. }
  520. }
  521. std::list<Socket*> sockets;
  522. server->getSockets(&sockets);
  523. std::list<Socket*>::iterator i;
  524. for (i = sockets.begin(); i != sockets.end(); i++) {
  525. int fd = (*i)->getFd();
  526. if (FD_ISSET(fd, fds)) {
  527. FD_CLR(fd, fds);
  528. server->processSocketEvent(*i);
  529. }
  530. }
  531. if (httpServer) {
  532. httpServer->getSockets(&sockets);
  533. for (i = sockets.begin(); i != sockets.end(); i++) {
  534. int fd = (*i)->getFd();
  535. if (FD_ISSET(fd, fds)) {
  536. FD_CLR(fd, fds);
  537. httpServer->processSocketEvent(*i);
  538. }
  539. }
  540. }
  541. inputDevice->PointerSync();
  542. }
  543. // Then let the timers do some processing. Rescheduling is done in
  544. // blockHandler().
  545. server->checkTimeouts();
  546. } catch (rdr::Exception& e) {
  547. vlog.error("XserverDesktop::wakeupHandler: %s",e.str());
  548. }
  549. }
  550. void XserverDesktop::writeBlockHandler(fd_set* fds)
  551. {
  552. try {
  553. std::list<Socket*> sockets;
  554. std::list<Socket*>::iterator i;
  555. server->getSockets(&sockets);
  556. for (i = sockets.begin(); i != sockets.end(); i++) {
  557. int fd = (*i)->getFd();
  558. if ((*i)->isShutdown()) {
  559. vlog.debug("client gone, sock %d",fd);
  560. server->removeSocket(*i);
  561. vncClientGone(fd);
  562. delete (*i);
  563. } else {
  564. if ((*i)->outStream().bufferUsage() > 0)
  565. FD_SET(fd, fds);
  566. }
  567. }
  568. if (httpServer) {
  569. httpServer->getSockets(&sockets);
  570. for (i = sockets.begin(); i != sockets.end(); i++) {
  571. int fd = (*i)->getFd();
  572. if ((*i)->isShutdown()) {
  573. vlog.debug("http client gone, sock %d",fd);
  574. httpServer->removeSocket(*i);
  575. delete (*i);
  576. } else {
  577. if ((*i)->outStream().bufferUsage() > 0)
  578. FD_SET(fd, fds);
  579. }
  580. }
  581. }
  582. } catch (rdr::Exception& e) {
  583. vlog.error("XserverDesktop::writeBlockHandler: %s",e.str());
  584. }
  585. }
  586. void XserverDesktop::writeWakeupHandler(fd_set* fds, int nfds)
  587. {
  588. if (nfds < 1)
  589. return;
  590. try {
  591. std::list<Socket*> sockets;
  592. std::list<Socket*>::iterator i;
  593. server->getSockets(&sockets);
  594. for (i = sockets.begin(); i != sockets.end(); i++) {
  595. int fd = (*i)->getFd();
  596. if (FD_ISSET(fd, fds)) {
  597. FD_CLR(fd, fds);
  598. (*i)->outStream().flush();
  599. }
  600. }
  601. if (httpServer) {
  602. httpServer->getSockets(&sockets);
  603. for (i = sockets.begin(); i != sockets.end(); i++) {
  604. int fd = (*i)->getFd();
  605. if (FD_ISSET(fd, fds)) {
  606. FD_CLR(fd, fds);
  607. (*i)->outStream().flush();
  608. }
  609. }
  610. }
  611. } catch (rdr::Exception& e) {
  612. vlog.error("XserverDesktop::writeWakeupHandler: %s",e.str());
  613. }
  614. }
  615. void XserverDesktop::addClient(Socket* sock, bool reverse)
  616. {
  617. vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
  618. server->addSocket(sock, reverse);
  619. }
  620. void XserverDesktop::disconnectClients()
  621. {
  622. vlog.debug("disconnecting all clients");
  623. return server->closeClients("Disconnection from server end");
  624. }
  625. int XserverDesktop::getQueryTimeout(void* opaqueId,
  626. const char** address,
  627. const char** username)
  628. {
  629. if (opaqueId && queryConnectId == opaqueId) {
  630. vlog.info("address=%s, username=%s, timeout=%d",
  631. queryConnectAddress.buf, queryConnectUsername.buf,
  632. (int)queryConnectTimeout);
  633. if (address) *address = queryConnectAddress.buf;
  634. if (username) *username = queryConnectUsername.buf;
  635. return queryConnectTimeout;
  636. }
  637. return 0;
  638. }
  639. void XserverDesktop::approveConnection(void* opaqueId, bool accept,
  640. const char* rejectMsg)
  641. {
  642. if (queryConnectId == opaqueId) {
  643. server->approveConnection((network::Socket*)opaqueId, accept, rejectMsg);
  644. queryConnectId = 0;
  645. }
  646. }
  647. ///////////////////////////////////////////////////////////////////////////
  648. //
  649. // SDesktop callbacks
  650. void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
  651. {
  652. inputDevice->PointerMove(pos);
  653. inputDevice->PointerButtonAction(buttonMask);
  654. }
  655. void XserverDesktop::clientCutText(const char* str, int len)
  656. {
  657. vncClientCutText(str, len);
  658. }
  659. extern RROutputPtr vncRandROutputCreate(ScreenPtr pScreen);
  660. unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
  661. const rfb::ScreenSet& layout)
  662. {
  663. #ifndef RANDR
  664. return rfb::resultProhibited;
  665. #else
  666. int availableOutputs;
  667. Bool ret;
  668. rrScrPrivPtr rp = rrGetScrPriv(pScreen);
  669. /*
  670. * First check that we don't have any active clone modes. That's just
  671. * too messy to deal with.
  672. */
  673. for (int i = 0;i < rp->numCrtcs;i++) {
  674. if (rp->crtcs[i]->numOutputs > 1) {
  675. vlog.error("Clone mode active. Refusing to touch screen layout.");
  676. return rfb::resultInvalid;
  677. }
  678. }
  679. /*
  680. * Next count how many useful outputs we have...
  681. *
  682. * This gets slightly complicated because we might need to hook a CRTC
  683. * up to the output, but also check that we don't try to use the same
  684. * CRTC for multiple outputs.
  685. */
  686. std::set<RRCrtcPtr> usedCrtcs;
  687. availableOutputs = 0;
  688. for (int i = 0;i < rp->numOutputs;i++) {
  689. RROutputPtr output;
  690. output = rp->outputs[i];
  691. if (output->crtc != NULL)
  692. availableOutputs++;
  693. else {
  694. for (int j = 0;j < output->numCrtcs;j++) {
  695. if (output->crtcs[j]->numOutputs != 0)
  696. continue;
  697. if (usedCrtcs.count(output->crtcs[j]) != 0)
  698. continue;
  699. availableOutputs++;
  700. usedCrtcs.insert(output->crtcs[j]);
  701. break;
  702. }
  703. }
  704. }
  705. /* Try to create more outputs if needed... (only works on Xvnc) */
  706. if (layout.num_screens() > availableOutputs) {
  707. for (int i = 0;i < (layout.num_screens() - availableOutputs);i++) {
  708. RROutputPtr output;
  709. output = vncRandROutputCreate(pScreen);
  710. if (output == NULL) {
  711. vlog.error("Unable to create more screens, as needed by the new client layout.");
  712. return rfb::resultInvalid;
  713. }
  714. }
  715. }
  716. /* First we might need to resize the screen */
  717. if ((fb_width != pScreen->width) || (fb_height != pScreen->height)) {
  718. /* Try to retain DPI when we resize */
  719. ret = RRScreenSizeSet(pScreen, fb_width, fb_height,
  720. pScreen->mmWidth * fb_width / pScreen->width,
  721. pScreen->mmHeight * fb_height / pScreen->height);
  722. if (!ret) {
  723. vlog.error("Failed to resize screen to %dx%d", fb_width, fb_height);
  724. return rfb::resultInvalid;
  725. }
  726. }
  727. /* Next, reconfigure all known outputs, and turn off the other ones */
  728. for (int i = 0;i < rp->numOutputs;i++) {
  729. RROutputPtr output;
  730. RRCrtcPtr crtc;
  731. RRModePtr mode;
  732. ScreenSet::const_iterator iter;
  733. output = rp->outputs[i];
  734. crtc = output->crtc;
  735. /* Known? */
  736. if (outputIdMap.count(output) == 0)
  737. continue;
  738. /* A known output should have a CRTC, but double check... */
  739. if (crtc == NULL) {
  740. vlog.error("Existing output '%s' has unexpectedly been disabled",
  741. output->name);
  742. continue;
  743. }
  744. /* Find the corresponding screen... */
  745. for (iter = layout.begin();iter != layout.end();++iter) {
  746. if (iter->id == outputIdMap[output])
  747. break;
  748. }
  749. /* Missing? */
  750. if (iter == layout.end()) {
  751. /* Disable and move on... */
  752. ret = RRCrtcSet(crtc, NULL, crtc->x, crtc->y, crtc->rotation, 0, NULL);
  753. if (!ret) {
  754. vlog.error("Failed to disable unused CRTC for output '%s'",
  755. output->name);
  756. return rfb::resultInvalid;
  757. }
  758. outputIdMap.erase(output);
  759. continue;
  760. }
  761. /* Need to switch mode? */
  762. if ((crtc->mode->mode.width == iter->dimensions.width()) &&
  763. (crtc->mode->mode.height == iter->dimensions.height()))
  764. mode = crtc->mode;
  765. else {
  766. mode = findRandRMode(output, iter->dimensions.width(),
  767. iter->dimensions.height());
  768. if (mode == NULL) {
  769. vlog.error("Failed to find a suitable mode for %dx%d for output '%s'",
  770. iter->dimensions.width(), iter->dimensions.height(),
  771. output->name);
  772. return rfb::resultInvalid;
  773. }
  774. }
  775. /* Reconfigure new mode and position */
  776. ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y,
  777. crtc->rotation, crtc->numOutputs, crtc->outputs);
  778. if (!ret) {
  779. vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
  780. output->name,
  781. iter->dimensions.width(), iter->dimensions.height(),
  782. iter->dimensions.tl.x, iter->dimensions.tl.y);
  783. return rfb::resultInvalid;
  784. }
  785. }
  786. /* Finally, allocate new outputs for new screens */
  787. ScreenSet::const_iterator iter;
  788. for (iter = layout.begin();iter != layout.end();++iter) {
  789. OutputIdMap::const_iterator oi;
  790. RROutputPtr output;
  791. RRCrtcPtr crtc;
  792. RRModePtr mode;
  793. int i;
  794. /* Does this screen have an output already? */
  795. for (oi = outputIdMap.begin();oi != outputIdMap.end();++oi) {
  796. if (oi->second == iter->id)
  797. break;
  798. }
  799. if (oi != outputIdMap.end())
  800. continue;
  801. /* Find an unused output */
  802. for (i = 0;i < rp->numOutputs;i++) {
  803. output = rp->outputs[i];
  804. crtc = output->crtc;
  805. /* In use? */
  806. if (outputIdMap.count(output) == 1)
  807. continue;
  808. /* Need a CRTC? */
  809. if (crtc == NULL) {
  810. for (int j = 0;j < output->numCrtcs;j++) {
  811. if (output->crtcs[j]->numOutputs != 0)
  812. continue;
  813. crtc = output->crtcs[j];
  814. break;
  815. }
  816. /* Couldn't find one... */
  817. if (crtc == NULL)
  818. continue;
  819. ret = RRCrtcSet(crtc, NULL, 0, 0, RR_Rotate_0,
  820. 1, &output);
  821. if (!ret) {
  822. vlog.error("Failed to associate a CRTC with output '%s'",
  823. output->name);
  824. return rfb::resultInvalid;
  825. }
  826. }
  827. break;
  828. }
  829. /* Shouldn't happen */
  830. if (i == rp->numOutputs)
  831. return rfb::resultInvalid;
  832. mode = findRandRMode(output, iter->dimensions.width(),
  833. iter->dimensions.height());
  834. if (mode == NULL) {
  835. vlog.error("Failed to find a suitable mode for %dx%d for output '%s'",
  836. iter->dimensions.width(), iter->dimensions.height(),
  837. output->name);
  838. return rfb::resultInvalid;
  839. }
  840. /*
  841. * Make sure we already have an entry for this, or
  842. * computeScreenLayout() will think it is a brand new output and
  843. * assign it a random id.
  844. */
  845. outputIdMap[output] = iter->id;
  846. /* Reconfigure new mode and position */
  847. ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y,
  848. crtc->rotation, crtc->numOutputs, crtc->outputs);
  849. if (!ret) {
  850. vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
  851. output->name,
  852. iter->dimensions.width(), iter->dimensions.height(),
  853. iter->dimensions.tl.x, iter->dimensions.tl.y);
  854. return rfb::resultInvalid;
  855. }
  856. }
  857. /*
  858. * Update timestamp for when screen layout was last changed.
  859. * This is normally done in the X11 request handlers, which is
  860. * why we have to deal with it manually here.
  861. */
  862. rp->lastSetTime = currentTime;
  863. return rfb::resultSuccess;
  864. #endif
  865. }
  866. void XserverDesktop::grabRegion(const rfb::Region& region)
  867. {
  868. if (directFbptr) return;
  869. if (!pScreen->GetImage) {
  870. vlog.error("VNC error: pScreen->GetImage == 0");
  871. return;
  872. }
  873. grabbing = true;
  874. int bytesPerPixel = format.bpp/8;
  875. int bytesPerRow = pScreen->width * bytesPerPixel;
  876. std::vector<rfb::Rect> rects;
  877. std::vector<rfb::Rect>::iterator i;
  878. region.get_rects(&rects);
  879. for (i = rects.begin(); i != rects.end(); i++) {
  880. for (int y = i->tl.y; y < i->br.y; y++) {
  881. DrawablePtr pDrawable;
  882. #if XORG < 19
  883. pDrawable = (DrawablePtr) WindowTable[pScreen->myNum];
  884. #else
  885. pDrawable = (DrawablePtr) pScreen->root;
  886. #endif
  887. (*pScreen->GetImage) (pDrawable, i->tl.x, y, i->width(), 1,
  888. ZPixmap, (unsigned long)~0L,
  889. ((char*)data
  890. + y * bytesPerRow + i->tl.x * bytesPerPixel));
  891. }
  892. }
  893. grabbing = false;
  894. }
  895. void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
  896. {
  897. if (down)
  898. inputDevice->KeyboardPress(keysym);
  899. else
  900. inputDevice->KeyboardRelease(keysym);
  901. }