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. 1155 Heap Paths

    题干前半略. Sample Input 1: 8 98 72 86 60 65 12 23 50   Sample Output 1: 98 86 23 98 86 12 98 72 65 98 72 ...

  2. The 2019 ICPC Asia Shanghai Regional Contest H Tree Partition k、Color Graph

    H题意: 给你一个n个节点n-1条无向边构成的树,每一个节点有一个权值wi,你需要把这棵树划分成k个子树,每一个子树的权值是这棵子树上所有节点权值之和. 你要输出这k棵子树的权值中那个最大的.你需要让 ...

  3. enumerate() -- Python

    #!usr/bin/env python #coding:utf-8 ''' enumerate()说明: 1.enumerate()是Python的内置函数: 2.enumerate字面上是枚举.列 ...

  4. 国产网络损伤仪SandStorm -- 基本概念:什么是仿真引擎

    "仿真引擎"在网络损伤仪SandStorm(www.minismb.com)或者网络IP仿真损伤仪中是一个最基本概念,它就相当于一个由两个物理以太网口组成的"网桥&quo ...

  5. 一个http请求的完整详细过程

    整个流程 域名解析: 与服务器建立连接:tcp连接: 发起HTTP请求: 服务器响应HTTP请求,浏览器得到html代码: 浏览器解析html代码,并请求html代码中的资源(如js.css.图片): ...

  6. spfa+链式前向星模板

    #include<bits/stdc++.h> #define inf 1<<30 using namespace std; struct Edge{ int nex,to,w ...

  7. springboot(六)Email demo

    项目中经常使用邮件发送提醒功能,比如说更新安全机制,发送邮件通知用户等 一.简单邮件发送 导入依赖: <dependency> <groupId>org.springframe ...

  8. vue slot nested bug

    vue slot nested bug slot name bug Error <slot name="global-system-guide-slot"></s ...

  9. YouTube 视频下载工具

    YouTube 视频下载工具 我不生产视频,只是优秀视频的搬运工! YouTube-dl https://github.com/search?q=youtube-dl https://github.c ...

  10. GitHub & GraphQL API

    GitHub & GraphQL API https://gist.github.com/xgqfrms/15559e7545f558d85c5efdea79171a3d refs xgqfr ...