VNC Cursor
2007-08-14 11:10:19| 分类:
VNC
| 标签:
|举报
|字号大中小 订阅
// Add the mouse pointer to the buffer
void
vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
{
POINT CursorPos;
ICONINFO IconInfo;
// If the mouse cursor handle is invalid then forget it
if (m_hcursor == NULL)
return;
// Get the cursor position
if (!GetCursorPos(&CursorPos))
return;
//vnclog.Print(LL_INTINFO, VNCLOG("CursorPos %i %i\n"),CursorPos.x, CursorPos.y);
// Translate position for hotspot
if (GetIconInfo(m_hcursor, &IconInfo))
{
CursorPos.x -= ((int) IconInfo.xHotspot);
CursorPos.y -= ((int) IconInfo.yHotspot);
/// Buffer has (0,0) coordinates, Cursor (screencoordinates)
CursorPos.x -= m_ScreenOffsetx;
CursorPos.y -= m_ScreenOffsety;
///
if (IconInfo.hbmMask != NULL)
DeleteObject(IconInfo.hbmMask);
if (IconInfo.hbmColor != NULL)
DeleteObject(IconInfo.hbmColor);
}
// Select the memory bitmap into the memory DC
HBITMAP oldbitmap;
if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
return;
// Draw the cursor
DrawIconEx(
m_hmemdc, // handle to device context
CursorPos.x, CursorPos.y,
m_hcursor, // handle to icon to draw
0,0, // width of the icon
0, // index of frame in animated cursor
NULL, // handle to background brush
DI_NORMAL | DI_COMPAT // icon-drawing flags
);
// Select the old bitmap back into the memory DC
SelectObject(m_hmemdc, oldbitmap);
// Save the bounding rectangle
m_cursorpos.tl = CursorPos;
m_cursorpos.br = rfb::Point(GetSystemMetrics(SM_CXCURSOR),
GetSystemMetrics(SM_CYCURSOR)).translate(CursorPos);
// Clip the bounding rect to the screen
// Copy the mouse cursor into the screen buffer, if any of it is visible
m_cursorpos = m_cursorpos.intersect(m_bmrect);
if (!m_cursorpos.is_empty()) {
CopyToBuffer(m_cursorpos, scrBuff, scrBuffSize);
}
}
// CURSOR HANDLING
// Obtain cursor image data in server's local format.
// The length of databuf[] should be at least (width * height * 4).
BOOL
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
{
// Protect the memory bitmap (is it really necessary here?)
omni_mutex_lock l(m_update_lock);
// Create bitmap, select it into memory DC
HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc, width, height);
if (membitmap == NULL) {
return FALSE;
}
HBITMAP oldbitmap = (HBITMAP) SelectObject(m_hmemdc, membitmap);
if (oldbitmap == NULL) {
DeleteObject(membitmap);
return FALSE;
}
// Draw the cursor
DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
SelectObject(m_hmemdc, oldbitmap);
// Prepare BITMAPINFO structure (copy most m_bminfo fields)
BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
bmi->bmiHeader.biWidth = width;
bmi->bmiHeader.biHeight = -height;
// Clear data buffer and extract RGB data
memset(databuf, 0x00, width * height * 4);
int lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);
// Cleanup
free(bmi);
DeleteObject(membitmap);
return (lines != 0);
}
// Copyright (C) 2000 Const Kaplinsky. All Rights Reserved.
#include "vncEncoder.h"
#include "vncBuffer.h"
#include "vncDesktop.h"
//
// New code implementing cursor shape updates.
//
BOOL
vncEncoder::IsXCursorSupported()
{
if (m_use_xcursor) return true;
if (m_use_richcursor) return true;
return false;
}
BOOL
vncEncoder::SendEmptyCursorShape(VSocket *outConn)
{
rfbFramebufferUpdateRectHeader hdr;
hdr.r.x = Swap16IfLE(0);
hdr.r.y = Swap16IfLE(0);
hdr.r.w = Swap16IfLE(0);
hdr.r.h = Swap16IfLE(0);
if (m_use_xcursor) {
hdr.encoding = Swap32IfLE(rfbEncodingXCursor);
} else {
hdr.encoding = Swap32IfLE(rfbEncodingRichCursor);
}
return outConn->SendExactQueue((char *)&hdr, sizeof(hdr));
}
BOOL
vncEncoder::SendCursorShape(VSocket *outConn, vncDesktop *desktop)
{
// Make sure the function is used correctly
if (!m_use_xcursor && !m_use_richcursor)
return FALSE;
// Check mouse cursor handle
HCURSOR hcursor = desktop->GetCursor();
if (hcursor == NULL) {
vnclog.Print(LL_INTINFO, VNCLOG("cursor handle is NULL.\n"));
return FALSE;
}
// Get cursor info
ICONINFO IconInfo;
if (!GetIconInfo(hcursor, &IconInfo)) {
vnclog.Print(LL_INTINFO, VNCLOG("GetIconInfo() failed.\n"));
return FALSE;
}
BOOL isColorCursor = FALSE;
if (IconInfo.hbmColor != NULL) {
isColorCursor = TRUE;
DeleteObject(IconInfo.hbmColor);
}
if (IconInfo.hbmMask == NULL) {
vnclog.Print(LL_INTINFO, VNCLOG("cursor bitmap handle is NULL.\n"));
return FALSE;
}
// Check bitmap info for the cursor
BITMAP bmMask;
if (!GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask)) {
vnclog.Print(LL_INTINFO, VNCLOG("GetObject() for bitmap failed.\n"));
DeleteObject(IconInfo.hbmMask);
return FALSE;
}
if (bmMask.bmPlanes != 1 || bmMask.bmBitsPixel != 1) {
vnclog.Print(LL_INTINFO, VNCLOG("incorrect data in cursor bitmap.\n"));
DeleteObject(IconInfo.hbmMask);
return FALSE;
}
// Get monochrome bitmap data for cursor
// NOTE: they say we should use GetDIBits() instead of GetBitmapBits().
BYTE *mbits = new BYTE[bmMask.bmWidthBytes * bmMask.bmHeight];
if (mbits == NULL)
return FALSE;
BOOL success = GetBitmapBits(IconInfo.hbmMask,
bmMask.bmWidthBytes * bmMask.bmHeight, mbits);
DeleteObject(IconInfo.hbmMask);
if (!success) {
vnclog.Print(LL_INTINFO, VNCLOG("GetBitmapBits() failed.\n"));
delete[] mbits;
return FALSE;
}
// Compute cursor dimensions
int width = bmMask.bmWidth;
int height = (isColorCursor) ? bmMask.bmHeight : bmMask.bmHeight/2;
// Call appropriate routine to send cursor shape update
if (!isColorCursor && m_use_xcursor) {
FixCursorMask(mbits, NULL, width, bmMask.bmHeight, bmMask.bmWidthBytes);
success = SendXCursorShape(outConn, mbits,
IconInfo.xHotspot, IconInfo.yHotspot,
width, height);
}
else if (m_use_richcursor) {
int cbits_size = width * height * 4;
BYTE *cbits = new BYTE[cbits_size];
if (cbits == NULL) {
delete[] mbits;
return FALSE;
}
if (!desktop->GetRichCursorData(cbits, hcursor, width, height)) {
vnclog.Print(LL_INTINFO, VNCLOG("vncDesktop::GetRichCursorData() failed.\n"));
delete[] mbits;
delete[] cbits;
return FALSE;
}
FixCursorMask(mbits, cbits, width, height, bmMask.bmWidthBytes);
success = SendRichCursorShape(outConn, mbits, cbits,
IconInfo.xHotspot, IconInfo.yHotspot,
width, height);
delete[] cbits;
}
else {
success = FALSE; // FIXME: We could convert RichCursor -> XCursor.
}
// Cleanup
delete[] mbits;
return success;
}
BOOL
vncEncoder::SendXCursorShape(VSocket *outConn, BYTE *mask,
int xhot, int yhot, int width, int height)
{
rfbFramebufferUpdateRectHeader hdr;
hdr.r.x = Swap16IfLE(xhot);
hdr.r.y = Swap16IfLE(yhot);
hdr.r.w = Swap16IfLE(width);
hdr.r.h = Swap16IfLE(height);
hdr.encoding = Swap32IfLE(rfbEncodingXCursor);
BYTE colors[6] = { 0, 0, 0, 0xFF, 0xFF, 0xFF };
int maskRowSize = (width + 7) / 8;
int maskSize = maskRowSize * height;
if ( !outConn->SendExactQueue((char *)&hdr, sizeof(hdr)) ||
!outConn->SendExactQueue((char *)colors, 6) ||
!outConn->SendExactQueue((char *)&mask[maskSize], maskSize) ||
!outConn->SendExactQueue((char *)mask, maskSize) ) {
return FALSE;
}
return TRUE;
}
BOOL
vncEncoder::SendRichCursorShape(VSocket *outConn, BYTE *mbits, BYTE *cbits,
int xhot, int yhot, int width, int height)
{
rfbFramebufferUpdateRectHeader hdr;
hdr.r.x = Swap16IfLE(xhot);
hdr.r.y = Swap16IfLE(yhot);
hdr.r.w = Swap16IfLE(width);
hdr.r.h = Swap16IfLE(height);
hdr.encoding = Swap32IfLE(rfbEncodingRichCursor);
// Cet cursor image in local pixel format
int srcbuf_rowsize = width * (m_localformat.bitsPerPixel / 8);
while (srcbuf_rowsize % sizeof(DWORD))
srcbuf_rowsize++; // Actually, this should never happen
// Translate image to client pixel format
int dstbuf_size = width * height * (m_remoteformat.bitsPerPixel / 8);
BYTE *dstbuf = new BYTE[dstbuf_size];
Translate(cbits, dstbuf, width, height, srcbuf_rowsize);
// Send the data
int mask_rowsize = (width + 7) / 8;
int mask_size = mask_rowsize * height;
if ( !outConn->SendExactQueue((char *)&hdr, sizeof(hdr)) ||
!outConn->SendExactQueue((char *)dstbuf, dstbuf_size) ||
!outConn->SendExactQueue((char *)mbits, mask_size) ) {
delete[] dstbuf;
return FALSE;
}
delete[] dstbuf;
return TRUE;
}
void
vncEncoder::FixCursorMask(BYTE *mbits, BYTE *cbits,
int width, int height, int width_bytes)
{
int packed_width_bytes = (width + 7) / 8;
// Pack and invert bitmap data (mbits)
int x, y;
for (y = 0; y < height; y++)
for (x = 0; x < packed_width_bytes; x++)
mbits[y * packed_width_bytes + x] = ~mbits[y * width_bytes + x];
// Replace "inverted background" bits with black color to ensure
// cross-platform interoperability. Not beautiful but necessary code.
if (cbits == NULL) {
BYTE m, c;
height /= 2;
for (y = 0; y < height; y++) {
for (x = 0; x < packed_width_bytes; x++) {
m = mbits[y * packed_width_bytes + x];
c = mbits[(height + y) * packed_width_bytes + x];
mbits[y * packed_width_bytes + x] |= ~(m | c);
mbits[(height + y) * packed_width_bytes + x] |= ~(m | c);
}
}
} else {
int bytes_pixel = m_localformat.bitsPerPixel / 8;
int bytes_row = width * bytes_pixel;
while (bytes_row % sizeof(DWORD))
bytes_row++; // Actually, this should never happen
BYTE bitmask;
int b1, b2;
for (y = 0; y < height; y++) {
bitmask = 0x80;
for (x = 0; x < width; x++) {
if ((mbits[y * packed_width_bytes + x / 8] & bitmask) == 0) {
for (b1 = 0; b1 < bytes_pixel; b1++) {
if (cbits[y * bytes_row + x * bytes_pixel + b1] != 0) {
mbits[y * packed_width_bytes + x / 8] ^= bitmask;
for (b2 = b1; b2 < bytes_pixel; b2++)
cbits[y * bytes_row + x * bytes_pixel + b2] = 0x00;
break;
}
}
}
if ((bitmask >>= 1) == 0)
bitmask = 0x80;
}
}
}
}
// Translate a rectangle (using arbitrary m_bytesPerRow value,
// always translating from the beginning of the source pixel array)
// NOTE: overloaded function!
inline void
vncEncoder::Translate(BYTE *source, BYTE *dest, int w, int h, int bytesPerRow)
{
// Call the translation function
(*m_transfunc) (m_transtable, &m_localformat, &m_transformat,
(char *)source, (char *)dest, bytesPerRow, w, h);
}
*****************************************************************************************
// 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);
}
评论这张
转发至微博
转发至微博
评论