123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009 Pierre Ossman for Cendio AB
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
- //
- // CConn.cxx
- //
-
- #include <unistd.h>
- #include "CConn.h"
- #include <rfb/CMsgWriter.h>
- #include <rfb/encodings.h>
- #include <rfb/secTypes.h>
- #include <rfb/CSecurityNone.h>
- #include <rfb/CSecurityVncAuth.h>
- #include <rfb/Hostname.h>
- #include <rfb/LogWriter.h>
- #include <rfb/util.h>
- #include <rfb/Password.h>
- #include <rfb/screenTypes.h>
- #include <network/TcpSocket.h>
-
- #include "TXViewport.h"
- #include "DesktopWindow.h"
- #include "ServerDialog.h"
- #include "PasswdDialog.h"
- #include "parameters.h"
-
- using namespace rfb;
-
- static rfb::LogWriter vlog("CConn");
-
- IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
- "pixel data - a debugging feature", 0);
-
- StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
- "F8");
- StringParameter windowName("name", "The X window name", "");
-
- CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
- char* vncServerName, bool reverse)
- : dpy(dpy_), argc(argc_),
- argv(argv_), serverHost(0), serverPort(0), sock(sock_), viewport(0),
- desktop(0), desktopEventHandler(0),
- currentEncoding(encodingTight), lastServerEncoding((unsigned int)-1),
- fullColour(::fullColour),
- autoSelect(::autoSelect), shared(::shared), formatChange(false),
- encodingChange(false), sameMachine(false), fullScreen(::fullScreen),
- ctrlDown(false), altDown(false),
- menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy),
- reverseConnection(reverse), firstUpdate(true)
- {
- CharArray menuKeyStr(menuKey.getData());
- menuKeysym = XStringToKeysym(menuKeyStr.buf);
-
- setShared(shared);
- addSecType(secTypeNone);
- addSecType(secTypeVncAuth);
- CharArray encStr(preferredEncoding.getData());
- int encNum = encodingNum(encStr.buf);
- if (encNum != -1) {
- currentEncoding = encNum;
- }
- cp.supportsDesktopResize = true;
- cp.supportsExtendedDesktopSize = true;
- cp.supportsDesktopRename = true;
- cp.supportsLocalCursor = useLocalCursor;
- cp.customCompressLevel = customCompressLevel;
- cp.compressLevel = compressLevel;
- cp.noJpeg = noJpeg;
- cp.qualityLevel = qualityLevel;
- initMenu();
-
- if (sock) {
- char* name = sock->getPeerEndpoint();
- vlog.info("Accepted connection from %s", name);
- if (name) free(name);
- } else {
- if (vncServerName) {
- getHostAndPort(vncServerName, &serverHost, &serverPort);
- } else {
- ServerDialog dlg(dpy, &options, &about);
- if (!dlg.show() || dlg.entry.getText()[0] == 0) {
- exit(1);
- }
- getHostAndPort(dlg.entry.getText(), &serverHost, &serverPort);
- }
-
- sock = new network::TcpSocket(serverHost, serverPort);
- vlog.info("connected to host %s port %d", serverHost, serverPort);
- }
-
- sameMachine = sock->sameMachine();
- sock->inStream().setBlockCallback(this);
- setServerName(sock->getPeerEndpoint());
- setStreams(&sock->inStream(), &sock->outStream());
- initialiseProtocol();
- }
-
- CConn::~CConn() {
- free(serverHost);
- delete desktop;
- delete viewport;
- delete sock;
- }
-
- // deleteWindow() is called when the user closes the desktop or menu windows.
-
- void CConn::deleteWindow(TXWindow* w) {
- if (w == &menu) {
- menu.unmap();
- } else if (w == viewport) {
- exit(1);
- }
- }
-
- // handleEvent() filters all events on the desktop and menu. Most are passed
- // straight through. The exception is the F8 key. When pressed on the
- // desktop, it is used to bring up the menu. An F8 press or release on the
- // menu is passed through as if it were on the desktop.
-
- void CConn::handleEvent(TXWindow* w, XEvent* ev)
- {
- KeySym ks;
- char str[256];
-
- switch (ev->type) {
- case KeyPress:
- case KeyRelease:
- XLookupString(&ev->xkey, str, 256, &ks, NULL);
- if (ks == menuKeysym && (ev->xkey.state & (ShiftMask|ControlMask)) == 0) {
- if (w == desktop && ev->type == KeyPress) {
- showMenu(ev->xkey.x_root, ev->xkey.y_root);
- break;
- } else if (w == &menu) {
- if (ev->type == KeyPress) menu.unmap();
- desktopEventHandler->handleEvent(w, ev);
- break;
- }
- }
- // drop through
-
- default:
- if (w == desktop) desktopEventHandler->handleEvent(w, ev);
- else if (w == &menu) menuEventHandler->handleEvent(w, ev);
- }
- }
-
- // blockCallback() is called when reading from the socket would block. We
- // process X events until the socket is ready for reading again.
-
- void CConn::blockCallback() {
- fd_set rfds;
- do {
- struct timeval tv;
- struct timeval* tvp = 0;
-
- // Process any incoming X events
- TXWindow::handleXEvents(dpy);
-
- // Process expired timers and get the time until the next one
- int timeoutMs = Timer::checkTimeouts();
- if (timeoutMs) {
- tv.tv_sec = timeoutMs / 1000;
- tv.tv_usec = (timeoutMs % 1000) * 1000;
- tvp = &tv;
- }
-
- // If there are X requests pending then poll, don't wait!
- if (XPending(dpy)) {
- tv.tv_usec = tv.tv_sec = 0;
- tvp = &tv;
- }
-
- // Wait for X events, VNC traffic, or the next timer expiry
- FD_ZERO(&rfds);
- FD_SET(ConnectionNumber(dpy), &rfds);
- FD_SET(sock->getFd(), &rfds);
- int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
- if (n < 0) throw rdr::SystemException("select",errno);
- } while (!(FD_ISSET(sock->getFd(), &rfds)));
- }
-
-
- // getPasswd() is called by the CSecurity object when it needs us to read a
- // password from the user.
-
- void CConn::getUserPasswd(char** user, char** password)
- {
- CharArray passwordFileStr(passwordFile.getData());
- if (!user && passwordFileStr.buf[0]) {
- FILE* fp = fopen(passwordFileStr.buf, "r");
- if (!fp) throw rfb::Exception("Opening password file failed");
- ObfuscatedPasswd obfPwd(256);
- obfPwd.length = fread(obfPwd.buf, 1, obfPwd.length, fp);
- fclose(fp);
- PlainPasswd passwd(obfPwd);
- *password = passwd.takeBuf();
- return;
- }
-
- const char* secType = secTypeName(getCurrentCSecurity()->getType());
- const char* titlePrefix = _("VNC authentication");
- unsigned int titleLen = strlen(titlePrefix) + strlen(secType) + 4;
- CharArray title(titleLen);
- snprintf(title.buf, titleLen, "%s [%s]", titlePrefix, secType);
- PasswdDialog dlg(dpy, title.buf, !user);
- if (!dlg.show()) throw rfb::Exception("Authentication cancelled");
- if (user)
- *user = strDup(dlg.userEntry.getText());
- *password = strDup(dlg.passwdEntry.getText());
- }
-
-
- // CConnection callback methods
-
- // getCSecurity() gets the appropriate CSecurity object for the security
- // types which we support.
- CSecurity* CConn::getCSecurity(int secType) {
- switch (secType) {
- case secTypeNone:
- return new CSecurityNone();
- case secTypeVncAuth:
- return new CSecurityVncAuth(this);
- default:
- throw rfb::Exception("Unsupported secType?");
- }
- }
-
- // serverInit() is called when the serverInit message has been received. At
- // this point we create the desktop window and display it. We also tell the
- // server the pixel format and encodings to use and request the first update.
- void CConn::serverInit() {
- CConnection::serverInit();
-
- // If using AutoSelect with old servers, start in FullColor
- // mode. See comment in autoSelectFormatAndEncoding.
- if (cp.beforeVersion(3, 8) && autoSelect) {
- fullColour = true;
- }
-
- serverPF = cp.pf();
- desktop = new DesktopWindow(dpy, cp.width, cp.height, serverPF, this);
- desktopEventHandler = desktop->setEventHandler(this);
- desktop->addEventMask(KeyPressMask | KeyReleaseMask);
- fullColourPF = desktop->getPF();
- if (!serverPF.trueColour)
- fullColour = true;
- recreateViewport();
- formatChange = encodingChange = true;
- requestNewUpdate();
- }
-
- // setDesktopSize() is called when the desktop size changes (including when
- // it is set initially).
- void CConn::setDesktopSize(int w, int h) {
- CConnection::setDesktopSize(w,h);
- resizeFramebuffer();
- }
-
- // setExtendedDesktopSize() is a more advanced version of setDesktopSize()
- void CConn::setExtendedDesktopSize(int reason, int result, int w, int h,
- const rfb::ScreenSet& layout) {
- CConnection::setExtendedDesktopSize(reason, result, w, h, layout);
-
- if ((reason == reasonClient) && (result != resultSuccess)) {
- vlog.error("SetDesktopSize failed: %d", result);
- return;
- }
-
- resizeFramebuffer();
- }
-
- // setName() is called when the desktop name changes
- void CConn::setName(const char* name) {
- CConnection::setName(name);
-
- CharArray windowNameStr(windowName.getData());
- if (!windowNameStr.buf[0]) {
- windowNameStr.replaceBuf(new char[256]);
- snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), cp.name());
- }
-
- if (viewport) {
- viewport->setName(windowNameStr.buf);
- }
- }
-
- // framebufferUpdateEnd() is called at the end of an update.
- // For each rectangle, the FdInStream will have timed the speed
- // of the connection, allowing us to select format and encoding
- // appropriately, and then request another incremental update.
- void CConn::framebufferUpdateEnd() {
- if (debugDelay != 0) {
- XSync(dpy, False);
- struct timeval tv;
- tv.tv_sec = debugDelay / 1000;
- tv.tv_usec = (debugDelay % 1000) * 1000;
- select(0, 0, 0, 0, &tv);
- std::list<rfb::Rect>::iterator i;
- for (i = debugRects.begin(); i != debugRects.end(); i++) {
- desktop->invertRect(*i);
- }
- debugRects.clear();
- }
- desktop->framebufferUpdateEnd();
-
- if (firstUpdate) {
- int width, height;
-
- if (cp.supportsSetDesktopSize &&
- sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) == 2) {
- ScreenSet layout;
-
- layout = cp.screenLayout;
-
- if (layout.num_screens() == 0)
- layout.add_screen(rfb::Screen());
- else if (layout.num_screens() != 1) {
- ScreenSet::iterator iter;
-
- while (true) {
- iter = layout.begin();
- ++iter;
-
- if (iter == layout.end())
- break;
-
- layout.remove_screen(iter->id);
- }
- }
-
- layout.begin()->dimensions.tl.x = 0;
- layout.begin()->dimensions.tl.y = 0;
- layout.begin()->dimensions.br.x = width;
- layout.begin()->dimensions.br.y = height;
-
- writer()->writeSetDesktopSize(width, height, layout);
- }
-
- firstUpdate = false;
- }
-
- if (autoSelect)
- autoSelectFormatAndEncoding();
- requestNewUpdate();
- }
-
- // The rest of the callbacks are fairly self-explanatory...
-
- void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
- {
- desktop->setColourMapEntries(firstColour, nColours, rgbs);
- }
-
- void CConn::bell() { XBell(dpy, 0); }
-
- void CConn::serverCutText(const char* str, int len) {
- desktop->serverCutText(str,len);
- }
-
- // We start timing on beginRect and stop timing on endRect, to
- // avoid skewing the bandwidth estimation as a result of the server
- // being slow or the network having high latency
- void CConn::beginRect(const Rect& r, unsigned int encoding)
- {
- sock->inStream().startTiming();
- if (encoding != encodingCopyRect) {
- lastServerEncoding = encoding;
- }
- }
-
- void CConn::endRect(const Rect& r, unsigned int encoding)
- {
- sock->inStream().stopTiming();
- if (debugDelay != 0) {
- desktop->invertRect(r);
- debugRects.push_back(r);
- }
- }
-
- void CConn::fillRect(const rfb::Rect& r, rfb::Pixel p) {
- desktop->fillRect(r,p);
- }
- void CConn::imageRect(const rfb::Rect& r, void* p) {
- desktop->imageRect(r,p);
- }
- void CConn::copyRect(const rfb::Rect& r, int sx, int sy) {
- desktop->copyRect(r,sx,sy);
- }
- void CConn::setCursor(int width, int height, const Point& hotspot,
- void* data, void* mask) {
- desktop->setCursor(width, height, hotspot, data, mask);
- }
-
-
- // Menu stuff - menuSelect() is called when the user selects a menu option.
-
- enum { ID_OPTIONS, ID_INFO, ID_FULLSCREEN, ID_REFRESH, ID_F8, ID_CTRLALTDEL,
- ID_ABOUT, ID_DISMISS, ID_EXIT, ID_NEWCONN, ID_CTRL, ID_ALT };
-
- void CConn::initMenu() {
- menuEventHandler = menu.setEventHandler(this);
- menu.addEventMask(KeyPressMask | KeyReleaseMask);
- menu.addEntry(_("Exit viewer"), ID_EXIT);
- menu.addEntry(0, 0);
- menu.addEntry(_("Full screen"), ID_FULLSCREEN);
- menu.check(ID_FULLSCREEN, fullScreen);
- menu.addEntry(0, 0);
- menu.addEntry(_("Ctrl"), ID_CTRL);
- menu.addEntry(_("Alt"), ID_ALT);
- CharArray menuKeyStr(menuKey.getData());
- CharArray sendMenuKey(64);
- snprintf(sendMenuKey.buf, 64, _("Send %s"), menuKeyStr.buf);
- menu.addEntry(sendMenuKey.buf, ID_F8);
- menu.addEntry(_("Send Ctrl-Alt-Del"), ID_CTRLALTDEL);
- menu.addEntry(0, 0);
- menu.addEntry(_("Refresh screen"), ID_REFRESH);
- menu.addEntry(0, 0);
- menu.addEntry(_("New connection..."), ID_NEWCONN);
- menu.addEntry(_("Options..."), ID_OPTIONS);
- menu.addEntry(_("Connection info..."), ID_INFO);
- menu.addEntry(_("About VNCviewer..."), ID_ABOUT);
- menu.addEntry(0, 0);
- menu.addEntry(_("Dismiss menu"), ID_DISMISS);
- menu.toplevel(_("VNC Menu"), this);
- menu.setBorderWidth(1);
- }
-
- void CConn::showMenu(int x, int y) {
- menu.check(ID_FULLSCREEN, fullScreen);
- if (x + menu.width() > viewport->width())
- x = viewport->width() - menu.width();
- if (y + menu.height() > viewport->height())
- y = viewport->height() - menu.height();
- menu.move(x, y);
- menu.raise();
- menu.map();
- }
-
- void CConn::menuSelect(long id, TXMenu* m) {
- switch (id) {
- case ID_NEWCONN:
- {
- menu.unmap();
- if (fullScreen) {
- fullScreen = false;
- if (viewport) recreateViewport();
- }
- int pid = fork();
- if (pid < 0) { perror("fork"); exit(1); }
- if (pid == 0) {
- delete sock;
- close(ConnectionNumber(dpy));
- struct timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 200*1000;
- select(0, 0, 0, 0, &tv);
- execlp(programName, programName, NULL);
- perror("execlp"); exit(1);
- }
- break;
- }
- case ID_OPTIONS:
- menu.unmap();
- options.show();
- break;
- case ID_INFO:
- {
- menu.unmap();
- char pfStr[100];
- char spfStr[100];
- cp.pf().print(pfStr, 100);
- serverPF.print(spfStr, 100);
- int secType = getCurrentCSecurity()->getType();
- char infoText[1024];
- snprintf(infoText, sizeof(infoText),
- _("Desktop name: %.80s\n"
- "Host: %.80s port: %d\n"
- "Size: %d x %d\n"
- "Pixel format: %s\n"
- "(server default %s)\n"
- "Requested encoding: %s\n"
- "Last used encoding: %s\n"
- "Line speed estimate: %d kbit/s\n"
- "Protocol version: %d.%d\n"
- "Security method: %s\n"),
- cp.name(), serverHost, serverPort, cp.width, cp.height,
- pfStr, spfStr, encodingName(currentEncoding),
- encodingName(lastServerEncoding),
- sock->inStream().kbitsPerSecond(),
- cp.majorVersion, cp.minorVersion,
- secTypeName(secType));
- info.setText(infoText);
- info.show();
- break;
- }
- case ID_FULLSCREEN:
- menu.unmap();
- fullScreen = !fullScreen;
- if (viewport) recreateViewport();
- break;
- case ID_REFRESH:
- menu.unmap();
- writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
- false);
- break;
- case ID_F8:
- menu.unmap();
- if (!viewOnly) {
- writer()->keyEvent(menuKeysym, true);
- writer()->keyEvent(menuKeysym, false);
- }
- break;
- case ID_CTRLALTDEL:
- menu.unmap();
- if (!viewOnly) {
- writer()->keyEvent(XK_Control_L, true);
- writer()->keyEvent(XK_Alt_L, true);
- writer()->keyEvent(XK_Delete, true);
- writer()->keyEvent(XK_Delete, false);
- writer()->keyEvent(XK_Alt_L, false);
- writer()->keyEvent(XK_Control_L, false);
- }
- break;
- case ID_CTRL:
- menu.unmap();
- if (!viewOnly) {
- ctrlDown = !ctrlDown;
- writer()->keyEvent(XK_Control_L, ctrlDown);
- menu.check(ID_CTRL, ctrlDown);
- }
- break;
- case ID_ALT:
- menu.unmap();
- if (!viewOnly) {
- altDown = !altDown;
- writer()->keyEvent(XK_Alt_L, altDown);
- menu.check(ID_ALT, altDown);
- }
- break;
- case ID_ABOUT:
- menu.unmap();
- about.show();
- break;
- case ID_DISMISS:
- menu.unmap();
- break;
- case ID_EXIT:
- exit(1);
- break;
- }
- }
-
-
- // OptionsDialogCallback. setOptions() sets the options dialog's checkboxes
- // etc to reflect our flags. getOptions() sets our flags according to the
- // options dialog's checkboxes.
-
- void CConn::setOptions() {
- char digit[2] = "0";
- options.autoSelect.checked(autoSelect);
- options.fullColour.checked(fullColour);
- options.veryLowColour.checked(!fullColour && lowColourLevel == 0);
- options.lowColour.checked(!fullColour && lowColourLevel == 1);
- options.mediumColour.checked(!fullColour && lowColourLevel == 2);
- options.tight.checked(currentEncoding == encodingTight);
- options.zrle.checked(currentEncoding == encodingZRLE);
- options.hextile.checked(currentEncoding == encodingHextile);
- options.raw.checked(currentEncoding == encodingRaw);
-
- options.customCompressLevel.checked(customCompressLevel);
- digit[0] = '0' + compressLevel;
- options.compressLevel.setText(digit);
- options.noJpeg.checked(!noJpeg);
- digit[0] = '0' + qualityLevel;
- options.qualityLevel.setText(digit);
-
- options.viewOnly.checked(viewOnly);
- options.acceptClipboard.checked(acceptClipboard);
- options.sendClipboard.checked(sendClipboard);
- options.sendPrimary.checked(sendPrimary);
- if (state() == RFBSTATE_NORMAL)
- options.shared.disabled(true);
- else
- options.shared.checked(shared);
- options.fullScreen.checked(fullScreen);
- options.useLocalCursor.checked(useLocalCursor);
- options.dotWhenNoCursor.checked(dotWhenNoCursor);
- }
-
- void CConn::getOptions() {
- autoSelect = options.autoSelect.checked();
- if (fullColour != options.fullColour.checked())
- formatChange = true;
- fullColour = options.fullColour.checked();
- if (!fullColour) {
- int newLowColourLevel = (options.veryLowColour.checked() ? 0 :
- options.lowColour.checked() ? 1 : 2);
- if (newLowColourLevel != lowColourLevel) {
- lowColourLevel.setParam(newLowColourLevel);
- formatChange = true;
- }
- }
- unsigned int newEncoding = (options.tight.checked() ? encodingTight :
- options.zrle.checked() ? encodingZRLE :
- options.hextile.checked() ? encodingHextile :
- encodingRaw);
- if (newEncoding != currentEncoding) {
- currentEncoding = newEncoding;
- encodingChange = true;
- }
-
- customCompressLevel.setParam(options.customCompressLevel.checked());
- if (cp.customCompressLevel != customCompressLevel) {
- cp.customCompressLevel = customCompressLevel;
- encodingChange = true;
- }
- compressLevel.setParam(options.compressLevel.getText());
- if (cp.compressLevel != compressLevel) {
- cp.compressLevel = compressLevel;
- encodingChange = true;
- }
- noJpeg.setParam(!options.noJpeg.checked());
- if (cp.noJpeg != noJpeg) {
- cp.noJpeg = noJpeg;
- encodingChange = true;
- }
- qualityLevel.setParam(options.qualityLevel.getText());
- if (cp.qualityLevel != qualityLevel) {
- cp.qualityLevel = qualityLevel;
- encodingChange = true;
- }
-
- viewOnly.setParam(options.viewOnly.checked());
- acceptClipboard.setParam(options.acceptClipboard.checked());
- sendClipboard.setParam(options.sendClipboard.checked());
- sendPrimary.setParam(options.sendPrimary.checked());
- shared = options.shared.checked();
- setShared(shared);
- if (fullScreen != options.fullScreen.checked()) {
- fullScreen = options.fullScreen.checked();
- if (viewport) recreateViewport();
- }
- useLocalCursor.setParam(options.useLocalCursor.checked());
- if (cp.supportsLocalCursor != useLocalCursor) {
- cp.supportsLocalCursor = useLocalCursor;
- encodingChange = true;
- if (desktop)
- desktop->resetLocalCursor();
- }
- dotWhenNoCursor.setParam(options.dotWhenNoCursor.checked());
- checkEncodings();
- }
-
- void CConn::resizeFramebuffer()
- {
- if (!desktop)
- return;
- if ((desktop->width() == cp.width) && (desktop->height() == cp.height))
- return;
-
- desktop->resize(cp.width, cp.height);
- recreateViewport();
- }
-
- void CConn::recreateViewport()
- {
- TXViewport* oldViewport = viewport;
- viewport = new TXViewport(dpy, cp.width, cp.height);
- desktop->setViewport(viewport);
- CharArray windowNameStr(windowName.getData());
- if (!windowNameStr.buf[0]) {
- windowNameStr.replaceBuf(new char[256]);
- snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), cp.name());
- }
- viewport->toplevel(windowNameStr.buf, this, argc, argv);
- viewport->setBumpScroll(fullScreen);
- XSetWindowAttributes attr;
- attr.override_redirect = fullScreen;
- XChangeWindowAttributes(dpy, viewport->win(), CWOverrideRedirect, &attr);
- XChangeWindowAttributes(dpy, menu.win(), CWOverrideRedirect, &attr);
- XChangeWindowAttributes(dpy, options.win(), CWOverrideRedirect, &attr);
- XChangeWindowAttributes(dpy, about.win(), CWOverrideRedirect, &attr);
- XChangeWindowAttributes(dpy, info.win(), CWOverrideRedirect, &attr);
- reconfigureViewport();
- menu.setTransientFor(viewport->win());
- viewport->map();
- if (fullScreen) {
- XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync,
- CurrentTime);
- } else {
- XUngrabKeyboard(dpy, CurrentTime);
- }
- if (oldViewport) delete oldViewport;
- }
-
- void CConn::reconfigureViewport()
- {
- viewport->setMaxSize(cp.width, cp.height);
- if (fullScreen) {
- viewport->resize(DisplayWidth(dpy,DefaultScreen(dpy)),
- DisplayHeight(dpy,DefaultScreen(dpy)));
- } else {
- int w = cp.width;
- int h = cp.height;
- if (w + wmDecorationWidth >= DisplayWidth(dpy,DefaultScreen(dpy)))
- w = DisplayWidth(dpy,DefaultScreen(dpy)) - wmDecorationWidth;
- if (h + wmDecorationHeight >= DisplayHeight(dpy,DefaultScreen(dpy)))
- h = DisplayHeight(dpy,DefaultScreen(dpy)) - wmDecorationHeight;
-
- int x = (DisplayWidth(dpy,DefaultScreen(dpy)) - w - wmDecorationWidth) / 2;
- int y = (DisplayHeight(dpy,DefaultScreen(dpy)) - h - wmDecorationHeight)/2;
-
- CharArray geometryStr(geometry.getData());
- viewport->setGeometry(geometryStr.buf, x, y, w, h);
- }
- }
-
- // Note: The method below is duplicated in win/vncviewer/CConn.cxx!
-
- // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
- // to the connection speed:
- //
- // First we wait for at least one second of bandwidth measurement.
- //
- // Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
- // which should be perceptually lossless.
- //
- // If the bandwidth is below that, we choose a more lossy JPEG quality.
- //
- // If the bandwidth drops below 256 Kbps, we switch to palette mode.
- //
- // Note: The system here is fairly arbitrary and should be replaced
- // with something more intelligent at the server end.
- //
- void CConn::autoSelectFormatAndEncoding()
- {
- int kbitsPerSecond = sock->inStream().kbitsPerSecond();
- unsigned int timeWaited = sock->inStream().timeWaited();
- bool newFullColour = fullColour;
- int newQualityLevel = qualityLevel;
-
- // Always use Tight
- currentEncoding = encodingTight;
-
- // Check that we have a decent bandwidth measurement
- if ((kbitsPerSecond == 0) || (timeWaited < 10000))
- return;
-
- // Select appropriate quality level
- if (!noJpeg) {
- if (kbitsPerSecond > 16000)
- newQualityLevel = 8;
- else
- newQualityLevel = 6;
-
- if (newQualityLevel != qualityLevel) {
- vlog.info("Throughput %d kbit/s - changing to quality %d ",
- kbitsPerSecond, newQualityLevel);
- cp.qualityLevel = newQualityLevel;
- qualityLevel.setParam(newQualityLevel);
- encodingChange = true;
- }
- }
-
- if (cp.beforeVersion(3, 8)) {
- // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
- // cursors "asynchronously". If this happens in the middle of a
- // pixel format change, the server will encode the cursor with
- // the old format, but the client will try to decode it
- // according to the new format. This will lead to a
- // crash. Therefore, we do not allow automatic format change for
- // old servers.
- return;
- }
-
- // Select best color level
- newFullColour = (kbitsPerSecond > 256);
- if (newFullColour != fullColour) {
- vlog.info("Throughput %d kbit/s - full color is now %s",
- kbitsPerSecond,
- newFullColour ? "enabled" : "disabled");
- fullColour = newFullColour;
- formatChange = true;
- }
- }
-
- // checkEncodings() sends a setEncodings message if one is needed.
- void CConn::checkEncodings()
- {
- if (encodingChange && writer()) {
- vlog.info("Using %s encoding",encodingName(currentEncoding));
- writer()->writeSetEncodings(currentEncoding, true);
- encodingChange = false;
- }
- }
-
- // requestNewUpdate() requests an update from the server, having set the
- // format and encoding appropriately.
- void CConn::requestNewUpdate()
- {
- if (formatChange) {
- if (fullColour) {
- desktop->setPF(fullColourPF);
- } else {
- if (lowColourLevel == 0)
- desktop->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
- else if (lowColourLevel == 1)
- desktop->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
- else
- desktop->setPF(PixelFormat(8,8,0,0));
- }
- char str[256];
- desktop->getPF().print(str, 256);
- vlog.info("Using pixel format %s",str);
- cp.setPF(desktop->getPF());
- writer()->writeSetPixelFormat(cp.pf());
- }
- checkEncodings();
- writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
- !formatChange);
- formatChange = false;
- }
|