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