VNC源码阅读--VNC图像更新机制

VNC源码阅读--VNC图像更新机制  


VNC的图像更新机制核心为,桌面区域更新记录策略和更新区域通知策略。桌面更新区域记录主要是通过hooks记录桌面上变化的矩形区域,只记录更新的矩形区不记录具体更新的数据。更新区域记录步骤大致如下:1.wm_hooks截获桌面变化的相关消息,并转化为自定义的消息发送给WMHooksThread线程处理。 2. WMHooksThread 中用SimpleUpdateTracker new_changes记录新的更新区域.3.把SimpleUpdateTracker new_changes更新拷贝到SDisplay中。4.每次要发送桌面更新的时候,把SDisplay中记录的更新区域传给VNCServerST 对象中。更新区域的通知主要有poll和push两种机制。push是服务器每隔10ms检查有没有更新,如果有更新则主动把更新推送给客户端,poll机制则是客户端主动请求更新,客户端通过发送framebufferupdate请求某一个区域更新,服务器处理该消息发送相应的更新。详细分析如下:

1.Wm_hooks截获消息并转化为自定义的消息发送给WMHooksThread线程处理。

Wm_hooks自定义的消息:

UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));

UINT WM_HK_WindowClientAreaChanged = UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));

UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));

UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));

钩子截获到消息以后,把它转化为自定义的消息,然后发送给WMHooksThread线程处理,消息转化如下:

边框更新消息:WM_NCPAINT,WM_NCACTIVATE

客户区域更新消息:BM_SETCHECK, BM_SETSTATE,EM_SETSEL,WM_CHAR,WM_ENABLE,WM_KEYUP,WM_LBUTTONUP,WM_MBUTTONUP,WM_PALETTECHANGED,WM_RBUTTONUP,WM_SYSCOLORCHANGE,WM_SETTEXT。

窗口改变消息:WM_HSCROLL,WM_VSCROLL,482,485。

矩形区更新消息:WM_DESTROY

窗口客户区消息:WM_PAINT

鼠标消息:WM_NCMOUSEMOVE,WM_MOUSEMOVE

2 . WMHooksThread 中用SimpleUpdateTracker new_changes记录新的更新区域

WMHooksThread::run() 函数中先判断出矩形区域改变的大小,然后调用NotifyHooksRegion(const Region& r)把改变的区域记录到SimpleUpdateTracker new_changes中。

NotifyHooksRegion(const Region& r) {

Lock l(hook_mgr_lock);

std::list<WMHooks*>::iterator i;

for (i=hooks.begin(); i!=hooks.end(); i++) {

    (*i)->new_changes.add_changed(r);

    if (!(*i)->notified) {

      (*i)->notified = true;

      PostMessage((*i)->getHandle(), WM_USER, 0, 0); // 把消息通知到clipper见下面一个处理函数

    }

}

}

3.把更新区域拷贝到SDisplay中

rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {

switch (msg) {

case WM_USER:

    {

      Sleep(0);

      Lock l(hook_mgr_lock);

      notified = false;

      new_changes.get_update(*clipper); //把更新通知到clipper中

      new_changes.clear();

    }

    break;

}

return MsgWindow::processMessage(msg, wParam, lParam);

}

Cliper在下面设置

rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {

if (clipper) delete clipper;

clipper = new ClippedUpdateTracker(*ut);

clipper->set_clip_region(clip_region);

return AddHook(this);

}

UpdateTracker* ut 为void SDisplay::start(VNCServer*vs)中设置 core->using_hooks = core->wm_hooks.setUpdateTracker(this);

4.把SDisplay中记录的数据传给VNCServerST 对象

在 SDisplay::processEvent(HANDLE event) {

try_update = flushChangeTracker() || try_update; //把变化的区域拷贝到VNCServerST中

      if (try_update)

        server->tryUpdate();          //把更新发送给服务器

}

flushChangeTracker()实现如下:

bool SDisplay::flushChangeTracker() {

if (change_tracker.is_empty())

    return false;

change_tracker.translate(screenRect.tl.negate());

change_tracker.get_update(*server); //server 实际指向VNCServerST 对象该函数把SDisplay中的更新拷贝到VNCServerST中。

change_tracker.clear();

return true;

}

两种数据更新方式:Push机制和Pull机制

Push:

SdisplayCore 中IntervalTimer cursorTimer定时器,每隔10ms尝试着检查一下是否有更新,如果有更新就发送更新给客户端。

第一步:

LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {

case TIMER_CURSOR:

     display->triggerUpdate(); //SDisplay* display;

}

第二步:

void SDisplay::triggerUpdate() {

     if (core)

         SetEvent(updateEvent); //使事件对象为受信状态

}

第三步:

SDisplay::processEvent(HANDLE event) {

     if (event == updateEvent) {

         if (try_update)

                   server->tryUpdate(); // VNCServer* server指针 指向子类VNCServerST

}

}

第四步:向每一个连接的客户端发送更新

void VNCServerST::tryUpdate()

{

     std::list<VNCSConnectionST*>::iterator ci, ci_next;

     for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {

         ci_next = ci; ci_next++;

         (*ci)->writeFramebufferUpdateOrClose();

     }

}

第五步:

void VNCSConnectionST::writeFramebufferUpdateOrClose()

{

     try {

         writeFramebufferUpdate();

     } catch(rdr::Exception &e) {

         close(e.str());

     }

}

第六步:SimpleUpdateTracker updates对象记录更新的区域,如果屏幕有更新则发送更新

void VNCSConnectionST::writeFramebufferUpdate(){

if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {

         int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);

         writer()->writeFramebufferUpdateStart(nRects);

         Region updatedRegion;

         writer()->writeRects(update, &image_getter, &updatedRegion); // SmsgWriter *

         updates.subtract(updatedRegion);

         if (drawRenderedCursor)

              writeRenderedCursorRect();

         writer()->writeFramebufferUpdateEnd();

         requested.clear();

     }

}

第七步:利用RFB协议发送更新

void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)

{

     startMsg(msgTypeFramebufferUpdate);

     os->pad(1);

     if (wsccb) nRects++;

     if (needSetDesktopSize) nRects++;

     os->writeU16(nRects);

     nRectsInUpdate = 0;

     nRectsInHeader = nRects;

     if (wsccb) {

         wsccb->writeSetCursorCallback();

         wsccb = 0;

     }


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值