blob: 41b3aa3d6b1a3646ea9d4875a3da67941fcf0578 [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)),
78 reverseConnection(false), requestUpdate(false), isClosed_(false) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000079}
80
81CConn::~CConn() {
82 delete window;
83}
84
85bool CConn::initialise(network::Socket* s, bool reverse) {
86 // Set the server's name for MRU purposes
87 CharArray endpoint(s->getPeerEndpoint());
88 setServerName(endpoint.buf);
89 if (!options.host.buf)
90 options.setHost(endpoint.buf);
91
92 // Initialise the underlying CConnection
93 setStreams(&s->inStream(), &s->outStream());
94
95 // Enable processing of window messages while blocked on I/O
96 s->inStream().setBlockCallback(this);
97
98 // Initialise the viewer options
99 applyOptions(options);
100
101 // - Set which auth schemes we support, in order of preference
102 addSecType(secTypeVncAuth);
103 addSecType(secTypeNone);
104
105 // Start the RFB protocol
106 sock = s;
107 reverseConnection = reverse;
108 initialiseProtocol();
109
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000110 return true;
111}
112
113
114void
115CConn::applyOptions(CConnOptions& opt) {
116 // - If any encoding-related settings have changed then we must
117 // notify the server of the new settings
118 encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
119 (options.useDesktopResize != opt.useDesktopResize) ||
120 (options.customCompressLevel != opt.customCompressLevel) ||
121 (options.compressLevel != opt.compressLevel) ||
122 (options.noJpeg != opt.noJpeg) ||
123 (options.qualityLevel != opt.qualityLevel) ||
124 (options.preferredEncoding != opt.preferredEncoding));
125
126 // - If the preferred pixel format has changed then notify the server
127 formatChange |= (options.fullColour != opt.fullColour);
128 if (!opt.fullColour)
129 formatChange |= (options.lowColourLevel != opt.lowColourLevel);
130
131 // - Save the new set of options
132 options = opt;
133
134 // - Set optional features in ConnParams
135 cp.supportsLocalCursor = options.useLocalCursor;
136 cp.supportsDesktopResize = options.useDesktopResize;
Peter Åstrandc39e0782009-01-15 12:21:42 +0000137 cp.supportsDesktopRename = true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000138 cp.customCompressLevel = options.customCompressLevel;
139 cp.compressLevel = options.compressLevel;
140 cp.noJpeg = options.noJpeg;
141 cp.qualityLevel = options.qualityLevel;
142
143 // - Configure connection sharing on/off
144 setShared(options.shared);
145
146 // - Whether to use protocol 3.3 for legacy compatibility
147 setProtocol3_3(options.protocol3_3);
148
149 // - Apply settings that affect the window, if it is visible
150 if (window) {
151 window->setMonitor(options.monitor.buf);
152 window->setFullscreen(options.fullScreen);
153 window->setEmulate3(options.emulate3);
154 window->setPointerEventInterval(options.pointerEventInterval);
155 window->setMenuKey(options.menuKey);
156 window->setDisableWinKeys(options.disableWinKeys);
157 window->setShowToolbar(options.showToolbar);
george82770bbbc2007-03-12 10:48:09 +0000158 window->printScale();
george82ffc14a62006-09-05 06:51:41 +0000159 if (options.autoScaling) {
160 window->setAutoScaling(true);
161 } else {
162 window->setAutoScaling(false);
163 window->setDesktopScale(options.scale);
164 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000165 if (!options.useLocalCursor)
166 window->setCursor(0, 0, Point(), 0, 0);
167 }
168}
169
170
171void
172CConn::displayChanged() {
173 // Display format has changed - recalculate the full-colour pixel format
174 calculateFullColourPF();
175}
176
177void
178CConn::paintCompleted() {
179 // A repaint message has just completed - request next update if necessary
180 requestNewUpdate();
181}
182
183bool
184CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
185 // - If it's one of our (F8 Menu) messages
186 switch (wParam) {
187 case IDM_FULLSCREEN:
188 options.fullScreen = !window->isFullscreen();
189 window->setFullscreen(options.fullScreen);
190 return true;
george824f72ab32006-09-10 05:13:45 +0000191 case IDM_ZOOM_IN:
george824f72ab32006-09-10 05:13:45 +0000192 case IDM_ZOOM_OUT:
george82b6d87aa2006-09-11 07:00:59 +0000193 {
194 if (options.autoScaling) {
195 options.scale = window->getDesktopScale();
196 options.autoScaling = false;
197 window->setAutoScaling(false);
198 }
199 if (wParam == IDM_ZOOM_IN) {
200 for (int i = 0; i < scaleCount; i++)
201 if (options.scale < scaleValues[i]) {
202 options.scale = scaleValues[i];
203 break;
204 }
205 } else {
206 for (int i = scaleCount-1; i >= 0; i--)
207 if (options.scale > scaleValues[i]) {
208 options.scale = scaleValues[i];
209 break;
210 }
211 }
212 if (options.scale != window->getDesktopScale())
213 window->setDesktopScale(options.scale);
214 }
george824f72ab32006-09-10 05:13:45 +0000215 return true;
216 case IDM_ACTUAL_SIZE:
217 if (options.autoScaling) {
218 options.autoScaling = false;
219 window->setAutoScaling(false);
220 }
george824f72ab32006-09-10 05:13:45 +0000221 options.scale = 100;
222 window->setDesktopScale(100);
223 return true;
224 case IDM_AUTO_SIZE:
george8274ea5f32006-09-11 11:40:12 +0000225 options.autoScaling = !options.autoScaling;
226 window->setAutoScaling(options.autoScaling);
227 if (!options.autoScaling) options.scale = window->getDesktopScale();
george824f72ab32006-09-10 05:13:45 +0000228 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000229 case IDM_SHOW_TOOLBAR:
230 options.showToolbar = !window->isToolbarEnabled();
231 window->setShowToolbar(options.showToolbar);
232 return true;
233 case IDM_CTRL_KEY:
234 window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
235 return true;
236 case IDM_ALT_KEY:
237 window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
238 return true;
239 case IDM_SEND_MENU_KEY:
240 window->kbd.keyEvent(this, options.menuKey, 0, true);
241 window->kbd.keyEvent(this, options.menuKey, 0, false);
242 return true;
243 case IDM_SEND_CAD:
244 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
245 window->kbd.keyEvent(this, VK_MENU, 0, true);
246 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
247 window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
248 window->kbd.keyEvent(this, VK_MENU, 0, false);
249 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
250 return true;
251 case IDM_SEND_CTLESC:
252 window->kbd.keyEvent(this, VK_CONTROL, 0, true);
253 window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
254 window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
255 window->kbd.keyEvent(this, VK_CONTROL, 0, false);
256 return true;
257 case IDM_REQUEST_REFRESH:
258 try {
259 writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
260 requestUpdate = false;
261 } catch (rdr::Exception& e) {
262 close(e.str());
263 }
264 return true;
265 case IDM_NEWCONN:
266 {
267 Thread* newThread = new CConnThread;
268 }
269 return true;
270 case IDM_OPTIONS:
271 // Update the monitor device name in the CConnOptions instance
272 options.monitor.replaceBuf(window->getMonitor());
273 showOptionsDialog();
274 return true;
275 case IDM_INFO:
276 infoDialog.showDialog(this);
277 return true;
278 case IDM_ABOUT:
279 AboutDialog::instance.showDialog();
280 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000281 case IDM_CONN_SAVE_AS:
282 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000283 };
284 return false;
285}
286
287
288void
289CConn::closeWindow() {
290 vlog.info("window closed");
291 close();
292}
293
294
295void
296CConn::refreshMenu(bool enableSysItems) {
297 HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
298
299 if (!enableSysItems) {
300 // Gray out menu items that might cause a World Of Pain
301 EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
302 EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
303 EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
304 EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
305 EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
306 }
307
308 // Update the modifier key menu items
309 UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
310 UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
311 CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
312 CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
313
314 // Ensure that the Send <MenuKey> menu item has the correct text
315 if (options.menuKey) {
316 TCharArray menuKeyStr(options.menuKeyName());
317 TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
318 _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
319 if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
320 InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
321 } else {
322 RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
323 }
324
325 // Set the menu fullscreen option tick
326 CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
327
328 // Set the menu toolbar option tick
329 int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
330 CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
331
332 // In the full-screen mode, "Show toolbar" should be grayed.
333 toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
334 EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
335}
336
337
338void
339CConn::blockCallback() {
340 // - An InStream has blocked on I/O while processing an RFB message
341 // We re-enable socket event notifications, so we'll know when more
342 // data is available, then we sit and dispatch window events until
343 // the notification arrives.
344 if (!isClosed()) {
345 if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
346 throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
347 }
348 while (true) {
349 // If we have closed then we can't block waiting for data
350 if (isClosed())
351 throw rdr::EndOfStream();
352
353 // Wait for socket data, or a message to process
354 DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
355 if (result == WAIT_OBJECT_0) {
356 // - Network event notification. Return control to I/O routine.
357 break;
358 } else if (result == WAIT_FAILED) {
359 // - The wait operation failed - raise an exception
360 throw rdr::SystemException("blockCallback wait error", GetLastError());
361 }
362
363 // - There should be a message in the message queue
364 MSG msg;
365 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
366 // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
367 // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
368 // state from one call to the next, which would be messed up by calls to
369 // TranslateMessage() (actually it looks like TranslateMessage() calls
370 // ToAscii() internally).
371 DispatchMessage(&msg);
372 }
373 }
374
375 // Before we return control to the InStream, reset the network event
376 WSAEventSelect(sock->getFd(), sockEvent, 0);
377 ResetEvent(sockEvent);
378}
379
380
381void CConn::keyEvent(rdr::U32 key, bool down) {
382 if (!options.sendKeyEvents) return;
383 try {
384 writer()->keyEvent(key, down);
385 } catch (rdr::Exception& e) {
386 close(e.str());
387 }
388}
389void CConn::pointerEvent(const Point& pos, int buttonMask) {
390 if (!options.sendPtrEvents) return;
391 try {
392 writer()->pointerEvent(pos, buttonMask);
393 } catch (rdr::Exception& e) {
394 close(e.str());
395 }
396}
397void CConn::clientCutText(const char* str, int len) {
398 if (!options.clientCutText) return;
399 if (state() != RFBSTATE_NORMAL) return;
400 try {
401 writer()->clientCutText(str, len);
402 } catch (rdr::Exception& e) {
403 close(e.str());
404 }
405}
406
407
408CSecurity* CConn::getCSecurity(int secType)
409{
410 switch (secType) {
411 case secTypeNone:
412 return new CSecurityNone();
413 case secTypeVncAuth:
414 return new CSecurityVncAuth(this);
415 default:
416 throw Exception("Unsupported secType?");
417 }
418}
419
420
421void
422CConn::setColourMapEntries(int first, int count, U16* rgbs) {
423 vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
424 int i;
425 for (i=0;i<count;i++)
426 window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
427 // *** change to 0, 256?
428 window->refreshWindowPalette(first, count);
429}
430
431void
432CConn::bell() {
433 if (options.acceptBell)
Peter Åstrand7eb594a2008-12-11 08:27:52 +0000434 MessageBeep((UINT)-1);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000435}
436
437
438void
439CConn::setDesktopSize(int w, int h) {
440 vlog.debug("setDesktopSize %dx%d", w, h);
441
442 // Resize the window's buffer
443 if (window)
444 window->setSize(w, h);
445
446 // Tell the underlying CConnection
447 CConnection::setDesktopSize(w, h);
448}
449
450void
451CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
452 if (!options.useLocalCursor) return;
453
454 // Set the window to use the new cursor
455 window->setCursor(w, h, hotspot, data, mask);
456}
457
458
459void
460CConn::close(const char* reason) {
461 // If already closed then ignore this
462 if (isClosed())
463 return;
464
465 // Hide the window, if it exists
466 if (window)
467 ShowWindow(window->getHandle(), SW_HIDE);
468
469 // Save the reason & flag that we're closed & shutdown the socket
470 isClosed_ = true;
471 closeReason_.replaceBuf(strDup(reason));
472 sock->shutdown();
473}
474
475
476void
477CConn::showOptionsDialog() {
478 optionsDialog.showDialog(this);
479}
480
481
482void
483CConn::framebufferUpdateEnd() {
484 if (debugDelay != 0) {
485 vlog.debug("debug delay %d",(int)debugDelay);
486 UpdateWindow(window->getHandle());
487 Sleep(debugDelay);
488 std::list<rfb::Rect>::iterator i;
489 for (i = debugRects.begin(); i != debugRects.end(); i++) {
490 window->invertRect(*i);
491 }
492 debugRects.clear();
493 }
494 if (options.autoSelect)
495 autoSelectFormatAndEncoding();
496
497 // Always request the next update
498 requestUpdate = true;
499
500 // Check that at least part of the window has changed
501 if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
502 if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
503 requestNewUpdate();
504 }
505
506 // Make sure the local cursor is shown
507 window->showCursor();
508}
509
510
Pierre Ossman78b23592009-03-12 12:25:11 +0000511// Note: The method below is duplicated in win/vncviewer/CConn.cxx!
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000512
513// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
514// to the connection speed:
515//
Pierre Ossman78b23592009-03-12 12:25:11 +0000516// First we wait for at least one second of bandwidth measurement.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000517//
Pierre Ossman78b23592009-03-12 12:25:11 +0000518// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
519// which should be perceptually lossless.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000520//
Pierre Ossman78b23592009-03-12 12:25:11 +0000521// If the bandwidth is below that, we choose a more lossy JPEG quality.
522//
523// If the bandwidth drops below 256 Kbps, we switch to palette mode.
524//
525// Note: The system here is fairly arbitrary and should be replaced
526// with something more intelligent at the server end.
527//
528void CConn::autoSelectFormatAndEncoding()
529{
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000530 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000531 unsigned int timeWaited = sock->inStream().timeWaited();
Pierre Ossman78b23592009-03-12 12:25:11 +0000532 bool newFullColour = options.fullColour;
533 int newQualityLevel = options.qualityLevel;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000534
Pierre Ossman78b23592009-03-12 12:25:11 +0000535 // Always use Tight
536 options.preferredEncoding = encodingTight;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000537
Pierre Ossman78b23592009-03-12 12:25:11 +0000538 // Check that we have a decent bandwidth measurement
539 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000540 return;
Pierre Ossman78b23592009-03-12 12:25:11 +0000541
542 // Select appropriate quality level
543 if (!options.noJpeg) {
544 if (kbitsPerSecond > 16000)
545 newQualityLevel = 8;
546 else
547 newQualityLevel = 6;
548
549 if (newQualityLevel != options.qualityLevel) {
550 vlog.info("Throughput %d kbit/s - changing to quality %d ",
551 kbitsPerSecond, newQualityLevel);
552 cp.qualityLevel = newQualityLevel;
553 options.qualityLevel = newQualityLevel;
554 encodingChange = true;
555 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000556 }
557
558 if (cp.beforeVersion(3, 8)) {
559 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
560 // cursors "asynchronously". If this happens in the middle of a
561 // pixel format change, the server will encode the cursor with
562 // the old format, but the client will try to decode it
563 // according to the new format. This will lead to a
564 // crash. Therefore, we do not allow automatic format change for
565 // old servers.
566 return;
567 }
568
569 // Select best color level
570 newFullColour = (kbitsPerSecond > 256);
571 if (newFullColour != options.fullColour) {
572 vlog.info("Throughput %d kbit/s - full color is now %s",
Pierre Ossman78b23592009-03-12 12:25:11 +0000573 kbitsPerSecond,
574 newFullColour ? "enabled" : "disabled");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000575 options.fullColour = newFullColour;
576 formatChange = true;
Pierre Ossman78b23592009-03-12 12:25:11 +0000577 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000578}
579
580void
581CConn::requestNewUpdate() {
582 if (!requestUpdate) return;
583
584 if (formatChange) {
585 // Select the required pixel format
586 if (options.fullColour) {
587 window->setPF(fullColourPF);
588 } else {
589 switch (options.lowColourLevel) {
590 case 0:
591 window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
592 break;
593 case 1:
594 window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
595 break;
596 case 2:
597 window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
598 break;
599 }
600 }
601
602 // Print the current pixel format
603 char str[256];
604 window->getPF().print(str, 256);
605 vlog.info("Using pixel format %s",str);
606
607 // Save the connection pixel format and tell server to use it
608 cp.setPF(window->getPF());
609 writer()->writeSetPixelFormat(cp.pf());
610
611 // Correct the local window's palette
612 if (!window->getNativePF().trueColour)
613 window->refreshWindowPalette(0, 1 << cp.pf().depth);
614 }
615
616 if (encodingChange) {
617 vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
618 writer()->writeSetEncodings(options.preferredEncoding, true);
619 }
620
621 writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
622 !formatChange);
623
624 encodingChange = formatChange = requestUpdate = false;
625}
626
627
628void
629CConn::calculateFullColourPF() {
630 // If the server is palette based then use palette locally
631 // Also, don't bother doing bgr222
632 if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
633 fullColourPF = serverDefaultPF;
634 options.fullColour = true;
635 } else {
636 // If server is trueColour, use lowest depth PF
637 PixelFormat native = window->getNativePF();
638 if ((serverDefaultPF.bpp < native.bpp) ||
639 ((serverDefaultPF.bpp == native.bpp) &&
640 (serverDefaultPF.depth < native.depth)))
641 fullColourPF = serverDefaultPF;
642 else
643 fullColourPF = window->getNativePF();
644 }
645 formatChange = true;
646}
647
648
649void
650CConn::setName(const char* name) {
651 if (window)
652 window->setName(name);
653 CConnection::setName(name);
654}
655
656
657void CConn::serverInit() {
658 CConnection::serverInit();
659
660 // If using AutoSelect with old servers, start in FullColor
661 // mode. See comment in autoSelectFormatAndEncoding.
662 if (cp.beforeVersion(3, 8) && options.autoSelect) {
663 options.fullColour = true;
664 }
665
666 // Show the window
667 window = new DesktopWindow(this);
668 window->setName(cp.name());
george82486740f2007-03-20 05:59:44 +0000669 window->setShowToolbar(options.showToolbar);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000670 window->setSize(cp.width, cp.height);
george82d2c22522006-09-10 11:45:19 +0000671 applyOptions(options);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000672
673 // Save the server's current format
674 serverDefaultPF = cp.pf();
675
676 // Calculate the full-colour format to use
677 calculateFullColourPF();
678
679 // Request the initial update
680 vlog.info("requesting initial update");
681 formatChange = encodingChange = requestUpdate = true;
682 requestNewUpdate();
683
684 // Update the window menu
685 HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
686 int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
687
688 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
689 AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
690 AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
691 _T("Show tool&bar"));
692 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
693 AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
694 AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
695 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
696 AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
697 AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
698 AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
699 AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
700 AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
701 AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
702 AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
703}
704
705void
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000706CConn::serverCutText(const char* str, rdr::U32 len) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000707 if (!options.serverCutText) return;
708 window->serverCutText(str, len);
709}
710
711
712void CConn::beginRect(const Rect& r, unsigned int encoding) {
713 sock->inStream().startTiming();
714}
715
716void CConn::endRect(const Rect& r, unsigned int encoding) {
717 sock->inStream().stopTiming();
718 lastUsedEncoding_ = encoding;
719 if (debugDelay != 0) {
720 window->invertRect(r);
721 debugRects.push_back(r);
722 }
723}
724
725void CConn::fillRect(const Rect& r, Pixel pix) {
726 window->fillRect(r, pix);
727}
728void CConn::imageRect(const Rect& r, void* pixels) {
729 window->imageRect(r, pixels);
730}
731void CConn::copyRect(const Rect& r, int srcX, int srcY) {
732 window->copyRect(r, srcX, srcY);
733}
734
735void CConn::getUserPasswd(char** user, char** password) {
736 if (!user && options.passwordFile.buf[0]) {
737 FILE* fp = fopen(options.passwordFile.buf, "rb");
738 if (fp) {
739 char data[256];
740 int datalen = fread(data, 1, 256, fp);
741 fclose(fp);
742 if (datalen == 8) {
743 ObfuscatedPasswd obfPwd;
744 obfPwd.buf = data;
745 obfPwd.length = datalen;
746 PlainPasswd passwd(obfPwd);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000747 obfPwd.takeBuf();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000748 *password = strDup(passwd.buf);
Peter Åstrand8ace9152008-12-03 11:57:18 +0000749 memset(data, 0, sizeof(data));
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000750 }
751 }
752 }
753 if (user && options.userName.buf)
754 *user = strDup(options.userName.buf);
755 if (password && options.password.buf)
756 *password = strDup(options.password.buf);
757 if ((user && !*user) || (password && !*password)) {
758 // Missing username or password - prompt the user
759 UserPasswdDialog userPasswdDialog;
760 userPasswdDialog.setCSecurity(getCurrentCSecurity());
761 userPasswdDialog.getUserPasswd(user, password);
762 }
763 if (user) options.setUserName(*user);
764 if (password) options.setPassword(*password);
765}
766