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