APP主循环

MainLoop

前面的wxApp的启动代码可以看到,执行完成wxApp::OnInit()函数后,接着就执行wxApp::OnRun()函数进入App的主循环,wxApp继承自wxAppBase,所以实际调用的是wxApp::OnRun(),过程如下:

wxAppBase::OnRun() -> wxAppConsole::OnRun() -> wxAppConsoleBase::MainLoop()

调用关系如下:

// src/common/appbase.cpp
int wxAppBase::OnRun() { return wxAppConsole::OnRun(); }
int wxAppConsoleBase::OnRun()
{
return MainLoop();
}

下面继续分析wxAppConsoleBase::MainLoop()的代码:

  1. 构建消息循环辅助对象wxEventLoopBaseTiedPtr
  2. 调用当前App的OnLaunched()方法;
  3. 调用主循环Run()函数,但是这个Run函数到底是谁的函数呢?
// src/common/appbase.cpp
int wxAppConsoleBase::MainLoop()
{
wxEventLoopBaseTiedPtr mainLoop(&m_mainLoop, CreateMainLoop()); if (wxTheApp)
wxTheApp->OnLaunched(); return m_mainLoop ? m_mainLoop->Run() : -1;
} wxEventLoopBase *wxAppConsoleBase::CreateMainLoop()
{
return GetTraits()->CreateEventLoop();
}
wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
{
return new wxEventLoop;
}

消息循环对象的创建

上文遗留一个问题,Run函数到底是哪个函数,这个问题肯定在wxEventLoopBaseTiedPtr对象中,我们看看这个消息循环辅助对象的创建:

  1. wxEventLoopBaseTiedPtr从哪里来的呢,这里需要重点关注宏展开:
  1. 使用wxDEFINE_TIED_SCOPED_PTR_TYPE宏创建出来的,wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopBase)创建了wxEventLoopBaseTiedPtr类型;
  2. wxEventLoopBaseTiedPtr继承自wxEventLoopBasePtr,父类是通过 wxDEFINE_SCOPED_PTR_TYPE宏创建的,对于此对象来说创建的就是wxEventLoopBasePtr。
  1. 操作中需要重点关注的2个函数:T * operator->() constT & operator*() const
  1. 参考构造函数可以得出,这个变了保存的就是wxApp::m_mainLoop变量的地址,同时在构造函数中将wxApp::CreateMainLoop()赋值给了wxApp::m_mainLoop变量;
  2. 这两个函数是重载操作符,通过->操作实际使用的的是内部保存的*m_ptr变量,这样在通过m_mainLoop->Run()调用时,实际调用的是wxApp::m_mainLoop->Run()函数;

源代码如下:

// src/common/appbase.cpp
// 类型 wxEventLoopBaseTiedPtr 的定义
wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopBase) // include/wx/scopedptr.h
// wxDEFINE_TIED_SCOPED_PTR_TYPE 宏定义,可以看到这里的定义:
// 1. 定义了 wxEventLoopBaseTiedPtr 类型
// 2. 对应的父类是 wxEventLoopBasePtr
#define wxDEFINE_TIED_SCOPED_PTR_TYPE(T) \
wxDEFINE_SCOPED_PTR_TYPE(T) \
class T ## TiedPtr : public T ## Ptr \
{ \
public: \
T ## TiedPtr(T **pp, T *p) \
: T ## Ptr(p), m_pp(pp) \
{ \
m_pOld = *pp; \
*pp = p; \
} \
private: \
T **m_pp; \
T *m_pOld; \
}; // 进而我们看下父类 wxEventLoopBasePtr 的定义
// 改类中 重载*和->操作符
#define wxDEFINE_SCOPED_PTR_TYPE(T) \
wxDECLARE_SCOPED_PTR(T, T ## Ptr) \
wxDEFINE_SCOPED_PTR(T, T ## Ptr) #define wxDECLARE_SCOPED_PTR(T, name) \
class name \
{ \
...
// 重载*和->操作符,很关键
T & operator*() const \
{ \
wxASSERT(m_ptr != NULL); \
return *m_ptr; \
} \
T * operator->() const \
{ \
wxASSERT(m_ptr != NULL); \
return m_ptr; \
} \
};

再回头我们看下 wxAppConsoleBase::CreateMainLoop() 函数的实现,此对象返回了一个新创建的wxEventLoop对象,过程如下:

  1. 调用 CreateMainLoop 这个函数是父类的函数;
  2. wxAppConsoleBase::GetTraits()会调用CreateTraits来创建traits,这里CreateTraits是虚方法,而且wxApp的继承关系是wxAppw -> wxAppBase,所以实际调用的是wxAppBase::CreateTraits(),所以这个loopBase实际是调用wxGUIAppTraits::CreateEventLoop()实现消息循环对象的创建,最终创建了 wxEventLoop 对象;
// src/common/appbase.cpp
//
wxEventLoopBase *wxAppConsoleBase::CreateMainLoop()
{
return GetTraits()->CreateEventLoop();
} // src/common/appcmn.cpp
// 虚方法,调用最后一次的实现
wxAppTraits *wxAppBase::CreateTraits()
{
return new wxGUIAppTraits;
} wxAppTraits *wxAppConsoleBase::GetTraits()
{
// FIXME-MT: protect this with a CS?
if ( !m_traits )
{
m_traits = CreateTraits(); wxASSERT_MSG( m_traits, wxT("wxApp::CreateTraits() failed?") );
} return m_traits;
} // src/common/app.cpp
wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
{
return new wxEventLoop;
}

消息循环

上面的代码分析,我们知道消息循环对象是wxEventLoop类型的,此对象保存在wxAppConsoleBase::m_mainLoop变量中,接着调用m_mainLoop->Run() 执行(下面的分析都是基于windows系统,其他的系统类似):

跟踪代码,先理清楚 wxEventLoop 的继承关系:

wxEventLoop -> wxGUIEventLoop -> wxMSWEventLoopBase -> wxEventLoopManual -> wxEventLoopBase

接着Run函数实际调用的是wxEventLoopBase::Run()

// src/common/apploopcmn.cpp
int wxEventLoopBase::Run()
{
// ...忽略非关键流程的代码
// Finally really run the loop.
return DoRun();
}

DoRun函数是wxEventLoopBase定义的纯虚方法,所以这个函数会调用最接近wxEventLoop的实现,这里调用的是wxEventLoopManual::DoRun(),继续看此函数的实现(只保留关键代码):

此函数有2层循环:

  1. 正常的消息循环,所有的用户消息都在这里接收和派发,如果收到WM_QUIT消息则退出此循环,进入到退出前剩余消息的循环;
  2. 退出前剩余消息的循环,检查剩余的Pending状态的消息,如果有则处理之,所有Pending状态的消息都处理完成后,退出此循环,函数返回。
int wxEventLoopManual::DoRun()
{
// this is the event loop itself
for ( ;; )
{
// a message came or no more idle processing to do, dispatch
// all the pending events and call Dispatch() to wait for the
// next message
if ( !ProcessEvents() )
{
// we got WM_QUIT
break;
}
} for ( ;; )
{
bool hasMoreEvents = false;
if ( wxTheApp && wxTheApp->HasPendingEvents() )
{
wxTheApp->ProcessPendingEvents();
hasMoreEvents = true;
} if ( Pending() )
{
Dispatch();
hasMoreEvents = true;
} if ( !hasMoreEvents )
break;
} return m_exitcode;
}

继续跟踪 wxEventLoopManual::ProcessEvents() 的处理:

  1. 检查是否有Pending的消息,如果有则处理之,这些消息是在收到本消息前还未处理的消息,所以优先处理,如果是程序退出请求,则完成并退出;
  2. 否则继续调用Dispatch处理当前消息。
bool wxEventLoopManual::ProcessEvents()
{
if ( wxTheApp )
{
wxTheApp->ProcessPendingEvents();
if ( m_shouldExit )
return false;
} return Dispatch();
}

Dispatch是虚方法,此方法是体系结构相关的,需要实现在各体系结构中,对于本流程来说,调用的是wxGUIEventLoop::Dispatch(),这个函数的处理过程就是获取到下一条消息,然后处理。

函数中使用到了另外两个虚方法GetNextMessageProcessMessage,分别调用的是wxMSWEventLoopBase::GetNextMessagewxGUIEventLoop::ProcessMessage

// src/msw/evtloop.cpp
// 体系结构相关的函数,用户派发消息
bool wxGUIEventLoop::Dispatch()
{
MSG msg;
if ( !GetNextMessage(&msg) )
return false; ProcessMessage(&msg);
return true;
} // 调用Win32API执行windows系统的消息收发
bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg)
{
const BOOL rc = ::GetMessage(msg, NULL, 0, 0);
...
return true;
}

消息派发

上文中讲述了消息循环的过程,接着我们看消息的派发过程:

消息派发是由wxGUIEventLoop::ProcessMessage处理的,先预处理,也就是本窗口处理,如果是本窗口的消息则处理掉,否则再调用::DispatchMessage分发给对应的窗口。

// 派发消息
void wxGUIEventLoop::ProcessMessage(WXMSG *msg)
{
// give us the chance to preprocess the message first
if ( !PreProcessMessage(msg) )
{
// if it wasn't done, dispatch it to the corresponding window
::TranslateMessage(msg);
::DispatchMessage(msg);
}
}

我们先看看消息的预处理过程wxGUIEventLoop::PreProcessMessage

  1. 通过msg的HWND来获取wxWindow指针,通过调用wxGetWindowFromHWND实现,具体如何获取到的后文有介绍;
  2. tooltip消息的处理,鼠标移动时调用;
  3. accelerators消息,这种消息处理优先级最高;
  4. 其他消息的处理。

注意到消息处理流程:从本窗口开始,如果本窗口不处理则丢给父窗口处理,直到最顶层窗口,如果还没有处理则返回失败,继续调用系统函数分发之。

bool wxGUIEventLoop::PreProcessMessage(WXMSG *msg)
{
HWND hwnd = msg->hwnd;
wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
wxWindow *wnd; #if wxUSE_TOOLTIPS
// we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
// popup the tooltip bubbles
if ( msg->message == WM_MOUSEMOVE )
{
// we should do it if one of window children has an associated tooltip
// (and not just if the window has a tooltip itself)
if ( wndThis->HasToolTips() )
wxToolTip::RelayEvent((WXMSG *)msg);
}
#endif // wxUSE_TOOLTIPS // try translations first: the accelerators override everything
for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
{
if ( wnd->MSWTranslateMessage((WXMSG *)msg))
return true;
if ( wnd->IsTopLevel() )
break;
} // now try the other hooks (kbd navigation is handled here)
for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
{
if ( wnd->MSWProcessMessage((WXMSG *)msg) )
return true;
if ( wnd->IsTopLevel() )
break;
} // no special preprocessing for this message, dispatch it normally
return false;
}

总结

到此为止,消息的循环派发过程就已经可以正常使用了,在windows系统中,调用Win32的::DispatchMessage可以将消息发送给指定的窗口,实际的过程就是消息派发线程调用该窗口的WinProc函数。

wxWidgets源码分析(2) - App主循环的更多相关文章

  1. wxWidgets源码分析(1) - App启动过程

    目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wx ...

  2. Spring Ioc源码分析系列--自动注入循环依赖的处理

    Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...

  3. 源码分析:CyclicBarrier 之循环栅栏

    简介 CyclicBarrier 是一个同步辅助工具,允许一组线程全部等待彼此达到共同屏障点,且等待的线程被释放后还可以重新使用,所以叫做Cyclic(循环的). 应用场景 比如出去旅行时,导游需要等 ...

  4. Spring源码分析(十七)循环依赖

    本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 实例化bean是一个非常复杂的过程,而其中比较难以理解的就是对循环依赖的解决, ...

  5. wxWidgets源码分析(9) - wxString

    目录 wxString wxString的中文字符支持 Windows Linux Unicode Linux UTF-8 总结 wxString与通用字符串的转换 wxString对象的创建 将wx ...

  6. wxWidgets源码分析(8) - MVC架构

    目录 MVC架构 wxDocManager文档管理器 模板类创建文档对象 视图对象的创建 创建顺序 框架菜单命令的执行过程 wxDocParentFrame菜单入口 wxDocManager类的处理 ...

  7. nova-api源码分析(APP的调用)

    调用APIRouter的 __call__函数 nova/wsgi.py class Router(object): def __init__(self, mapper): self.map = ma ...

  8. nova-api源码分析(APP中用到的开源库)

    源码版本:H版 1.paste.deploy 参考文章: http://pythonpaste.org/deploy/ http://blog.csdn.net/xiangmin2587/articl ...

  9. Java源码分析(1):二分查找 + 循环递归实现

    源代码 源码地址 public static int binarySearch(int[] a, int key) { return binarySearch0(a, 0, a.length, key ...

随机推荐

  1. Educational Codeforces Round 67 E.Tree Painting (树形dp)

    题目链接 题意:给你一棵无根树,每次你可以选择一个点从白点变成黑点(除第一个点外别的点都要和黑点相邻),变成黑点后可以获得一个权值(白点组成连通块的大小) 问怎么使权值最大 思路:首先,一但根确定了, ...

  2. 【bzoj 2038】 [2009国家集训队]小Z的袜子(算法效率--莫队分块算法 模版题)

    题意:小Z有N只袜子,有不同的颜色.他有M个提问,问从编号为[L,R]的袜子中随机选一双同色的袜子的概率,用最简分数表示. 解法:经典的莫队算法--无修改.不强制在线(可离线).状态转移可以一步完成. ...

  3. leetcode18 四数之和 双指针

    题外话: 这道题让我想起了 挑战程序设计竞赛有一个抽签问题,类似的a+b+c+d=target,可以重复使用一个数. a+b+c+d=target转化为 a+b=target-c-d.  如果只是判断 ...

  4. PAT L2-020 功夫传人【BFS】

    一门武功能否传承久远并被发扬光大,是要看缘分的.一般来说,师傅传授给徒弟的武功总要打个折扣,于是越往后传,弟子们的功夫就越弱-- 直到某一支的某一代突然出现一个天分特别高的弟子(或者是吃到了灵丹.挖到 ...

  5. C++ part6.5

    1.虚函数表建立和虚函数表指针初始化 虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组.而虚函数表指针是在运行期,也就是构造函数被调用时进行初始化的,这是实现多态的 ...

  6. 开源软件ffmpeg使用中的问题

    error while decoding MB 20 10, bytestream -13 经过调试,发现这部是 int ret = avcodec_decode_video2(pCodecConte ...

  7. python xml转excle

    <?xml version="1.0" encoding="UTF-8"?> <RECORDS xmlns:xsi="http:// ...

  8. TypeScript 1.7 & TypeScript 1.8

    TypeScript 1.7 & TypeScript 1.8 1 1 https://zh.wikipedia.org/wiki/TypeScript TypeScript是一种由微软开发的 ...

  9. window.navigator All In One

    window.navigator All In One navigator "use strict"; /** * * @author xgqfrms * @license MIT ...

  10. HTML5 Animation Creator | Hype 官方文档

    HTML5 Animation Creator | Hype 官方文档 HTML5 Animation Build 7个 专业术语 场景,元素,属性,关键帧,动画,时间线,操作 Hype 官方文档 h ...