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.

CConn.cxx 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. //
  19. // CConn.cxx
  20. //
  21. #include <unistd.h>
  22. #include "CConn.h"
  23. #include <rfb/CMsgWriter.h>
  24. #include <rfb/encodings.h>
  25. #include <rfb/secTypes.h>
  26. #include <rfb/CSecurityNone.h>
  27. #include <rfb/CSecurityVncAuth.h>
  28. #include <rfb/Hostname.h>
  29. #include <rfb/LogWriter.h>
  30. #include <rfb/util.h>
  31. #include <network/TcpSocket.h>
  32. #include "TXViewport.h"
  33. #include "DesktopWindow.h"
  34. #include "ServerDialog.h"
  35. #include "PasswdDialog.h"
  36. #include "parameters.h"
  37. using namespace rfb;
  38. static rfb::LogWriter vlog("CConn");
  39. IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
  40. "pixel data - a debugging feature", 0);
  41. StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
  42. "F8");
  43. StringParameter windowName("name", "The X window name", "");
  44. CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
  45. char* vncServerName)
  46. : dpy(dpy_), argc(argc_),
  47. argv(argv_), serverHost(0), serverPort(0), sock(sock_), viewport(0),
  48. desktop(0), desktopEventHandler(0),
  49. currentEncoding(encodingTight), lastServerEncoding((unsigned int)-1),
  50. fullColour(::fullColour),
  51. autoSelect(::autoSelect), shared(::shared), formatChange(false),
  52. encodingChange(false), sameMachine(false), fullScreen(::fullScreen),
  53. ctrlDown(false), altDown(false),
  54. menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy)
  55. {
  56. CharArray menuKeyStr(menuKey.getData());
  57. menuKeysym = XStringToKeysym(menuKeyStr.buf);
  58. setShared(shared);
  59. addSecType(secTypeNone);
  60. addSecType(secTypeVncAuth);
  61. CharArray encStr(preferredEncoding.getData());
  62. int encNum = encodingNum(encStr.buf);
  63. if (encNum != -1) {
  64. currentEncoding = encNum;
  65. autoSelect = false;
  66. }
  67. cp.supportsDesktopResize = true;
  68. cp.supportsLocalCursor = useLocalCursor;
  69. cp.customCompressLevel = customCompressLevel;
  70. cp.compressLevel = compressLevel;
  71. cp.noJpeg = noJpeg;
  72. cp.qualityLevel = qualityLevel;
  73. initMenu();
  74. if (sock) {
  75. char* name = sock->getPeerEndpoint();
  76. vlog.info("Accepted connection from %s", name);
  77. if (name) free(name);
  78. } else {
  79. if (vncServerName) {
  80. getHostAndPort(vncServerName, &serverHost, &serverPort);
  81. } else {
  82. ServerDialog dlg(dpy, &options, &about);
  83. if (!dlg.show() || dlg.entry.getText()[0] == 0) {
  84. exit(1);
  85. }
  86. getHostAndPort(dlg.entry.getText(), &serverHost, &serverPort);
  87. }
  88. sock = new network::TcpSocket(serverHost, serverPort);
  89. vlog.info("connected to host %s port %d", serverHost, serverPort);
  90. }
  91. sameMachine = sock->sameMachine();
  92. sock->inStream().setBlockCallback(this);
  93. setServerName(sock->getPeerEndpoint());
  94. setStreams(&sock->inStream(), &sock->outStream());
  95. initialiseProtocol();
  96. }
  97. CConn::~CConn() {
  98. free(serverHost);
  99. delete desktop;
  100. delete viewport;
  101. delete sock;
  102. }
  103. // deleteWindow() is called when the user closes the desktop or menu windows.
  104. void CConn::deleteWindow(TXWindow* w) {
  105. if (w == &menu) {
  106. menu.unmap();
  107. } else if (w == viewport) {
  108. exit(1);
  109. }
  110. }
  111. // handleEvent() filters all events on the desktop and menu. Most are passed
  112. // straight through. The exception is the F8 key. When pressed on the
  113. // desktop, it is used to bring up the menu. An F8 press or release on the
  114. // menu is passed through as if it were on the desktop.
  115. void CConn::handleEvent(TXWindow* w, XEvent* ev)
  116. {
  117. KeySym ks;
  118. char str[256];
  119. switch (ev->type) {
  120. case KeyPress:
  121. case KeyRelease:
  122. XLookupString(&ev->xkey, str, 256, &ks, NULL);
  123. if (ks == menuKeysym && (ev->xkey.state & (ShiftMask|ControlMask)) == 0) {
  124. if (w == desktop && ev->type == KeyPress) {
  125. showMenu(ev->xkey.x_root, ev->xkey.y_root);
  126. break;
  127. } else if (w == &menu) {
  128. if (ev->type == KeyPress) menu.unmap();
  129. desktopEventHandler->handleEvent(w, ev);
  130. break;
  131. }
  132. }
  133. // drop through
  134. default:
  135. if (w == desktop) desktopEventHandler->handleEvent(w, ev);
  136. else if (w == &menu) menuEventHandler->handleEvent(w, ev);
  137. }
  138. }
  139. // blockCallback() is called when reading from the socket would block. We
  140. // process X events until the socket is ready for reading again.
  141. void CConn::blockCallback() {
  142. fd_set rfds;
  143. do {
  144. TXWindow::handleXEvents(dpy);
  145. struct timeval tv;
  146. struct timeval* tvp;
  147. if (Timer::getTimeout(&tv))
  148. tvp = &tv;
  149. else
  150. tvp = 0;
  151. FD_ZERO(&rfds);
  152. FD_SET(ConnectionNumber(dpy), &rfds);
  153. FD_SET(sock->getFd(), &rfds);
  154. int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
  155. if (n < 0) throw rdr::SystemException("select",errno);
  156. Timer::callTimers();
  157. } while (!(FD_ISSET(sock->getFd(), &rfds)));
  158. }
  159. // getPasswd() is called by the CSecurity object when it needs us to read a
  160. // password from the user.
  161. bool CConn::getUserPasswd(char** user, char** password)
  162. {
  163. CharArray passwordFileStr(passwordFile.getData());
  164. if (!user && passwordFileStr.buf[0]) {
  165. FILE* fp = fopen(passwordFileStr.buf, "r");
  166. if (!fp) return false;
  167. char data[256];
  168. int datalen = fread(data, 1, 256, fp);
  169. fclose(fp);
  170. if (datalen != 8) return false;
  171. vncAuthUnobfuscatePasswd(data);
  172. *password = strDup(data);
  173. memset(data, 0, strlen(data));
  174. return true;
  175. }
  176. const char* secType = secTypeName(getCurrentCSecurity()->getType());
  177. const char* titlePrefix = "VNC Authentication";
  178. CharArray title(strlen(titlePrefix) + strlen(secType) + 4);
  179. sprintf(title.buf, "%s [%s]", titlePrefix, secType);
  180. PasswdDialog dlg(dpy, title.buf, !user);
  181. if (!dlg.show()) return false;
  182. if (user)
  183. *user = strDup(dlg.userEntry.getText());
  184. *password = strDup(dlg.passwdEntry.getText());
  185. return true;
  186. }
  187. // CConnection callback methods
  188. // getCSecurity() gets the appropriate CSecurity object for the security
  189. // types which we support.
  190. CSecurity* CConn::getCSecurity(int secType) {
  191. switch (secType) {
  192. case secTypeNone:
  193. return new CSecurityNone();
  194. case secTypeVncAuth:
  195. return new CSecurityVncAuth(this);
  196. default:
  197. throw rfb::Exception("Unsupported secType?");
  198. }
  199. }
  200. // serverInit() is called when the serverInit message has been received. At
  201. // this point we create the desktop window and display it. We also tell the
  202. // server the pixel format and encodings to use and request the first update.
  203. void CConn::serverInit() {
  204. CConnection::serverInit();
  205. serverPF = cp.pf();
  206. desktop = new DesktopWindow(dpy, cp.width, cp.height, serverPF, this);
  207. desktopEventHandler = desktop->setEventHandler(this);
  208. desktop->addEventMask(KeyPressMask | KeyReleaseMask);
  209. fullColourPF = desktop->getPF();
  210. if (!serverPF.trueColour)
  211. fullColour = true;
  212. recreateViewport();
  213. formatChange = encodingChange = true;
  214. requestNewUpdate();
  215. }
  216. // setDesktopSize() is called when the desktop size changes (including when
  217. // it is set initially).
  218. void CConn::setDesktopSize(int w, int h) {
  219. CConnection::setDesktopSize(w,h);
  220. if (desktop) {
  221. desktop->resize(w, h);
  222. recreateViewport();
  223. }
  224. }
  225. // framebufferUpdateEnd() is called at the end of an update.
  226. // For each rectangle, the FdInStream will have timed the speed
  227. // of the connection, allowing us to select format and encoding
  228. // appropriately, and then request another incremental update.
  229. void CConn::framebufferUpdateEnd() {
  230. if (debugDelay != 0) {
  231. XSync(dpy, False);
  232. struct timeval tv;
  233. tv.tv_sec = debugDelay / 1000;
  234. tv.tv_usec = (debugDelay % 1000) * 1000;
  235. select(0, 0, 0, 0, &tv);
  236. std::list<rfb::Rect>::iterator i;
  237. for (i = debugRects.begin(); i != debugRects.end(); i++) {
  238. desktop->invertRect(*i);
  239. }
  240. debugRects.clear();
  241. }
  242. desktop->framebufferUpdateEnd();
  243. if (autoSelect)
  244. autoSelectFormatAndEncoding();
  245. requestNewUpdate();
  246. }
  247. // The rest of the callbacks are fairly self-explanatory...
  248. void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
  249. {
  250. desktop->setColourMapEntries(firstColour, nColours, rgbs);
  251. }
  252. void CConn::bell() { XBell(dpy, 0); }
  253. void CConn::serverCutText(const char* str, int len) {
  254. desktop->serverCutText(str,len);
  255. }
  256. // We start timing on beginRect and stop timing on endRect, to
  257. // avoid skewing the bandwidth estimation as a result of the server
  258. // being slow or the network having high latency
  259. void CConn::beginRect(const Rect& r, unsigned int encoding)
  260. {
  261. sock->inStream().startTiming();
  262. if (encoding != encodingCopyRect) {
  263. lastServerEncoding = encoding;
  264. }
  265. }
  266. void CConn::endRect(const Rect& r, unsigned int encoding)
  267. {
  268. sock->inStream().stopTiming();
  269. if (debugDelay != 0) {
  270. desktop->invertRect(r);
  271. debugRects.push_back(r);
  272. }
  273. }
  274. void CConn::fillRect(const rfb::Rect& r, rfb::Pixel p) {
  275. desktop->fillRect(r,p);
  276. }
  277. void CConn::imageRect(const rfb::Rect& r, void* p) {
  278. desktop->imageRect(r,p);
  279. }
  280. void CConn::copyRect(const rfb::Rect& r, int sx, int sy) {
  281. desktop->copyRect(r,sx,sy);
  282. }
  283. void CConn::setCursor(const Point& hotspot, const Point& size,
  284. void* data, void* mask) {
  285. desktop->setCursor(hotspot, size, data, mask);
  286. }
  287. // Menu stuff - menuSelect() is called when the user selects a menu option.
  288. enum { ID_OPTIONS, ID_INFO, ID_FULLSCREEN, ID_REFRESH, ID_F8, ID_CTRLALTDEL,
  289. ID_ABOUT, ID_DISMISS, ID_EXIT, ID_NEWCONN, ID_CTRL, ID_ALT };
  290. void CConn::initMenu() {
  291. menuEventHandler = menu.setEventHandler(this);
  292. menu.addEventMask(KeyPressMask | KeyReleaseMask);
  293. menu.addEntry("Exit viewer", ID_EXIT);
  294. menu.addEntry(0, 0);
  295. menu.addEntry("Full screen", ID_FULLSCREEN);
  296. menu.check(ID_FULLSCREEN, fullScreen);
  297. menu.addEntry(0, 0);
  298. menu.addEntry("Ctrl", ID_CTRL);
  299. menu.addEntry("Alt", ID_ALT);
  300. CharArray menuKeyStr(menuKey.getData());
  301. CharArray sendMenuKey(6+strlen(menuKeyStr.buf));
  302. sprintf(sendMenuKey.buf, "Send %s", menuKeyStr.buf);
  303. menu.addEntry(sendMenuKey.buf, ID_F8);
  304. menu.addEntry("Send Ctrl-Alt-Del", ID_CTRLALTDEL);
  305. menu.addEntry(0, 0);
  306. menu.addEntry("Refresh screen", ID_REFRESH);
  307. menu.addEntry(0, 0);
  308. menu.addEntry("New connection...", ID_NEWCONN);
  309. menu.addEntry("Options...", ID_OPTIONS);
  310. menu.addEntry("Connection info...", ID_INFO);
  311. menu.addEntry("About VNCviewer...", ID_ABOUT);
  312. menu.addEntry(0, 0);
  313. menu.addEntry("Dismiss menu", ID_DISMISS);
  314. menu.toplevel("VNC Menu", this);
  315. menu.setBorderWidth(1);
  316. }
  317. void CConn::showMenu(int x, int y) {
  318. menu.check(ID_FULLSCREEN, fullScreen);
  319. menu.move(x, y);
  320. menu.raise();
  321. menu.map();
  322. }
  323. void CConn::menuSelect(long id, TXMenu* m) {
  324. switch (id) {
  325. case ID_NEWCONN:
  326. {
  327. menu.unmap();
  328. if (fullScreen) {
  329. fullScreen = false;
  330. if (viewport) recreateViewport();
  331. }
  332. int pid = fork();
  333. if (pid < 0) { perror("fork"); exit(1); }
  334. if (pid == 0) {
  335. delete sock;
  336. close(ConnectionNumber(dpy));
  337. struct timeval tv;
  338. tv.tv_sec = 0;
  339. tv.tv_usec = 200*1000;
  340. select(0, 0, 0, 0, &tv);
  341. execlp(programName, programName, 0);
  342. perror("execlp"); exit(1);
  343. }
  344. break;
  345. }
  346. case ID_OPTIONS:
  347. menu.unmap();
  348. options.show();
  349. break;
  350. case ID_INFO:
  351. {
  352. menu.unmap();
  353. char pfStr[100];
  354. char spfStr[100];
  355. cp.pf().print(pfStr, 100);
  356. serverPF.print(spfStr, 100);
  357. int secType = getCurrentCSecurity()->getType();
  358. char infoText[1024];
  359. sprintf(infoText,
  360. "Desktop name: %.80s\n"
  361. "Host: %.80s port: %d\n"
  362. "Size: %d x %d\n"
  363. "Pixel format: %s\n"
  364. "(server default %s)\n"
  365. "Requested encoding: %s\n"
  366. "Last used encoding: %s\n"
  367. "Line speed estimate: %d kbit/s\n"
  368. "Protocol version: %d.%d\n"
  369. "Security method: %s\n",
  370. cp.name(), serverHost, serverPort, cp.width, cp.height,
  371. pfStr, spfStr, encodingName(currentEncoding),
  372. encodingName(lastServerEncoding),
  373. sock->inStream().kbitsPerSecond(),
  374. cp.majorVersion, cp.minorVersion,
  375. secTypeName(secType));
  376. info.setText(infoText);
  377. info.show();
  378. break;
  379. }
  380. case ID_FULLSCREEN:
  381. menu.unmap();
  382. fullScreen = !fullScreen;
  383. if (viewport) recreateViewport();
  384. break;
  385. case ID_REFRESH:
  386. menu.unmap();
  387. writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
  388. false);
  389. break;
  390. case ID_F8:
  391. menu.unmap();
  392. if (!viewOnly) {
  393. writer()->writeKeyEvent(menuKeysym, true);
  394. writer()->writeKeyEvent(menuKeysym, false);
  395. }
  396. break;
  397. case ID_CTRLALTDEL:
  398. menu.unmap();
  399. if (!viewOnly) {
  400. writer()->writeKeyEvent(XK_Control_L, true);
  401. writer()->writeKeyEvent(XK_Alt_L, true);
  402. writer()->writeKeyEvent(XK_Delete, true);
  403. writer()->writeKeyEvent(XK_Delete, false);
  404. writer()->writeKeyEvent(XK_Alt_L, false);
  405. writer()->writeKeyEvent(XK_Control_L, false);
  406. }
  407. break;
  408. case ID_CTRL:
  409. menu.unmap();
  410. if (!viewOnly) {
  411. ctrlDown = !ctrlDown;
  412. writer()->writeKeyEvent(XK_Control_L, ctrlDown);
  413. menu.check(ID_CTRL, ctrlDown);
  414. }
  415. break;
  416. case ID_ALT:
  417. menu.unmap();
  418. if (!viewOnly) {
  419. altDown = !altDown;
  420. writer()->writeKeyEvent(XK_Alt_L, altDown);
  421. menu.check(ID_ALT, altDown);
  422. }
  423. break;
  424. case ID_ABOUT:
  425. menu.unmap();
  426. about.show();
  427. break;
  428. case ID_DISMISS:
  429. menu.unmap();
  430. break;
  431. case ID_EXIT:
  432. exit(1);
  433. break;
  434. }
  435. }
  436. // OptionsDialogCallback. setOptions() sets the options dialog's checkboxes
  437. // etc to reflect our flags. getOptions() sets our flags according to the
  438. // options dialog's checkboxes.
  439. void CConn::setOptions() {
  440. char digit[2] = "0";
  441. options.autoSelect.checked(autoSelect);
  442. options.fullColour.checked(fullColour);
  443. options.veryLowColour.checked(!fullColour && lowColourLevel == 0);
  444. options.lowColour.checked(!fullColour && lowColourLevel == 1);
  445. options.mediumColour.checked(!fullColour && lowColourLevel == 2);
  446. options.tight.checked(currentEncoding == encodingTight);
  447. options.zrle.checked(currentEncoding == encodingZRLE);
  448. options.hextile.checked(currentEncoding == encodingHextile);
  449. options.raw.checked(currentEncoding == encodingRaw);
  450. options.customCompressLevel.checked(customCompressLevel);
  451. digit[0] = '0' + compressLevel;
  452. options.compressLevel.setText(digit);
  453. options.noJpeg.checked(!noJpeg);
  454. digit[0] = '0' + qualityLevel;
  455. options.qualityLevel.setText(digit);
  456. options.viewOnly.checked(viewOnly);
  457. options.acceptClipboard.checked(acceptClipboard);
  458. options.sendClipboard.checked(sendClipboard);
  459. options.sendPrimary.checked(sendPrimary);
  460. if (state() == RFBSTATE_NORMAL)
  461. options.shared.disabled(true);
  462. else
  463. options.shared.checked(shared);
  464. options.fullScreen.checked(fullScreen);
  465. options.useLocalCursor.checked(useLocalCursor);
  466. options.dotWhenNoCursor.checked(dotWhenNoCursor);
  467. }
  468. void CConn::getOptions() {
  469. autoSelect = options.autoSelect.checked();
  470. if (fullColour != options.fullColour.checked())
  471. formatChange = true;
  472. fullColour = options.fullColour.checked();
  473. if (!fullColour) {
  474. int newLowColourLevel = (options.veryLowColour.checked() ? 0 :
  475. options.lowColour.checked() ? 1 : 2);
  476. if (newLowColourLevel != lowColourLevel) {
  477. lowColourLevel.setParam(newLowColourLevel);
  478. formatChange = true;
  479. }
  480. }
  481. unsigned int newEncoding = (options.tight.checked() ? encodingTight :
  482. options.zrle.checked() ? encodingZRLE :
  483. options.hextile.checked() ? encodingHextile :
  484. encodingRaw);
  485. if (newEncoding != currentEncoding) {
  486. currentEncoding = newEncoding;
  487. encodingChange = true;
  488. }
  489. customCompressLevel.setParam(options.customCompressLevel.checked());
  490. if (cp.customCompressLevel != customCompressLevel) {
  491. cp.customCompressLevel = customCompressLevel;
  492. encodingChange = true;
  493. }
  494. compressLevel.setParam(options.compressLevel.getText());
  495. if (cp.compressLevel != compressLevel) {
  496. cp.compressLevel = compressLevel;
  497. encodingChange = true;
  498. }
  499. noJpeg.setParam(!options.noJpeg.checked());
  500. if (cp.noJpeg != noJpeg) {
  501. cp.noJpeg = noJpeg;
  502. encodingChange = true;
  503. }
  504. qualityLevel.setParam(options.qualityLevel.getText());
  505. if (cp.qualityLevel != qualityLevel) {
  506. cp.qualityLevel = qualityLevel;
  507. encodingChange = true;
  508. }
  509. viewOnly.setParam(options.viewOnly.checked());
  510. acceptClipboard.setParam(options.acceptClipboard.checked());
  511. sendClipboard.setParam(options.sendClipboard.checked());
  512. sendPrimary.setParam(options.sendPrimary.checked());
  513. shared = options.shared.checked();
  514. setShared(shared);
  515. if (fullScreen != options.fullScreen.checked()) {
  516. fullScreen = options.fullScreen.checked();
  517. if (viewport) recreateViewport();
  518. }
  519. useLocalCursor.setParam(options.useLocalCursor.checked());
  520. if (cp.supportsLocalCursor != useLocalCursor) {
  521. cp.supportsLocalCursor = useLocalCursor;
  522. encodingChange = true;
  523. if (desktop)
  524. desktop->resetLocalCursor();
  525. }
  526. dotWhenNoCursor.setParam(options.dotWhenNoCursor.checked());
  527. checkEncodings();
  528. }
  529. void CConn::recreateViewport()
  530. {
  531. TXViewport* oldViewport = viewport;
  532. viewport = new TXViewport(dpy, cp.width, cp.height);
  533. desktop->setViewport(viewport);
  534. CharArray windowNameStr(windowName.getData());
  535. if (!windowNameStr.buf[0]) {
  536. windowNameStr.replaceBuf(new char[256]);
  537. sprintf(windowNameStr.buf,"VNC: %.240s",cp.name());
  538. }
  539. viewport->toplevel(windowNameStr.buf, this, argc, argv);
  540. viewport->setBumpScroll(fullScreen);
  541. XSetWindowAttributes attr;
  542. attr.override_redirect = fullScreen;
  543. XChangeWindowAttributes(dpy, viewport->win(), CWOverrideRedirect, &attr);
  544. XChangeWindowAttributes(dpy, menu.win(), CWOverrideRedirect, &attr);
  545. XChangeWindowAttributes(dpy, options.win(), CWOverrideRedirect, &attr);
  546. XChangeWindowAttributes(dpy, about.win(), CWOverrideRedirect, &attr);
  547. XChangeWindowAttributes(dpy, info.win(), CWOverrideRedirect, &attr);
  548. reconfigureViewport();
  549. menu.setTransientFor(viewport->win());
  550. viewport->map();
  551. if (fullScreen) {
  552. XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync,
  553. CurrentTime);
  554. } else {
  555. XUngrabKeyboard(dpy, CurrentTime);
  556. }
  557. if (oldViewport) delete oldViewport;
  558. }
  559. void CConn::reconfigureViewport()
  560. {
  561. viewport->setMaxSize(cp.width, cp.height);
  562. if (fullScreen) {
  563. viewport->resize(DisplayWidth(dpy,DefaultScreen(dpy)),
  564. DisplayHeight(dpy,DefaultScreen(dpy)));
  565. } else {
  566. int w = cp.width;
  567. int h = cp.height;
  568. if (w + wmDecorationWidth >= DisplayWidth(dpy,DefaultScreen(dpy)))
  569. w = DisplayWidth(dpy,DefaultScreen(dpy)) - wmDecorationWidth;
  570. if (h + wmDecorationHeight >= DisplayHeight(dpy,DefaultScreen(dpy)))
  571. h = DisplayHeight(dpy,DefaultScreen(dpy)) - wmDecorationHeight;
  572. int x = (DisplayWidth(dpy,DefaultScreen(dpy)) - w - wmDecorationWidth) / 2;
  573. int y = (DisplayHeight(dpy,DefaultScreen(dpy)) - h - wmDecorationHeight)/2;
  574. CharArray geometryStr(geometry.getData());
  575. viewport->setGeometry(geometryStr.buf, x, y, w, h);
  576. }
  577. }
  578. // Note: The method below is duplicated in vncviewer/cview.cxx!
  579. // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
  580. // to the connection speed:
  581. //
  582. // Above 16Mbps (timing for at least a second), same machine, switch to raw
  583. // Above 3Mbps, switch to hextile
  584. // Otherwise, switch to Tight
  585. //
  586. // Above 256Kbps, use full colour mode
  587. //
  588. void CConn::autoSelectFormatAndEncoding()
  589. {
  590. int kbitsPerSecond = sock->inStream().kbitsPerSecond();
  591. unsigned int newEncoding = currentEncoding;
  592. bool newFullColour = fullColour;
  593. unsigned int timeWaited = sock->inStream().timeWaited();
  594. // Select best encoding
  595. if (kbitsPerSecond > 16000 && sameMachine && timeWaited >= 10000) {
  596. newEncoding = encodingRaw;
  597. } else if (kbitsPerSecond > 3000 && timeWaited >= 10000) {
  598. newEncoding = encodingHextile;
  599. } else {
  600. newEncoding = encodingTight;
  601. }
  602. if (newEncoding != currentEncoding) {
  603. vlog.info("Throughput %d kbit/s - changing to %s encoding",
  604. kbitsPerSecond, encodingName(newEncoding));
  605. currentEncoding = newEncoding;
  606. encodingChange = true;
  607. }
  608. if (kbitsPerSecond == 0) {
  609. return;
  610. }
  611. if (cp.majorVersion <= 3 && cp.minorVersion <= 7) {
  612. // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
  613. // cursors "asynchronously". If this happens in the middle of a
  614. // pixel format change, the server will encode the cursor with
  615. // the old format, but the client will try to decode it
  616. // according to the new format. This will lead to a
  617. // crash. Therefore, we do not allow automatic format change for
  618. // old servers.
  619. return;
  620. }
  621. // Select best color level
  622. newFullColour = (kbitsPerSecond > 256);
  623. if (newFullColour != fullColour) {
  624. vlog.info("Throughput %d kbit/s - full color is now %s",
  625. kbitsPerSecond,
  626. newFullColour ? "enabled" : "disabled");
  627. fullColour = newFullColour;
  628. formatChange = true;
  629. }
  630. }
  631. // checkEncodings() sends a setEncodings message if one is needed.
  632. void CConn::checkEncodings()
  633. {
  634. if (encodingChange && writer()) {
  635. vlog.info("Using %s encoding",encodingName(currentEncoding));
  636. writer()->writeSetEncodings(currentEncoding, true);
  637. encodingChange = false;
  638. }
  639. }
  640. // requestNewUpdate() requests an update from the server, having set the
  641. // format and encoding appropriately.
  642. void CConn::requestNewUpdate()
  643. {
  644. if (formatChange) {
  645. if (fullColour) {
  646. desktop->setPF(fullColourPF);
  647. } else {
  648. if (lowColourLevel == 0)
  649. desktop->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
  650. else if (lowColourLevel == 1)
  651. desktop->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
  652. else
  653. desktop->setPF(PixelFormat(8,8,0,0));
  654. }
  655. char str[256];
  656. desktop->getPF().print(str, 256);
  657. vlog.info("Using pixel format %s",str);
  658. cp.setPF(desktop->getPF());
  659. writer()->writeSetPixelFormat(cp.pf());
  660. }
  661. checkEncodings();
  662. writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
  663. !formatChange);
  664. formatChange = false;
  665. }