浅析winvnc鼠标和屏幕信息的传送和鼠标数据算法处理
1.client端
void ClientConnection::Run()
==>ClientConnection::ReadServerInit
void ClientConnection::ReadServerInit()
{
ReadExact((char *)&m_si, sz_rfbServerInitMsg);
m_si.framebufferWidth = Swap16IfLE(m_si.framebufferWidth); // 比如1204
m_si.framebufferHeight = Swap16IfLE(m_si.framebufferHeight); // 比如768
m_si.format.redMax = Swap16IfLE(m_si.format.redMax);
m_si.format.greenMax = Swap16IfLE(m_si.format.greenMax);
m_si.format.blueMax = Swap16IfLE(m_si.format.blueMax);
m_si.nameLength = Swap32IfLE(m_si.nameLength);
...
}
ClientConnection::WndProc
==>
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
{
if (!_this->m_running) return 0;
if (GetFocus() != hwnd) return 0;
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if (_this->InFullScreenMode()) {
if (_this->BumpScroll(x,y))
return 0;
}
if ( _this->m_opts.m_ViewOnly) return 0;
_this->ProcessPointerEvent(x,y, wParam, iMsg);
return 0;
}
==>ClientConnection::ProcessPointerEvent(int x, int y, DWORD keyflags, UINT msg)
==>SubProcessPointerEvent(x, y, keyflags);
inline void
ClientConnection::SubProcessPointerEvent(int x, int y, DWORD keyflags)
{
int mask;
if (m_opts.m_SwapMouse) {
mask = ( ((keyflags & MK_LBUTTON) ? rfbButton1Mask : 0) |
((keyflags & MK_MBUTTON) ? rfbButton3Mask : 0) |
((keyflags & MK_RBUTTON) ? rfbButton2Mask : 0) );
} else {
mask = ( ((keyflags & MK_LBUTTON) ? rfbButton1Mask : 0) |
((keyflags & MK_MBUTTON) ? rfbButton2Mask : 0) |
((keyflags & MK_RBUTTON) ? rfbButton3Mask : 0) );
}
try {
SendPointerEvent((x + m_hScrollPos) * m_opts.m_scale_den / m_opts.m_scale_num,
// client的实际窗口尺寸与server的framebufferWidth之间存在的scale缩放比例数值[luther.gliethttp].
(y + m_vScrollPos) * m_opts.m_scale_den / m_opts.m_scale_num, mask);
} catch (Exception &e) {
e.Report();
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}
}
inline void
ClientConnection::SendPointerEvent(int x, int y, int buttonMask)
{
rfbPointerEventMsg pe;
pe.type = rfbPointerEvent;
pe.buttonMask = buttonMask;
if (x < 0) x = 0;
if (y < 0) y = 0;
pe.x = Swap16IfLE(x);
pe.y = Swap16IfLE(y);
WriteExact((char *)&pe, sz_rfbPointerEventMsg);
}
2.server端
void vncClientThread::run(void *arg)
{
....
// Send the server format message to the client
rfbServerInitMsg server_ini;
server_ini.format = m_client->m_buffer->GetLocalFormat();
// Endian swaps
server_ini.framebufferWidth = Swap16IfLE(m_client->m_fullscreen.right); // 比如1204
server_ini.framebufferHeight = Swap16IfLE(m_client->m_fullscreen.bottom); // 比如768
server_ini.format.redMax = Swap16IfLE(server_ini.format.redMax);
server_ini.format.greenMax = Swap16IfLE(server_ini.format.greenMax);
server_ini.format.blueMax = Swap16IfLE(server_ini.format.blueMax);
server_ini.nameLength = Swap32IfLE(strlen(desktopname));
m_socket->SendExact((char *)&server_ini, sizeof(server_ini)); // 发送server信息1
m_socket->SendExact(desktopname, strlen(desktopname)); // 发送server信息2
...
}
vncDesktop::InitBitmap()
==>
m_bmrect.left = m_bmrect.top = 0;
m_bmrect.right = GetDeviceCaps(m_hrootdc, HORZRES); // 屏幕水平像素,比如:1024
m_bmrect.bottom = GetDeviceCaps(m_hrootdc, VERTRES); // 屏幕垂直像素,比如:768
vncDesktop::SetPixFormat
==>m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.right - m_bmrect.left);
vncClientThread::run
==>m_client->m_fullscreen = m_client->m_buffer->GetSize();
RECT
vncBuffer::GetSize()
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = m_scrinfo.framebufferWidth;
rect.bottom = m_scrinfo.framebufferHeight;
return rect;
}
server端,计算client发送过来的鼠标坐标
case rfbPointerEvent:
DWORD flags = MOUSEEVENTF_ABSOLUTE; // 鼠标值为绝对数值,非dx相对移动数值[luther.gliethttp]
// Generate coordinate values
unsigned long x = (msg.pe.x * 65535) / (m_client->m_fullscreen.right); // 计算绝对鼠标坐标值x
unsigned long y = (msg.pe.y * 65535) / (m_client->m_fullscreen.bottom); // 计算绝对鼠标坐标值y
如果使用了MOUSEEVENTF_ABSOLUTE标志,说明windows系统内部处理鼠标坐标时,屏幕左上角内部坐标值(0,0),屏幕右下角内部坐标值(65535,65535),
msg.pe.x和msg.pe.y是client经过绝对计算之后,server鼠标实际坐标值,因为我们要传递鼠标实际值,而不是相对值(dx,dy),所以
我们就需要将实际像素坐标转换为屏幕内部坐标值,比如:server的屏幕分辨率设为1024*768,那么
x=1024就应该对应内部坐标xo=65535,
y=768就应该对应内部坐标yo=768,所以(x,y)要先通过上面的转换,才能传给windows正常显示鼠标位置,因为windows系统内部,发现MOUSEEVENTF_ABSOLUTE标志
之后,会在windows系统内部执行相应的反运算,进而反应真实鼠标位置到1024*768分辨率的显示器上[luther.gliethttp].
比如我们现在由client传过来的坐标为(123,456),那么我们需要这样转换
123*(65535/1024)
456*(65535/768)
但是上面计算精度不高,所以就有了vnc server上面的转换代码,
(123*65535) / 1024
(456*65535) / 768
我想这样大家就应该明白了吧[luther.gliethttp]:)
|