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