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. Codeforces Round #647 (Div. 2) D. Johnny and Contribution(BFS)

    题目链接:https://codeforces.com/contest/1362/problem/D 题意 有一个 $n$ 点 $m$ 边的图,每个结点有一个从 $1 \sim n$ 的指定数字,每个 ...

  2. POJ-3984 迷宫问题 (BFS)

    题意:有一个\(5\)X\(5\)的\(01\)图,输出从左上角走到右下角的最短路径. 题解:基础的bfs,这里困难的是如何输出这个最短路径,我们可以用一个结构体来存点和路径,我们每次向外去拓展的时候 ...

  3. BIM技术基础-Revit2019建筑

    第三章 三维信息建模(中) 3.5 拉伸屋顶的创建 拉伸屋顶→ 在立面中创建,屋顶的形状在垂直截面上一致的 利用参照平面或者已有的墙面或者已有的轴线 3.5.1利用拾取平面创建 画一个参照平面作为屋檐 ...

  4. 在Python中使用BeautifulSoup进行网页爬取

    目录 什么是网页抓取? 为什么我们要从互联网上抓取数据? 网站采集合法吗? HTTP请求/响应模型 创建网络爬虫 步骤1:浏览并检查网站/网页 步骤2:创建用户代理 步骤3:导入请求库 检查状态码 步 ...

  5. 排序算法 以及HKU的一些数据结构 相关题目 以及 K叉树,二叉树 排列

    冒泡排序.选择排序.快速排序.插入排序.希尔排序.归并排序.基数排序以及堆排序,桶排序 https://www.cnblogs.com/Glory-D/p/7884525.html https://b ...

  6. Http和Https之为什么Https更安全

    [除夕了,加油干.希望自己新的一年万事顺意,祝大家身体健康,心想事成!] 我们都知道 HTTPS 安全,可是为什么安全呢? 看小电影还是浏览正常网站,一定要检查是不是 HTTPS 的,因为Https相 ...

  7. hihoCoder Challenge 1

    #1034 : 毁灭者问题 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在 Warcraft III 之冰封王座中,毁灭者是不死族打三本后期时的一个魔法飞行单位. 毁 ...

  8. TypeScript 4.1 Quick Start Tutorials

    TypeScript 4.1 Quick Start Tutorials TypeScript 4.1 快速上手教程 https://typescript-41-quick-start-tutoria ...

  9. Lua 从入门到放弃

    Lua 从入门到放弃 What is Lua? Lua is a powerful, efficient, lightweight, embeddable scripting language. It ...

  10. bash for mac

    bash for mac https://sourabhbajaj.com/mac-setup/iTerm/ https://sourabhbajaj.com/mac-setup/iTerm/zsh. ...