diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:36:48 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:36:48 +0000 |
commit | d1e4e901e29d8ef27a6583648c7148b1a5b2facf (patch) | |
tree | fb34ead4006e43ac22aa6368c6a622320f234261 /vncviewer/DesktopWindow.cxx | |
parent | 729598cb00d791bbdfe23ebe0023d3a1c3962f83 (diff) | |
download | tigervnc-d1e4e901e29d8ef27a6583648c7148b1a5b2facf.tar.gz tigervnc-d1e4e901e29d8ef27a6583648c7148b1a5b2facf.zip |
Migrating to new directory structure adopted from the RealVNC's source tree. Windows part should be complete. For the Unix part, more changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@592 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'vncviewer/DesktopWindow.cxx')
-rw-r--r-- | vncviewer/DesktopWindow.cxx | 1103 |
1 files changed, 0 insertions, 1103 deletions
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx deleted file mode 100644 index 27ef2dc2..00000000 --- a/vncviewer/DesktopWindow.cxx +++ /dev/null @@ -1,1103 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. 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 <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 <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; - - -// -// -=- 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; - if (msg == WM_CREATE) - SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLong(wnd, GWL_USERDATA, 0); - DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_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 (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); - } -} - -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; - if (msg == WM_CREATE) - SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLong(wnd, GWL_USERDATA, 0); - DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_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) - : buffer(0), - showToolbar(false), - client_size(0, 0, 16, 16), window_size(0, 0, 32, 32), - cursorVisible(false), cursorAvailable(false), cursorInBuffer(false), - systemCursorVisible(true), trackingMouseLeave(false), - handle(0), frameHandle(0), has_focus(false), palette_changed(false), - fullscreenActive(false), fullscreenRestore(false), - bumpScroll(false), 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); - - // 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; - 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 display the window is on - MonitorInfo mi(handle); - - // 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, mi.rcMonitor.left, mi.rcMonitor.top, - mi.rcMonitor.right-mi.rcMonitor.left, - mi.rcMonitor.bottom-mi.rcMonitor.top, - 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 (showToolbar && !tb.isVisible() && !fullscreenActive) { - tb.show(); - } else if (!showToolbar && tb.isVisible()) { - tb.hide(); - } -} - -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(max(0, min(tl.x, buffer->width()-client_size.width())), - max(0, min(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) - break; - - // 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); - - 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 (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 (tb.isVisible()) - 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 (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { - // Ensure that the window isn't resized too large - if (wpos->cx > reqd_size.width()) { - wpos->cx = reqd_size.width(); - wpos->x = current.left; - } - 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); - - // Determine whether scrollbars are required - calculateScrollBars(); - - // Redraw if required - if ((!old_offset.equals(desktopToClient(Point(0, 0))))) - 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; - } - 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 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); - - // - Notify the callback that a paint message has finished processing - callback->paintCompleted(); - } - 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: -#ifdef WM_MOUSEWHEEL - case WM_MOUSEWHEEL: -#endif - if (has_focus) - { - 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(); - break; - } - - // 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)) - break; - - // Send a pointer event to the server - oldpos = p; - if (buffer->isScaling()) { - p.x /= double(buffer->getScale()) / 100.0; - p.y /= double(buffer->getScale()) / 100.0; - } - ptr.pointerEvent(callback, p, mask); -#ifdef WM_MOUSEWHEEL - } -#endif - } else { - cursorOutsideBuffer(); - } - break; - } - - return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam); -} - - -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->getPF().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); - } -} - -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; - RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; - InvalidateRect(frameHandle, &invalid, FALSE); - 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->getPF())) - 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); - - // 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 (tb.isVisible()) - 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::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { - hideLocalCursor(); - - cursor.hotspot = hotspot; - - cursor.setSize(w, h); - cursor.setPF(buffer->getPF()); - cursor.imageRect(cursor.getRect(), data); - memcpy(cursor.mask.buf, mask, cursor.maskLen()); - cursor.crop(); - - cursorBacking.setSize(w, h); - cursorBacking.setPF(buffer->getPF()); - - cursorAvailable = true; - - showLocalCursor(); -} - -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 = max(0, si.nMax-si.nPage); - scrolloffset.y = min(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 = max(0, si.nMax-si.nPage); - scrolloffset.x = min(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::setName(const char* name) { - SetWindowText(handle, TStr(name)); -} - - -void -DesktopWindow::serverCutText(const char* str, int 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); -} |