diff options
Diffstat (limited to 'win/vncviewer/DesktopWindow.cxx')
-rw-r--r-- | win/vncviewer/DesktopWindow.cxx | 1350 |
1 files changed, 0 insertions, 1350 deletions
diff --git a/win/vncviewer/DesktopWindow.cxx b/win/vncviewer/DesktopWindow.cxx deleted file mode 100644 index fee0a1b0..00000000 --- a/win/vncviewer/DesktopWindow.cxx +++ /dev/null @@ -1,1350 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2010-2011 D. R. Commander. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include <windows.h> -#include <commctrl.h> -#include <math.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> -#include <rfb_win32/WMShatter.h> -#include <rfb_win32/LowLevelKeyEvents.h> -#include <rfb_win32/MonitorInfo.h> -#include <rfb_win32/DeviceContext.h> -#include <rfb_win32/Win32Util.h> -#include <rfb_win32/MsgBox.h> -#include <rfb/util.h> -#include <vncviewer/DesktopWindow.h> -#include <vncviewer/resource.h> - -using namespace rfb; -using namespace rfb::win32; - - -// - Statics & consts - -static LogWriter vlog("DesktopWindow"); - -const int TIMER_BUMPSCROLL = 1; -const int TIMER_POINTER_INTERVAL = 2; -const int TIMER_POINTER_3BUTTON = 3; -const int TIMER_UPDATE = 4; - - -// -// -=- DesktopWindowClass - -// -// Window class used as the basis for all DesktopWindow instances -// - -class DesktopWindowClass { -public: - DesktopWindowClass(); - ~DesktopWindowClass(); - ATOM classAtom; - HINSTANCE instance; -}; - -LRESULT CALLBACK DesktopWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { - LRESULT result = 0; - if (msg == WM_CREATE) - SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLongPtr(wnd, GWLP_USERDATA, 0); - DesktopWindow* _this = (DesktopWindow*) GetWindowLongPtr(wnd, GWLP_USERDATA); - if (!_this) { - vlog.info("null _this in %x, message %u", wnd, msg); - return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam); - } - - try { - result = _this->processMessage(msg, wParam, lParam); - } catch (rfb::UnsupportedPixelFormatException &e) { - MsgBox(0, (TCHAR *) e.str(), MB_OK | MB_ICONINFORMATION); - _this->getCallback()->closeWindow(); - } catch (rdr::Exception& e) { - vlog.error("untrapped: %s", e.str()); - } - - return result; -}; - -static HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED); -static HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); - -DesktopWindowClass::DesktopWindowClass() : classAtom(0) { - WNDCLASS wndClass; - wndClass.style = 0; - wndClass.lpfnWndProc = DesktopWindowProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = instance = GetModuleHandle(0); - wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); - if (!wndClass.hIcon) - printf("unable to load icon:%ld", GetLastError()); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.hbrBackground = NULL; - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = _T("rfb::win32::DesktopWindowClass"); - classAtom = RegisterClass(&wndClass); - if (!classAtom) { - throw rdr::SystemException("unable to register DesktopWindow window class", GetLastError()); - } -} - -DesktopWindowClass::~DesktopWindowClass() { - if (classAtom) { - UnregisterClass((const TCHAR*)classAtom, instance); - } -} - -static DesktopWindowClass baseClass; - -// -// -=- FrameClass - -// -// Window class used for child windows that display pixel data -// - -class FrameClass { -public: - FrameClass(); - ~FrameClass(); - ATOM classAtom; - HINSTANCE instance; -}; - -LRESULT CALLBACK FrameProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { - LRESULT result = 0; - if (msg == WM_CREATE) - SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLongPtr(wnd, GWLP_USERDATA, 0); - DesktopWindow* _this = (DesktopWindow*) GetWindowLongPtr(wnd, GWLP_USERDATA); - if (!_this) { - vlog.info("null _this in %x, message %u", wnd, msg); - return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam); - } - - try { - result = _this->processFrameMessage(msg, wParam, lParam); - } catch (rdr::Exception& e) { - vlog.error("untrapped: %s", e.str()); - } - - return result; -} - -FrameClass::FrameClass() : classAtom(0) { - WNDCLASS wndClass; - wndClass.style = 0; - wndClass.lpfnWndProc = FrameProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = instance = GetModuleHandle(0); - wndClass.hIcon = 0; - wndClass.hCursor = NULL; - wndClass.hbrBackground = NULL; - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = _T("rfb::win32::FrameClass"); - classAtom = RegisterClass(&wndClass); - if (!classAtom) { - throw rdr::SystemException("unable to register Frame window class", GetLastError()); - } -} - -FrameClass::~FrameClass() { - if (classAtom) { - UnregisterClass((const TCHAR*)classAtom, instance); - } -} - -FrameClass frameClass; - - -// -// -=- DesktopWindow instance implementation -// - -DesktopWindow::DesktopWindow(Callback* cb) - : bumpScroll(false), palette_changed(false), fullscreenActive(false), - fullscreenRestore(false), systemCursorVisible(true), trackingMouseLeave(false), - cursorInBuffer(false), cursorVisible(false), cursorAvailable(false), - internalSetCursor(false), cursorImage(0), cursorMask(0), - cursorWidth(0), cursorHeight(0), showToolbar(false), - buffer(0), has_focus(false), autoScaling(false), - window_size(0, 0, 32, 32), client_size(0, 0, 16, 16), handle(0), - frameHandle(0), callback(cb) { - - // Create the window - const char* name = "DesktopWindow"; - handle = CreateWindow((const TCHAR*)baseClass.classAtom, TStr(name), - WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - 0, 0, 10, 10, 0, 0, baseClass.instance, this); - if (!handle) - throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); - vlog.debug("created window \"%s\" (%x)", name, handle); - - // Create the toolbar - tb.create(handle); - vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle()); - - // Create the frame window - frameHandle = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, - 0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, handle, 0, frameClass.instance, this); - if (!frameHandle) { - throw rdr::SystemException("unable to create rfb frame window instance", GetLastError()); - } - vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHandle); - - // Initialise the CPointer pointer handler - ptr.setHWND(frameHandle); - ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL); - ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON); - - // Initialise the bumpscroll timer - bumpScrollTimer.setHWND(handle); - bumpScrollTimer.setId(TIMER_BUMPSCROLL); - - // Initialise the update timer - updateTimer.setHWND(handle); - updateTimer.setId(TIMER_UPDATE); - - // Hook the clipboard - clipboard.setNotifier(this); - - // Create the backing buffer - buffer = new win32::ScaledDIBSectionBuffer(frameHandle); - - // Show the window - centerWindow(handle, 0); - ShowWindow(handle, SW_SHOW); -} - -DesktopWindow::~DesktopWindow() { - vlog.debug("~DesktopWindow"); - showSystemCursor(); - if (handle) { - disableLowLevelKeyEvents(handle); - DestroyWindow(handle); - handle = 0; - } - delete buffer; - if (cursorImage) delete [] cursorImage; - if (cursorMask) delete [] cursorMask; - vlog.debug("~DesktopWindow done"); -} - - -void DesktopWindow::setFullscreen(bool fs) { - if (fs && !fullscreenActive) { - fullscreenActive = bumpScroll = true; - - // Un-minimize the window if required - if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) - ShowWindow(handle, SW_RESTORE); - - // Save the current window position - GetWindowRect(handle, &fullscreenOldRect); - - // Find the size of the virtual display - int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN); - int cy = GetSystemMetrics(SM_CYVIRTUALSCREEN); - int x = GetSystemMetrics(SM_XVIRTUALSCREEN); - int y = GetSystemMetrics(SM_YVIRTUALSCREEN); - - // Hide the toolbar - if (tb.isVisible()) - tb.hide(); - SetWindowLong(frameHandle, GWL_EXSTYLE, 0); - - // Set the window full-screen - DWORD flags = GetWindowLong(handle, GWL_STYLE); - fullscreenOldFlags = flags; - flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE); - vlog.debug("flags=%x", flags); - - SetWindowLong(handle, GWL_STYLE, flags); - SetWindowPos(handle, HWND_TOP, x, y, cx, cy, SWP_FRAMECHANGED); - } else if (!fs && fullscreenActive) { - fullscreenActive = bumpScroll = false; - - // Show the toolbar - if (showToolbar) - tb.show(); - SetWindowLong(frameHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE); - - // Set the window non-fullscreen - SetWindowLong(handle, GWL_STYLE, fullscreenOldFlags); - - // Set the window position - SetWindowPos(handle, HWND_NOTOPMOST, - fullscreenOldRect.left, fullscreenOldRect.top, - fullscreenOldRect.right - fullscreenOldRect.left, - fullscreenOldRect.bottom - fullscreenOldRect.top, - SWP_FRAMECHANGED); - } - - // Adjust the viewport offset to cope with change in size between FS - // and previous window state. - setViewportOffset(scrolloffset); -} - -void DesktopWindow::setShowToolbar(bool st) -{ - showToolbar = st; - if (fullscreenActive) return; - - RECT r; - GetWindowRect(handle, &r); - bool maximized = GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE; - - if (showToolbar && !tb.isVisible()) { - refreshToolbarButtons(); - tb.show(); - if (!maximized) r.bottom += tb.getHeight(); - } else if (!showToolbar && tb.isVisible()) { - tb.hide(); - if (!maximized) r.bottom -= tb.getHeight(); - } - // Resize the chiled windows even if the parent window size - // has not been changed (the main window is maximized) - if (maximized) SendMessage(handle, WM_SIZE, 0, 0); - else SetWindowPos(handle, NULL, 0, 0, r.right-r.left, r.bottom-r.top, SWP_NOMOVE | SWP_NOZORDER); -} - -void DesktopWindow::refreshToolbarButtons() { - int scale = getDesktopScale(); - if (scale <= 10) { - tb.enableButton(ID_ZOOM_IN, true); - tb.enableButton(ID_ZOOM_OUT, false); - } else if (scale >= 200) { - tb.enableButton(ID_ZOOM_IN, false); - tb.enableButton(ID_ZOOM_OUT, true); - } else { - tb.enableButton(ID_ZOOM_IN, true); - tb.enableButton(ID_ZOOM_OUT, true); - } - if (buffer->isScaling() || isAutoScaling()) tb.enableButton(ID_ACTUAL_SIZE, true); - else tb.enableButton(ID_ACTUAL_SIZE, false); - if (isAutoScaling()) tb.pressButton(ID_AUTO_SIZE, true); - else tb.pressButton(ID_AUTO_SIZE, false); -} - -void DesktopWindow::setDisableWinKeys(bool dwk) { - // Enable low-level event hooking, so we get special keys directly - if (dwk) - enableLowLevelKeyEvents(handle); - else - disableLowLevelKeyEvents(handle); -} - - -void DesktopWindow::setMonitor(const char* monitor) { - MonitorInfo mi(monitor); - mi.moveTo(handle); -} - -char* DesktopWindow::getMonitor() const { - MonitorInfo mi(handle); - return strDup(mi.szDevice); -} - - -bool DesktopWindow::setViewportOffset(const Point& tl) { - Point np = Point(__rfbmax(0, __rfbmin(tl.x, buffer->width()-client_size.width())), - __rfbmax(0, __rfbmin(tl.y, buffer->height()-client_size.height()))); - Point delta = np.translate(scrolloffset.negate()); - if (!np.equals(scrolloffset)) { - scrolloffset = np; - ScrollWindowEx(frameHandle, -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); - UpdateWindow(frameHandle); - return true; - } - return false; -} - - -bool DesktopWindow::processBumpScroll(const Point& pos) -{ - if (!bumpScroll) return false; - int bumpScrollPixels = 20; - bumpScrollDelta = Point(); - - if (pos.x == client_size.width()-1) - bumpScrollDelta.x = bumpScrollPixels; - else if (pos.x == 0) - bumpScrollDelta.x = -bumpScrollPixels; - if (pos.y == client_size.height()-1) - bumpScrollDelta.y = bumpScrollPixels; - else if (pos.y == 0) - bumpScrollDelta.y = -bumpScrollPixels; - - if (bumpScrollDelta.x || bumpScrollDelta.y) { - if (bumpScrollTimer.isActive()) return true; - if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) { - bumpScrollTimer.start(25); - return true; - } - } - - bumpScrollTimer.stop(); - return false; -} - - -LRESULT -DesktopWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - - // -=- Process standard window messages - - case WM_NOTIFY: - if (wParam == ID_TOOLBAR) - tb.processWM_NOTIFY(wParam, lParam); - break; - - case WM_DISPLAYCHANGE: - // Display format has changed - notify callback - callback->displayChanged(); - break; - - // -=- Window position - - // Prevent the window from being resized to be too large if in normal mode. - // If maximized or fullscreen the allow oversized windows. - - case WM_WINDOWPOSCHANGING: - { - WINDOWPOS* wpos = (WINDOWPOS*)lParam; - if ((wpos->flags & SWP_NOSIZE) || isAutoScaling()) - break; - - // Calculate the minimum size of main window - RECT r; - Rect min_size; - int tbMinWidth = 0, tbMinHeight = 0; - if (isToolbarEnabled()) { - tbMinWidth = tb.getTotalWidth(); - tbMinHeight = tb.getHeight(); - SetRect(&r, 0, 0, tbMinWidth, tbMinHeight); - AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); - min_size = Rect(r.left, r.top, r.right, r.bottom); - } - - // Work out how big the window should ideally be - DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE); - DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); - DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); - - SetRect(&r, 0, 0, buffer->width(), buffer->height()); - AdjustWindowRectEx(&r, style, FALSE, style_ex); - Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); - if (current_style & WS_VSCROLL) - reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); - if (current_style & WS_HSCROLL) - reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); - - SetRect(&r, reqd_size.tl.x, reqd_size.tl.y, reqd_size.br.x, reqd_size.br.y); - if (isToolbarEnabled()) - r.bottom += tb.getHeight(); - AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); - reqd_size = Rect(r.left, r.top, r.right, r.bottom); - - RECT current; - GetWindowRect(handle, ¤t); - - if (min_size.width() > reqd_size.width()) { - reqd_size.tl.x = min_size.tl.x; - reqd_size.br.x = min_size.br.x; - } - if (min_size.height() > reqd_size.height()) { - reqd_size.tl.y = min_size.tl.y; - reqd_size.br.y = min_size.br.y; - } - - if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { - // Ensure that the window isn't resized too large or too small - if ((wpos->cx < min_size.width()) && isToolbarEnabled()) { - wpos->cx = min_size.width(); - wpos->x = current.left; - } else if ((wpos->cx > reqd_size.width())) { - wpos->cx = reqd_size.width(); - wpos->x = current.left; - } - if ((wpos->cy < min_size.height()) && isToolbarEnabled()) { - wpos->cy = min_size.height(); - wpos->y = current.top; - } else if (wpos->cy > reqd_size.height()) { - wpos->cy = reqd_size.height(); - wpos->y = current.top; - } - } - } - break; - - // Resize child windows and update window size info we have cached. - - case WM_SIZE: - { - Point old_offset = desktopToClient(Point(0, 0)); - RECT r; - - // Resize child windows - GetClientRect(handle, &r); - if (tb.isVisible()) { - MoveWindow(frameHandle, 0, tb.getHeight(), r.right, r.bottom - tb.getHeight(), TRUE); - } else { - MoveWindow(frameHandle, 0, 0, r.right, r.bottom, TRUE); - } - tb.autoSize(); - - // Update the cached sizing information - GetWindowRect(frameHandle, &r); - window_size = Rect(r.left, r.top, r.right, r.bottom); - GetClientRect(frameHandle, &r); - client_size = Rect(r.left, r.top, r.right, r.bottom); - - // Perform the AutoScaling operation - if (isAutoScaling()) { - fitBufferToWindow(false); - } else { - // Determine whether scrollbars are required - calculateScrollBars(); - } - - // Redraw if required - if ((!old_offset.equals(desktopToClient(Point(0, 0)))) || isAutoScaling()) - InvalidateRect(frameHandle, 0, TRUE); - } - break; - - // -=- Bump-scrolling - - case WM_TIMER: - switch (wParam) { - case TIMER_BUMPSCROLL: - if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta))) - bumpScrollTimer.stop(); - break; - case TIMER_POINTER_INTERVAL: - case TIMER_POINTER_3BUTTON: - ptr.handleTimer(callback, wParam); - break; - case TIMER_UPDATE: - updateWindow(); - break; - } - break; - - // -=- Track whether or not the window has focus - - case WM_SETFOCUS: - has_focus = true; - break; - case WM_KILLFOCUS: - has_focus = false; - cursorOutsideBuffer(); - // Restore the keyboard to a consistent state - kbd.releaseAllKeys(callback); - break; - - // -=- If the menu is about to be shown, make sure it's up to date - - case WM_INITMENU: - callback->refreshMenu(true); - break; - - // -=- Handle the extra window menu items - - // Pass system menu messages to the callback and only attempt - // to process them ourselves if the callback returns false. - case WM_SYSCOMMAND: - // Call the supplied callback - if (callback->sysCommand(wParam, lParam)) - break; - - // - Not processed by the callback, so process it as a system message - switch (wParam & 0xfff0) { - - // When restored, ensure that full-screen mode is re-enabled if required. - case SC_RESTORE: - { - if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) { - rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); - setFullscreen(fullscreenRestore); - } - else if (fullscreenActive) - setFullscreen(false); - else - rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); - - return 0; - } - - // If we are maximized or minimized then that cancels full-screen mode. - case SC_MINIMIZE: - case SC_MAXIMIZE: - fullscreenRestore = fullscreenActive; - setFullscreen(false); - break; - - } - break; - - // Treat all menu commands as system menu commands - case WM_COMMAND: - SendMessage(handle, WM_SYSCOMMAND, wParam, lParam); - return 0; - - // -=- Handle keyboard input - - case WM_KEYUP: - case WM_KEYDOWN: - // Hook the MenuKey to pop-up the window menu - if (menuKey && (wParam == menuKey)) { - - bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; - bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0; - bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; - if (!(ctrlDown || altDown || shiftDown)) { - - // If MenuKey is being released then pop-up the menu - if ((msg == WM_KEYDOWN)) { - // Make sure it's up to date - // - // NOTE: Here we call refreshMenu only to grey out Move and Size - // menu items. Other things will be refreshed once again - // while processing the WM_INITMENU message. - // - callback->refreshMenu(false); - - // Show it under the pointer - POINT pt; - GetCursorPos(&pt); - cursorInBuffer = false; - TrackPopupMenu(GetSystemMenu(handle, FALSE), - TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, handle, 0); - } - - // Ignore the MenuKey keypress for both press & release events - return 0; - } - } - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - kbd.keyEvent(callback, wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN)); - return 0; - - // -=- Handle mouse wheel scroll events - -#ifdef WM_MOUSEWHEEL - case WM_MOUSEWHEEL: - processMouseMessage(msg, wParam, lParam); - break; -#endif - - // -=- Handle the window closing - - case WM_CLOSE: - vlog.debug("WM_CLOSE %x", handle); - callback->closeWindow(); - break; - - } - - return rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); -} - -LRESULT -DesktopWindow::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - - // -=- Paint the remote frame buffer - - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC paintDC = BeginPaint(frameHandle, &ps); - if (!paintDC) - throw rdr::SystemException("unable to BeginPaint", GetLastError()); - Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); - - if (!pr.is_empty()) { - - // Draw using the correct palette - PaletteSelector pSel(paintDC, windowPalette.getHandle()); - - if (buffer->bitmap) { - // Update the bitmap's palette - if (palette_changed) { - palette_changed = false; - buffer->refreshPalette(); - } - - // Get device context - BitmapDC bitmapDC(paintDC, buffer->bitmap); - - // Blit the border if required - Rect bufpos = desktopToClient(buffer->getRect()); - if (!pr.enclosed_by(bufpos)) { - vlog.debug("draw border"); - HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); - RECT r; - SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); - } - - // Do the blit - Point buf_pos = clientToDesktop(pr.tl); - - if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), - bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) - throw rdr::SystemException("unable to BitBlt to window", GetLastError()); - } - } - - EndPaint(frameHandle, &ps); - } - return 0; - - // -=- Palette management - - case WM_PALETTECHANGED: - vlog.debug("WM_PALETTECHANGED"); - if ((HWND)wParam == frameHandle) { - vlog.debug("ignoring"); - break; - } - case WM_QUERYNEWPALETTE: - vlog.debug("re-selecting palette"); - { - WindowDC wdc(frameHandle); - PaletteSelector pSel(wdc, windowPalette.getHandle()); - if (pSel.isRedrawRequired()) { - InvalidateRect(frameHandle, 0, FALSE); - UpdateWindow(frameHandle); - } - } - return TRUE; - - case WM_VSCROLL: - case WM_HSCROLL: - { - Point delta; - int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; - - switch (LOWORD(wParam)) { - case SB_PAGEUP: newpos -= 50; break; - case SB_PAGEDOWN: newpos += 50; break; - case SB_LINEUP: newpos -= 5; break; - case SB_LINEDOWN: newpos += 5; break; - case SB_THUMBTRACK: - case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; - default: vlog.info("received unknown scroll message"); - }; - - if (msg == WM_HSCROLL) - setViewportOffset(Point(newpos, scrolloffset.y)); - else - setViewportOffset(Point(scrolloffset.x, newpos)); - - SCROLLINFO si; - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = newpos; - SetScrollInfo(frameHandle, (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); - } - break; - - // -=- Cursor shape/visibility handling - - case WM_SETCURSOR: - if (LOWORD(lParam) != HTCLIENT) - break; - SetCursor(cursorInBuffer ? dotCursor : arrowCursor); - return TRUE; - - case WM_MOUSELEAVE: - trackingMouseLeave = false; - cursorOutsideBuffer(); - return 0; - - // -=- Mouse input handling - - case WM_MOUSEMOVE: - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - processMouseMessage(msg, wParam, lParam); - break; - } - - return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam); -} - -void -DesktopWindow::processMouseMessage(UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (!has_focus) { - cursorOutsideBuffer(); - return; - } - - if (!trackingMouseLeave) { - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = frameHandle; - _TrackMouseEvent(&tme); - trackingMouseLeave = true; - } - int mask = 0; - if (LOWORD(wParam) & MK_LBUTTON) mask |= 1; - if (LOWORD(wParam) & MK_MBUTTON) mask |= 2; - if (LOWORD(wParam) & MK_RBUTTON) mask |= 4; - -#ifdef WM_MOUSEWHEEL - if (msg == WM_MOUSEWHEEL) { - int delta = (short)HIWORD(wParam); - int repeats = (abs(delta)+119) / 120; - int wheelMask = (delta > 0) ? 8 : 16; - vlog.debug("repeats %d, mask %d\n",repeats,wheelMask); - for (int i=0; i<repeats; i++) { - ptr.pointerEvent(callback, oldpos, mask | wheelMask); - ptr.pointerEvent(callback, oldpos, mask); - } - } else { -#endif - Point clientPos = Point(LOWORD(lParam), HIWORD(lParam)); - Point p = clientToDesktop(clientPos); - - // If the mouse is not within the server buffer area, do nothing - cursorInBuffer = buffer->getRect().contains(p); - if (!cursorInBuffer) { - cursorOutsideBuffer(); - return; - } - - // If we're locally rendering the cursor then redraw it - if (cursorAvailable) { - // - Render the cursor! - if (!p.equals(cursorPos)) { - hideLocalCursor(); - cursorPos = p; - showLocalCursor(); - if (cursorVisible) - hideSystemCursor(); - } - } - - // If we are doing bump-scrolling then try that first... - if (processBumpScroll(clientPos)) - return; - - // Send a pointer event to the server - oldpos = p; - if (buffer->isScaling()) { - p.x /= buffer->getScaleRatioX(); - p.y /= buffer->getScaleRatioY(); - } - ptr.pointerEvent(callback, p, mask); -#ifdef WM_MOUSEWHEEL - } -#endif -} - -void DesktopWindow::updateWindow() -{ - Rect rect; - - updateTimer.stop(); - - rect = damage.get_bounding_rect(); - damage.clear(); - - RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; - InvalidateRect(frameHandle, &invalid, FALSE); -} - -void -DesktopWindow::hideLocalCursor() { - // - Blit the cursor backing store over the cursor - // *** ALWAYS call this BEFORE changing buffer PF!!! - if (cursorVisible) { - cursorVisible = false; - buffer->DIBSectionBuffer::imageRect(cursorBackingRect, cursorBacking.data); - invalidateDesktopRect(cursorBackingRect, false); - } -} - -void -DesktopWindow::showLocalCursor() { - if (cursorAvailable && !cursorVisible && cursorInBuffer) { - if (!buffer->getScaledPixelFormat().equal(cursor.getPF()) || - cursor.getRect().is_empty()) { - vlog.info("attempting to render invalid local cursor"); - cursorAvailable = false; - showSystemCursor(); - return; - } - cursorVisible = true; - - cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate()); - cursorBackingRect = cursorBackingRect.intersect(buffer->getRect()); - buffer->getImage(cursorBacking.data, cursorBackingRect); - - renderLocalCursor(); - - invalidateDesktopRect(cursorBackingRect, false); - // Since we render the cursor onto the framebuffer, we need to update - // right away to get a responsive cursor. - updateWindow(); - } -} - -void DesktopWindow::cursorOutsideBuffer() -{ - cursorInBuffer = false; - hideLocalCursor(); - showSystemCursor(); -} - -void -DesktopWindow::renderLocalCursor() -{ - Rect r = cursor.getRect(); - r = r.translate(cursorPos).translate(cursor.hotspot.negate()); - buffer->DIBSectionBuffer::maskRect(r, cursor.data, cursor.mask.buf); -} - -void -DesktopWindow::hideSystemCursor() { - if (systemCursorVisible) { - vlog.debug("hide system cursor"); - systemCursorVisible = false; - ShowCursor(FALSE); - } -} - -void -DesktopWindow::showSystemCursor() { - if (!systemCursorVisible) { - vlog.debug("show system cursor"); - systemCursorVisible = true; - ShowCursor(TRUE); - } -} - - -bool -DesktopWindow::invalidateDesktopRect(const Rect& crect, bool scaling) { - Rect rect; - if (buffer->isScaling() && scaling) { - rect = desktopToClient(buffer->calculateScaleBoundary(crect)); - } else rect = desktopToClient(crect); - if (rect.intersect(client_size).is_empty()) return false; - damage.assign_union(rfb::Region(rect)); - if (!updateTimer.isActive()) - updateTimer.start(100); - return true; -} - - -void -DesktopWindow::notifyClipboardChanged(const char* text, int len) { - callback->clientCutText(text, len); -} - - -void -DesktopWindow::setPF(const PixelFormat& pf) { - // If the cursor is the wrong format then clear it - if (!pf.equal(buffer->getScaledPixelFormat())) - setCursor(0, 0, Point(), 0, 0); - - // Update the desktop buffer - buffer->setPF(pf); - - // Redraw the window - InvalidateRect(frameHandle, 0, FALSE); -} - -void -DesktopWindow::setSize(int w, int h) { - vlog.debug("setSize %dx%d", w, h); - - // If the locally-rendered cursor is visible then remove it - hideLocalCursor(); - - // Resize the backing buffer - buffer->setSize(w, h); - - // Calculate the pixel buffer aspect correlation. It's used - // for the autoScaling operation. - aspect_corr = (double)w / h; - - // If the window is not maximised or full-screen then resize it - if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { - // Resize the window to the required size - RECT r = {0, 0, w, h}; - AdjustWindowRectEx(&r, GetWindowLong(frameHandle, GWL_STYLE), FALSE, - GetWindowLong(frameHandle, GWL_EXSTYLE)); - if (isToolbarEnabled()) - r.bottom += tb.getHeight(); - AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); - - // Resize about the center of the window, and clip to current monitor - MonitorInfo mi(handle); - resizeWindow(handle, r.right-r.left, r.bottom-r.top); - mi.clipTo(handle); - } else { - // Ensure the screen contents are consistent - InvalidateRect(frameHandle, 0, FALSE); - } - - // Enable/disable scrollbars as appropriate - calculateScrollBars(); -} - -void DesktopWindow::setAutoScaling(bool as) { - autoScaling = as; - if (isToolbarEnabled()) refreshToolbarButtons(); - if (as) fitBufferToWindow(); -} - -void DesktopWindow::setDesktopScale(int scale_) { - if (buffer->getScale() == scale_ || scale_ <= 0) return; - bool state = buffer->isScaling(); - buffer->setScale(scale_); - state ^= buffer->isScaling(); - if (state) convertCursorToBuffer(); - if (isToolbarEnabled()) refreshToolbarButtons(); - if (!(isAutoScaling() || isFullscreen() || (GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE))) resizeDesktopWindowToBuffer(); - printScale(); - InvalidateRect(frameHandle, 0, FALSE); -} - -void DesktopWindow::setDesktopScaleFilter(unsigned int scaleFilterID) { - if (scaleFilterID == getDesktopScaleFilterID() || scaleFilterID > scaleFilterMaxNumber) return; - buffer->setScaleFilter(scaleFilterID); - InvalidateRect(frameHandle, 0, FALSE); -} - -void DesktopWindow::convertCursorToBuffer() { - if (memcmp(&(cursor.getPF()), &(buffer->getPF()), sizeof(PixelBuffer)) == 0) return; - internalSetCursor = true; - setCursor(cursorWidth, cursorHeight, cursorHotspot, cursorImage, cursorMask); - internalSetCursor = false; -} - -void DesktopWindow::fitBufferToWindow(bool repaint) { - double scale_ratio; - double resized_aspect_corr = double(client_size.width()) / client_size.height(); - DWORD style = GetWindowLong(frameHandle, GWL_STYLE); - if (style & (WS_VSCROLL | WS_HSCROLL)) { - style &= ~(WS_VSCROLL | WS_HSCROLL); - SetWindowLong(frameHandle, GWL_STYLE, style); - SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); - // Update the cached client size - RECT r; - GetClientRect(frameHandle, &r); - client_size = Rect(r.left, r.top, r.right, r.bottom); - } - bool state = buffer->isScaling(); - if (resized_aspect_corr > aspect_corr) { - scale_ratio = (double)client_size.height() / buffer->getSrcHeight(); - buffer->setScaleWindowSize(ceil(buffer->getSrcWidth()*scale_ratio), client_size.height()); - } else { - scale_ratio = (double)client_size.width() / buffer->getSrcWidth(); - buffer->setScaleWindowSize(client_size.width(), ceil(buffer->getSrcHeight()*scale_ratio)); - } - state ^= buffer->isScaling(); - if (state) convertCursorToBuffer(); - printScale(); - InvalidateRect(frameHandle, 0, FALSE); -} - -void DesktopWindow::printScale() { - setName(desktopName); -} - -void -DesktopWindow::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { - hideLocalCursor(); - - cursor.hotspot = hotspot; - - cursor.setSize(w, h); - cursor.setPF(buffer->getScaledPixelFormat()); - - // Convert the current cursor pixel format to bpp32 if scaling mode is on. - // It need because ScaledDIBSection buffer always works with bpp32 pixel data - // in scaling mode. - if (buffer->isScaling()) { - U8 *ptr = (U8*)cursor.data; - U8 *dataPtr = (U8*)data; - U32 pixel = 0; - int bytesPerPixel = buffer->getPixelFormat().bpp / 8; - int pixelCount = w * h; - PixelFormat pf = buffer->getPixelFormat(); - - while (pixelCount--) { - if (bytesPerPixel == 1) { - pixel = *dataPtr++; - } else if (bytesPerPixel == 2) { - int b0 = *dataPtr++; int b1 = *dataPtr++; - pixel = b1 << 8 | b0; - } else if (bytesPerPixel == 4) { - int b0 = *dataPtr++; int b1 = *dataPtr++; - int b2 = *dataPtr++; int b3 = *dataPtr++; - pixel = b3 << 24 | b2 << 16 | b1 << 8 | b0; - } else { - pixel = 0; - } - *ptr++ = (U8)((((pixel >> pf.blueShift ) & pf.blueMax ) * 255 + pf.blueMax /2) / pf.blueMax); - *ptr++ = (U8)((((pixel >> pf.greenShift) & pf.greenMax) * 255 + pf.greenMax/2) / pf.greenMax); - *ptr++ = (U8)((((pixel >> pf.redShift ) & pf.redMax ) * 255 + pf.redMax /2) / pf.redMax); - *ptr++ = (U8)0; - } - } else { - cursor.imageRect(cursor.getRect(), data); - } - memcpy(cursor.mask.buf, mask, cursor.maskLen()); - cursor.crop(); - - cursorBacking.setSize(w, h); - cursorBacking.setPF(buffer->getScaledPixelFormat()); - - cursorAvailable = true; - - showLocalCursor(); - - // Save the cursor parameters - if (!internalSetCursor) { - if (cursorImage) delete [] cursorImage; - if (cursorMask) delete [] cursorMask; - int cursorImageSize = (buffer->getPixelFormat().bpp/8) * w * h; - cursorImage = new U8[cursorImageSize]; - cursorMask = new U8[cursor.maskLen()]; - memcpy(cursorImage, data, cursorImageSize); - memcpy(cursorMask, mask, cursor.maskLen()); - cursorWidth = w; - cursorHeight = h; - cursorHotspot = hotspot; - } -} - -PixelFormat -DesktopWindow::getNativePF() const { - vlog.debug("getNativePF()"); - return WindowDC(handle).getPF(); -} - - -void -DesktopWindow::refreshWindowPalette(int start, int count) { - vlog.debug("refreshWindowPalette(%d, %d)", start, count); - - Colour colours[256]; - if (count > 256) { - vlog.debug("%d palette entries", count); - throw rdr::Exception("too many palette entries"); - } - - // Copy the palette from the DIBSectionBuffer - ColourMap* cm = buffer->getColourMap(); - if (!cm) return; - for (int i=0; i<count; i++) { - int r, g, b; - cm->lookup(i, &r, &g, &b); - colours[i].r = r; - colours[i].g = g; - colours[i].b = b; - } - - // Set the window palette - windowPalette.setEntries(start, count, colours); - - // Cause the window to be redrawn - palette_changed = true; - InvalidateRect(handle, 0, FALSE); -} - - -void DesktopWindow::calculateScrollBars() { - // Calculate the required size of window - DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE); - DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); - DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); - DWORD old_style; - RECT r; - SetRect(&r, 0, 0, buffer->width(), buffer->height()); - AdjustWindowRectEx(&r, style, FALSE, style_ex); - Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); - - if (!bumpScroll) { - // We only enable scrollbars if bump-scrolling is not active. - // Effectively, this means if full-screen is not active, - // but I think it's better to make these things explicit. - - // Work out whether scroll bars are required - do { - old_style = style; - - if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { - style |= WS_HSCROLL; - reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); - } - if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { - style |= WS_VSCROLL; - reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); - } - } while (style != old_style); - } - - // Tell Windows to update the window style & cached settings - if (style != current_style) { - SetWindowLong(frameHandle, GWL_STYLE, style); - SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); - } - - // Update the scroll settings - SCROLLINFO si; - if (style & WS_VSCROLL) { - si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - si.nMin = 0; - si.nMax = buffer->height(); - si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); - maxscrolloffset.y = __rfbmax(0, si.nMax-si.nPage); - scrolloffset.y = __rfbmin(maxscrolloffset.y, scrolloffset.y); - si.nPos = scrolloffset.y; - SetScrollInfo(frameHandle, SB_VERT, &si, TRUE); - } - if (style & WS_HSCROLL) { - si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - si.nMin = 0; - si.nMax = buffer->width(); - si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); - maxscrolloffset.x = __rfbmax(0, si.nMax-si.nPage); - scrolloffset.x = __rfbmin(maxscrolloffset.x, scrolloffset.x); - si.nPos = scrolloffset.x; - SetScrollInfo(frameHandle, SB_HORZ, &si, TRUE); - } - - // Update the cached client size - GetClientRect(frameHandle, &r); - client_size = Rect(r.left, r.top, r.right, r.bottom); -} - -void DesktopWindow::resizeDesktopWindowToBuffer() { - RECT r; - DWORD style = GetWindowLong(frameHandle, GWL_STYLE) & ~(WS_VSCROLL | WS_HSCROLL); - DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); - - // Calculate the required size of the desktop window - SetRect(&r, 0, 0, buffer->width(), buffer->height()); - AdjustWindowRectEx(&r, style, FALSE, style_ex); - if (isToolbarEnabled()) - r.bottom += tb.getHeight(); - AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); - - // Set the required size, center the main window and clip to the current monitor - SetWindowPos(handle, 0, 0, 0, r.right-r.left, r.bottom-r.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); - centerWindow(handle, NULL); - MonitorInfo mi(getMonitor()); - mi.clipTo(handle); - - // Enable/disable scrollbars as appropriate - calculateScrollBars(); -} - - -void DesktopWindow::framebufferUpdateEnd() -{ - updateWindow(); -} - - -void -DesktopWindow::setName(const char* name) { - if (name != desktopName) { - strCopy(desktopName, name, sizeof(desktopName)); - } - char *newTitle = new char[strlen(desktopName)+20]; - sprintf(newTitle, "TigerVNC: %.240s @ %i%%", desktopName, getDesktopScale()); - SetWindowText(handle, TStr(newTitle)); - delete [] newTitle; -} - - -void -DesktopWindow::serverCutText(const char* str, rdr::U32 len) { - CharArray t(len+1); - memcpy(t.buf, str, len); - t.buf[len] = 0; - clipboard.setClipText(t.buf); -} - - -void DesktopWindow::fillRect(const Rect& r, Pixel pix) { - Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; - if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor(); - buffer->fillRect(r, pix); - invalidateDesktopRect(r); -} -void DesktopWindow::imageRect(const Rect& r, void* pixels) { - Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; - if (cursorBackingRect.overlaps(img_rect)) hideLocalCursor(); - buffer->imageRect(r, pixels); - invalidateDesktopRect(r); -} -void DesktopWindow::copyRect(const Rect& r, int srcX, int srcY) { - Rect img_rect = buffer->isScaling() ? buffer->calculateScaleBoundary(r) : r; - if (cursorBackingRect.overlaps(img_rect) || - cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+img_rect.width(), srcY+img_rect.height()))) - hideLocalCursor(); - buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); - invalidateDesktopRect(r); -} - -void DesktopWindow::invertRect(const Rect& r) { - int stride; - rdr::U8* p = buffer->isScaling() ? buffer->getPixelsRW(buffer->calculateScaleBoundary(r), &stride) - : buffer->getPixelsRW(r, &stride); - for (int y = 0; y < r.height(); y++) { - for (int x = 0; x < r.width(); x++) { - switch (buffer->getPF().bpp) { - case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break; - case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break; - case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break; - } - } - } - invalidateDesktopRect(r); -} |