blob: 874db094daf2458603d3ef75210f7b5979bae1ca [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;
54const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER;
55const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
george824f72ab32006-09-10 05:13:45 +000056const int IDM_ZOOM_IN = ID_ZOOM_IN;
57const int IDM_ZOOM_OUT = ID_ZOOM_OUT;
58const int IDM_ACTUAL_SIZE = ID_ACTUAL_SIZE;
59const int IDM_AUTO_SIZE = ID_AUTO_SIZE;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000060
61
62static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
63 "pixel data - a debugging feature", 0);
64
george82b6d87aa2006-09-11 07:00:59 +000065const int scaleValues[9] = {10, 25, 50, 75, 90, 100, 125, 150, 200};
66const int scaleCount = 9;
67
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000068
69//
70// -=- CConn implementation
71//
72
73RegKey CConn::userConfigKey;
74
75
76CConn::CConn()
Peter Åstrand2dd9eea2008-12-11 08:23:55 +000077 : window(0), sameMachine(false), encodingChange(false), formatChange(false),
78 lastUsedEncoding_(encodingRaw), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)),
79 reverseConnection(false), requestUpdate(false), 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
111 m_fileTransfer.initialize(&s->inStream(), &s->outStream());
112
113 return true;
114}
115
116
117void
118CConn::applyOptions(CConnOptions& opt) {
119 // - If any encoding-related settings have changed then we must
120 // notify the server of the new settings
121 encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
122 (options.useDesktopResize != opt.useDesktopResize) ||
123 (options.customCompressLevel != opt.customCompressLevel) ||
124 (options.compressLevel != opt.compressLevel) ||
125 (options.noJpeg != opt.noJpeg) ||
126 (options.qualityLevel != opt.qualityLevel) ||
127 (options.preferredEncoding != opt.preferredEncoding));
128
129 // - If the preferred pixel format has changed then notify the server
130 formatChange |= (options.fullColour != opt.fullColour);
131 if (!opt.fullColour)
132 formatChange |= (options.lowColourLevel != opt.lowColourLevel);
133
134 // - Save the new set of options
135 options = opt;
136
137 // - Set optional features in ConnParams
138 cp.supportsLocalCursor = options.useLocalCursor;
139 cp.supportsDesktopResize = options.useDesktopResize;
Peter Åstrandc39e0782009-01-15 12:21:42 +0000140 cp.supportsDesktopRename = true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000141 cp.customCompressLevel = options.customCompressLevel;
142 cp.compressLevel = options.compressLevel;
143 cp.noJpeg = options.noJpeg;
144 cp.qualityLevel = options.qualityLevel;
145
146 // - Configure connection sharing on/off
147 setShared(options.shared);
148
149 // - Whether to use protocol 3.3 for legacy compatibility
150 setProtocol3_3(options.protocol3_3);
151
152 // - Apply settings that affect the window, if it is visible
153 if (window) {
154 window->setMonitor(options.monitor.buf);
155 window->setFullscreen(options.fullScreen);
156 window->setEmulate3(options.emulate3);
157 window->setPointerEventInterval(options.pointerEventInterval);
158 window->setMenuKey(options.menuKey);
159 window->setDisableWinKeys(options.disableWinKeys);
160 window->setShowToolbar(options.showToolbar);
george82770bbbc2007-03-12 10:48:09 +0000161 window->printScale();
george82ffc14a62006-09-05 06:51:41 +0000162 if (options.autoScaling) {
163 window->setAutoScaling(true);
164 } else {
165 window->setAutoScaling(false);
166 window->setDesktopScale(options.scale);
167 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000168 if (!options.useLocalCursor)
169 window->setCursor(0, 0, Point(), 0, 0);
170 }
171}
172
173
174void
175CConn::displayChanged() {
176 // Display format has changed - recalculate the full-colour pixel format
177 calculateFullColourPF();
178}
179
180void
181CConn::paintCompleted() {
182 // A repaint message has just completed - request next update if necessary
183 requestNewUpdate();
184}
185
186bool
187CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
188 // - If it's one of our (F8 Menu) messages
189 switch (wParam) {
190 case IDM_FULLSCREEN:
191 options.fullScreen = !window->isFullscreen();
192 window->setFullscreen(options.fullScreen);
193 return true;
george824f72ab32006-09-10 05:13:45 +0000194 case IDM_ZOOM_IN:
george824f72ab32006-09-10 05:13:45 +0000195 case IDM_ZOOM_OUT:
george82b6d87aa2006-09-11 07:00:59 +0000196 {
197 if (options.autoScaling) {
198 options.scale = window->getDesktopScale();
199 options.autoScaling = false;
200 window->setAutoScaling(false);
201 }
202 if (wParam == IDM_ZOOM_IN) {
203 for (int i = 0; i < scaleCount; i++)
204 if (options.scale < scaleValues[i]) {
205 options.scale = scaleValues[i];
206 break;
207 }
208 } else {
209 for (int i = scaleCount-1; i >= 0; i--)
210 if (options.scale > scaleValues[i]) {
211 options.scale = scaleValues[i];
212 break;
213 }
214 }
215 if (options.scale != window->getDesktopScale())
216 window->setDesktopScale(options.scale);
217 }
george824f72ab32006-09-10 05:13:45 +0000218 return true;
219 case IDM_ACTUAL_SIZE:
220 if (options.autoScaling) {
221 options.autoScaling = false;
222 window->setAutoScaling(false);
223 }
george824f72ab32006-09-10 05:13:45 +0000224 options.scale = 100;
225 window->setDesktopScale(100);
226 return true;
227 case IDM_AUTO_SIZE:
george8274ea5f32006-09-11 11:40:12 +0000228 options.autoScaling = !options.autoScaling;
229 window->setAutoScaling(options.autoScaling);
230 if (!options.autoScaling) options.scale = window->getDesktopScale();
george824f72ab32006-09-10 05:13:45 +0000231 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000232 case IDM_SHOW_TOOLBAR:
233 options.showToolbar = !window->isToolbarEnabled();
234 window->setShowToolbar(options.showToolbar);
235 return true;
236 case IDM_CTRL_KEY:
237 window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
238 return true;
239 case IDM_ALT_KEY:
240 window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
241 return true;
242 case IDM_SEND_MENU_KEY:
243 window->kbd.keyEvent(this, options.menuKey, 0, true);
244 window->kbd.keyEvent(this, options.menuKey, 0, false);
245 return true;
246 case IDM_SEND_CAD:
247 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
248 window->kbd.keyEvent(this, VK_MENU, 0, true);
249 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
250 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
251 window->kbd.keyEvent(this, VK_MENU, 0, false);
252 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
253 return true;
254 case IDM_SEND_CTLESC:
255 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
256 window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
257 window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
258 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
259 return true;
260 case IDM_REQUEST_REFRESH:
261 try {
262 writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
263 requestUpdate = false;
264 } catch (rdr::Exception& e) {
265 close(e.str());
266 }
267 return true;
268 case IDM_NEWCONN:
269 {
270 Thread* newThread = new CConnThread;
271 }
272 return true;
273 case IDM_OPTIONS:
274 // Update the monitor device name in the CConnOptions instance
275 options.monitor.replaceBuf(window->getMonitor());
276 showOptionsDialog();
277 return true;
278 case IDM_INFO:
279 infoDialog.showDialog(this);
280 return true;
281 case IDM_ABOUT:
282 AboutDialog::instance.showDialog();
283 return true;
284 case IDM_FILE_TRANSFER:
285 m_fileTransfer.show(window->getHandle());
286 return true;
287 case IDM_CONN_SAVE_AS:
288 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000289 };
290 return false;
291}
292
293
294void
295CConn::closeWindow() {
296 vlog.info("window closed");
297 close();
298}
299
300
301void
302CConn::refreshMenu(bool enableSysItems) {
303 HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
304
305 if (!enableSysItems) {
306 // Gray out menu items that might cause a World Of Pain
307 EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
308 EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
309 EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
310 EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
311 EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
312 }
313
314 // Update the modifier key menu items
315 UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
316 UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
317 CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
318 CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
319
320 // Ensure that the Send <MenuKey> menu item has the correct text
321 if (options.menuKey) {
322 TCharArray menuKeyStr(options.menuKeyName());
323 TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
324 _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
325 if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
326 InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
327 } else {
328 RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
329 }
330
331 // Set the menu fullscreen option tick
332 CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
333
334 // Set the menu toolbar option tick
335 int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
336 CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
337
338 // In the full-screen mode, "Show toolbar" should be grayed.
339 toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
340 EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
341}
342
343
344void
345CConn::blockCallback() {
346 // - An InStream has blocked on I/O while processing an RFB message
347 // We re-enable socket event notifications, so we'll know when more
348 // data is available, then we sit and dispatch window events until
349 // the notification arrives.
350 if (!isClosed()) {
351 if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
352 throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
353 }
354 while (true) {
355 // If we have closed then we can't block waiting for data
356 if (isClosed())
357 throw rdr::EndOfStream();
358
359 // Wait for socket data, or a message to process
360 DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
361 if (result == WAIT_OBJECT_0) {
362 // - Network event notification. Return control to I/O routine.
363 break;
364 } else if (result == WAIT_FAILED) {
365 // - The wait operation failed - raise an exception
366 throw rdr::SystemException("blockCallback wait error", GetLastError());
367 }
368
369 // - There should be a message in the message queue
370 MSG msg;
371 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
372 // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
373 // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
374 // state from one call to the next, which would be messed up by calls to
375 // TranslateMessage() (actually it looks like TranslateMessage() calls
376 // ToAscii() internally).
377 DispatchMessage(&msg);
378 }
379 }
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
413
414CSecurity* CConn::getCSecurity(int secType)
415{
416 switch (secType) {
417 case secTypeNone:
418 return new CSecurityNone();
419 case secTypeVncAuth:
420 return new CSecurityVncAuth(this);
421 default:
422 throw Exception("Unsupported secType?");
423 }
424}
425
426
427void
428CConn::setColourMapEntries(int first, int count, U16* rgbs) {
429 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
430 int i;
431 for (i=0;i<count;i++)
432 window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
433 // *** change to 0, 256?
434 window->refreshWindowPalette(first, count);
435}
436
437void
438CConn::bell() {
439 if (options.acceptBell)
Peter Åstrand7eb594a2008-12-11 08:27:52 +0000440 MessageBeep((UINT)-1);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000441}
442
443
444void
445CConn::setDesktopSize(int w, int h) {
446 vlog.debug("setDesktopSize %dx%d", w, h);
447
448 // Resize the window's buffer
449 if (window)
450 window->setSize(w, h);
451
452 // Tell the underlying CConnection
453 CConnection::setDesktopSize(w, h);
454}
455
456void
457CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
458 if (!options.useLocalCursor) return;
459
460 // Set the window to use the new cursor
461 window->setCursor(w, h, hotspot, data, mask);
462}
463
464
465void
466CConn::close(const char* reason) {
467 // If already closed then ignore this
468 if (isClosed())
469 return;
470
471 // Hide the window, if it exists
472 if (window)
473 ShowWindow(window->getHandle(), SW_HIDE);
474
475 // Save the reason & flag that we're closed & shutdown the socket
476 isClosed_ = true;
477 closeReason_.replaceBuf(strDup(reason));
478 sock->shutdown();
479}
480
481
482void
483CConn::showOptionsDialog() {
484 optionsDialog.showDialog(this);
485}
486
487
488void
489CConn::framebufferUpdateEnd() {
490 if (debugDelay != 0) {
491 vlog.debug("debug delay %d",(int)debugDelay);
492 UpdateWindow(window->getHandle());
493 Sleep(debugDelay);
494 std::list<rfb::Rect>::iterator i;
495 for (i = debugRects.begin(); i != debugRects.end(); i++) {
496 window->invertRect(*i);
497 }
498 debugRects.clear();
499 }
500 if (options.autoSelect)
501 autoSelectFormatAndEncoding();
502
503 // Always request the next update
504 requestUpdate = true;
505
506 // Check that at least part of the window has changed
507 if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
508 if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
509 requestNewUpdate();
510 }
511
512 // Make sure the local cursor is shown
513 window->showCursor();
514}
515
516
Pierre Ossman78b23592009-03-12 12:25:11 +0000517// Note: The method below is duplicated in win/vncviewer/CConn.cxx!
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000518
519// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
520// to the connection speed:
521//
Pierre Ossman78b23592009-03-12 12:25:11 +0000522// First we wait for at least one second of bandwidth measurement.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000523//
Pierre Ossman78b23592009-03-12 12:25:11 +0000524// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
525// which should be perceptually lossless.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000526//
Pierre Ossman78b23592009-03-12 12:25:11 +0000527// If the bandwidth is below that, we choose a more lossy JPEG quality.
528//
529// If the bandwidth drops below 256 Kbps, we switch to palette mode.
530//
531// Note: The system here is fairly arbitrary and should be replaced
532// with something more intelligent at the server end.
533//
534void CConn::autoSelectFormatAndEncoding()
535{
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000536 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000537 unsigned int timeWaited = sock->inStream().timeWaited();
Pierre Ossman78b23592009-03-12 12:25:11 +0000538 bool newFullColour = options.fullColour;
539 int newQualityLevel = options.qualityLevel;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000540
Pierre Ossman78b23592009-03-12 12:25:11 +0000541 // Always use Tight
542 options.preferredEncoding = encodingTight;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000543
Pierre Ossman78b23592009-03-12 12:25:11 +0000544 // Check that we have a decent bandwidth measurement
545 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000546 return;
Pierre Ossman78b23592009-03-12 12:25:11 +0000547
548 // Select appropriate quality level
549 if (!options.noJpeg) {
550 if (kbitsPerSecond > 16000)
551 newQualityLevel = 8;
552 else
553 newQualityLevel = 6;
554
555 if (newQualityLevel != options.qualityLevel) {
556 vlog.info("Throughput %d kbit/s - changing to quality %d ",
557 kbitsPerSecond, newQualityLevel);
558 cp.qualityLevel = newQualityLevel;
559 options.qualityLevel = newQualityLevel;
560 encodingChange = true;
561 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000562 }
563
564 if (cp.beforeVersion(3, 8)) {
565 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
566 // cursors "asynchronously". If this happens in the middle of a
567 // pixel format change, the server will encode the cursor with
568 // the old format, but the client will try to decode it
569 // according to the new format. This will lead to a
570 // crash. Therefore, we do not allow automatic format change for
571 // old servers.
572 return;
573 }
574
575 // Select best color level
576 newFullColour = (kbitsPerSecond > 256);
577 if (newFullColour != options.fullColour) {
578 vlog.info("Throughput %d kbit/s - full color is now %s",
Pierre Ossman78b23592009-03-12 12:25:11 +0000579 kbitsPerSecond,
580 newFullColour ? "enabled" : "disabled");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000581 options.fullColour = newFullColour;
582 formatChange = true;
Pierre Ossman78b23592009-03-12 12:25:11 +0000583 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000584}
585
586void
587CConn::requestNewUpdate() {
588 if (!requestUpdate) return;
589
590 if (formatChange) {
591 // Select the required pixel format
592 if (options.fullColour) {
593 window->setPF(fullColourPF);
594 } else {
595 switch (options.lowColourLevel) {
596 case 0:
597 window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
598 break;
599 case 1:
600 window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
601 break;
602 case 2:
603 window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
604 break;
605 }
606 }
607
608 // Print the current pixel format
609 char str[256];
610 window->getPF().print(str, 256);
611 vlog.info("Using pixel format %s",str);
612
613 // Save the connection pixel format and tell server to use it
614 cp.setPF(window->getPF());
615 writer()->writeSetPixelFormat(cp.pf());
616
617 // Correct the local window's palette
618 if (!window->getNativePF().trueColour)
619 window->refreshWindowPalette(0, 1 << cp.pf().depth);
620 }
621
622 if (encodingChange) {
623 vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
624 writer()->writeSetEncodings(options.preferredEncoding, true);
625 }
626
627 writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
628 !formatChange);
629
630 encodingChange = formatChange = requestUpdate = false;
631}
632
633
634void
635CConn::calculateFullColourPF() {
636 // If the server is palette based then use palette locally
637 // Also, don't bother doing bgr222
638 if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
639 fullColourPF = serverDefaultPF;
640 options.fullColour = true;
641 } else {
642 // If server is trueColour, use lowest depth PF
643 PixelFormat native = window->getNativePF();
644 if ((serverDefaultPF.bpp < native.bpp) ||
645 ((serverDefaultPF.bpp == native.bpp) &&
646 (serverDefaultPF.depth < native.depth)))
647 fullColourPF = serverDefaultPF;
648 else
649 fullColourPF = window->getNativePF();
650 }
651 formatChange = true;
652}
653
654
655void
656CConn::setName(const char* name) {
657 if (window)
658 window->setName(name);
659 CConnection::setName(name);
660}
661
662
663void CConn::serverInit() {
664 CConnection::serverInit();
665
666 // If using AutoSelect with old servers, start in FullColor
667 // mode. See comment in autoSelectFormatAndEncoding.
668 if (cp.beforeVersion(3, 8) && options.autoSelect) {
669 options.fullColour = true;
670 }
671
672 // Show the window
673 window = new DesktopWindow(this);
674 window->setName(cp.name());
george82486740f2007-03-20 05:59:44 +0000675 window->setShowToolbar(options.showToolbar);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000676 window->setSize(cp.width, cp.height);
george82d2c22522006-09-10 11:45:19 +0000677 applyOptions(options);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000678
679 // Save the server's current format
680 serverDefaultPF = cp.pf();
681
682 // Calculate the full-colour format to use
683 calculateFullColourPF();
684
685 // Request the initial update
686 vlog.info("requesting initial update");
687 formatChange = encodingChange = requestUpdate = true;
688 requestNewUpdate();
689
690 // Update the window menu
691 HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
692 int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
693
694 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
695 AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
696 AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
697 _T("Show tool&bar"));
698 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
699 AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
700 AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
701 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
702 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
703 AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
704 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
705 AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
706 AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
707 AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
708 AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
709}
710
711void
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000712CConn::serverCutText(const char* str, rdr::U32 len) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000713 if (!options.serverCutText) return;
714 window->serverCutText(str, len);
715}
716
717
718void CConn::beginRect(const Rect& r, unsigned int encoding) {
719 sock->inStream().startTiming();
720}
721
722void CConn::endRect(const Rect& r, unsigned int encoding) {
723 sock->inStream().stopTiming();
724 lastUsedEncoding_ = encoding;
725 if (debugDelay != 0) {
726 window->invertRect(r);
727 debugRects.push_back(r);
728 }
729}
730
731void CConn::fillRect(const Rect& r, Pixel pix) {
732 window->fillRect(r, pix);
733}
734void CConn::imageRect(const Rect& r, void* pixels) {
735 window->imageRect(r, pixels);
736}
737void CConn::copyRect(const Rect& r, int srcX, int srcY) {
738 window->copyRect(r, srcX, srcY);
739}
740
741void CConn::getUserPasswd(char** user, char** password) {
742 if (!user && options.passwordFile.buf[0]) {
743 FILE* fp = fopen(options.passwordFile.buf, "rb");
744 if (fp) {
745 char data[256];
746 int datalen = fread(data, 1, 256, fp);
747 fclose(fp);
748 if (datalen == 8) {
749 ObfuscatedPasswd obfPwd;
750 obfPwd.buf = data;
751 obfPwd.length = datalen;
752 PlainPasswd passwd(obfPwd);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000753 obfPwd.takeBuf();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000754 *password = strDup(passwd.buf);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000755 memset(data, 0, sizeof(data));
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000756 }
757 }
758 }
759 if (user && options.userName.buf)
760 *user = strDup(options.userName.buf);
761 if (password && options.password.buf)
762 *password = strDup(options.password.buf);
763 if ((user && !*user) || (password && !*password)) {
764 // Missing username or password - prompt the user
765 UserPasswdDialog userPasswdDialog;
766 userPasswdDialog.setCSecurity(getCurrentCSecurity());
767 userPasswdDialog.getUserPasswd(user, password);
768 }
769 if (user) options.setUserName(*user);
770 if (password) options.setPassword(*password);
771}
772
773bool CConn::processFTMsg(int type) {
774 return m_fileTransfer.processFTMsg(type);
775}