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.

DesktopWindow.cxx 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2010 D. R. Commander. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #include <windows.h>
  20. #include <commctrl.h>
  21. #include <math.h>
  22. #include <rfb/Configuration.h>
  23. #include <rfb/LogWriter.h>
  24. #include <rfb_win32/WMShatter.h>
  25. #include <rfb_win32/LowLevelKeyEvents.h>
  26. #include <rfb_win32/MonitorInfo.h>
  27. #include <rfb_win32/DeviceContext.h>
  28. #include <rfb_win32/Win32Util.h>
  29. #include <rfb_win32/MsgBox.h>
  30. #include <rfb/util.h>
  31. #include <vncviewer/DesktopWindow.h>
  32. #include <vncviewer/resource.h>
  33. using namespace rfb;
  34. using namespace rfb::win32;
  35. // - Statics & consts
  36. static LogWriter vlog("DesktopWindow");
  37. const int TIMER_BUMPSCROLL = 1;
  38. const int TIMER_POINTER_INTERVAL = 2;
  39. const int TIMER_POINTER_3BUTTON = 3;
  40. const int TIMER_UPDATE = 4;
  41. //
  42. // -=- DesktopWindowClass
  43. //
  44. // Window class used as the basis for all DesktopWindow instances
  45. //
  46. class DesktopWindowClass {
  47. public:
  48. DesktopWindowClass();
  49. ~DesktopWindowClass();
  50. ATOM classAtom;
  51. HINSTANCE instance;
  52. };
  53. LRESULT CALLBACK DesktopWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  54. LRESULT result = 0;
  55. if (msg == WM_CREATE)
  56. SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams);
  57. else if (msg == WM_DESTROY)
  58. SetWindowLongPtr(wnd, GWLP_USERDATA, 0);
  59. DesktopWindow* _this = (DesktopWindow*) GetWindowLongPtr(wnd, GWLP_USERDATA);
  60. if (!_this) {
  61. vlog.info("null _this in %x, message %u", wnd, msg);
  62. return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
  63. }
  64. try {
  65. result = _this->processMessage(msg, wParam, lParam);
  66. } catch (rfb::UnsupportedPixelFormatException &e) {
  67. MsgBox(0, (TCHAR *) e.str(), MB_OK | MB_ICONINFORMATION);
  68. _this->getCallback()->closeWindow();
  69. } catch (rdr::Exception& e) {
  70. vlog.error("untrapped: %s", e.str());
  71. }
  72. return result;
  73. };
  74. static HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
  75. static HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
  76. DesktopWindowClass::DesktopWindowClass() : classAtom(0) {
  77. WNDCLASS wndClass;
  78. wndClass.style = 0;
  79. wndClass.lpfnWndProc = DesktopWindowProc;
  80. wndClass.cbClsExtra = 0;
  81. wndClass.cbWndExtra = 0;
  82. wndClass.hInstance = instance = GetModuleHandle(0);
  83. wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
  84. if (!wndClass.hIcon)
  85. printf("unable to load icon:%ld", GetLastError());
  86. wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  87. wndClass.hbrBackground = NULL;
  88. wndClass.lpszMenuName = 0;
  89. wndClass.lpszClassName = _T("rfb::win32::DesktopWindowClass");
  90. classAtom = RegisterClass(&wndClass);
  91. if (!classAtom) {
  92. throw rdr::SystemException("unable to register DesktopWindow window class", GetLastError());
  93. }
  94. }
  95. DesktopWindowClass::~DesktopWindowClass() {
  96. if (classAtom) {
  97. UnregisterClass((const TCHAR*)classAtom, instance);
  98. }
  99. }
  100. static DesktopWindowClass baseClass;
  101. //
  102. // -=- FrameClass
  103. //
  104. // Window class used for child windows that display pixel data
  105. //
  106. class FrameClass {
  107. public:
  108. FrameClass();
  109. ~FrameClass();
  110. ATOM classAtom;
  111. HINSTANCE instance;
  112. };
  113. LRESULT CALLBACK FrameProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  114. LRESULT result = 0;
  115. if (msg == WM_CREATE)
  116. SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams);
  117. else if (msg == WM_DESTROY)
  118. SetWindowLongPtr(wnd, GWLP_USERDATA, 0);
  119. DesktopWindow* _this = (DesktopWindow*) GetWindowLongPtr(wnd, GWLP_USERDATA);
  120. if (!_this) {
  121. vlog.info("null _this in %x, message %u", wnd, msg);
  122. return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
  123. }
  124. try {
  125. result = _this->processFrameMessage(msg, wParam, lParam);
  126. } catch (rdr::Exception& e) {
  127. vlog.error("untrapped: %s", e.str());
  128. }
  129. return result;
  130. }
  131. FrameClass::FrameClass() : classAtom(0) {
  132. WNDCLASS wndClass;
  133. wndClass.style = 0;
  134. wndClass.lpfnWndProc = FrameProc;
  135. wndClass.cbClsExtra = 0;
  136. wndClass.cbWndExtra = 0;
  137. wndClass.hInstance = instance = GetModuleHandle(0);
  138. wndClass.hIcon = 0;
  139. wndClass.hCursor = NULL;
  140. wndClass.hbrBackground = NULL;
  141. wndClass.lpszMenuName = 0;
  142. wndClass.lpszClassName = _T("rfb::win32::FrameClass");
  143. classAtom = RegisterClass(&wndClass);
  144. if (!classAtom) {
  145. throw rdr::SystemException("unable to register Frame window class", GetLastError());
  146. }
  147. }
  148. FrameClass::~FrameClass() {
  149. if (classAtom) {
  150. UnregisterClass((const TCHAR*)classAtom, instance);
  151. }
  152. }
  153. FrameClass frameClass;
  154. //
  155. // -=- DesktopWindow instance implementation
  156. //
  157. DesktopWindow::DesktopWindow(Callback* cb)
  158. : bumpScroll(false), palette_changed(false), fullscreenActive(false),
  159. fullscreenRestore(false), systemCursorVisible(true), trackingMouseLeave(false),
  160. cursorInBuffer(false), cursorVisible(false), cursorAvailable(false),
  161. internalSetCursor(false), cursorImage(0), cursorMask(0),
  162. cursorWidth(0), cursorHeight(0), showToolbar(false),
  163. buffer(0), has_focus(false), autoScaling(false),
  164. window_size(0, 0, 32, 32), client_size(0, 0, 16, 16), handle(0),
  165. frameHandle(0), callback(cb) {
  166. // Create the window
  167. const char* name = "DesktopWindow";
  168. handle = CreateWindow((const TCHAR*)baseClass.classAtom, TStr(name),
  169. WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  170. 0, 0, 10, 10, 0, 0, baseClass.instance, this);
  171. if (!handle)
  172. throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
  173. vlog.debug("created window \"%s\" (%x)", name, handle);
  174. // Create the toolbar
  175. tb.create(handle);
  176. vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle());
  177. // Create the frame window
  178. frameHandle = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom,
  179. 0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
  180. CW_USEDEFAULT, CW_USEDEFAULT, handle, 0, frameClass.instance, this);
  181. if (!frameHandle) {
  182. throw rdr::SystemException("unable to create rfb frame window instance", GetLastError());
  183. }
  184. vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHandle);
  185. // Initialise the CPointer pointer handler
  186. ptr.setHWND(frameHandle);
  187. ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
  188. ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
  189. // Initialise the bumpscroll timer
  190. bumpScrollTimer.setHWND(handle);
  191. bumpScrollTimer.setId(TIMER_BUMPSCROLL);
  192. // Initialise the update timer
  193. updateTimer.setHWND(handle);
  194. updateTimer.setId(TIMER_UPDATE);
  195. // Hook the clipboard
  196. clipboard.setNotifier(this);
  197. // Create the backing buffer
  198. buffer = new win32::ScaledDIBSectionBuffer(frameHandle);
  199. // Show the window
  200. centerWindow(handle, 0);
  201. ShowWindow(handle, SW_SHOW);
  202. }
  203. DesktopWindow::~DesktopWindow() {
  204. vlog.debug("~DesktopWindow");
  205. showSystemCursor();
  206. if (handle) {
  207. disableLowLevelKeyEvents(handle);
  208. DestroyWindow(handle);
  209. handle = 0;
  210. }
  211. delete buffer;
  212. if (cursorImage) delete [] cursorImage;
  213. if (cursorMask) delete [] cursorMask;
  214. vlog.debug("~DesktopWindow done");
  215. }
  216. void DesktopWindow::setFullscreen(bool fs) {
  217. if (fs && !fullscreenActive) {
  218. fullscreenActive = bumpScroll = true;
  219. // Un-minimize the window if required
  220. if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE)
  221. ShowWindow(handle, SW_RESTORE);
  222. // Save the current window position
  223. GetWindowRect(handle, &fullscreenOldRect);
  224. // Find the size of the display the window is on
  225. MonitorInfo mi(handle);
  226. // Hide the toolbar
  227. if (tb.isVisible())
  228. tb.hide();
  229. SetWindowLong(frameHandle, GWL_EXSTYLE, 0);
  230. // Set the window full-screen
  231. DWORD flags = GetWindowLong(handle, GWL_STYLE);
  232. fullscreenOldFlags = flags;
  233. flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
  234. vlog.debug("flags=%x", flags);
  235. SetWindowLong(handle, GWL_STYLE, flags);
  236. SetWindowPos(handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
  237. mi.rcMonitor.right-mi.rcMonitor.left,
  238. mi.rcMonitor.bottom-mi.rcMonitor.top,
  239. SWP_FRAMECHANGED);
  240. } else if (!fs && fullscreenActive) {
  241. fullscreenActive = bumpScroll = false;
  242. // Show the toolbar
  243. if (showToolbar)
  244. tb.show();
  245. SetWindowLong(frameHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE);
  246. // Set the window non-fullscreen
  247. SetWindowLong(handle, GWL_STYLE, fullscreenOldFlags);
  248. // Set the window position
  249. SetWindowPos(handle, HWND_NOTOPMOST,
  250. fullscreenOldRect.left, fullscreenOldRect.top,
  251. fullscreenOldRect.right - fullscreenOldRect.left,
  252. fullscreenOldRect.bottom - fullscreenOldRect.top,
  253. SWP_FRAMECHANGED);
  254. }
  255. // Adjust the viewport offset to cope with change in size between FS
  256. // and previous window state.
  257. setViewportOffset(scrolloffset);
  258. }
  259. void DesktopWindow::setShowToolbar(bool st)
  260. {
  261. showToolbar = st;
  262. if (fullscreenActive) return;
  263. RECT r;
  264. GetWindowRect(handle, &r);
  265. bool maximized = GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE;
  266. if (showToolbar && !tb.isVisible()) {
  267. refreshToolbarButtons();
  268. tb.show();
  269. if (!maximized) r.bottom += tb.getHeight();
  270. } else if (!showToolbar && tb.isVisible()) {
  271. tb.hide();
  272. if (!maximized) r.bottom -= tb.getHeight();
  273. }
  274. // Resize the chiled windows even if the parent window size
  275. // has not been changed (the main window is maximized)
  276. if (maximized) SendMessage(handle, WM_SIZE, 0, 0);
  277. else SetWindowPos(handle, NULL, 0, 0, r.right-r.left, r.bottom-r.top, SWP_NOMOVE | SWP_NOZORDER);
  278. }
  279. void DesktopWindow::refreshToolbarButtons() {
  280. int scale = getDesktopScale();
  281. if (scale <= 10) {
  282. tb.enableButton(ID_ZOOM_IN, true);
  283. tb.enableButton(ID_ZOOM_OUT, false);
  284. } else if (scale >= 200) {
  285. tb.enableButton(ID_ZOOM_IN, false);
  286. tb.enableButton(ID_ZOOM_OUT, true);
  287. } else {
  288. tb.enableButton(ID_ZOOM_IN, true);
  289. tb.enableButton(ID_ZOOM_OUT, true);
  290. }
  291. if (buffer->isScaling() || isAutoScaling()) tb.enableButton(ID_ACTUAL_SIZE, true);
  292. else tb.enableButton(ID_ACTUAL_SIZE, false);
  293. if (isAutoScaling()) tb.pressButton(ID_AUTO_SIZE, true);
  294. else tb.pressButton(ID_AUTO_SIZE, false);
  295. }
  296. void DesktopWindow::setDisableWinKeys(bool dwk) {
  297. // Enable low-level event hooking, so we get special keys directly
  298. if (dwk)
  299. enableLowLevelKeyEvents(handle);
  300. else
  301. disableLowLevelKeyEvents(handle);
  302. }
  303. void DesktopWindow::setMonitor(const char* monitor) {
  304. MonitorInfo mi(monitor);
  305. mi.moveTo(handle);
  306. }
  307. char* DesktopWindow::getMonitor() const {
  308. MonitorInfo mi(handle);
  309. return strDup(mi.szDevice);
  310. }
  311. bool DesktopWindow::setViewportOffset(const Point& tl) {
  312. Point np = Point(__rfbmax(0, __rfbmin(tl.x, buffer->width()-client_size.width())),
  313. __rfbmax(0, __rfbmin(tl.y, buffer->height()-client_size.height())));
  314. Point delta = np.translate(scrolloffset.negate());
  315. if (!np.equals(scrolloffset)) {
  316. scrolloffset = np;
  317. ScrollWindowEx(frameHandle, -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
  318. UpdateWindow(frameHandle);
  319. return true;
  320. }
  321. return false;
  322. }
  323. bool DesktopWindow::processBumpScroll(const Point& pos)
  324. {
  325. if (!bumpScroll) return false;
  326. int bumpScrollPixels = 20;
  327. bumpScrollDelta = Point();
  328. if (pos.x == client_size.width()-1)
  329. bumpScrollDelta.x = bumpScrollPixels;
  330. else if (pos.x == 0)
  331. bumpScrollDelta.x = -bumpScrollPixels;
  332. if (pos.y == client_size.height()-1)
  333. bumpScrollDelta.y = bumpScrollPixels;
  334. else if (pos.y == 0)
  335. bumpScrollDelta.y = -bumpScrollPixels;
  336. if (bumpScrollDelta.x || bumpScrollDelta.y) {
  337. if (bumpScrollTimer.isActive()) return true;
  338. if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
  339. bumpScrollTimer.start(25);
  340. return true;
  341. }
  342. }
  343. bumpScrollTimer.stop();
  344. return false;
  345. }
  346. LRESULT
  347. DesktopWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
  348. switch (msg) {
  349. // -=- Process standard window messages
  350. case WM_NOTIFY:
  351. if (wParam == ID_TOOLBAR)
  352. tb.processWM_NOTIFY(wParam, lParam);
  353. break;
  354. case WM_DISPLAYCHANGE:
  355. // Display format has changed - notify callback
  356. callback->displayChanged();
  357. break;
  358. // -=- Window position
  359. // Prevent the window from being resized to be too large if in normal mode.
  360. // If maximized or fullscreen the allow oversized windows.
  361. case WM_WINDOWPOSCHANGING:
  362. {
  363. WINDOWPOS* wpos = (WINDOWPOS*)lParam;
  364. if ((wpos->flags & SWP_NOSIZE) || isAutoScaling())
  365. break;
  366. // Calculate the minimum size of main window
  367. RECT r;
  368. Rect min_size;
  369. int tbMinWidth = 0, tbMinHeight = 0;
  370. if (isToolbarEnabled()) {
  371. tbMinWidth = tb.getTotalWidth();
  372. tbMinHeight = tb.getHeight();
  373. SetRect(&r, 0, 0, tbMinWidth, tbMinHeight);
  374. AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
  375. min_size = Rect(r.left, r.top, r.right, r.bottom);
  376. }
  377. // Work out how big the window should ideally be
  378. DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE);
  379. DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
  380. DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE);
  381. SetRect(&r, 0, 0, buffer->width(), buffer->height());
  382. AdjustWindowRectEx(&r, style, FALSE, style_ex);
  383. Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
  384. if (current_style & WS_VSCROLL)
  385. reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
  386. if (current_style & WS_HSCROLL)
  387. reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
  388. SetRect(&r, reqd_size.tl.x, reqd_size.tl.y, reqd_size.br.x, reqd_size.br.y);
  389. if (isToolbarEnabled())
  390. r.bottom += tb.getHeight();
  391. AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
  392. reqd_size = Rect(r.left, r.top, r.right, r.bottom);
  393. RECT current;
  394. GetWindowRect(handle, &current);
  395. if (min_size.width() > reqd_size.width()) {
  396. reqd_size.tl.x = min_size.tl.x;
  397. reqd_size.br.x = min_size.br.x;
  398. }
  399. if (min_size.height() > reqd_size.height()) {
  400. reqd_size.tl.y = min_size.tl.y;
  401. reqd_size.br.y = min_size.br.y;
  402. }
  403. if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) {
  404. // Ensure that the window isn't resized too large or too small
  405. if ((wpos->cx < min_size.width()) && isToolbarEnabled()) {
  406. wpos->cx = min_size.width();
  407. wpos->x = current.left;
  408. } else if ((wpos->cx > reqd_size.width())) {
  409. wpos->cx = reqd_size.width();
  410. wpos->x = current.left;
  411. }
  412. if ((wpos->cy < min_size.height()) && isToolbarEnabled()) {
  413. wpos->cy = min_size.height();
  414. wpos->y = current.top;
  415. } else if (wpos->cy > reqd_size.height()) {
  416. wpos->cy = reqd_size.height();
  417. wpos->y = current.top;
  418. }
  419. }
  420. }
  421. break;
  422. // Resize child windows and update window size info we have cached.
  423. case WM_SIZE:
  424. {
  425. Point old_offset = desktopToClient(Point(0, 0));
  426. RECT r;
  427. // Resize child windows
  428. GetClientRect(handle, &r);
  429. if (tb.isVisible()) {
  430. MoveWindow(frameHandle, 0, tb.getHeight(), r.right, r.bottom - tb.getHeight(), TRUE);
  431. } else {
  432. MoveWindow(frameHandle, 0, 0, r.right, r.bottom, TRUE);
  433. }
  434. tb.autoSize();
  435. // Update the cached sizing information
  436. GetWindowRect(frameHandle, &r);
  437. window_size = Rect(r.left, r.top, r.right, r.bottom);
  438. GetClientRect(frameHandle, &r);
  439. client_size = Rect(r.left, r.top, r.right, r.bottom);
  440. // Perform the AutoScaling operation
  441. if (isAutoScaling()) {
  442. fitBufferToWindow(false);
  443. } else {
  444. // Determine whether scrollbars are required
  445. calculateScrollBars();
  446. }
  447. // Redraw if required
  448. if ((!old_offset.equals(desktopToClient(Point(0, 0)))) || isAutoScaling())
  449. InvalidateRect(frameHandle, 0, TRUE);
  450. }
  451. break;
  452. // -=- Bump-scrolling
  453. case WM_TIMER:
  454. switch (wParam) {
  455. case TIMER_BUMPSCROLL:
  456. if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
  457. bumpScrollTimer.stop();
  458. break;
  459. case TIMER_POINTER_INTERVAL:
  460. case TIMER_POINTER_3BUTTON:
  461. ptr.handleTimer(callback, wParam);
  462. break;
  463. case TIMER_UPDATE:
  464. updateWindow();
  465. break;
  466. }
  467. break;
  468. // -=- Track whether or not the window has focus
  469. case WM_SETFOCUS:
  470. has_focus = true;
  471. break;
  472. case WM_KILLFOCUS:
  473. has_focus = false;
  474. cursorOutsideBuffer();
  475. // Restore the keyboard to a consistent state
  476. kbd.releaseAllKeys(callback);
  477. break;
  478. // -=- If the menu is about to be shown, make sure it's up to date
  479. case WM_INITMENU:
  480. callback->refreshMenu(true);
  481. break;
  482. // -=- Handle the extra window menu items
  483. // Pass system menu messages to the callback and only attempt
  484. // to process them ourselves if the callback returns false.
  485. case WM_SYSCOMMAND:
  486. // Call the supplied callback
  487. if (callback->sysCommand(wParam, lParam))
  488. break;
  489. // - Not processed by the callback, so process it as a system message
  490. switch (wParam & 0xfff0) {
  491. // When restored, ensure that full-screen mode is re-enabled if required.
  492. case SC_RESTORE:
  493. {
  494. if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) {
  495. rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
  496. setFullscreen(fullscreenRestore);
  497. }
  498. else if (fullscreenActive)
  499. setFullscreen(false);
  500. else
  501. rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
  502. return 0;
  503. }
  504. // If we are maximized or minimized then that cancels full-screen mode.
  505. case SC_MINIMIZE:
  506. case SC_MAXIMIZE:
  507. fullscreenRestore = fullscreenActive;
  508. setFullscreen(false);
  509. break;
  510. }
  511. break;
  512. // Treat all menu commands as system menu commands
  513. case WM_COMMAND:
  514. SendMessage(handle, WM_SYSCOMMAND, wParam, lParam);
  515. return 0;
  516. // -=- Handle keyboard input
  517. case WM_KEYUP:
  518. case WM_KEYDOWN:
  519. // Hook the MenuKey to pop-up the window menu
  520. if (menuKey && (wParam == menuKey)) {
  521. bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
  522. bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
  523. bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
  524. if (!(ctrlDown || altDown || shiftDown)) {
  525. // If MenuKey is being released then pop-up the menu
  526. if ((msg == WM_KEYDOWN)) {
  527. // Make sure it's up to date
  528. //
  529. // NOTE: Here we call refreshMenu only to grey out Move and Size
  530. // menu items. Other things will be refreshed once again
  531. // while processing the WM_INITMENU message.
  532. //
  533. callback->refreshMenu(false);
  534. // Show it under the pointer
  535. POINT pt;
  536. GetCursorPos(&pt);
  537. cursorInBuffer = false;
  538. TrackPopupMenu(GetSystemMenu(handle, FALSE),
  539. TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, handle, 0);
  540. }
  541. // Ignore the MenuKey keypress for both press & release events
  542. return 0;
  543. }
  544. }
  545. case WM_SYSKEYDOWN:
  546. case WM_SYSKEYUP:
  547. kbd.keyEvent(callback, wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
  548. return 0;
  549. // -=- Handle mouse wheel scroll events
  550. #ifdef WM_MOUSEWHEEL
  551. case WM_MOUSEWHEEL:
  552. processMouseMessage(msg, wParam, lParam);
  553. break;
  554. #endif
  555. // -=- Handle the window closing
  556. case WM_CLOSE:
  557. vlog.debug("WM_CLOSE %x", handle);
  558. callback->closeWindow();
  559. break;
  560. }
  561. return rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
  562. }
  563. LRESULT
  564. DesktopWindow::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
  565. switch (msg) {
  566. // -=- Paint the remote frame buffer
  567. case WM_PAINT:
  568. {
  569. PAINTSTRUCT ps;
  570. HDC paintDC = BeginPaint(frameHandle, &ps);
  571. if (!paintDC)
  572. throw rdr::SystemException("unable to BeginPaint", GetLastError());
  573. Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
  574. if (!pr.is_empty()) {
  575. // Draw using the correct palette
  576. PaletteSelector pSel(paintDC, windowPalette.getHandle());
  577. if (buffer->bitmap) {
  578. // Update the bitmap's palette
  579. if (palette_changed) {
  580. palette_changed = false;
  581. buffer->refreshPalette();
  582. }
  583. // Get device context
  584. BitmapDC bitmapDC(paintDC, buffer->bitmap);
  585. // Blit the border if required
  586. Rect bufpos = desktopToClient(buffer->getRect());
  587. if (!pr.enclosed_by(bufpos)) {
  588. vlog.debug("draw border");
  589. HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
  590. RECT r;
  591. SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
  592. SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
  593. SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
  594. SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
  595. }
  596. // Do the blit
  597. Point buf_pos = clientToDesktop(pr.tl);
  598. if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
  599. bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
  600. throw rdr::SystemException("unable to BitBlt to window", GetLastError());
  601. }
  602. }
  603. EndPaint(frameHandle, &ps);
  604. }
  605. return 0;
  606. // -=- Palette management
  607. case WM_PALETTECHANGED:
  608. vlog.debug("WM_PALETTECHANGED");
  609. if ((HWND)wParam == frameHandle) {
  610. vlog.debug("ignoring");
  611. break;
  612. }
  613. case WM_QUERYNEWPALETTE:
  614. vlog.debug("re-selecting palette");
  615. {
  616. WindowDC wdc(frameHandle);
  617. PaletteSelector pSel(wdc, windowPalette.getHandle());
  618. if (pSel.isRedrawRequired()) {
  619. InvalidateRect(frameHandle, 0, FALSE);
  620. UpdateWindow(frameHandle);
  621. }
  622. }
  623. return TRUE;
  624. case WM_VSCROLL:
  625. case WM_HSCROLL:
  626. {
  627. Point delta;
  628. int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
  629. switch (LOWORD(wParam)) {
  630. case SB_PAGEUP: newpos -= 50; break;
  631. case SB_PAGEDOWN: newpos += 50; break;
  632. case SB_LINEUP: newpos -= 5; break;
  633. case SB_LINEDOWN: newpos += 5; break;
  634. case SB_THUMBTRACK:
  635. case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
  636. default: vlog.info("received unknown scroll message");
  637. };
  638. if (msg == WM_HSCROLL)
  639. setViewportOffset(Point(newpos, scrolloffset.y));
  640. else
  641. setViewportOffset(Point(scrolloffset.x, newpos));
  642. SCROLLINFO si;
  643. si.cbSize = sizeof(si);
  644. si.fMask = SIF_POS;
  645. si.nPos = newpos;
  646. SetScrollInfo(frameHandle, (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE);
  647. }
  648. break;
  649. // -=- Cursor shape/visibility handling
  650. case WM_SETCURSOR:
  651. if (LOWORD(lParam) != HTCLIENT)
  652. break;
  653. SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
  654. return TRUE;
  655. case WM_MOUSELEAVE:
  656. trackingMouseLeave = false;
  657. cursorOutsideBuffer();
  658. return 0;
  659. // -=- Mouse input handling
  660. case WM_MOUSEMOVE:
  661. case WM_LBUTTONUP:
  662. case WM_MBUTTONUP:
  663. case WM_RBUTTONUP:
  664. case WM_LBUTTONDOWN:
  665. case WM_MBUTTONDOWN:
  666. case WM_RBUTTONDOWN:
  667. processMouseMessage(msg, wParam, lParam);
  668. break;
  669. }
  670. return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam);
  671. }
  672. void
  673. DesktopWindow::processMouseMessage(UINT msg, WPARAM wParam, LPARAM lParam)
  674. {
  675. if (!has_focus) {
  676. cursorOutsideBuffer();
  677. return;
  678. }
  679. if (!trackingMouseLeave) {
  680. TRACKMOUSEEVENT tme;
  681. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  682. tme.dwFlags = TME_LEAVE;
  683. tme.hwndTrack = frameHandle;
  684. _TrackMouseEvent(&tme);
  685. trackingMouseLeave = true;
  686. }
  687. int mask = 0;
  688. if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
  689. if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
  690. if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
  691. #ifdef WM_MOUSEWHEEL
  692. if (msg == WM_MOUSEWHEEL) {
  693. int delta = (short)HIWORD(wParam);
  694. int repeats = (abs(delta)+119) / 120;
  695. int wheelMask = (delta > 0) ? 8 : 16;
  696. vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
  697. for (int i=0; i<repeats; i++) {
  698. ptr.pointerEvent(callback, oldpos, mask | wheelMask);
  699. ptr.pointerEvent(callback, oldpos, mask);
  700. }
  701. } else {
  702. #endif
  703. Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
  704. Point p = clientToDesktop(clientPos);
  705. // If the mouse is not within the server buffer area, do nothing
  706. cursorInBuffer = buffer->getRect().contains(p);
  707. if (!cursorInBuffer) {
  708. cursorOutsideBuffer();
  709. return;
  710. }
  711. // If we're locally rendering the cursor then redraw it
  712. if (cursorAvailable) {
  713. // - Render the cursor!
  714. if (!p.equals(cursorPos)) {
  715. hideLocalCursor();
  716. cursorPos = p;
  717. showLocalCursor();
  718. if (cursorVisible)
  719. hideSystemCursor();
  720. }
  721. }
  722. // If we are doing bump-scrolling then try that first...
  723. if (processBumpScroll(clientPos))
  724. return;
  725. // Send a pointer event to the server
  726. oldpos = p;
  727. if (buffer->isScaling()) {
  728. p.x /= buffer->getScaleRatioX();
  729. p.y /= buffer->getScaleRatioY();
  730. }
  731. ptr.pointerEvent(callback, p, mask);
  732. #ifdef WM_MOUSEWHEEL
  733. }
  734. #endif
  735. }
  736. void DesktopWindow::updateWindow()
  737. {
  738. Rect rect;
  739. updateTimer.stop();
  740. rect = damage.get_bounding_rect();
  741. damage.clear();
  742. RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
  743. InvalidateRect(frameHandle, &invalid, FALSE);
  744. }
  745. void
  746. DesktopWindow::hideLocalCursor() {
  747. // - Blit the cursor backing store over the cursor
  748. // *** ALWAYS call this BEFORE changing buffer PF!!!
  749. if (cursorVisible) {
  750. cursorVisible = false;
  751. buffer->DIBSectionBuffer::imageRect(cursorBackingRect, cursorBacking.data);
  752. invalidateDesktopRect(cursorBackingRect, false);
  753. }
  754. }
  755. void
  756. DesktopWindow::showLocalCursor() {
  757. if (cursorAvailable && !cursorVisible && cursorInBuffer) {
  758. if (!buffer->getScaledPixelFormat().equal(cursor.getPF()) ||
  759. cursor.getRect().is_empty()) {
  760. vlog.info("attempting to render invalid local cursor");
  761. cursorAvailable = false;
  762. showSystemCursor();
  763. return;
  764. }
  765. cursorVisible = true;
  766. cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
  767. cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
  768. buffer->getImage(cursorBacking.data, cursorBackingRect);
  769. renderLocalCursor();
  770. invalidateDesktopRect(cursorBackingRect, false);
  771. // Since we render the cursor onto the framebuffer, we need to update
  772. // right away to get a responsive cursor.
  773. updateWindow();
  774. }
  775. }
  776. void DesktopWindow::cursorOutsideBuffer()
  777. {
  778. cursorInBuffer = false;
  779. hideLocalCursor();
  780. showSystemCursor();
  781. }
  782. void
  783. DesktopWindow::renderLocalCursor()
  784. {
  785. Rect r = cursor.getRect();
  786. r = r.translate(cursorPos).translate(cursor.hotspot.negate());
  787. buffer->DIBSectionBuffer::maskRect(r, cursor.data, cursor.mask.buf);
  788. }
  789. void
  790. DesktopWindow::hideSystemCursor() {
  791. if (systemCursorVisible) {
  792. vlog.debug("hide system cursor");
  793. systemCursorVisible = false;
  794. ShowCursor(FALSE);
  795. }
  796. }
  797. void
  798. DesktopWindow::showSystemCursor() {
  799. if (!systemCursorVisible) {
  800. vlog.debug("show system cursor");
  801. systemCursorVisible = true;
  802. ShowCursor(TRUE);
  803. }
  804. }
  805. bool
  806. DesktopWindow::invalidateDesktopRect(const Rect& crect, bool scaling) {
  807. Rect rect;
  808. if (buffer->isScaling() && scaling) {
  809. rect = desktopToClient(buffer->calculateScaleBoundary(crect));
  810. } else rect = desktopToClient(crect);
  811. if (rect.intersect(client_size).is_empty()) return false;
  812. damage.assign_union(rfb::Region(rect));
  813. if (!updateTimer.isActive())
  814. updateTimer.start(100);
  815. return true;
  816. }
  817. void
  818. DesktopWindow::notifyClipboardChanged(const char* text, int len) {
  819. callback->clientCutText(text, len);
  820. }
  821. void
  822. DesktopWindow::setPF(const PixelFormat& pf) {
  823. // If the cursor is the wrong format then clear it
  824. if (!pf.equal(buffer->getScaledPixelFormat()))
  825. setCursor(0, 0, Point(), 0, 0);
  826. // Update the desktop buffer
  827. buffer->setPF(pf);
  828. // Redraw the window
  829. InvalidateRect(frameHandle, 0, FALSE);
  830. }
  831. void
  832. DesktopWindow::setSize(int w, int h) {
  833. vlog.debug("setSize %dx%d", w, h);
  834. // If the locally-rendered cursor is visible then remove it
  835. hideLocalCursor();
  836. // Resize the backing buffer
  837. buffer->setSize(w, h);
  838. // Calculate the pixel buffer aspect correlation. It's used
  839. // for the autoScaling operation.
  840. aspect_corr = (double)w / h;
  841. // If the window is not maximised or full-screen then resize it
  842. if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) {
  843. // Resize the window to the required size
  844. RECT r = {0, 0, w, h};
  845. AdjustWindowRectEx(&r, GetWindowLong(frameHandle, GWL_STYLE), FALSE,
  846. GetWindowLong(frameHandle, GWL_EXSTYLE));
  847. if (isToolbarEnabled())
  848. r.bottom += tb.getHeight();
  849. AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
  850. // Resize about the center of the window, and clip to current monitor
  851. MonitorInfo mi(handle);
  852. resizeWindow(handle, r.right-r.left, r.bottom-r.top);
  853. mi.clipTo(handle);
  854. } else {
  855. // Ensure the screen contents are consistent
  856. InvalidateRect(frameHandle, 0, FALSE);
  857. }
  858. // Enable/disable scrollbars as appropriate
  859. calculateScrollBars();
  860. }
  861. void DesktopWindow::setAutoScaling(bool as) {
  862. autoScaling = as;
  863. if (isToolbarEnabled()) refreshToolbarButtons();
  864. if (as) fitBufferToWindow();
  865. }
  866. void DesktopWindow::setDesktopScale(int scale_) {
  867. if (buffer->getScale() == scale_ || scale_ <= 0) return;
  868. bool state = buffer->isScaling();
  869. buffer->setScale(scale_);
  870. state ^= buffer->isScaling();
  871. if (state) convertCursorToBuffer();
  872. if (isToolbarEnabled()) refreshToolbarButtons();
  873. if (!(isAutoScaling() || isFullscreen() || (GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE))) resizeDesktopWindowToBuffer();
  874. printScale();
  875. InvalidateRect(frameHandle, 0, FALSE);
  876. }
  877. void DesktopWindow::setDesktopScaleFilter(unsigned int scaleFilterID) {
  878. if (scaleFilterID == getDesktopScaleFilterID() || scaleFilterID > scaleFilterMaxNumber) return;
  879. buffer->setScaleFilter(scaleFilterID);
  880. InvalidateRect(frameHandle, 0, FALSE);
  881. }
  882. void DesktopWindow::convertCursorToBuffer() {
  883. if (memcmp(&(cursor.getPF()), &(buffer->getPF()), sizeof(PixelBuffer)) == 0) return;
  884. internalSetCursor = true;
  885. setCursor(cursorWidth, cursorHeight, cursorHotspot, cursorImage, cursorMask);
  886. internalSetCursor = false;
  887. }
  888. void DesktopWindow::fitBufferToWindow(bool repaint) {
  889. double scale_ratio;
  890. double resized_aspect_corr = double(client_size.width()) / client_size.height();
  891. DWORD style = GetWindowLong(frameHandle, GWL_STYLE);
  892. if (style & (WS_VSCROLL | WS_HSCROLL)) {
  893. style &= ~(WS_VSCROLL | WS_HSCROLL);
  894. SetWindowLong(frameHandle, GWL_STYLE, style);
  895. SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  896. // Update the cached client size
  897. RECT r;
  898. GetClientRect(frameHandle, &r);
  899. client_size = Rect(r.left, r.top, r.right, r.bottom);
  900. }
  901. bool state = buffer->isScaling();
  902. if (resized_aspect_corr > aspect_corr) {
  903. scale_ratio = (double)client_size.height() / buffer->getSrcHeight();
  904. buffer->setScaleWindowSize(ceil(buffer->getSrcWidth()*scale_ratio), client_size.height());
  905. } else {
  906. scale_ratio = (double)client_size.width() / buffer->getSrcWidth();
  907. buffer->setScaleWindowSize(client_size.width(), ceil(buffer->getSrcHeight()*scale_ratio));
  908. }
  909. state ^= buffer->isScaling();
  910. if (state) convertCursorToBuffer();
  911. printScale();
  912. InvalidateRect(frameHandle, 0, FALSE);
  913. }
  914. void DesktopWindow::printScale() {
  915. setName(desktopName);
  916. }
  917. void
  918. DesktopWindow::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
  919. hideLocalCursor();
  920. cursor.hotspot = hotspot;
  921. cursor.setSize(w, h);
  922. cursor.setPF(buffer->getScaledPixelFormat());
  923. // Convert the current cursor pixel format to bpp32 if scaling mode is on.
  924. // It need because ScaledDIBSection buffer always works with bpp32 pixel data
  925. // in scaling mode.
  926. if (buffer->isScaling()) {
  927. U8 *ptr = (U8*)cursor.data;
  928. U8 *dataPtr = (U8*)data;
  929. U32 pixel = 0;
  930. int bytesPerPixel = buffer->getPixelFormat().bpp / 8;
  931. int pixelCount = w * h;
  932. PixelFormat pf = buffer->getPixelFormat();
  933. while (pixelCount--) {
  934. if (bytesPerPixel == 1) {
  935. pixel = *dataPtr++;
  936. } else if (bytesPerPixel == 2) {
  937. int b0 = *dataPtr++; int b1 = *dataPtr++;
  938. pixel = b1 << 8 | b0;
  939. } else if (bytesPerPixel == 4) {
  940. int b0 = *dataPtr++; int b1 = *dataPtr++;
  941. int b2 = *dataPtr++; int b3 = *dataPtr++;
  942. pixel = b3 << 24 | b2 << 16 | b1 << 8 | b0;
  943. } else {
  944. pixel = 0;
  945. }
  946. *ptr++ = (U8)((((pixel >> pf.blueShift ) & pf.blueMax ) * 255 + pf.blueMax /2) / pf.blueMax);
  947. *ptr++ = (U8)((((pixel >> pf.greenShift) & pf.greenMax) * 255 + pf.greenMax/2) / pf.greenMax);
  948. *ptr++ = (U8)((((pixel >> pf.redShift ) & pf.redMax ) * 255 + pf.redMax /2) / pf.redMax);
  949. *ptr++ = (U8)0;
  950. }
  951. } else {
  952. cursor.imageRect(cursor.getRect(), data);
  953. }
  954. memcpy(cursor.mask.buf, mask, cursor.maskLen());
  955. cursor.crop();
  956. cursorBacking.setSize(w, h);
  957. cursorBacking.setPF(buffer->getScaledPixelFormat());
  958. cursorAvailable = true;
  959. showLocalCursor();
  960. // Save the cursor parameters
  961. if (!internalSetCursor) {
  962. if (cursorImage) delete [] cursorImage;
  963. if (cursorMask) delete [] cursorMask;
  964. int cursorImageSize = (buffer->getPixelFormat().bpp/8) * w * h;
  965. cursorImage = new U8[cursorImageSize];
  966. cursorMask = new U8[cursor.maskLen()];
  967. memcpy(cursorImage, data, cursorImageSize);
  968. memcpy(cursorMask, mask, cursor.maskLen());
  969. cursorWidth = w;
  970. cursorHeight = h;
  971. cursorHotspot = hotspot;
  972. }
  973. }
  974. PixelFormat
  975. DesktopWindow::getNativePF() const {
  976. vlog.debug("getNativePF()");
  977. return WindowDC(handle).getPF();
  978. }
  979. void
  980. DesktopWindow::refreshWindowPalette(int start, int count) {
  981. vlog.debug("refreshWindowPalette(%d, %d)", start, count);
  982. Colour colours[256];
  983. if (count > 256) {
  984. vlog.debug("%d palette entries", count);
  985. throw rdr::Exception("too many palette entries");
  986. }
  987. // Copy the palette from the DIBSectionBuffer
  988. ColourMap* cm = buffer->getColourMap();
  989. if (!cm) return;
  990. for (int i=0; i<count; i++) {
  991. int r, g, b;
  992. cm->lookup(i, &r, &g, &b);
  993. colours[i].r = r;
  994. colours[i].g = g;
  995. colours[i].b = b;
  996. }
  997. // Set the window palette
  998. windowPalette.setEntries(start, count, colours);
  999. // Cause the window to be redrawn
  1000. palette_changed = true;
  1001. InvalidateRect(handle, 0, FALSE);
  1002. }
  1003. void DesktopWindow::calculateScrollBars() {
  1004. // Calculate the required size of window
  1005. DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE);
  1006. DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
  1007. DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE);
  1008. DWORD old_style;
  1009. RECT r;
  1010. SetRect(&r, 0, 0, buffer->width(), buffer->height());
  1011. AdjustWindowRectEx(&r, style, FALSE, style_ex);
  1012. Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
  1013. if (!bumpScroll) {
  1014. // We only enable scrollbars if bump-scrolling is not active.
  1015. // Effectively, this means if full-screen is not active,
  1016. // but I think it's better to make these things explicit.
  1017. // Work out whether scroll bars are required
  1018. do {
  1019. old_style = style;
  1020. if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
  1021. style |= WS_HSCROLL;
  1022. reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
  1023. }
  1024. if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
  1025. style |= WS_VSCROLL;
  1026. reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
  1027. }
  1028. } while (style != old_style);
  1029. }
  1030. // Tell Windows to update the window style & cached settings
  1031. if (style != current_style) {
  1032. SetWindowLong(frameHandle, GWL_STYLE, style);
  1033. SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  1034. }
  1035. // Update the scroll settings
  1036. SCROLLINFO si;
  1037. if (style & WS_VSCROLL) {
  1038. si.cbSize = sizeof(si);
  1039. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  1040. si.nMin = 0;
  1041. si.nMax = buffer->height();
  1042. si.nPage = buffer->height() - (reqd_size.height() - window_size.height());
  1043. maxscrolloffset.y = __rfbmax(0, si.nMax-si.nPage);
  1044. scrolloffset.y = __rfbmin(maxscrolloffset.y, scrolloffset.y);
  1045. si.nPos = scrolloffset.y;
  1046. SetScrollInfo(frameHandle, SB_VERT, &si, TRUE);
  1047. }
  1048. if (style & WS_HSCROLL) {
  1049. si.cbSize = sizeof(si);
  1050. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  1051. si.nMin = 0;
  1052. si.nMax = buffer->width();
  1053. si.nPage = buffer->width() - (reqd_size.width() - window_size.width());
  1054. maxscrolloffset.x = __rfbmax(0, si.nMax-si.nPage);
  1055. scrolloffset.x = __rfbmin(maxscrolloffset.x, scrolloffset.x);
  1056. si.nPos = scrolloffset.x;
  1057. SetScrollInfo(frameHandle, SB_HORZ, &si, TRUE);
  1058. }
  1059. // Update the cached client size
  1060. GetClientRect(frameHandle, &r);
  1061. client_size = Rect(r.left, r.top, r.right, r.bottom);
  1062. }
  1063. void DesktopWindow::resizeDesktopWindowToBuffer() {
  1064. RECT r;
  1065. DWORD style = GetWindowLong(frameHandle, GWL_STYLE) & ~(WS_VSCROLL | WS_HSCROLL);
  1066. DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE);
  1067. // Calculate the required size of the desktop window
  1068. SetRect(&r, 0, 0, buffer->width(), buffer->height());
  1069. AdjustWindowRectEx(&r, style, FALSE, style_ex);
  1070. if (isToolbarEnabled())
  1071. r.bottom += tb.getHeight();
  1072. AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
  1073. // Set the required size, center the main window and clip to the current monitor
  1074. SetWindowPos(handle, 0, 0, 0, r.right-r.left, r.bottom-r.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
  1075. centerWindow(handle, NULL);
  1076. MonitorInfo mi(getMonitor());
  1077. mi.clipTo(handle);
  1078. // Enable/disable scrollbars as appropriate
  1079. calculateScrollBars();
  1080. }
  1081. void DesktopWindow::framebufferUpdateEnd()
  1082. {
  1083. updateWindow();
  1084. }
  1085. void
  1086. DesktopWindow::setName(const char* name) {
  1087. if (name != desktopName) {
  1088. strCopy(desktopName, name, sizeof(desktopName));
  1089. }
  1090. char *newTitle = new char[strlen(desktopName)+20];
  1091. sprintf(newTitle, "TigerVNC: %.240s @ %i%%", desktopName, getDesktopScale());
  1092. SetWindowText(handle, TStr(newTitle));
  1093. delete [] newTitle;
  1094. }
  1095. void
  1096. DesktopWindow::serverCutText(const char* str, rdr::U32 len) {
  1097. CharArray t(len+1);
  1098. memcpy(t.buf, str, len);
  1099. t.buf[len] = 0;
  1100. clipboard.setClipText(t.buf);
  1101. }
  1102. void DesktopWindow::fillRect(const Rect& r, Pixel pix) {
  1103. Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r;
  1104. if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor();
  1105. buffer->fillRect(r, pix);
  1106. invalidateDesktopRect(r);
  1107. }
  1108. void DesktopWindow::imageRect(const Rect& r, void* pixels) {
  1109. Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r;
  1110. if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor();
  1111. buffer->imageRect(r, pixels);
  1112. invalidateDesktopRect(r);
  1113. }
  1114. void DesktopWindow::copyRect(const Rect& r, int srcX, int srcY) {
  1115. Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r;
  1116. if (cursorBackingRect.overlaps(img_rect) ||
  1117. cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+img_rect.width(), srcY+img_rect.height())))
  1118. hideLocalCursor();
  1119. buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
  1120. invalidateDesktopRect(r);
  1121. }
  1122. void DesktopWindow::invertRect(const Rect& r) {
  1123. int stride;
  1124. rdr::U8* p = buffer->isScaling() ? buffer->getPixelsRW(buffer->calculateScaleBoundary(r), &stride)
  1125. : buffer->getPixelsRW(r, &stride);
  1126. for (int y = 0; y < r.height(); y++) {
  1127. for (int x = 0; x < r.width(); x++) {
  1128. switch (buffer->getPF().bpp) {
  1129. case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break;
  1130. case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break;
  1131. case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
  1132. }
  1133. }
  1134. }
  1135. invalidateDesktopRect(r);
  1136. }