blob: b067134afc2850f0d8eef0b6f18e903c30c83084 [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
Adam Tkac243fd7c2010-12-08 13:47:41 +000019#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
22
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000023#include <winsock2.h>
24#include <vncviewer/UserPasswdDialog.h>
25#include <vncviewer/CConn.h>
26#include <vncviewer/CConnThread.h>
27#include <vncviewer/resource.h>
28#include <rfb/encodings.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000029#include <rfb/Security.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000030#include <rfb/CMsgWriter.h>
31#include <rfb/Configuration.h>
Adam Tkac243fd7c2010-12-08 13:47:41 +000032#ifdef HAVE_GNUTLS
33#include <rfb/CSecurityTLS.h>
34#endif
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000035#include <rfb/LogWriter.h>
36#include <rfb_win32/AboutDialog.h>
37
38using namespace rfb;
39using namespace rfb::win32;
40using namespace rdr;
41
42// - Statics & consts
43
44static LogWriter vlog("CConn");
45
46
47const int IDM_FULLSCREEN = ID_FULLSCREEN;
48const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY;
49const int IDM_SEND_CAD = ID_SEND_CAD;
50const int IDM_SEND_CTLESC = ID_SEND_CTLESC;
51const int IDM_ABOUT = ID_ABOUT;
52const int IDM_OPTIONS = ID_OPTIONS;
53const int IDM_INFO = ID_INFO;
54const int IDM_NEWCONN = ID_NEW_CONNECTION;
55const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH;
56const int IDM_CTRL_KEY = ID_CTRL_KEY;
57const int IDM_ALT_KEY = ID_ALT_KEY;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000058const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
george824f72ab32006-09-10 05:13:45 +000059const int IDM_ZOOM_IN = ID_ZOOM_IN;
60const int IDM_ZOOM_OUT = ID_ZOOM_OUT;
61const int IDM_ACTUAL_SIZE = ID_ACTUAL_SIZE;
62const int IDM_AUTO_SIZE = ID_AUTO_SIZE;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000063
64
65static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
66 "pixel data - a debugging feature", 0);
67
george82b6d87aa2006-09-11 07:00:59 +000068const int scaleValues[9] = {10, 25, 50, 75, 90, 100, 125, 150, 200};
69const int scaleCount = 9;
70
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000071
72//
73// -=- CConn implementation
74//
75
76RegKey CConn::userConfigKey;
77
78
79CConn::CConn()
Peter Åstrand2dd9eea2008-12-11 08:23:55 +000080 : window(0), sameMachine(false), encodingChange(false), formatChange(false),
81 lastUsedEncoding_(encodingRaw), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)),
Pierre Ossmand6d19942009-03-24 12:29:50 +000082 reverseConnection(false), requestUpdate(false), firstUpdate(true),
Adam Tkacf586b842011-04-27 11:20:18 +000083 pendingUpdate(false), isClosed_(false) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000084}
85
86CConn::~CConn() {
87 delete window;
88}
89
90bool CConn::initialise(network::Socket* s, bool reverse) {
91 // Set the server's name for MRU purposes
92 CharArray endpoint(s->getPeerEndpoint());
Adam Tkac0b428712011-02-01 15:06:03 +000093
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000094 if (!options.host.buf)
95 options.setHost(endpoint.buf);
Adam Tkac0b428712011-02-01 15:06:03 +000096 setServerName(options.host.buf);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000097
98 // Initialise the underlying CConnection
99 setStreams(&s->inStream(), &s->outStream());
100
101 // Enable processing of window messages while blocked on I/O
102 s->inStream().setBlockCallback(this);
103
104 // Initialise the viewer options
105 applyOptions(options);
106
Adam Tkacb10489b2010-04-23 14:16:04 +0000107 CSecurity::upg = this;
Adam Tkac243fd7c2010-12-08 13:47:41 +0000108#ifdef HAVE_GNUTLS
109 CSecurityTLS::msg = this;
110#endif
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000111
112 // Start the RFB protocol
113 sock = s;
114 reverseConnection = reverse;
115 initialiseProtocol();
116
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000117 return true;
118}
119
120
121void
122CConn::applyOptions(CConnOptions& opt) {
123 // - If any encoding-related settings have changed then we must
124 // notify the server of the new settings
125 encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
126 (options.useDesktopResize != opt.useDesktopResize) ||
127 (options.customCompressLevel != opt.customCompressLevel) ||
128 (options.compressLevel != opt.compressLevel) ||
129 (options.noJpeg != opt.noJpeg) ||
130 (options.qualityLevel != opt.qualityLevel) ||
131 (options.preferredEncoding != opt.preferredEncoding));
132
133 // - If the preferred pixel format has changed then notify the server
134 formatChange |= (options.fullColour != opt.fullColour);
135 if (!opt.fullColour)
136 formatChange |= (options.lowColourLevel != opt.lowColourLevel);
137
138 // - Save the new set of options
139 options = opt;
140
141 // - Set optional features in ConnParams
142 cp.supportsLocalCursor = options.useLocalCursor;
143 cp.supportsDesktopResize = options.useDesktopResize;
Pierre Ossmand6d19942009-03-24 12:29:50 +0000144 cp.supportsExtendedDesktopSize = options.useDesktopResize;
Peter Åstrandc39e0782009-01-15 12:21:42 +0000145 cp.supportsDesktopRename = true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000146 cp.customCompressLevel = options.customCompressLevel;
147 cp.compressLevel = options.compressLevel;
148 cp.noJpeg = options.noJpeg;
149 cp.qualityLevel = options.qualityLevel;
150
151 // - Configure connection sharing on/off
152 setShared(options.shared);
153
154 // - Whether to use protocol 3.3 for legacy compatibility
155 setProtocol3_3(options.protocol3_3);
156
157 // - Apply settings that affect the window, if it is visible
158 if (window) {
159 window->setMonitor(options.monitor.buf);
160 window->setFullscreen(options.fullScreen);
161 window->setEmulate3(options.emulate3);
162 window->setPointerEventInterval(options.pointerEventInterval);
163 window->setMenuKey(options.menuKey);
164 window->setDisableWinKeys(options.disableWinKeys);
165 window->setShowToolbar(options.showToolbar);
george82770bbbc2007-03-12 10:48:09 +0000166 window->printScale();
george82ffc14a62006-09-05 06:51:41 +0000167 if (options.autoScaling) {
168 window->setAutoScaling(true);
169 } else {
170 window->setAutoScaling(false);
171 window->setDesktopScale(options.scale);
172 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000173 if (!options.useLocalCursor)
174 window->setCursor(0, 0, Point(), 0, 0);
175 }
Adam Tkac1238c042011-02-01 14:33:41 +0000176
177 security->SetSecTypes(options.secTypes);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000178}
179
180
181void
182CConn::displayChanged() {
183 // Display format has changed - recalculate the full-colour pixel format
184 calculateFullColourPF();
185}
186
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000187
188bool
189CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
190 // - If it's one of our (F8 Menu) messages
191 switch (wParam) {
192 case IDM_FULLSCREEN:
193 options.fullScreen = !window->isFullscreen();
194 window->setFullscreen(options.fullScreen);
195 return true;
george824f72ab32006-09-10 05:13:45 +0000196 case IDM_ZOOM_IN:
george824f72ab32006-09-10 05:13:45 +0000197 case IDM_ZOOM_OUT:
george82b6d87aa2006-09-11 07:00:59 +0000198 {
199 if (options.autoScaling) {
200 options.scale = window->getDesktopScale();
201 options.autoScaling = false;
202 window->setAutoScaling(false);
203 }
Peter Åstrand145d1f82010-02-10 07:26:56 +0000204 if (wParam == (unsigned)IDM_ZOOM_IN) {
george82b6d87aa2006-09-11 07:00:59 +0000205 for (int i = 0; i < scaleCount; i++)
206 if (options.scale < scaleValues[i]) {
207 options.scale = scaleValues[i];
208 break;
209 }
210 } else {
211 for (int i = scaleCount-1; i >= 0; i--)
212 if (options.scale > scaleValues[i]) {
213 options.scale = scaleValues[i];
214 break;
215 }
216 }
217 if (options.scale != window->getDesktopScale())
218 window->setDesktopScale(options.scale);
219 }
george824f72ab32006-09-10 05:13:45 +0000220 return true;
221 case IDM_ACTUAL_SIZE:
222 if (options.autoScaling) {
223 options.autoScaling = false;
224 window->setAutoScaling(false);
225 }
george824f72ab32006-09-10 05:13:45 +0000226 options.scale = 100;
227 window->setDesktopScale(100);
228 return true;
229 case IDM_AUTO_SIZE:
george8274ea5f32006-09-11 11:40:12 +0000230 options.autoScaling = !options.autoScaling;
231 window->setAutoScaling(options.autoScaling);
232 if (!options.autoScaling) options.scale = window->getDesktopScale();
george824f72ab32006-09-10 05:13:45 +0000233 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000234 case IDM_SHOW_TOOLBAR:
235 options.showToolbar = !window->isToolbarEnabled();
236 window->setShowToolbar(options.showToolbar);
237 return true;
238 case IDM_CTRL_KEY:
239 window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
240 return true;
241 case IDM_ALT_KEY:
242 window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
243 return true;
244 case IDM_SEND_MENU_KEY:
245 window->kbd.keyEvent(this, options.menuKey, 0, true);
246 window->kbd.keyEvent(this, options.menuKey, 0, false);
247 return true;
248 case IDM_SEND_CAD:
249 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
250 window->kbd.keyEvent(this, VK_MENU, 0, true);
251 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
252 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
253 window->kbd.keyEvent(this, VK_MENU, 0, false);
254 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
255 return true;
256 case IDM_SEND_CTLESC:
257 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
258 window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
259 window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
260 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
261 return true;
262 case IDM_REQUEST_REFRESH:
263 try {
264 writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
265 requestUpdate = false;
266 } catch (rdr::Exception& e) {
267 close(e.str());
268 }
269 return true;
270 case IDM_NEWCONN:
271 {
Peter Åstrand145d1f82010-02-10 07:26:56 +0000272 new CConnThread;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000273 }
274 return true;
275 case IDM_OPTIONS:
276 // Update the monitor device name in the CConnOptions instance
277 options.monitor.replaceBuf(window->getMonitor());
278 showOptionsDialog();
279 return true;
280 case IDM_INFO:
281 infoDialog.showDialog(this);
282 return true;
283 case IDM_ABOUT:
284 AboutDialog::instance.showDialog();
285 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000286 case IDM_CONN_SAVE_AS:
287 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000288 };
289 return false;
290}
291
292
293void
294CConn::closeWindow() {
295 vlog.info("window closed");
296 close();
297}
298
299
300void
301CConn::refreshMenu(bool enableSysItems) {
302 HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
303
304 if (!enableSysItems) {
305 // Gray out menu items that might cause a World Of Pain
306 EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
307 EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
308 EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
309 EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
310 EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
311 }
312
313 // Update the modifier key menu items
314 UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
315 UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
316 CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
317 CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
318
319 // Ensure that the Send <MenuKey> menu item has the correct text
320 if (options.menuKey) {
321 TCharArray menuKeyStr(options.menuKeyName());
322 TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
323 _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
324 if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
325 InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
326 } else {
327 RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
328 }
329
330 // Set the menu fullscreen option tick
331 CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
332
333 // Set the menu toolbar option tick
334 int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
335 CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
336
337 // In the full-screen mode, "Show toolbar" should be grayed.
338 toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
339 EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
340}
341
342
343void
344CConn::blockCallback() {
345 // - An InStream has blocked on I/O while processing an RFB message
346 // We re-enable socket event notifications, so we'll know when more
347 // data is available, then we sit and dispatch window events until
348 // the notification arrives.
349 if (!isClosed()) {
350 if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
351 throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
352 }
353 while (true) {
354 // If we have closed then we can't block waiting for data
355 if (isClosed())
356 throw rdr::EndOfStream();
357
358 // Wait for socket data, or a message to process
359 DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
Adam Tkacf586b842011-04-27 11:20:18 +0000360 if (result == WAIT_FAILED) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000361 // - 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 }
Adam Tkacf586b842011-04-27 11:20:18 +0000375
376 if (result == WAIT_OBJECT_0)
377 // - Network event notification. Return control to I/O routine.
378 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000379 }
380
381 // Before we return control to the InStream, reset the network event
382 WSAEventSelect(sock->getFd(), sockEvent, 0);
383 ResetEvent(sockEvent);
384}
385
386
387void CConn::keyEvent(rdr::U32 key, bool down) {
388 if (!options.sendKeyEvents) return;
389 try {
390 writer()->keyEvent(key, down);
391 } catch (rdr::Exception& e) {
392 close(e.str());
393 }
394}
395void CConn::pointerEvent(const Point& pos, int buttonMask) {
396 if (!options.sendPtrEvents) return;
397 try {
398 writer()->pointerEvent(pos, buttonMask);
399 } catch (rdr::Exception& e) {
400 close(e.str());
401 }
402}
403void CConn::clientCutText(const char* str, int len) {
404 if (!options.clientCutText) return;
405 if (state() != RFBSTATE_NORMAL) return;
406 try {
407 writer()->clientCutText(str, len);
408 } catch (rdr::Exception& e) {
409 close(e.str());
410 }
411}
412
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000413void
414CConn::setColourMapEntries(int first, int count, U16* rgbs) {
415 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
416 int i;
417 for (i=0;i<count;i++)
418 window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
419 // *** change to 0, 256?
420 window->refreshWindowPalette(first, count);
421}
422
423void
424CConn::bell() {
425 if (options.acceptBell)
Peter Åstrand7eb594a2008-12-11 08:27:52 +0000426 MessageBeep((UINT)-1);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000427}
428
429
430void
431CConn::setDesktopSize(int w, int h) {
432 vlog.debug("setDesktopSize %dx%d", w, h);
433
434 // Resize the window's buffer
435 if (window)
436 window->setSize(w, h);
437
438 // Tell the underlying CConnection
439 CConnection::setDesktopSize(w, h);
440}
441
Pierre Ossmand6d19942009-03-24 12:29:50 +0000442
443void
444CConn::setExtendedDesktopSize(int reason, int result, int w, int h,
445 const rfb::ScreenSet& layout) {
Peter Åstrand145d1f82010-02-10 07:26:56 +0000446 if ((reason == (signed)reasonClient) && (result != (signed)resultSuccess)) {
Pierre Ossmand6d19942009-03-24 12:29:50 +0000447 vlog.error("SetDesktopSize failed: %d", result);
448 return;
449 }
450
451 // Resize the window's buffer
452 if (window)
453 window->setSize(w, h);
454
455 // Tell the underlying CConnection
456 CConnection::setExtendedDesktopSize(reason, result, w, h, layout);
457}
458
459
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000460void
461CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
462 if (!options.useLocalCursor) return;
463
464 // Set the window to use the new cursor
465 window->setCursor(w, h, hotspot, data, mask);
466}
467
468
469void
470CConn::close(const char* reason) {
471 // If already closed then ignore this
472 if (isClosed())
473 return;
474
475 // Hide the window, if it exists
476 if (window)
477 ShowWindow(window->getHandle(), SW_HIDE);
478
479 // Save the reason & flag that we're closed & shutdown the socket
480 isClosed_ = true;
481 closeReason_.replaceBuf(strDup(reason));
482 sock->shutdown();
483}
484
Adam Tkac243fd7c2010-12-08 13:47:41 +0000485bool CConn::showMsgBox(int flags, const char* title, const char* text)
486{
487 UINT winflags = 0;
488 int ret;
489
490 /* Translate flags */
491 if ((flags & M_OK) != 0)
492 winflags |= MB_OK;
493 if ((flags & M_OKCANCEL) != 0)
494 winflags |= MB_OKCANCEL;
495 if ((flags & M_YESNO) != 0)
496 winflags |= MB_YESNO;
497 if ((flags & M_ICONERROR) != 0)
498 winflags |= MB_ICONERROR;
499 if ((flags & M_ICONQUESTION) != 0)
500 winflags |= MB_ICONQUESTION;
501 if ((flags & M_ICONWARNING) != 0)
502 winflags |= MB_ICONWARNING;
503 if ((flags & M_ICONINFORMATION) != 0)
504 winflags |= MB_ICONINFORMATION;
505 if ((flags & M_DEFBUTTON1) != 0)
506 winflags |= MB_DEFBUTTON1;
507 if ((flags & M_DEFBUTTON2) != 0)
508 winflags |= MB_DEFBUTTON2;
509
510 ret = MessageBox(NULL, text, title, flags);
511 return (ret == IDOK || ret == IDYES) ? true : false;
512}
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000513
514void
515CConn::showOptionsDialog() {
516 optionsDialog.showDialog(this);
517}
518
519
520void
Adam Tkacf586b842011-04-27 11:20:18 +0000521CConn::framebufferUpdateStart() {
522 if (!formatChange) {
523 requestUpdate = pendingUpdate = true;
524 requestNewUpdate();
525 } else
526 pendingUpdate = false;
527}
528
529
530void
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000531CConn::framebufferUpdateEnd() {
532 if (debugDelay != 0) {
533 vlog.debug("debug delay %d",(int)debugDelay);
534 UpdateWindow(window->getHandle());
535 Sleep(debugDelay);
536 std::list<rfb::Rect>::iterator i;
537 for (i = debugRects.begin(); i != debugRects.end(); i++) {
538 window->invertRect(*i);
539 }
540 debugRects.clear();
541 }
Pierre Ossmanb2ff1602009-03-25 12:13:28 +0000542 window->framebufferUpdateEnd();
Pierre Ossmand6d19942009-03-24 12:29:50 +0000543
544 if (firstUpdate) {
545 int width, height;
546
547 if (cp.supportsSetDesktopSize &&
548 sscanf(options.desktopSize.buf, "%dx%d", &width, &height) == 2) {
549 ScreenSet layout;
550
551 layout = cp.screenLayout;
552
553 if (layout.num_screens() == 0)
554 layout.add_screen(rfb::Screen());
555 else if (layout.num_screens() != 1) {
556 ScreenSet::iterator iter;
557
558 while (true) {
559 iter = layout.begin();
560 ++iter;
561
562 if (iter == layout.end())
563 break;
564
565 layout.remove_screen(iter->id);
566 }
567 }
568
569 layout.begin()->dimensions.tl.x = 0;
570 layout.begin()->dimensions.tl.y = 0;
571 layout.begin()->dimensions.br.x = width;
572 layout.begin()->dimensions.br.y = height;
573
574 writer()->writeSetDesktopSize(width, height, layout);
575 }
576
577 firstUpdate = false;
578 }
579
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000580 // Always request the next update
581 requestUpdate = true;
582
Adam Tkacf586b842011-04-27 11:20:18 +0000583 // A format change prevented us from sending this before the update,
584 // so make sure to send it now.
585 if (formatChange && !pendingUpdate)
586 requestNewUpdate();
587
588 if (options.autoSelect)
589 autoSelectFormatAndEncoding();
590
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000591 // Check that at least part of the window has changed
592 if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
593 if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
594 requestNewUpdate();
595 }
596
597 // Make sure the local cursor is shown
598 window->showCursor();
599}
600
601
Pierre Ossman78b23592009-03-12 12:25:11 +0000602// Note: The method below is duplicated in win/vncviewer/CConn.cxx!
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000603
604// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
605// to the connection speed:
606//
Pierre Ossman78b23592009-03-12 12:25:11 +0000607// First we wait for at least one second of bandwidth measurement.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000608//
Pierre Ossman78b23592009-03-12 12:25:11 +0000609// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
610// which should be perceptually lossless.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000611//
Pierre Ossman78b23592009-03-12 12:25:11 +0000612// If the bandwidth is below that, we choose a more lossy JPEG quality.
613//
614// If the bandwidth drops below 256 Kbps, we switch to palette mode.
615//
616// Note: The system here is fairly arbitrary and should be replaced
617// with something more intelligent at the server end.
618//
619void CConn::autoSelectFormatAndEncoding()
620{
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000621 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000622 unsigned int timeWaited = sock->inStream().timeWaited();
Pierre Ossman78b23592009-03-12 12:25:11 +0000623 bool newFullColour = options.fullColour;
624 int newQualityLevel = options.qualityLevel;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000625
Pierre Ossman78b23592009-03-12 12:25:11 +0000626 // Always use Tight
627 options.preferredEncoding = encodingTight;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000628
Pierre Ossman78b23592009-03-12 12:25:11 +0000629 // Check that we have a decent bandwidth measurement
630 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000631 return;
Pierre Ossman78b23592009-03-12 12:25:11 +0000632
633 // Select appropriate quality level
634 if (!options.noJpeg) {
635 if (kbitsPerSecond > 16000)
636 newQualityLevel = 8;
637 else
638 newQualityLevel = 6;
639
640 if (newQualityLevel != options.qualityLevel) {
641 vlog.info("Throughput %d kbit/s - changing to quality %d ",
642 kbitsPerSecond, newQualityLevel);
643 cp.qualityLevel = newQualityLevel;
644 options.qualityLevel = newQualityLevel;
645 encodingChange = true;
646 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000647 }
648
649 if (cp.beforeVersion(3, 8)) {
650 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
651 // cursors "asynchronously". If this happens in the middle of a
652 // pixel format change, the server will encode the cursor with
653 // the old format, but the client will try to decode it
654 // according to the new format. This will lead to a
655 // crash. Therefore, we do not allow automatic format change for
656 // old servers.
657 return;
658 }
659
660 // Select best color level
661 newFullColour = (kbitsPerSecond > 256);
662 if (newFullColour != options.fullColour) {
663 vlog.info("Throughput %d kbit/s - full color is now %s",
Pierre Ossman78b23592009-03-12 12:25:11 +0000664 kbitsPerSecond,
665 newFullColour ? "enabled" : "disabled");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000666 options.fullColour = newFullColour;
667 formatChange = true;
Pierre Ossman78b23592009-03-12 12:25:11 +0000668 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000669}
670
671void
672CConn::requestNewUpdate() {
673 if (!requestUpdate) return;
674
675 if (formatChange) {
Adam Tkacf586b842011-04-27 11:20:18 +0000676
677 /* Catch incorrect requestNewUpdate calls */
678 assert(pendingUpdate == false);
679
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000680 // Select the required pixel format
681 if (options.fullColour) {
682 window->setPF(fullColourPF);
683 } else {
684 switch (options.lowColourLevel) {
685 case 0:
686 window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
687 break;
688 case 1:
689 window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
690 break;
691 case 2:
692 window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
693 break;
694 }
695 }
696
697 // Print the current pixel format
698 char str[256];
699 window->getPF().print(str, 256);
700 vlog.info("Using pixel format %s",str);
701
702 // Save the connection pixel format and tell server to use it
703 cp.setPF(window->getPF());
704 writer()->writeSetPixelFormat(cp.pf());
705
706 // Correct the local window's palette
707 if (!window->getNativePF().trueColour)
708 window->refreshWindowPalette(0, 1 << cp.pf().depth);
709 }
710
711 if (encodingChange) {
712 vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
713 writer()->writeSetEncodings(options.preferredEncoding, true);
714 }
715
716 writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
717 !formatChange);
718
719 encodingChange = formatChange = requestUpdate = false;
720}
721
722
723void
724CConn::calculateFullColourPF() {
725 // If the server is palette based then use palette locally
726 // Also, don't bother doing bgr222
727 if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
728 fullColourPF = serverDefaultPF;
729 options.fullColour = true;
730 } else {
731 // If server is trueColour, use lowest depth PF
732 PixelFormat native = window->getNativePF();
733 if ((serverDefaultPF.bpp < native.bpp) ||
734 ((serverDefaultPF.bpp == native.bpp) &&
735 (serverDefaultPF.depth < native.depth)))
736 fullColourPF = serverDefaultPF;
737 else
738 fullColourPF = window->getNativePF();
739 }
740 formatChange = true;
741}
742
743
744void
745CConn::setName(const char* name) {
746 if (window)
747 window->setName(name);
748 CConnection::setName(name);
749}
750
751
752void CConn::serverInit() {
753 CConnection::serverInit();
754
755 // If using AutoSelect with old servers, start in FullColor
756 // mode. See comment in autoSelectFormatAndEncoding.
757 if (cp.beforeVersion(3, 8) && options.autoSelect) {
758 options.fullColour = true;
759 }
760
761 // Show the window
762 window = new DesktopWindow(this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000763
764 // Update the window menu
765 HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
766 int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
767
768 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
769 AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
770 AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
771 _T("Show tool&bar"));
772 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
773 AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
774 AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
775 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
776 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
777 AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
778 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
779 AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
780 AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
781 AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
782 AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
Pierre Ossman0df7c7a2009-04-09 12:00:08 +0000783
784 // Set window attributes
785 window->setName(cp.name());
786 window->setShowToolbar(options.showToolbar);
787 window->setSize(cp.width, cp.height);
788 applyOptions(options);
789
790 // Save the server's current format
791 serverDefaultPF = cp.pf();
792
793 // Calculate the full-colour format to use
794 calculateFullColourPF();
795
796 // Request the initial update
797 vlog.info("requesting initial update");
798 formatChange = encodingChange = requestUpdate = true;
799 requestNewUpdate();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000800}
801
802void
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000803CConn::serverCutText(const char* str, rdr::U32 len) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000804 if (!options.serverCutText) return;
805 window->serverCutText(str, len);
806}
807
808
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000809void CConn::beginRect(const Rect& r, int encoding) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000810 sock->inStream().startTiming();
811}
812
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000813void CConn::endRect(const Rect& r, int encoding) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000814 sock->inStream().stopTiming();
815 lastUsedEncoding_ = encoding;
816 if (debugDelay != 0) {
817 window->invertRect(r);
818 debugRects.push_back(r);
819 }
820}
821
822void CConn::fillRect(const Rect& r, Pixel pix) {
823 window->fillRect(r, pix);
824}
825void CConn::imageRect(const Rect& r, void* pixels) {
826 window->imageRect(r, pixels);
827}
828void CConn::copyRect(const Rect& r, int srcX, int srcY) {
829 window->copyRect(r, srcX, srcY);
830}
831
832void CConn::getUserPasswd(char** user, char** password) {
833 if (!user && options.passwordFile.buf[0]) {
834 FILE* fp = fopen(options.passwordFile.buf, "rb");
835 if (fp) {
836 char data[256];
837 int datalen = fread(data, 1, 256, fp);
838 fclose(fp);
839 if (datalen == 8) {
840 ObfuscatedPasswd obfPwd;
841 obfPwd.buf = data;
842 obfPwd.length = datalen;
843 PlainPasswd passwd(obfPwd);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000844 obfPwd.takeBuf();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000845 *password = strDup(passwd.buf);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000846 memset(data, 0, sizeof(data));
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000847 }
848 }
849 }
850 if (user && options.userName.buf)
851 *user = strDup(options.userName.buf);
852 if (password && options.password.buf)
853 *password = strDup(options.password.buf);
854 if ((user && !*user) || (password && !*password)) {
855 // Missing username or password - prompt the user
856 UserPasswdDialog userPasswdDialog;
Adam Tkacf324dc42010-04-23 14:10:17 +0000857 userPasswdDialog.setCSecurity(csecurity);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000858 userPasswdDialog.getUserPasswd(user, password);
859 }
860 if (user) options.setUserName(*user);
861 if (password) options.setPassword(*password);
862}
863