mirror of
https://github.com/apache/cloudstack.git
synced 2025-12-21 04:53:37 +01:00
481 lines
13 KiB
C++
481 lines
13 KiB
C++
// Copyright (C) 2000 Const Kaplinsky. All Rights Reserved.
|
|
//
|
|
// This file is part of the VNC system.
|
|
//
|
|
// The VNC system 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 program 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 program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
|
|
//
|
|
// If the source code for the VNC system is not available from the place
|
|
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
|
|
// the authors on vnc@uk.research.att.com for information on obtaining it.
|
|
|
|
// XCursor and RichCursor encodings
|
|
//
|
|
// Support for cursor shape updates for ClientConnection class.
|
|
|
|
#include "stdhdrs.h"
|
|
#include "vncviewer.h"
|
|
#include "ClientConnection.h"
|
|
|
|
void ClientConnection::ReadCursorShape(rfbFramebufferUpdateRectHeader *pfburh) {
|
|
|
|
vnclog.Print(6, _T("Receiving cursor shape update, cursor %dx%d\n"),
|
|
(int)pfburh->r.w, (int)pfburh->r.h);
|
|
|
|
int bytesPerRow = (pfburh->r.w + 7) / 8;
|
|
int bytesMaskData = bytesPerRow * pfburh->r.h;
|
|
int bytesSourceData =
|
|
pfburh->r.w * pfburh->r.h * (m_myFormat.bitsPerPixel / 8);
|
|
CheckBufferSize(bytesMaskData);
|
|
|
|
SoftCursorFree();
|
|
|
|
if (pfburh->r.w * pfburh->r.h == 0)
|
|
return;
|
|
|
|
// Ignore cursor shape updates if requested by user
|
|
if (m_opts.m_ignoreShapeUpdates) {
|
|
int bytesToSkip = (pfburh->encoding == rfbEncodingXCursor) ?
|
|
(6 + 2 * bytesMaskData) : (bytesSourceData + bytesMaskData);
|
|
CheckBufferSize(bytesToSkip);
|
|
ReadExact(m_netbuf, bytesToSkip);
|
|
return;
|
|
}
|
|
|
|
// Read cursor pixel data.
|
|
|
|
rcSource = new COLORREF[pfburh->r.w * pfburh->r.h];
|
|
|
|
if (pfburh->encoding == rfbEncodingXCursor) {
|
|
CARD8 xcolors[6];
|
|
ReadExact((char *)xcolors, 6);
|
|
COLORREF rcolors[2];
|
|
rcolors[1] = PALETTERGB(xcolors[0], xcolors[1], xcolors[2]);
|
|
rcolors[0] = PALETTERGB(xcolors[3], xcolors[4], xcolors[5]);
|
|
|
|
ReadExact(m_netbuf, bytesMaskData);
|
|
int x, y, n, b;
|
|
int i = 0;
|
|
for (y = 0; y < pfburh->r.h; y++) {
|
|
for (x = 0; x < pfburh->r.w / 8; x++) {
|
|
b = m_netbuf[y * bytesPerRow + x];
|
|
for (n = 7; n >= 0; n--)
|
|
rcSource[i++] = rcolors[b >> n & 1];
|
|
}
|
|
for (n = 7; n >= 8 - pfburh->r.w % 8; n--) {
|
|
rcSource[i++] = rcolors[m_netbuf[y * bytesPerRow + x] >> n & 1];
|
|
}
|
|
}
|
|
} else {
|
|
// rfb.EncodingRichCursor
|
|
CheckBufferSize(bytesSourceData);
|
|
ReadExact(m_netbuf, bytesSourceData);
|
|
SETUP_COLOR_SHORTCUTS;
|
|
char *p = m_netbuf;
|
|
for (int i = 0; i < pfburh->r.w * pfburh->r.h; i++) {
|
|
switch (m_myFormat.bitsPerPixel) {
|
|
case 8:
|
|
rcSource[i] = COLOR_FROM_PIXEL8_ADDRESS(p);
|
|
p++;
|
|
break;
|
|
case 16:
|
|
rcSource[i] = COLOR_FROM_PIXEL16_ADDRESS(p);
|
|
p += 2;
|
|
break;
|
|
case 32:
|
|
rcSource[i] = COLOR_FROM_PIXEL32_ADDRESS(p);
|
|
p += 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read and decode mask data.
|
|
|
|
ReadExact(m_netbuf, bytesMaskData);
|
|
|
|
rcMask = new bool[pfburh->r.w * pfburh->r.h];
|
|
|
|
int x, y, n, b;
|
|
int i = 0;
|
|
for (y = 0; y < pfburh->r.h; y++) {
|
|
for (x = 0; x < pfburh->r.w / 8; x++) {
|
|
b = m_netbuf[y * bytesPerRow + x];
|
|
for (n = 7; n >= 0; n--)
|
|
rcMask[i++] = (b >> n & 1) != 0;
|
|
}
|
|
for (n = 7; n >= 8 - pfburh->r.w % 8; n--) {
|
|
rcMask[i++] = (m_netbuf[y * bytesPerRow + x] >> n & 1) != 0;
|
|
}
|
|
}
|
|
|
|
// Set remaining data associated with cursor.
|
|
|
|
omni_mutex_lock l(m_cursorMutex);
|
|
|
|
rcWidth = pfburh->r.w;
|
|
rcHeight = pfburh->r.h;
|
|
rcHotX = (pfburh->r.x < rcWidth) ? pfburh->r.x : rcWidth - 1;
|
|
rcHotY = (pfburh->r.y < rcHeight) ? pfburh->r.y : rcHeight - 1;
|
|
|
|
{
|
|
omni_mutex_lock l(m_bitmapdcMutex);
|
|
ObjectSelector b1(m_hBitmapDC, m_hBitmap);
|
|
PaletteSelector ps1(m_hBitmapDC, m_hPalette);
|
|
m_hSavedAreaDC = CreateCompatibleDC(m_hBitmapDC);
|
|
m_hSavedAreaBitmap =
|
|
CreateCompatibleBitmap(m_hBitmapDC, rcWidth, rcHeight);
|
|
}
|
|
|
|
SoftCursorSaveArea();
|
|
SoftCursorDraw();
|
|
|
|
rcCursorHidden = false;
|
|
rcLockSet = false;
|
|
|
|
prevCursorSet = true;
|
|
}
|
|
|
|
// marscha PointerPos
|
|
void ClientConnection::ReadCursorPos(rfbFramebufferUpdateRectHeader *pfburh)
|
|
{
|
|
int x = (int)pfburh->r.x;
|
|
if (x >= m_si.framebufferWidth)
|
|
x = m_si.framebufferWidth - 1;
|
|
int y = (int)pfburh->r.y;
|
|
if (y >= m_si.framebufferHeight)
|
|
y = m_si.framebufferHeight - 1;
|
|
//vnclog.Print(2, _T("reading cursor pos (%d, %d)\n"), x, y);
|
|
SoftCursorMove(x, y);
|
|
}
|
|
|
|
//
|
|
// SoftCursorLockArea(). This method should be used to prevent
|
|
// collisions between simultaneous framebuffer update operations and
|
|
// cursor drawing operations caused by movements of pointing device.
|
|
// The parameters denote a rectangle where mouse cursor should not
|
|
// be drawn. Every next call to this function expands locked area so
|
|
// previous locks remain active.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorLockArea(int x, int y, int w, int h) {
|
|
|
|
omni_mutex_lock l(m_cursorMutex);
|
|
|
|
if (!prevCursorSet)
|
|
return;
|
|
|
|
if (!rcLockSet) {
|
|
rcLockX = x;
|
|
rcLockY = y;
|
|
rcLockWidth = w;
|
|
rcLockHeight = h;
|
|
rcLockSet = true;
|
|
} else {
|
|
int newX = (x < rcLockX) ? x : rcLockX;
|
|
int newY = (y < rcLockY) ? y : rcLockY;
|
|
rcLockWidth = (x + w > rcLockX + rcLockWidth) ?
|
|
(x + w - newX) : (rcLockX + rcLockWidth - newX);
|
|
rcLockHeight = (y + h > rcLockY + rcLockHeight) ?
|
|
(y + h - newY) : (rcLockY + rcLockHeight - newY);
|
|
rcLockX = newX;
|
|
rcLockY = newY;
|
|
}
|
|
|
|
if (!rcCursorHidden && SoftCursorInLockedArea()) {
|
|
SoftCursorRestoreArea();
|
|
rcCursorHidden = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// SoftCursorUnlockScreen(). This function discards all locks
|
|
// performed since previous SoftCursorUnlockScreen() call.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorUnlockScreen() {
|
|
|
|
omni_mutex_lock l(m_cursorMutex);
|
|
|
|
if (!prevCursorSet)
|
|
return;
|
|
|
|
if (rcCursorHidden) {
|
|
SoftCursorSaveArea();
|
|
SoftCursorDraw();
|
|
rcCursorHidden = false;
|
|
}
|
|
rcLockSet = false;
|
|
}
|
|
|
|
//
|
|
// SoftCursorMove(). Moves soft cursor in particular location. This
|
|
// function respects locking of screen areas so when the cursor is
|
|
// moved in the locked area, it becomes invisible until
|
|
// SoftCursorUnlockScreen() method is called.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorMove(int x, int y) {
|
|
|
|
omni_mutex_lock l(m_cursorMutex);
|
|
|
|
if (prevCursorSet && !rcCursorHidden) {
|
|
SoftCursorRestoreArea();
|
|
rcCursorHidden = true;
|
|
}
|
|
|
|
rcCursorX = x;
|
|
rcCursorY = y;
|
|
|
|
if (prevCursorSet && !(rcLockSet && SoftCursorInLockedArea())) {
|
|
SoftCursorSaveArea();
|
|
SoftCursorDraw();
|
|
rcCursorHidden = false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free all data associated with cursor.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorFree() {
|
|
|
|
omni_mutex_lock l(m_cursorMutex);
|
|
|
|
if (prevCursorSet) {
|
|
if (!rcCursorHidden)
|
|
SoftCursorRestoreArea();
|
|
DeleteObject(m_hSavedAreaBitmap);
|
|
DeleteDC(m_hSavedAreaDC);
|
|
delete[] rcSource;
|
|
rcSource=NULL;
|
|
delete[] rcMask;
|
|
rcMask=NULL;
|
|
prevCursorSet = false;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
//
|
|
// Low-level methods implementing software cursor functionality.
|
|
//
|
|
|
|
//
|
|
// Check if cursor is within locked part of screen.
|
|
//
|
|
|
|
bool ClientConnection::SoftCursorInLockedArea() {
|
|
|
|
return (rcLockX < rcCursorX - rcHotX + rcWidth &&
|
|
rcLockY < rcCursorY - rcHotY + rcHeight &&
|
|
rcLockX + rcLockWidth > rcCursorX - rcHotX &&
|
|
rcLockY + rcLockHeight > rcCursorY - rcHotY);
|
|
}
|
|
|
|
//
|
|
// Save screen data in memory buffer.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorSaveArea() {
|
|
|
|
RECT r;
|
|
SoftCursorToScreen(&r, NULL);
|
|
int x = r.left;
|
|
int y = r.top;
|
|
int w = r.right - r.left;
|
|
int h = r.bottom - r.top;
|
|
|
|
omni_mutex_lock l(m_bitmapdcMutex);
|
|
ObjectSelector b1(m_hBitmapDC, m_hBitmap);
|
|
PaletteSelector ps1(m_hBitmapDC, m_hPalette);
|
|
ObjectSelector b2(m_hSavedAreaDC, m_hSavedAreaBitmap);
|
|
PaletteSelector ps2(m_hSavedAreaDC, m_hPalette);
|
|
|
|
if (!BitBlt(m_hSavedAreaDC, 0, 0, w, h, m_hBitmapDC, x, y, SRCCOPY)) {
|
|
vnclog.Print(0, _T("Error saving screen under cursor\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore screen data saved in memory buffer.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorRestoreArea() {
|
|
|
|
RECT r;
|
|
SoftCursorToScreen(&r, NULL);
|
|
int x = r.left;
|
|
int y = r.top;
|
|
int w = r.right - r.left;
|
|
int h = r.bottom - r.top;
|
|
|
|
omni_mutex_lock l(m_bitmapdcMutex);
|
|
ObjectSelector b1(m_hBitmapDC, m_hBitmap);
|
|
PaletteSelector ps1(m_hBitmapDC, m_hPalette);
|
|
ObjectSelector b2(m_hSavedAreaDC, m_hSavedAreaBitmap);
|
|
PaletteSelector ps2(m_hSavedAreaDC, m_hPalette);
|
|
|
|
if (!BitBlt(m_hBitmapDC, x, y, w, h, m_hSavedAreaDC, 0, 0, SRCCOPY)) {
|
|
vnclog.Print(0, _T("Error restoring screen under cursor\n"));
|
|
}
|
|
|
|
InvalidateScreenRect(&r);
|
|
}
|
|
|
|
//
|
|
// Draw cursor.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorDraw() {
|
|
|
|
int x, y, x0, y0;
|
|
int offset;
|
|
|
|
omni_mutex_lock l(m_bitmapdcMutex);
|
|
ObjectSelector b(m_hBitmapDC, m_hBitmap);
|
|
PaletteSelector p(m_hBitmapDC, m_hPalette);
|
|
|
|
SETUP_COLOR_SHORTCUTS;
|
|
|
|
for (y = 0; y < rcHeight; y++) {
|
|
y0 = rcCursorY - rcHotY + y;
|
|
if (y0 >= 0 && y0 < m_si.framebufferHeight) {
|
|
for (x = 0; x < rcWidth; x++) {
|
|
x0 = rcCursorX - rcHotX + x;
|
|
if (x0 >= 0 && x0 < m_si.framebufferWidth) {
|
|
offset = y * rcWidth + x;
|
|
if (rcMask[offset]) {
|
|
SETPIXEL(m_hBitmapDC, x0, y0, rcSource[offset]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RECT r;
|
|
SoftCursorToScreen(&r, NULL);
|
|
InvalidateScreenRect(&r);
|
|
}
|
|
|
|
//
|
|
// Calculate position, size and offset for the part of cursor
|
|
// located inside framebuffer bounds.
|
|
//
|
|
|
|
void ClientConnection::SoftCursorToScreen(RECT *screenArea, POINT *cursorOffset) {
|
|
|
|
int cx = 0, cy = 0;
|
|
|
|
int x = rcCursorX - rcHotX;
|
|
int y = rcCursorY - rcHotY;
|
|
int w = rcWidth;
|
|
int h = rcHeight;
|
|
|
|
if (x < 0) {
|
|
cx = -x;
|
|
w -= cx;
|
|
x = 0;
|
|
} else if (x + w > m_si.framebufferWidth) {
|
|
w = m_si.framebufferWidth - x;
|
|
}
|
|
if (y < 0) {
|
|
cy = -y;
|
|
h -= cy;
|
|
y = 0;
|
|
} else if (y + h > m_si.framebufferHeight) {
|
|
h = m_si.framebufferHeight - y;
|
|
}
|
|
|
|
if (w < 0) {
|
|
cx = 0; x = 0; w = 0;
|
|
}
|
|
if (h < 0) {
|
|
cy = 0; y = 0; h = 0;
|
|
}
|
|
|
|
if (screenArea != NULL) {
|
|
SetRect(screenArea, x, y, x + w, y + h);
|
|
}
|
|
if (cursorOffset != NULL) {
|
|
cursorOffset->x = cx;
|
|
cursorOffset->y = cy;
|
|
}
|
|
}
|
|
|
|
void ClientConnection::InvalidateScreenRect(const RECT *pRect) {
|
|
RECT rect;
|
|
|
|
// If we're scaling, we transform the coordinates of the rectangle
|
|
// received into the corresponding window coords, and invalidate
|
|
// *that* region.
|
|
|
|
if (m_opts.m_scaling) {
|
|
// First, we adjust coords to avoid rounding down when scaling.
|
|
int n = m_opts.m_scale_num;
|
|
int d = m_opts.m_scale_den;
|
|
int left = (pRect->left / d) * d;
|
|
int top = (pRect->top / d) * d;
|
|
int right = (pRect->right + d - 1) / d * d; // round up
|
|
int bottom = (pRect->bottom + d - 1) / d * d; // round up
|
|
|
|
// Then we scale the rectangle, which should now give whole numbers.
|
|
rect.left = (left * n / d) - m_hScrollPos;
|
|
rect.top = (top * n / d) - m_vScrollPos;
|
|
rect.right = (right * n / d) - m_hScrollPos;
|
|
rect.bottom = (bottom * n / d) - m_vScrollPos;
|
|
} else {
|
|
rect.left = pRect->left - m_hScrollPos;
|
|
rect.top = pRect->top - m_vScrollPos;
|
|
rect.right = pRect->right - m_hScrollPos;
|
|
rect.bottom = pRect->bottom - m_vScrollPos;
|
|
}
|
|
InvalidateRect(m_hwnd, &rect, FALSE);
|
|
}
|
|
|
|
void ClientConnection::InvalidateRegion(const RECT *pRect,HRGN *prgn) {
|
|
RECT rect;
|
|
|
|
// If we're scaling, we transform the coordinates of the rectangle
|
|
// received into the corresponding window coords, and invalidate
|
|
// *that* region.
|
|
|
|
if (m_opts.m_scaling) {
|
|
// First, we adjust coords to avoid rounding down when scaling.
|
|
int n = m_opts.m_scale_num;
|
|
int d = m_opts.m_scale_den;
|
|
int left = (pRect->left / d) * d;
|
|
int top = (pRect->top / d) * d;
|
|
int right = (pRect->right + d - 1) / d * d; // round up
|
|
int bottom = (pRect->bottom + d - 1) / d * d; // round up
|
|
|
|
// Then we scale the rectangle, which should now give whole numbers.
|
|
rect.left = (left * n / d) - m_hScrollPos;
|
|
rect.top = (top * n / d) - m_vScrollPos;
|
|
rect.right = (right * n / d) - m_hScrollPos;
|
|
rect.bottom = (bottom * n / d) - m_vScrollPos;
|
|
} else {
|
|
rect.left = pRect->left - m_hScrollPos;
|
|
rect.top = pRect->top - m_vScrollPos;
|
|
rect.right = pRect->right - m_hScrollPos;
|
|
rect.bottom = pRect->bottom - m_vScrollPos;
|
|
}
|
|
HRGN tempregion = CreateRectRgnIndirect(&rect);
|
|
CombineRgn(*prgn,*prgn,tempregion,RGN_OR);
|
|
DeleteObject(tempregion);
|
|
}
|
|
|