blob: f289d384a3be478aa66215208e74aac74d58986b [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 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
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000019#include <winsock2.h>
20#include <vncviewer/UserPasswdDialog.h>
21#include <vncviewer/CConn.h>
22#include <vncviewer/CConnThread.h>
23#include <vncviewer/resource.h>
24#include <rfb/encodings.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000025#include <rfb/Security.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000026#include <rfb/CMsgWriter.h>
27#include <rfb/Configuration.h>
28#include <rfb/LogWriter.h>
29#include <rfb_win32/AboutDialog.h>
30
31using namespace rfb;
32using namespace rfb::win32;
33using namespace rdr;
34
35// - Statics & consts
36
37static LogWriter vlog("CConn");
38
39
40const int IDM_FULLSCREEN = ID_FULLSCREEN;
41const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY;
42const int IDM_SEND_CAD = ID_SEND_CAD;
43const int IDM_SEND_CTLESC = ID_SEND_CTLESC;
44const int IDM_ABOUT = ID_ABOUT;
45const int IDM_OPTIONS = ID_OPTIONS;
46const int IDM_INFO = ID_INFO;
47const int IDM_NEWCONN = ID_NEW_CONNECTION;
48const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH;
49const int IDM_CTRL_KEY = ID_CTRL_KEY;
50const int IDM_ALT_KEY = ID_ALT_KEY;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000051const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
george824f72ab32006-09-10 05:13:45 +000052const int IDM_ZOOM_IN = ID_ZOOM_IN;
53const int IDM_ZOOM_OUT = ID_ZOOM_OUT;
54const int IDM_ACTUAL_SIZE = ID_ACTUAL_SIZE;
55const int IDM_AUTO_SIZE = ID_AUTO_SIZE;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000056
57
58static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
59 "pixel data - a debugging feature", 0);
60
george82b6d87aa2006-09-11 07:00:59 +000061const int scaleValues[9] = {10, 25, 50, 75, 90, 100, 125, 150, 200};
62const int scaleCount = 9;
63
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000064
65//
66// -=- CConn implementation
67//
68
69RegKey CConn::userConfigKey;
70
71
72CConn::CConn()
Peter Åstrand2dd9eea2008-12-11 08:23:55 +000073 : window(0), sameMachine(false), encodingChange(false), formatChange(false),
74 lastUsedEncoding_(encodingRaw), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)),
Pierre Ossmand6d19942009-03-24 12:29:50 +000075 reverseConnection(false), requestUpdate(false), firstUpdate(true),
76 isClosed_(false) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000077}
78
79CConn::~CConn() {
80 delete window;
81}
82
83bool CConn::initialise(network::Socket* s, bool reverse) {
84 // Set the server's name for MRU purposes
85 CharArray endpoint(s->getPeerEndpoint());
86 setServerName(endpoint.buf);
87 if (!options.host.buf)
88 options.setHost(endpoint.buf);
89
90 // Initialise the underlying CConnection
91 setStreams(&s->inStream(), &s->outStream());
92
93 // Enable processing of window messages while blocked on I/O
94 s->inStream().setBlockCallback(this);
95
96 // Initialise the viewer options
97 applyOptions(options);
98
Adam Tkacb10489b2010-04-23 14:16:04 +000099 CSecurity::upg = this;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000100
101 // Start the RFB protocol
102 sock = s;
103 reverseConnection = reverse;
104 initialiseProtocol();
105
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000106 return true;
107}
108
109
110void
111CConn::applyOptions(CConnOptions& opt) {
112 // - If any encoding-related settings have changed then we must
113 // notify the server of the new settings
114 encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
115 (options.useDesktopResize != opt.useDesktopResize) ||
116 (options.customCompressLevel != opt.customCompressLevel) ||
117 (options.compressLevel != opt.compressLevel) ||
118 (options.noJpeg != opt.noJpeg) ||
119 (options.qualityLevel != opt.qualityLevel) ||
120 (options.preferredEncoding != opt.preferredEncoding));
121
122 // - If the preferred pixel format has changed then notify the server
123 formatChange |= (options.fullColour != opt.fullColour);
124 if (!opt.fullColour)
125 formatChange |= (options.lowColourLevel != opt.lowColourLevel);
126
127 // - Save the new set of options
128 options = opt;
129
130 // - Set optional features in ConnParams
131 cp.supportsLocalCursor = options.useLocalCursor;
132 cp.supportsDesktopResize = options.useDesktopResize;
Pierre Ossmand6d19942009-03-24 12:29:50 +0000133 cp.supportsExtendedDesktopSize = options.useDesktopResize;
Peter Åstrandc39e0782009-01-15 12:21:42 +0000134 cp.supportsDesktopRename = true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000135 cp.customCompressLevel = options.customCompressLevel;
136 cp.compressLevel = options.compressLevel;
137 cp.noJpeg = options.noJpeg;
138 cp.qualityLevel = options.qualityLevel;
139
140 // - Configure connection sharing on/off
141 setShared(options.shared);
142
143 // - Whether to use protocol 3.3 for legacy compatibility
144 setProtocol3_3(options.protocol3_3);
145
146 // - Apply settings that affect the window, if it is visible
147 if (window) {
148 window->setMonitor(options.monitor.buf);
149 window->setFullscreen(options.fullScreen);
150 window->setEmulate3(options.emulate3);
151 window->setPointerEventInterval(options.pointerEventInterval);
152 window->setMenuKey(options.menuKey);
153 window->setDisableWinKeys(options.disableWinKeys);
154 window->setShowToolbar(options.showToolbar);
george82770bbbc2007-03-12 10:48:09 +0000155 window->printScale();
george82ffc14a62006-09-05 06:51:41 +0000156 if (options.autoScaling) {
157 window->setAutoScaling(true);
158 } else {
159 window->setAutoScaling(false);
160 window->setDesktopScale(options.scale);
161 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000162 if (!options.useLocalCursor)
163 window->setCursor(0, 0, Point(), 0, 0);
164 }
165}
166
167
168void
169CConn::displayChanged() {
170 // Display format has changed - recalculate the full-colour pixel format
171 calculateFullColourPF();
172}
173
174void
175CConn::paintCompleted() {
176 // A repaint message has just completed - request next update if necessary
177 requestNewUpdate();
178}
179
180bool
181CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
182 // - If it's one of our (F8 Menu) messages
183 switch (wParam) {
184 case IDM_FULLSCREEN:
185 options.fullScreen = !window->isFullscreen();
186 window->setFullscreen(options.fullScreen);
187 return true;
george824f72ab32006-09-10 05:13:45 +0000188 case IDM_ZOOM_IN:
george824f72ab32006-09-10 05:13:45 +0000189 case IDM_ZOOM_OUT:
george82b6d87aa2006-09-11 07:00:59 +0000190 {
191 if (options.autoScaling) {
192 options.scale = window->getDesktopScale();
193 options.autoScaling = false;
194 window->setAutoScaling(false);
195 }
Peter Åstrand145d1f82010-02-10 07:26:56 +0000196 if (wParam == (unsigned)IDM_ZOOM_IN) {
george82b6d87aa2006-09-11 07:00:59 +0000197 for (int i = 0; i < scaleCount; i++)
198 if (options.scale < scaleValues[i]) {
199 options.scale = scaleValues[i];
200 break;
201 }
202 } else {
203 for (int i = scaleCount-1; i >= 0; i--)
204 if (options.scale > scaleValues[i]) {
205 options.scale = scaleValues[i];
206 break;
207 }
208 }
209 if (options.scale != window->getDesktopScale())
210 window->setDesktopScale(options.scale);
211 }
george824f72ab32006-09-10 05:13:45 +0000212 return true;
213 case IDM_ACTUAL_SIZE:
214 if (options.autoScaling) {
215 options.autoScaling = false;
216 window->setAutoScaling(false);
217 }
george824f72ab32006-09-10 05:13:45 +0000218 options.scale = 100;
219 window->setDesktopScale(100);
220 return true;
221 case IDM_AUTO_SIZE:
george8274ea5f32006-09-11 11:40:12 +0000222 options.autoScaling = !options.autoScaling;
223 window->setAutoScaling(options.autoScaling);
224 if (!options.autoScaling) options.scale = window->getDesktopScale();
george824f72ab32006-09-10 05:13:45 +0000225 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000226 case IDM_SHOW_TOOLBAR:
227 options.showToolbar = !window->isToolbarEnabled();
228 window->setShowToolbar(options.showToolbar);
229 return true;
230 case IDM_CTRL_KEY:
231 window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
232 return true;
233 case IDM_ALT_KEY:
234 window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
235 return true;
236 case IDM_SEND_MENU_KEY:
237 window->kbd.keyEvent(this, options.menuKey, 0, true);
238 window->kbd.keyEvent(this, options.menuKey, 0, false);
239 return true;
240 case IDM_SEND_CAD:
241 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
242 window->kbd.keyEvent(this, VK_MENU, 0, true);
243 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
244 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
245 window->kbd.keyEvent(this, VK_MENU, 0, false);
246 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
247 return true;
248 case IDM_SEND_CTLESC:
249 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
250 window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
251 window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
252 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
253 return true;
254 case IDM_REQUEST_REFRESH:
255 try {
256 writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
257 requestUpdate = false;
258 } catch (rdr::Exception& e) {
259 close(e.str());
260 }
261 return true;
262 case IDM_NEWCONN:
263 {
Peter Åstrand145d1f82010-02-10 07:26:56 +0000264 new CConnThread;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000265 }
266 return true;
267 case IDM_OPTIONS:
268 // Update the monitor device name in the CConnOptions instance
269 options.monitor.replaceBuf(window->getMonitor());
270 showOptionsDialog();
271 return true;
272 case IDM_INFO:
273 infoDialog.showDialog(this);
274 return true;
275 case IDM_ABOUT:
276 AboutDialog::instance.showDialog();
277 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000278 case IDM_CONN_SAVE_AS:
279 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000280 };
281 return false;
282}
283
284
285void
286CConn::closeWindow() {
287 vlog.info("window closed");
288 close();
289}
290
291
292void
293CConn::refreshMenu(bool enableSysItems) {
294 HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
295
296 if (!enableSysItems) {
297 // Gray out menu items that might cause a World Of Pain
298 EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
299 EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
300 EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
301 EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
302 EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
303 }
304
305 // Update the modifier key menu items
306 UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
307 UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
308 CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
309 CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
310
311 // Ensure that the Send <MenuKey> menu item has the correct text
312 if (options.menuKey) {
313 TCharArray menuKeyStr(options.menuKeyName());
314 TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
315 _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
316 if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
317 InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
318 } else {
319 RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
320 }
321
322 // Set the menu fullscreen option tick
323 CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
324
325 // Set the menu toolbar option tick
326 int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
327 CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
328
329 // In the full-screen mode, "Show toolbar" should be grayed.
330 toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
331 EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
332}
333
334
335void
336CConn::blockCallback() {
337 // - An InStream has blocked on I/O while processing an RFB message
338 // We re-enable socket event notifications, so we'll know when more
339 // data is available, then we sit and dispatch window events until
340 // the notification arrives.
341 if (!isClosed()) {
342 if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
343 throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
344 }
345 while (true) {
346 // If we have closed then we can't block waiting for data
347 if (isClosed())
348 throw rdr::EndOfStream();
349
350 // Wait for socket data, or a message to process
351 DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
352 if (result == WAIT_OBJECT_0) {
353 // - Network event notification. Return control to I/O routine.
354 break;
355 } else if (result == WAIT_FAILED) {
356 // - The wait operation failed - raise an exception
357 throw rdr::SystemException("blockCallback wait error", GetLastError());
358 }
359
360 // - There should be a message in the message queue
361 MSG msg;
362 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
363 // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
364 // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
365 // state from one call to the next, which would be messed up by calls to
366 // TranslateMessage() (actually it looks like TranslateMessage() calls
367 // ToAscii() internally).
368 DispatchMessage(&msg);
369 }
370 }
371
372 // Before we return control to the InStream, reset the network event
373 WSAEventSelect(sock->getFd(), sockEvent, 0);
374 ResetEvent(sockEvent);
375}
376
377
378void CConn::keyEvent(rdr::U32 key, bool down) {
379 if (!options.sendKeyEvents) return;
380 try {
381 writer()->keyEvent(key, down);
382 } catch (rdr::Exception& e) {
383 close(e.str());
384 }
385}
386void CConn::pointerEvent(const Point& pos, int buttonMask) {
387 if (!options.sendPtrEvents) return;
388 try {
389 writer()->pointerEvent(pos, buttonMask);
390 } catch (rdr::Exception& e) {
391 close(e.str());
392 }
393}
394void CConn::clientCutText(const char* str, int len) {
395 if (!options.clientCutText) return;
396 if (state() != RFBSTATE_NORMAL) return;
397 try {
398 writer()->clientCutText(str, len);
399 } catch (rdr::Exception& e) {
400 close(e.str());
401 }
402}
403
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000404void
405CConn::setColourMapEntries(int first, int count, U16* rgbs) {
406 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
407 int i;
408 for (i=0;i<count;i++)
409 window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
410 // *** change to 0, 256?
411 window->refreshWindowPalette(first, count);
412}
413
414void
415CConn::bell() {
416 if (options.acceptBell)
Peter Åstrand7eb594a2008-12-11 08:27:52 +0000417 MessageBeep((UINT)-1);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000418}
419
420
421void
422CConn::setDesktopSize(int w, int h) {
423 vlog.debug("setDesktopSize %dx%d", w, h);
424
425 // Resize the window's buffer
426 if (window)
427 window->setSize(w, h);
428
429 // Tell the underlying CConnection
430 CConnection::setDesktopSize(w, h);
431}
432
Pierre Ossmand6d19942009-03-24 12:29:50 +0000433
434void
435CConn::setExtendedDesktopSize(int reason, int result, int w, int h,
436 const rfb::ScreenSet& layout) {
Peter Åstrand145d1f82010-02-10 07:26:56 +0000437 if ((reason == (signed)reasonClient) && (result != (signed)resultSuccess)) {
Pierre Ossmand6d19942009-03-24 12:29:50 +0000438 vlog.error("SetDesktopSize failed: %d", result);
439 return;
440 }
441
442 // Resize the window's buffer
443 if (window)
444 window->setSize(w, h);
445
446 // Tell the underlying CConnection
447 CConnection::setExtendedDesktopSize(reason, result, w, h, layout);
448}
449
450
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000451void
452CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
453 if (!options.useLocalCursor) return;
454
455 // Set the window to use the new cursor
456 window->setCursor(w, h, hotspot, data, mask);
457}
458
459
460void
461CConn::close(const char* reason) {
462 // If already closed then ignore this
463 if (isClosed())
464 return;
465
466 // Hide the window, if it exists
467 if (window)
468 ShowWindow(window->getHandle(), SW_HIDE);
469
470 // Save the reason & flag that we're closed & shutdown the socket
471 isClosed_ = true;
472 closeReason_.replaceBuf(strDup(reason));
473 sock->shutdown();
474}
475
476
477void
478CConn::showOptionsDialog() {
479 optionsDialog.showDialog(this);
480}
481
482
483void
484CConn::framebufferUpdateEnd() {
485 if (debugDelay != 0) {
486 vlog.debug("debug delay %d",(int)debugDelay);
487 UpdateWindow(window->getHandle());
488 Sleep(debugDelay);
489 std::list<rfb::Rect>::iterator i;
490 for (i = debugRects.begin(); i != debugRects.end(); i++) {
491 window->invertRect(*i);
492 }
493 debugRects.clear();
494 }
Pierre Ossmanb2ff1602009-03-25 12:13:28 +0000495 window->framebufferUpdateEnd();
Pierre Ossmand6d19942009-03-24 12:29:50 +0000496
497 if (firstUpdate) {
498 int width, height;
499
500 if (cp.supportsSetDesktopSize &&
501 sscanf(options.desktopSize.buf, "%dx%d", &width, &height) == 2) {
502 ScreenSet layout;
503
504 layout = cp.screenLayout;
505
506 if (layout.num_screens() == 0)
507 layout.add_screen(rfb::Screen());
508 else if (layout.num_screens() != 1) {
509 ScreenSet::iterator iter;
510
511 while (true) {
512 iter = layout.begin();
513 ++iter;
514
515 if (iter == layout.end())
516 break;
517
518 layout.remove_screen(iter->id);
519 }
520 }
521
522 layout.begin()->dimensions.tl.x = 0;
523 layout.begin()->dimensions.tl.y = 0;
524 layout.begin()->dimensions.br.x = width;
525 layout.begin()->dimensions.br.y = height;
526
527 writer()->writeSetDesktopSize(width, height, layout);
528 }
529
530 firstUpdate = false;
531 }
532
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000533 if (options.autoSelect)
534 autoSelectFormatAndEncoding();
535
536 // Always request the next update
537 requestUpdate = true;
538
539 // Check that at least part of the window has changed
540 if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
541 if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
542 requestNewUpdate();
543 }
544
545 // Make sure the local cursor is shown
546 window->showCursor();
547}
548
549
Pierre Ossman78b23592009-03-12 12:25:11 +0000550// Note: The method below is duplicated in win/vncviewer/CConn.cxx!
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000551
552// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
553// to the connection speed:
554//
Pierre Ossman78b23592009-03-12 12:25:11 +0000555// First we wait for at least one second of bandwidth measurement.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000556//
Pierre Ossman78b23592009-03-12 12:25:11 +0000557// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
558// which should be perceptually lossless.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000559//
Pierre Ossman78b23592009-03-12 12:25:11 +0000560// If the bandwidth is below that, we choose a more lossy JPEG quality.
561//
562// If the bandwidth drops below 256 Kbps, we switch to palette mode.
563//
564// Note: The system here is fairly arbitrary and should be replaced
565// with something more intelligent at the server end.
566//
567void CConn::autoSelectFormatAndEncoding()
568{
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000569 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000570 unsigned int timeWaited = sock->inStream().timeWaited();
Pierre Ossman78b23592009-03-12 12:25:11 +0000571 bool newFullColour = options.fullColour;
572 int newQualityLevel = options.qualityLevel;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000573
Pierre Ossman78b23592009-03-12 12:25:11 +0000574 // Always use Tight
575 options.preferredEncoding = encodingTight;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000576
Pierre Ossman78b23592009-03-12 12:25:11 +0000577 // Check that we have a decent bandwidth measurement
578 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000579 return;
Pierre Ossman78b23592009-03-12 12:25:11 +0000580
581 // Select appropriate quality level
582 if (!options.noJpeg) {
583 if (kbitsPerSecond > 16000)
584 newQualityLevel = 8;
585 else
586 newQualityLevel = 6;
587
588 if (newQualityLevel != options.qualityLevel) {
589 vlog.info("Throughput %d kbit/s - changing to quality %d ",
590 kbitsPerSecond, newQualityLevel);
591 cp.qualityLevel = newQualityLevel;
592 options.qualityLevel = newQualityLevel;
593 encodingChange = true;
594 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000595 }
596
597 if (cp.beforeVersion(3, 8)) {
598 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
599 // cursors "asynchronously". If this happens in the middle of a
600 // pixel format change, the server will encode the cursor with
601 // the old format, but the client will try to decode it
602 // according to the new format. This will lead to a
603 // crash. Therefore, we do not allow automatic format change for
604 // old servers.
605 return;
606 }
607
608 // Select best color level
609 newFullColour = (kbitsPerSecond > 256);
610 if (newFullColour != options.fullColour) {
611 vlog.info("Throughput %d kbit/s - full color is now %s",
Pierre Ossman78b23592009-03-12 12:25:11 +0000612 kbitsPerSecond,
613 newFullColour ? "enabled" : "disabled");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000614 options.fullColour = newFullColour;
615 formatChange = true;
Pierre Ossman78b23592009-03-12 12:25:11 +0000616 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000617}
618
619void
620CConn::requestNewUpdate() {
621 if (!requestUpdate) return;
622
623 if (formatChange) {
624 // Select the required pixel format
625 if (options.fullColour) {
626 window->setPF(fullColourPF);
627 } else {
628 switch (options.lowColourLevel) {
629 case 0:
630 window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
631 break;
632 case 1:
633 window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
634 break;
635 case 2:
636 window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
637 break;
638 }
639 }
640
641 // Print the current pixel format
642 char str[256];
643 window->getPF().print(str, 256);
644 vlog.info("Using pixel format %s",str);
645
646 // Save the connection pixel format and tell server to use it
647 cp.setPF(window->getPF());
648 writer()->writeSetPixelFormat(cp.pf());
649
650 // Correct the local window's palette
651 if (!window->getNativePF().trueColour)
652 window->refreshWindowPalette(0, 1 << cp.pf().depth);
653 }
654
655 if (encodingChange) {
656 vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
657 writer()->writeSetEncodings(options.preferredEncoding, true);
658 }
659
660 writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
661 !formatChange);
662
663 encodingChange = formatChange = requestUpdate = false;
664}
665
666
667void
668CConn::calculateFullColourPF() {
669 // If the server is palette based then use palette locally
670 // Also, don't bother doing bgr222
671 if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
672 fullColourPF = serverDefaultPF;
673 options.fullColour = true;
674 } else {
675 // If server is trueColour, use lowest depth PF
676 PixelFormat native = window->getNativePF();
677 if ((serverDefaultPF.bpp < native.bpp) ||
678 ((serverDefaultPF.bpp == native.bpp) &&
679 (serverDefaultPF.depth < native.depth)))
680 fullColourPF = serverDefaultPF;
681 else
682 fullColourPF = window->getNativePF();
683 }
684 formatChange = true;
685}
686
687
688void
689CConn::setName(const char* name) {
690 if (window)
691 window->setName(name);
692 CConnection::setName(name);
693}
694
695
696void CConn::serverInit() {
697 CConnection::serverInit();
698
699 // If using AutoSelect with old servers, start in FullColor
700 // mode. See comment in autoSelectFormatAndEncoding.
701 if (cp.beforeVersion(3, 8) && options.autoSelect) {
702 options.fullColour = true;
703 }
704
705 // Show the window
706 window = new DesktopWindow(this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000707
708 // Update the window menu
709 HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
710 int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
711
712 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
713 AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
714 AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
715 _T("Show tool&bar"));
716 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
717 AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
718 AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
719 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
720 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
721 AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
722 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
723 AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
724 AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
725 AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
726 AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
Pierre Ossman0df7c7a2009-04-09 12:00:08 +0000727
728 // Set window attributes
729 window->setName(cp.name());
730 window->setShowToolbar(options.showToolbar);
731 window->setSize(cp.width, cp.height);
732 applyOptions(options);
733
734 // Save the server's current format
735 serverDefaultPF = cp.pf();
736
737 // Calculate the full-colour format to use
738 calculateFullColourPF();
739
740 // Request the initial update
741 vlog.info("requesting initial update");
742 formatChange = encodingChange = requestUpdate = true;
743 requestNewUpdate();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000744}
745
746void
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000747CConn::serverCutText(const char* str, rdr::U32 len) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000748 if (!options.serverCutText) return;
749 window->serverCutText(str, len);
750}
751
752
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000753void CConn::beginRect(const Rect& r, int encoding) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000754 sock->inStream().startTiming();
755}
756
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000757void CConn::endRect(const Rect& r, int encoding) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000758 sock->inStream().stopTiming();
759 lastUsedEncoding_ = encoding;
760 if (debugDelay != 0) {
761 window->invertRect(r);
762 debugRects.push_back(r);
763 }
764}
765
766void CConn::fillRect(const Rect& r, Pixel pix) {
767 window->fillRect(r, pix);
768}
769void CConn::imageRect(const Rect& r, void* pixels) {
770 window->imageRect(r, pixels);
771}
772void CConn::copyRect(const Rect& r, int srcX, int srcY) {
773 window->copyRect(r, srcX, srcY);
774}
775
776void CConn::getUserPasswd(char** user, char** password) {
777 if (!user && options.passwordFile.buf[0]) {
778 FILE* fp = fopen(options.passwordFile.buf, "rb");
779 if (fp) {
780 char data[256];
781 int datalen = fread(data, 1, 256, fp);
782 fclose(fp);
783 if (datalen == 8) {
784 ObfuscatedPasswd obfPwd;
785 obfPwd.buf = data;
786 obfPwd.length = datalen;
787 PlainPasswd passwd(obfPwd);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000788 obfPwd.takeBuf();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000789 *password = strDup(passwd.buf);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000790 memset(data, 0, sizeof(data));
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000791 }
792 }
793 }
794 if (user && options.userName.buf)
795 *user = strDup(options.userName.buf);
796 if (password && options.password.buf)
797 *password = strDup(options.password.buf);
798 if ((user && !*user) || (password && !*password)) {
799 // Missing username or password - prompt the user
800 UserPasswdDialog userPasswdDialog;
Adam Tkacf324dc42010-04-23 14:10:17 +0000801 userPasswdDialog.setCSecurity(csecurity);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000802 userPasswdDialog.getUserPasswd(user, password);
803 }
804 if (user) options.setUserName(*user);
805 if (password) options.setPassword(*password);
806}
807