You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cview.cxx 43KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502
  1. /* Copyright (C) 2002-2004 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. #define WIN32_LEAN_AND_MEAN
  19. #if (_WIN32_WINNT < 0x0400)
  20. #define _WIN32_WINNT 0x0400
  21. #endif
  22. #include <windows.h>
  23. #include <winsock2.h>
  24. #include <tchar.h>
  25. #include <commctrl.h>
  26. #include <network/TcpSocket.h>
  27. #include <vncviewer/CView.h>
  28. #include <vncviewer/UserPasswdDialog.h>
  29. #include <vncviewer/resource.h>
  30. #include <rfb/encodings.h>
  31. #include <rfb/secTypes.h>
  32. #include <rfb/CSecurityNone.h>
  33. #include <rfb/CSecurityVncAuth.h>
  34. #include <rfb/CMsgWriter.h>
  35. #include <rfb/Configuration.h>
  36. #include <rfb/LogWriter.h>
  37. #include <rfb_win32/WMShatter.h>
  38. using namespace rfb;
  39. using namespace rfb::win32;
  40. using namespace rdr;
  41. // - Statics & consts
  42. static LogWriter vlog("CView");
  43. const int IDM_FULLSCREEN = 1;
  44. const int IDM_SEND_MENU_KEY = 2;
  45. const int IDM_SEND_CAD = 3;
  46. const int IDM_ABOUT = 4;
  47. const int IDM_OPTIONS = 5;
  48. const int IDM_INFO = 6;
  49. const int IDM_NEWCONN = 7;
  50. const int IDM_REQUEST_REFRESH = 9;
  51. const int IDM_CTRL_KEY = 10;
  52. const int IDM_ALT_KEY = 11;
  53. const int TIMER_BUMPSCROLL = 1;
  54. const int TIMER_POINTER_INTERVAL = 2;
  55. const int TIMER_POINTER_3BUTTON = 3;
  56. IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
  57. "pixel data - a debugging feature", 0);
  58. //
  59. // -=- CViewClass
  60. //
  61. // Window class used as the basis for all CView instances
  62. //
  63. class CViewClass {
  64. public:
  65. CViewClass();
  66. ~CViewClass();
  67. ATOM classAtom;
  68. HINSTANCE instance;
  69. };
  70. LRESULT CALLBACK CViewProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  71. LRESULT result;
  72. // *** vlog.debug("CViewMsg %x->(%x, %x, %x)", wnd, msg, wParam, lParam);
  73. if (msg == WM_CREATE)
  74. SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
  75. else if (msg == WM_DESTROY)
  76. SetWindowLong(wnd, GWL_USERDATA, 0);
  77. CView* _this = (CView*) GetWindowLong(wnd, GWL_USERDATA);
  78. if (!_this) {
  79. vlog.info("null _this in %x, message %u", wnd, msg);
  80. return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
  81. }
  82. try {
  83. result = _this->processMessage(msg, wParam, lParam);
  84. } catch (rdr::Exception& e) {
  85. vlog.error("untrapped: %s", e.str());
  86. }
  87. return result;
  88. };
  89. HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
  90. HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
  91. CViewClass::CViewClass() : classAtom(0) {
  92. WNDCLASS wndClass;
  93. wndClass.style = 0;
  94. wndClass.lpfnWndProc = CViewProc;
  95. wndClass.cbClsExtra = 0;
  96. wndClass.cbWndExtra = 0;
  97. wndClass.hInstance = instance = GetModuleHandle(0);
  98. wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
  99. if (!wndClass.hIcon)
  100. printf("unable to load icon:%ld", GetLastError());
  101. wndClass.hCursor = NULL;
  102. wndClass.hbrBackground = NULL;
  103. wndClass.lpszMenuName = 0;
  104. wndClass.lpszClassName = _T("rfb::win32::CViewClass");
  105. classAtom = RegisterClass(&wndClass);
  106. if (!classAtom) {
  107. throw rdr::SystemException("unable to register CView window class", GetLastError());
  108. }
  109. }
  110. CViewClass::~CViewClass() {
  111. if (classAtom) {
  112. UnregisterClass((const TCHAR*)classAtom, instance);
  113. }
  114. }
  115. CViewClass baseClass;
  116. //
  117. // -=- CView instance implementation
  118. //
  119. RegKey CView::userConfigKey;
  120. CView::CView()
  121. : quit_on_destroy(false), buffer(0), sock(0), readyToRead(false),
  122. client_size(0, 0, 16, 16), window_size(0, 0, 32, 32),
  123. cursorVisible(false), cursorAvailable(false), cursorInBuffer(false),
  124. systemCursorVisible(true), trackingMouseLeave(false),
  125. hwnd(0), requestUpdate(false), has_focus(false), palette_changed(false),
  126. sameMachine(false), encodingChange(false), formatChange(false),
  127. lastUsedEncoding_(encodingRaw), fullScreenActive(false),
  128. bumpScroll(false), manager(0) {
  129. // Create the window
  130. const TCHAR* name = _T("VNC Viewer 4.0b");
  131. hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW,
  132. 0, 0, 10, 10, 0, 0, baseClass.instance, this);
  133. if (!hwnd) {
  134. throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
  135. }
  136. vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), hwnd);
  137. // Initialise the CPointer pointer handler
  138. ptr.setHWND(getHandle());
  139. ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
  140. ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
  141. // Initialise the bumpscroll timer
  142. bumpScrollTimer.setHWND(getHandle());
  143. bumpScrollTimer.setId(TIMER_BUMPSCROLL);
  144. // Hook the clipboard
  145. clipboard.setNotifier(this);
  146. // Create the backing buffer
  147. buffer = new win32::DIBSectionBuffer(getHandle());
  148. }
  149. CView::~CView() {
  150. vlog.debug("~CView");
  151. showSystemCursor();
  152. if (hwnd) {
  153. setVisible(false);
  154. DestroyWindow(hwnd);
  155. hwnd = 0;
  156. }
  157. delete buffer;
  158. vlog.debug("~CView done");
  159. }
  160. bool CView::initialise(network::Socket* s) {
  161. // Update the window menu
  162. HMENU wndmenu = GetSystemMenu(hwnd, FALSE);
  163. AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
  164. AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
  165. AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
  166. AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
  167. AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
  168. AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
  169. AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
  170. AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
  171. if (manager) AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
  172. AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
  173. AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
  174. AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
  175. // Set the server's name for MRU purposes
  176. CharArray endpoint(s->getPeerEndpoint());
  177. setServerName(endpoint.buf);
  178. if (!options.host.buf)
  179. options.setHost(endpoint.buf);
  180. // Initialise the underlying CConnection
  181. setStreams(&s->inStream(), &s->outStream());
  182. // Enable processing of window messages while blocked on I/O
  183. s->inStream().setBlockCallback(this);
  184. // Initialise the viewer options
  185. applyOptions(options);
  186. // - Set which auth schemes we support
  187. addSecType(secTypeNone);
  188. addSecType(secTypeVncAuth);
  189. initialiseProtocol();
  190. WSAAsyncSelect(s->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
  191. sock = s;
  192. return true;
  193. }
  194. void
  195. CView::applyOptions(CViewOptions& opt) {
  196. // *** CHANGE THIS TO USE CViewOptions::operator= ***
  197. // - Take the username, password, config filename, and host spec
  198. options.setUserName(opt.userName.buf);
  199. options.setPassword(opt.password.buf);
  200. options.setHost(opt.host.buf);
  201. options.setConfigFileName(opt.configFileName.buf);
  202. options.setMonitor(opt.monitor.buf);
  203. // - Set optional features in ConnParams
  204. encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
  205. (options.useDesktopResize != opt.useDesktopResize));
  206. cp.supportsLocalCursor = options.useLocalCursor = opt.useLocalCursor;
  207. cp.supportsDesktopResize = options.useDesktopResize = opt.useDesktopResize;
  208. encodingChange |= ((options.customCompressLevel != opt.customCompressLevel) ||
  209. (options.compressLevel != opt.compressLevel) ||
  210. (options.noJpeg != opt.noJpeg) ||
  211. (options.qualityLevel != opt.qualityLevel));
  212. cp.customCompressLevel = options.customCompressLevel = opt.customCompressLevel;
  213. cp.compressLevel = options.compressLevel = opt.compressLevel;
  214. cp.noJpeg = options.noJpeg = opt.noJpeg;
  215. cp.qualityLevel = options.qualityLevel = opt.qualityLevel;
  216. if (cursorAvailable)
  217. hideLocalCursor();
  218. cursorAvailable = cursorAvailable && options.useLocalCursor;
  219. // - Switch full-screen mode on/off
  220. options.fullScreen = opt.fullScreen;
  221. setFullscreen(options.fullScreen);
  222. // - Handle format/encoding options
  223. encodingChange |= (options.preferredEncoding != opt.preferredEncoding);
  224. options.preferredEncoding = opt.preferredEncoding;
  225. formatChange |= (options.fullColour != opt.fullColour);
  226. options.fullColour = opt.fullColour;
  227. if (!options.fullColour)
  228. formatChange |= (options.lowColourLevel != opt.lowColourLevel);
  229. options.lowColourLevel = opt.lowColourLevel;
  230. options.autoSelect = opt.autoSelect;
  231. // - Sharing
  232. options.shared = opt.shared;
  233. setShared(options.shared);
  234. // - Inputs
  235. options.sendPtrEvents = opt.sendPtrEvents;
  236. options.sendKeyEvents = opt.sendKeyEvents;
  237. options.clientCutText = opt.clientCutText;
  238. options.serverCutText = opt.serverCutText;
  239. options.emulate3 = opt.emulate3;
  240. ptr.enableEmulate3(opt.emulate3);
  241. options.pointerEventInterval = opt.pointerEventInterval;
  242. ptr.enableInterval(opt.pointerEventInterval);
  243. options.menuKey = opt.menuKey;
  244. // - Protocol version override
  245. options.protocol3_3 = opt.protocol3_3;
  246. setProtocol3_3(options.protocol3_3);
  247. // - Bell
  248. options.acceptBell = opt.acceptBell;
  249. }
  250. void
  251. CView::setFullscreen(bool fs) {
  252. // Set the menu fullscreen option tick
  253. CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_FULLSCREEN,
  254. (options.fullScreen ? MF_CHECKED : 0) | MF_BYCOMMAND);
  255. // If the window is not visible then we ignore the request.
  256. // setVisible() will call us to correct the full-screen state when
  257. // the window is visible, to keep things consistent.
  258. if (!IsWindowVisible(getHandle()))
  259. return;
  260. if (fs && !fullScreenActive) {
  261. fullScreenActive = bumpScroll = true;
  262. // Un-minimize the window if required
  263. if (GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE)
  264. ShowWindow(getHandle(), SW_RESTORE);
  265. // Save the non-fullscreen window position
  266. RECT wrect;
  267. GetWindowRect(getHandle(), &wrect);
  268. fullScreenOldRect = Rect(wrect.left, wrect.top, wrect.right, wrect.bottom);
  269. // Find the size of the display the window is on
  270. MonitorInfo mi(getHandle());
  271. // Set the window full-screen
  272. DWORD flags = GetWindowLong(getHandle(), GWL_STYLE);
  273. fullScreenOldFlags = flags;
  274. flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
  275. vlog.debug("flags=%x", flags);
  276. SetWindowLong(getHandle(), GWL_STYLE, flags);
  277. SetWindowPos(getHandle(), HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
  278. mi.rcMonitor.right-mi.rcMonitor.left,
  279. mi.rcMonitor.bottom-mi.rcMonitor.top,
  280. SWP_FRAMECHANGED);
  281. } else if (!fs && fullScreenActive) {
  282. fullScreenActive = bumpScroll = false;
  283. // Set the window non-fullscreen
  284. SetWindowLong(getHandle(), GWL_STYLE, fullScreenOldFlags);
  285. SetWindowPos(getHandle(), HWND_NOTOPMOST,
  286. fullScreenOldRect.tl.x, fullScreenOldRect.tl.y,
  287. fullScreenOldRect.width(), fullScreenOldRect.height(),
  288. SWP_FRAMECHANGED);
  289. }
  290. // Adjust the viewport offset to cope with change in size between FS
  291. // and previous window state.
  292. setViewportOffset(scrolloffset);
  293. }
  294. bool CView::setViewportOffset(const Point& tl) {
  295. /* ***
  296. Point np = Point(max(0, min(maxscrolloffset.x, tl.x)),
  297. max(0, min(maxscrolloffset.y, tl.y)));
  298. */
  299. Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())),
  300. max(0, min(tl.y, buffer->height()-client_size.height())));
  301. Point delta = np.translate(scrolloffset.negate());
  302. if (!np.equals(scrolloffset)) {
  303. scrolloffset = np;
  304. ScrollWindowEx(getHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
  305. UpdateWindow(getHandle());
  306. return true;
  307. }
  308. return false;
  309. }
  310. bool CView::processBumpScroll(const Point& pos)
  311. {
  312. if (!bumpScroll) return false;
  313. int bumpScrollPixels = 20;
  314. bumpScrollDelta = Point();
  315. if (pos.x == client_size.width()-1)
  316. bumpScrollDelta.x = bumpScrollPixels;
  317. else if (pos.x == 0)
  318. bumpScrollDelta.x = -bumpScrollPixels;
  319. if (pos.y == client_size.height()-1)
  320. bumpScrollDelta.y = bumpScrollPixels;
  321. else if (pos.y == 0)
  322. bumpScrollDelta.y = -bumpScrollPixels;
  323. if (bumpScrollDelta.x || bumpScrollDelta.y) {
  324. if (bumpScrollTimer.isActive()) return true;
  325. if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
  326. bumpScrollTimer.start(25);
  327. return true;
  328. }
  329. }
  330. bumpScrollTimer.stop();
  331. return false;
  332. }
  333. LRESULT
  334. CView::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
  335. switch (msg) {
  336. // -=- Process standard window messages
  337. case WM_DISPLAYCHANGE:
  338. // Display has changed - use new pixel format
  339. calculateFullColourPF();
  340. break;
  341. case WM_PAINT:
  342. {
  343. PAINTSTRUCT ps;
  344. HDC paintDC = BeginPaint(getHandle(), &ps);
  345. if (!paintDC)
  346. throw SystemException("unable to BeginPaint", GetLastError());
  347. Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
  348. if (!pr.is_empty()) {
  349. // Draw using the correct palette
  350. PaletteSelector pSel(paintDC, windowPalette.getHandle());
  351. if (buffer->bitmap) {
  352. // Update the bitmap's palette
  353. if (palette_changed) {
  354. palette_changed = false;
  355. buffer->refreshPalette();
  356. }
  357. // Get device context
  358. BitmapDC bitmapDC(paintDC, buffer->bitmap);
  359. // Blit the border if required
  360. Rect bufpos = bufferToClient(buffer->getRect());
  361. if (!pr.enclosed_by(bufpos)) {
  362. vlog.debug("draw border");
  363. HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
  364. RECT r;
  365. SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
  366. SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
  367. SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
  368. SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
  369. }
  370. // Do the blit
  371. Point buf_pos = clientToBuffer(pr.tl);
  372. if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
  373. bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
  374. throw SystemException("unable to BitBlt to window", GetLastError());
  375. } else {
  376. // Blit a load of black
  377. if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
  378. 0, 0, 0, BLACKNESS))
  379. throw SystemException("unable to BitBlt to blank window", GetLastError());
  380. }
  381. }
  382. EndPaint(getHandle(), &ps);
  383. // - Request the next update from the server, if required
  384. requestNewUpdate();
  385. }
  386. return 0;
  387. // -=- Palette management
  388. case WM_PALETTECHANGED:
  389. vlog.debug("WM_PALETTECHANGED");
  390. if ((HWND)wParam == getHandle()) {
  391. vlog.debug("ignoring");
  392. break;
  393. }
  394. case WM_QUERYNEWPALETTE:
  395. vlog.debug("re-selecting palette");
  396. {
  397. WindowDC wdc(getHandle());
  398. PaletteSelector pSel(wdc, windowPalette.getHandle());
  399. if (pSel.isRedrawRequired()) {
  400. InvalidateRect(getHandle(), 0, FALSE);
  401. UpdateWindow(getHandle());
  402. }
  403. }
  404. return TRUE;
  405. // -=- Window position
  406. // Prevent the window from being resized to be too large if in normal mode.
  407. // If maximized or fullscreen the allow oversized windows.
  408. case WM_WINDOWPOSCHANGING:
  409. {
  410. WINDOWPOS* wpos = (WINDOWPOS*)lParam;
  411. if (wpos->flags & SWP_NOSIZE)
  412. break;
  413. // Work out how big the window should ideally be
  414. DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
  415. DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
  416. RECT r;
  417. SetRect(&r, 0, 0, buffer->width(), buffer->height());
  418. AdjustWindowRect(&r, style, FALSE);
  419. Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
  420. if (current_style & WS_VSCROLL)
  421. reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
  422. if (current_style & WS_HSCROLL)
  423. reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
  424. RECT current;
  425. GetWindowRect(getHandle(), &current);
  426. // Ensure that the window isn't resized too large
  427. // If the window is maximized or full-screen then any size is allowed
  428. if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
  429. if (wpos->cx > reqd_size.width()) {
  430. wpos->cx = reqd_size.width();
  431. wpos->x = current.left;
  432. }
  433. if (wpos->cy > reqd_size.height()) {
  434. wpos->cy = reqd_size.height();
  435. wpos->y = current.top;
  436. }
  437. }
  438. }
  439. break;
  440. // Add scrollbars if required and update window size info we have cached.
  441. case WM_SIZE:
  442. {
  443. Point old_offset = bufferToClient(Point(0, 0));
  444. // Update the cached sizing information
  445. RECT r;
  446. GetWindowRect(getHandle(), &r);
  447. window_size = Rect(r.left, r.top, r.right, r.bottom);
  448. GetClientRect(getHandle(), &r);
  449. client_size = Rect(r.left, r.top, r.right, r.bottom);
  450. // Determine whether scrollbars are required
  451. calculateScrollBars();
  452. // Redraw if required
  453. if (!old_offset.equals(bufferToClient(Point(0, 0))))
  454. InvalidateRect(getHandle(), 0, TRUE);
  455. }
  456. break;
  457. case WM_VSCROLL:
  458. case WM_HSCROLL:
  459. {
  460. Point delta;
  461. int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
  462. switch (LOWORD(wParam)) {
  463. case SB_PAGEUP: newpos -= 50; break;
  464. case SB_PAGEDOWN: newpos += 50; break;
  465. case SB_LINEUP: newpos -= 5; break;
  466. case SB_LINEDOWN: newpos += 5; break;
  467. case SB_THUMBTRACK:
  468. case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
  469. default: vlog.info("received unknown scroll message");
  470. };
  471. if (msg == WM_HSCROLL)
  472. setViewportOffset(Point(newpos, scrolloffset.y));
  473. else
  474. setViewportOffset(Point(scrolloffset.x, newpos));
  475. SCROLLINFO si;
  476. si.cbSize = sizeof(si);
  477. si.fMask = SIF_POS;
  478. si.nPos = newpos;
  479. SetScrollInfo(getHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE);
  480. }
  481. break;
  482. // -=- Bump-scrolling
  483. case WM_TIMER:
  484. switch (wParam) {
  485. case TIMER_BUMPSCROLL:
  486. if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
  487. bumpScrollTimer.stop();
  488. break;
  489. case TIMER_POINTER_INTERVAL:
  490. case TIMER_POINTER_3BUTTON:
  491. try {
  492. ptr.handleTimer(writer(), wParam);
  493. } catch (rdr::Exception& e) {
  494. close(e.str());
  495. }
  496. break;
  497. }
  498. break;
  499. // -=- Cursor shape/visibility handling
  500. case WM_SETCURSOR:
  501. if (LOWORD(lParam) != HTCLIENT)
  502. break;
  503. SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
  504. return TRUE;
  505. case WM_MOUSELEAVE:
  506. trackingMouseLeave = false;
  507. cursorOutsideBuffer();
  508. return 0;
  509. // -=- Mouse input handling
  510. case WM_MOUSEMOVE:
  511. case WM_LBUTTONUP:
  512. case WM_MBUTTONUP:
  513. case WM_RBUTTONUP:
  514. case WM_LBUTTONDOWN:
  515. case WM_MBUTTONDOWN:
  516. case WM_RBUTTONDOWN:
  517. case WM_MOUSEWHEEL:
  518. if (has_focus)
  519. {
  520. if (!trackingMouseLeave) {
  521. TRACKMOUSEEVENT tme;
  522. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  523. tme.dwFlags = TME_LEAVE;
  524. tme.hwndTrack = hwnd;
  525. _TrackMouseEvent(&tme);
  526. trackingMouseLeave = true;
  527. }
  528. int mask = 0;
  529. if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
  530. if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
  531. if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
  532. if (msg == WM_MOUSEWHEEL) {
  533. int delta = (short)HIWORD(wParam);
  534. int repeats = (abs(delta)+119) / 120;
  535. int wheelMask = (delta > 0) ? 8 : 16;
  536. vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
  537. for (int i=0; i<repeats; i++) {
  538. writePointerEvent(oldpos.x, oldpos.y, mask | wheelMask);
  539. writePointerEvent(oldpos.x, oldpos.y, mask);
  540. }
  541. } else {
  542. Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
  543. Point p = clientToBuffer(clientPos);
  544. // If the mouse is not within the server buffer area, do nothing
  545. cursorInBuffer = buffer->getRect().contains(p);
  546. if (!cursorInBuffer) {
  547. cursorOutsideBuffer();
  548. break;
  549. }
  550. // If we're locally rendering the cursor then redraw it
  551. if (cursorAvailable) {
  552. // - Render the cursor!
  553. if (!p.equals(cursorPos)) {
  554. hideLocalCursor();
  555. cursorPos = p;
  556. showLocalCursor();
  557. if (cursorVisible)
  558. hideSystemCursor();
  559. }
  560. }
  561. // If we are doing bump-scrolling then try that first...
  562. if (processBumpScroll(clientPos))
  563. break;
  564. // Send a pointer event to the server
  565. writePointerEvent(p.x, p.y, mask);
  566. oldpos = p;
  567. }
  568. } else {
  569. cursorOutsideBuffer();
  570. }
  571. break;
  572. // -=- Track whether or not the window has focus
  573. case WM_SETFOCUS:
  574. has_focus = true;
  575. break;
  576. case WM_KILLFOCUS:
  577. has_focus = false;
  578. cursorOutsideBuffer();
  579. // Restore the remote keys to consistent states
  580. try {
  581. kbd.releaseAllKeys(writer());
  582. } catch (rdr::Exception& e) {
  583. close(e.str());
  584. }
  585. break;
  586. // -=- Handle the extra window menu items
  587. // Process the items added to the system menu
  588. case WM_SYSCOMMAND:
  589. // - First check whether it's one of our messages
  590. switch (wParam) {
  591. case IDM_FULLSCREEN:
  592. options.fullScreen = !options.fullScreen;
  593. setFullscreen(options.fullScreen);
  594. return 0;
  595. case IDM_CTRL_KEY:
  596. writeKeyEvent(VK_CONTROL, 0, !kbd.keyPressed(VK_CONTROL));
  597. return 0;
  598. case IDM_ALT_KEY:
  599. writeKeyEvent(VK_MENU, 0, !kbd.keyPressed(VK_MENU));
  600. return 0;
  601. case IDM_SEND_MENU_KEY:
  602. writeKeyEvent(options.menuKey, 0, true);
  603. writeKeyEvent(options.menuKey, 0, false);
  604. return 0;
  605. case IDM_SEND_CAD:
  606. writeKeyEvent(VK_CONTROL, 0, true);
  607. writeKeyEvent(VK_MENU, 0, true);
  608. writeKeyEvent(VK_DELETE, 0, true);
  609. writeKeyEvent(VK_DELETE, 0, false);
  610. writeKeyEvent(VK_MENU, 0, false);
  611. writeKeyEvent(VK_CONTROL, 0, false);
  612. return 0;
  613. case IDM_REQUEST_REFRESH:
  614. try {
  615. writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
  616. requestUpdate = false;
  617. } catch (rdr::Exception& e) {
  618. close(e.str());
  619. }
  620. return 0;
  621. case IDM_NEWCONN:
  622. manager->addClient(0);
  623. return 0;
  624. case IDM_OPTIONS:
  625. // Update the monitor device name in the CViewOptions instance
  626. {
  627. MonitorInfo mi(getHandle());
  628. options.setMonitor(mi.szDevice);
  629. optionsDialog.showDialog(this);
  630. return 0;
  631. }
  632. case IDM_INFO:
  633. infoDialog.showDialog(this);
  634. return 0;
  635. case IDM_ABOUT:
  636. AboutDialog::instance.showDialog();
  637. return 0;
  638. };
  639. // - Not one of our messages, so process it as a system message
  640. switch (wParam & 0xfff0) {
  641. // When restored, ensure that full-screen mode is re-enabled if required.
  642. case SC_RESTORE:
  643. rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
  644. setFullscreen(options.fullScreen);
  645. return 0;
  646. // If we are maximized or minimized then that cancels full-screen mode.
  647. case SC_MINIMIZE:
  648. case SC_MAXIMIZE:
  649. setFullscreen(false);
  650. break;
  651. // If the system menu is shown then make sure it's up to date
  652. case SC_KEYMENU:
  653. case SC_MOUSEMENU:
  654. updateF8Menu(false);
  655. break;
  656. };
  657. break;
  658. // Treat all menu commands as system menu commands
  659. case WM_COMMAND:
  660. SendMessage(getHandle(), WM_SYSCOMMAND, wParam, lParam);
  661. return 0;
  662. case WM_MENUCHAR:
  663. vlog.debug("menuchar");
  664. break;
  665. // -=- Handle keyboard input
  666. case WM_KEYUP:
  667. case WM_KEYDOWN:
  668. // Hook the MenuKey to pop-up the window menu
  669. if (options.menuKey && (wParam == options.menuKey)) {
  670. bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
  671. bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
  672. bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
  673. if (!(ctrlDown || altDown || shiftDown)) {
  674. // If MenuKey is being released then pop-up the menu
  675. if ((msg == WM_KEYDOWN)) {
  676. // Make sure it's up to date
  677. updateF8Menu(true);
  678. // Show it under the pointer
  679. POINT pt;
  680. GetCursorPos(&pt);
  681. cursorInBuffer = false;
  682. TrackPopupMenu(GetSystemMenu(getHandle(), FALSE),
  683. TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0);
  684. }
  685. // Ignore the MenuKey keypress for both press & release events
  686. return 0;
  687. }
  688. }
  689. case WM_SYSKEYDOWN:
  690. case WM_SYSKEYUP:
  691. writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
  692. return 0;
  693. // -=- Handle the window closing
  694. case WM_CLOSE:
  695. vlog.debug("WM_CLOSE %x", getHandle());
  696. if (quit_on_destroy) {
  697. vlog.debug("posting WM_QUIT");
  698. PostQuitMessage(0);
  699. } else {
  700. vlog.debug("not posting WM_QUIT");
  701. }
  702. break;
  703. // -=- Process incoming socket data
  704. case WM_USER:
  705. readyToRead = true;
  706. break;
  707. }
  708. return rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
  709. }
  710. void CView::blockCallback() {
  711. // - An InStream has blocked on I/O while processing an RFB message
  712. // We re-enable socket event notifications, so we'll know when more
  713. // data is available, then we sit and dispatch window events until
  714. // the notification arrives.
  715. readyToRead = false;
  716. WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
  717. MSG msg;
  718. while (true) {
  719. if (readyToRead) {
  720. // - Network event notification. Return control to I/O routine.
  721. WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, 0);
  722. return;
  723. }
  724. DWORD result = GetMessage(&msg, NULL, 0, 0);
  725. if (result == 0) {
  726. vlog.debug("WM_QUIT");
  727. throw QuitMessage(msg.wParam);
  728. } else if (result < 0) {
  729. throw rdr::SystemException("GetMessage error", GetLastError());
  730. }
  731. // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
  732. // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key
  733. // state from one call to the next, which would be messed up by calls to
  734. // TranslateMessage() (actually it looks like TranslateMessage() calls
  735. // ToAscii() internally).
  736. DispatchMessage(&msg);
  737. }
  738. }
  739. void
  740. CView::hideLocalCursor() {
  741. // - Blit the cursor backing store over the cursor
  742. // *** ALWAYS call this BEFORE changing buffer PF!!!
  743. if (cursorVisible) {
  744. cursorVisible = false;
  745. buffer->imageRect(cursorBackingRect, cursorBacking.data);
  746. invalidateBufferRect(cursorBackingRect);
  747. }
  748. }
  749. void
  750. CView::showLocalCursor() {
  751. if (cursorAvailable && !cursorVisible && cursorInBuffer) {
  752. if (!cp.pf().equal(cursor.getPF()) ||
  753. cursor.getRect().is_empty()) {
  754. vlog.info("attempting to render invalid local cursor");
  755. cursorAvailable = false;
  756. showSystemCursor();
  757. return;
  758. }
  759. cursorVisible = true;
  760. cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
  761. cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
  762. buffer->getImage(cursorBacking.data, cursorBackingRect);
  763. renderLocalCursor();
  764. invalidateBufferRect(cursorBackingRect);
  765. }
  766. }
  767. void CView::cursorOutsideBuffer()
  768. {
  769. cursorInBuffer = false;
  770. hideLocalCursor();
  771. showSystemCursor();
  772. }
  773. void
  774. CView::renderLocalCursor()
  775. {
  776. Rect r = cursor.getRect();
  777. r = r.translate(cursorPos).translate(cursor.hotspot.negate());
  778. buffer->maskRect(r, cursor.data, cursor.mask.buf);
  779. }
  780. void
  781. CView::hideSystemCursor() {
  782. if (systemCursorVisible) {
  783. vlog.debug("hide system cursor");
  784. systemCursorVisible = false;
  785. ShowCursor(FALSE);
  786. }
  787. }
  788. void
  789. CView::showSystemCursor() {
  790. if (!systemCursorVisible) {
  791. vlog.debug("show system cursor");
  792. systemCursorVisible = true;
  793. ShowCursor(TRUE);
  794. }
  795. }
  796. bool
  797. CView::invalidateBufferRect(const Rect& crect) {
  798. Rect rect = bufferToClient(crect);
  799. if (rect.intersect(client_size).is_empty()) return false;
  800. RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
  801. InvalidateRect(getHandle(), &invalid, FALSE);
  802. return true;
  803. }
  804. void
  805. CView::notifyClipboardChanged(const char* text, int len) {
  806. if (!options.clientCutText) return;
  807. if (state() != RFBSTATE_NORMAL) return;
  808. try {
  809. writer()->writeClientCutText(text, len);
  810. } catch (rdr::Exception& e) {
  811. close(e.str());
  812. }
  813. }
  814. CSecurity* CView::getCSecurity(int secType)
  815. {
  816. switch (secType) {
  817. case secTypeNone:
  818. return new CSecurityNone();
  819. case secTypeVncAuth:
  820. return new CSecurityVncAuth(this);
  821. default:
  822. throw Exception("Unsupported secType?");
  823. }
  824. }
  825. void
  826. CView::setColourMapEntries(int first, int count, U16* rgbs) {
  827. vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
  828. int i;
  829. for (i=0;i<count;i++) {
  830. buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
  831. }
  832. // *** change to 0, 256?
  833. refreshWindowPalette(first, count);
  834. palette_changed = true;
  835. InvalidateRect(getHandle(), 0, FALSE);
  836. }
  837. void
  838. CView::bell() {
  839. if (options.acceptBell)
  840. MessageBeep(-1);
  841. }
  842. void
  843. CView::setDesktopSize(int w, int h) {
  844. vlog.debug("setDesktopSize %dx%d", w, h);
  845. // If the locally-rendered cursor is visible then remove it
  846. hideLocalCursor();
  847. // Resize the backing buffer
  848. buffer->setSize(w, h);
  849. // If the window is not maximised or full-screen then resize it
  850. if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
  851. // Resize the window to the required size
  852. RECT r = {0, 0, w, h};
  853. AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE);
  854. SetWindowPos(getHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top,
  855. SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  856. // Move the window to the desired monitor
  857. if (options.monitor.buf)
  858. moveToMonitor(getHandle(), options.monitor.buf);
  859. // Clip to the system work area
  860. centerWindow(getHandle(), 0, true);
  861. } else {
  862. // Ensure the screen contents are consistent
  863. InvalidateRect(getHandle(), 0, FALSE);
  864. }
  865. // Tell the underlying CConnection
  866. CConnection::setDesktopSize(w, h);
  867. // Enable/disable scrollbars as appropriate
  868. calculateScrollBars();
  869. }
  870. void
  871. CView::setCursor(const Point& hotspot, const Point& size, void* data, void* mask) {
  872. if (!options.useLocalCursor) return;
  873. hideLocalCursor();
  874. cursor.hotspot = hotspot;
  875. cursor.setSize(size.x, size.y);
  876. cursor.setPF(cp.pf());
  877. cursor.imageRect(cursor.getRect(), data);
  878. memcpy(cursor.mask.buf, mask, cursor.maskLen());
  879. cursor.crop();
  880. cursorBacking.setSize(size.x, size.y);
  881. cursorBacking.setPF(cp.pf());
  882. cursorAvailable = true;
  883. showLocalCursor();
  884. }
  885. PixelFormat
  886. CView::getNativePF() const {
  887. vlog.debug("getNativePF()");
  888. return WindowDC(getHandle()).getPF();
  889. }
  890. void
  891. CView::setVisible(bool visible) {
  892. ShowWindow(getHandle(), visible ? SW_SHOW : SW_HIDE);
  893. if (visible) {
  894. // When the window becomes visible, make it active
  895. SetForegroundWindow(getHandle());
  896. SetActiveWindow(getHandle());
  897. // If the window should be full-screen, then do so
  898. setFullscreen(options.fullScreen);
  899. } else {
  900. // Disable full-screen mode
  901. setFullscreen(false);
  902. }
  903. }
  904. void
  905. CView::close(const char* reason) {
  906. setVisible(false);
  907. if (reason) {
  908. vlog.info("closing - %s", reason);
  909. MsgBox(NULL, TStr(reason), MB_ICONINFORMATION | MB_OK);
  910. }
  911. SendMessage(getHandle(), WM_CLOSE, 0, 0);
  912. }
  913. void
  914. CView::framebufferUpdateEnd() {
  915. if (debugDelay != 0) {
  916. vlog.debug("debug delay %d",(int)debugDelay);
  917. UpdateWindow(getHandle());
  918. Sleep(debugDelay);
  919. std::list<rfb::Rect>::iterator i;
  920. for (i = debugRects.begin(); i != debugRects.end(); i++) {
  921. invertRect(*i);
  922. }
  923. debugRects.clear();
  924. }
  925. if (options.autoSelect)
  926. autoSelectFormatAndEncoding();
  927. // Always request the next update
  928. requestUpdate = true;
  929. // Check that at least part of the window has changed
  930. if (!GetUpdateRect(getHandle(), 0, FALSE)) {
  931. if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE))
  932. requestNewUpdate();
  933. }
  934. showLocalCursor();
  935. }
  936. // Note: The method below is duplicated in vncviewer_unix/CConn.cxx!
  937. // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
  938. // to the connection speed:
  939. //
  940. // Above 16Mbps (timing for at least a second), same machine, switch to raw
  941. // Above 3Mbps, switch to hextile
  942. // Otherwise, switch to Tight
  943. //
  944. // Above 256Kbps, use full colour mode
  945. //
  946. void
  947. CView::autoSelectFormatAndEncoding() {
  948. int kbitsPerSecond = sock->inStream().kbitsPerSecond();
  949. unsigned int newEncoding = options.preferredEncoding;
  950. bool newFullColour = options.fullColour;
  951. unsigned int timeWaited = sock->inStream().timeWaited();
  952. // Select best encoding
  953. if (kbitsPerSecond > 16000 && sameMachine && timeWaited >= 10000) {
  954. newEncoding = encodingRaw;
  955. } else if (kbitsPerSecond > 3000 && timeWaited >= 10000) {
  956. newEncoding = encodingHextile;
  957. } else {
  958. newEncoding = encodingTight;
  959. }
  960. if (newEncoding != options.preferredEncoding) {
  961. vlog.info("Throughput %d kbit/s - changing to %s encoding",
  962. kbitsPerSecond, encodingName(newEncoding));
  963. options.preferredEncoding = newEncoding;
  964. encodingChange = true;
  965. }
  966. if (kbitsPerSecond == 0) {
  967. return;
  968. }
  969. if (cp.majorVersion <= 3 && cp.minorVersion <= 7) {
  970. // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
  971. // cursors "asynchronously". If this happens in the middle of a
  972. // pixel format change, the server will encode the cursor with
  973. // the old format, but the client will try to decode it
  974. // according to the new format. This will lead to a
  975. // crash. Therefore, we do not allow automatic format change for
  976. // old servers.
  977. return;
  978. }
  979. // Select best color level
  980. newFullColour = (kbitsPerSecond > 256);
  981. if (newFullColour != options.fullColour) {
  982. vlog.info("Throughput %d kbit/s - full color is now %s",
  983. kbitsPerSecond,
  984. newFullColour ? "enabled" : "disabled");
  985. options.fullColour = newFullColour;
  986. formatChange = true;
  987. }
  988. }
  989. void
  990. CView::requestNewUpdate() {
  991. if (!requestUpdate) return;
  992. if (formatChange) {
  993. // Hide the rendered cursor, if any, to prevent
  994. // the backing buffer being used in the wrong format
  995. hideLocalCursor();
  996. // Select the required pixel format
  997. if (options.fullColour) {
  998. buffer->setPF(fullColourPF);
  999. } else {
  1000. switch (options.lowColourLevel) {
  1001. case 0:
  1002. buffer->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
  1003. break;
  1004. case 1:
  1005. buffer->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
  1006. break;
  1007. case 2:
  1008. buffer->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
  1009. break;
  1010. }
  1011. }
  1012. // Print the current pixel format
  1013. char str[256];
  1014. buffer->getPF().print(str, 256);
  1015. vlog.info("Using pixel format %s",str);
  1016. // Save the connection pixel format and tell server to use it
  1017. cp.setPF(buffer->getPF());
  1018. writer()->writeSetPixelFormat(cp.pf());
  1019. // Correct the local window's palette
  1020. if (!getNativePF().trueColour)
  1021. refreshWindowPalette(0, 1 << cp.pf().depth);
  1022. }
  1023. if (encodingChange) {
  1024. vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
  1025. writer()->writeSetEncodings(options.preferredEncoding, true);
  1026. }
  1027. writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
  1028. !formatChange);
  1029. encodingChange = formatChange = requestUpdate = false;
  1030. }
  1031. void
  1032. CView::writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down) {
  1033. if (!options.sendKeyEvents) return;
  1034. try {
  1035. kbd.keyEvent(writer(), vkey, flags, down);
  1036. } catch (rdr::Exception& e) {
  1037. close(e.str());
  1038. }
  1039. }
  1040. void
  1041. CView::writePointerEvent(int x, int y, int buttonMask) {
  1042. if (!options.sendPtrEvents) return;
  1043. try {
  1044. ptr.pointerEvent(writer(), x, y, buttonMask);
  1045. } catch (rdr::Exception& e) {
  1046. close(e.str());
  1047. }
  1048. }
  1049. void
  1050. CView::refreshWindowPalette(int start, int count) {
  1051. vlog.debug("refreshWindowPalette(%d, %d)", start, count);
  1052. Colour colours[256];
  1053. if (count > 256) {
  1054. vlog.debug("%d palette entries", count);
  1055. throw rdr::Exception("too many palette entries");
  1056. }
  1057. // Copy the palette from the DIBSectionBuffer
  1058. ColourMap* cm = buffer->getColourMap();
  1059. if (!cm) return;
  1060. for (int i=0; i<count; i++) {
  1061. int r, g, b;
  1062. cm->lookup(i, &r, &g, &b);
  1063. colours[i].r = r;
  1064. colours[i].g = g;
  1065. colours[i].b = b;
  1066. }
  1067. // Set the window palette
  1068. windowPalette.setEntries(start, count, colours);
  1069. // Cause the window to be redrawn
  1070. InvalidateRect(getHandle(), 0, 0);
  1071. }
  1072. void CView::calculateScrollBars() {
  1073. // Calculate the required size of window
  1074. DWORD current_style = GetWindowLong(getHandle(), GWL_STYLE);
  1075. DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
  1076. DWORD old_style;
  1077. RECT r;
  1078. SetRect(&r, 0, 0, buffer->width(), buffer->height());
  1079. AdjustWindowRect(&r, style, FALSE);
  1080. Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
  1081. if (!bumpScroll) {
  1082. // We only enable scrollbars if bump-scrolling is not active.
  1083. // Effectively, this means if full-screen is not active,
  1084. // but I think it's better to make these things explicit.
  1085. // Work out whether scroll bars are required
  1086. do {
  1087. old_style = style;
  1088. if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
  1089. style |= WS_HSCROLL;
  1090. reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
  1091. }
  1092. if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
  1093. style |= WS_VSCROLL;
  1094. reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
  1095. }
  1096. } while (style != old_style);
  1097. }
  1098. // Tell Windows to update the window style & cached settings
  1099. if (style != current_style) {
  1100. SetWindowLong(getHandle(), GWL_STYLE, style);
  1101. SetWindowPos(getHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  1102. }
  1103. // Update the scroll settings
  1104. SCROLLINFO si;
  1105. if (style & WS_VSCROLL) {
  1106. si.cbSize = sizeof(si);
  1107. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  1108. si.nMin = 0;
  1109. si.nMax = buffer->height();
  1110. si.nPage = buffer->height() - (reqd_size.height() - window_size.height());
  1111. maxscrolloffset.y = max(0, si.nMax-si.nPage);
  1112. scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y);
  1113. si.nPos = scrolloffset.y;
  1114. SetScrollInfo(getHandle(), SB_VERT, &si, TRUE);
  1115. }
  1116. if (style & WS_HSCROLL) {
  1117. si.cbSize = sizeof(si);
  1118. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  1119. si.nMin = 0;
  1120. si.nMax = buffer->width();
  1121. si.nPage = buffer->width() - (reqd_size.width() - window_size.width());
  1122. maxscrolloffset.x = max(0, si.nMax-si.nPage);
  1123. scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x);
  1124. si.nPos = scrolloffset.x;
  1125. SetScrollInfo(getHandle(), SB_HORZ, &si, TRUE);
  1126. }
  1127. }
  1128. void
  1129. CView::calculateFullColourPF() {
  1130. // If the server is palette based then use palette locally
  1131. // Also, don't bother doing bgr222
  1132. if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
  1133. fullColourPF = serverDefaultPF;
  1134. options.fullColour = true;
  1135. } else {
  1136. // If server is trueColour, use lowest depth PF
  1137. PixelFormat native = getNativePF();
  1138. if ((serverDefaultPF.bpp < native.bpp) ||
  1139. ((serverDefaultPF.bpp == native.bpp) &&
  1140. (serverDefaultPF.depth < native.depth)))
  1141. fullColourPF = serverDefaultPF;
  1142. else
  1143. fullColourPF = getNativePF();
  1144. }
  1145. formatChange = true;
  1146. }
  1147. void
  1148. CView::updateF8Menu(bool hideSystemCommands) {
  1149. HMENU menu = GetSystemMenu(getHandle(), FALSE);
  1150. if (hideSystemCommands) {
  1151. // Gray out menu items that might cause a World Of Pain
  1152. HMENU menu = GetSystemMenu(getHandle(), FALSE);
  1153. EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
  1154. EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  1155. EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  1156. EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
  1157. EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
  1158. }
  1159. // Update the modifier key menu items
  1160. UINT ctrlCheckFlags = kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
  1161. UINT altCheckFlags = kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
  1162. CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
  1163. CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
  1164. // Ensure that the Send <MenuKey> menu item has the correct text
  1165. if (options.menuKey) {
  1166. TCharArray menuKeyStr(options.menuKeyName());
  1167. TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
  1168. _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
  1169. if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
  1170. InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
  1171. } else {
  1172. RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
  1173. }
  1174. }
  1175. void
  1176. CView::setName(const char* name) {
  1177. vlog.debug("setName %s", name);
  1178. ::SetWindowText(getHandle(), TStr(name));
  1179. CConnection::setName(name);
  1180. }
  1181. void CView::serverInit() {
  1182. CConnection::serverInit();
  1183. // Save the server's current format
  1184. serverDefaultPF = cp.pf();
  1185. // Calculate the full-colour format to use
  1186. calculateFullColourPF();
  1187. // Request the initial update
  1188. vlog.info("requesting initial update");
  1189. formatChange = encodingChange = requestUpdate = true;
  1190. requestNewUpdate();
  1191. // Show the window
  1192. setVisible(true);
  1193. }
  1194. void
  1195. CView::serverCutText(const char* str, int len) {
  1196. if (!options.serverCutText) return;
  1197. CharArray t(len+1);
  1198. memcpy(t.buf, str, len);
  1199. t.buf[len] = 0;
  1200. clipboard.setClipText(t.buf);
  1201. }
  1202. void CView::beginRect(const Rect& r, unsigned int encoding) {
  1203. sock->inStream().startTiming();
  1204. }
  1205. void CView::endRect(const Rect& r, unsigned int encoding) {
  1206. sock->inStream().stopTiming();
  1207. lastUsedEncoding_ = encoding;
  1208. if (debugDelay != 0) {
  1209. invertRect(r);
  1210. debugRects.push_back(r);
  1211. }
  1212. }
  1213. void CView::fillRect(const Rect& r, Pixel pix) {
  1214. if (cursorBackingRect.overlaps(r)) hideLocalCursor();
  1215. buffer->fillRect(r, pix);
  1216. invalidateBufferRect(r);
  1217. }
  1218. void CView::imageRect(const Rect& r, void* pixels) {
  1219. if (cursorBackingRect.overlaps(r)) hideLocalCursor();
  1220. buffer->imageRect(r, pixels);
  1221. invalidateBufferRect(r);
  1222. }
  1223. void CView::copyRect(const Rect& r, int srcX, int srcY) {
  1224. if (cursorBackingRect.overlaps(r) ||
  1225. cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height())))
  1226. hideLocalCursor();
  1227. buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
  1228. invalidateBufferRect(r);
  1229. }
  1230. void CView::invertRect(const Rect& r) {
  1231. int stride;
  1232. rdr::U8* p = buffer->getPixelsRW(r, &stride);
  1233. for (int y = 0; y < r.height(); y++) {
  1234. for (int x = 0; x < r.width(); x++) {
  1235. switch (buffer->getPF().bpp) {
  1236. case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break;
  1237. case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break;
  1238. case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
  1239. }
  1240. }
  1241. }
  1242. invalidateBufferRect(r);
  1243. }
  1244. bool CView::getUserPasswd(char** user, char** password) {
  1245. if (user && options.userName.buf)
  1246. *user = strDup(options.userName.buf);
  1247. if (password && options.password.buf)
  1248. *password = strDup(options.password.buf);
  1249. if ((user && !*user) || (password && !*password)) {
  1250. // Missing username or password - prompt the user
  1251. UserPasswdDialog userPasswdDialog;
  1252. userPasswdDialog.setCSecurity(getCurrentCSecurity());
  1253. if (!userPasswdDialog.getUserPasswd(user, password))
  1254. return false;
  1255. }
  1256. if (user) options.setUserName(*user);
  1257. if (password) options.setPassword(*password);
  1258. return true;
  1259. }