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

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