Qt程序提供了事件循环机制,那么它是怎么工作的,工作原理又是什么呢?让我们通过qt源码揭开它神秘的面纱。
在开始之前,我们先思考下面几个问题:
1.事件循环是何时,以及如何启动的?
2.对于Windows,Qt是如何捕获事件(如:鼠标事件,键盘按键事件)?如何对捕获到的事件进行处理的?
3.如何将Windows消息转换为QEvent?
4.用户自定义的事件又是如何被处理的?
一、Windows消息机制简介
在Windows操作系统,所有的WIN32程序都建立在消息循环的基础之上,windows的消息机制可以分为以下几大步:
- 注册窗口过程处理函数
- 创建窗体
- show窗体
- 获取消息
- 翻译消息
- 分发消息至窗口处理函数
二、事件循环初始化
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
首先实例化QApplication对象,在构造QApplication时初始化threadData,如下所示:
父对象如果存在则使用父对象的threadData,表明和父对象使用同一个工作线程处理事件,反之通过QThreadData::current()拿到当前线程threadData。对于主事件循环来说,则通过QThreadData::current()拿到当前主线程,通过主线程处理事件。
class QThreadData
{
public:
QThreadData(int initialRefCount = 1);
~QThreadData();
static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
/**
* 省略 .......
*/
public:
int loopLevel;
int scopeLevel;
QStack<QEventLoop *> eventLoops;
QPostEventList postEventList; // 待处理的事件列表
QAtomicPointer<QThread> thread; // 工作线程
QAtomicPointer<void> threadId; // 工作线程Id
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher; // 事件分发者,在不同平台下创建不同的事件分发者
QVector<void *> tls;
FlaggedDebugSignatures flaggedSignatures;
};
以windows为例,eventDispatcher的初始化:
事件分发者(eventDispatcher),初始化调用堆栈如上
Step 1
// src\corelib\kernel\qcoreapplication.cpp
QCoreApplication::QCoreApplication(int &argc, char **argv
#ifndef Q_QDOC
, int _internal
#endif
)
#ifdef QT_NO_QOBJECT
: d_ptr(new QCoreApplicationPrivate(argc, argv, _internal))
#else
: QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
#endif
{
d_func()->q_ptr = this;
d_func()->init(); // 调用QCoreApplicationPrivate的init函数
#ifndef QT_NO_QOBJECT
QCoreApplicationPrivate::eventDispatcher->startingUp();
#endif
}
程序在启动时,通过main函数实例化QApplication对象。在QCoreApplication构造函数中调用QCoreApplicationPrivate的init函数初始参数
Step 2
// src\corelib\kernel\qcoreapplication.cpp
void QCoreApplicationPrivate::init()
{
/**
* 省略 .......
*/
auto thisThreadData = threadData.loadRelaxed();
eventDispatcher = thisThreadData->eventDispatcher.loadRelaxed();
// otherwise we create one
if (!eventDispatcher)
createEventDispatcher(); // 调用createEventDispatcher创建事件分发者
Q_ASSERT(eventDispatcher);
if (!eventDispatcher->parent()) {
eventDispatcher->moveToThread(thisThreadData->thread.loadAcquire());
eventDispatcher->setParent(q);
}
thisThreadData->eventDispatcher = eventDispatcher; // 事件分发者赋值给threadData的eventDispatcher。当threadData.postEventList列表中有新加入的事件时,需要借助事件分发者向外投递一个消息,处理此事件
eventDispatcherReady();
/**
* 省略 .......
*/
}
在QCoreApplicationPrivate的init函数中,如果事件分发者没有创建则通过createEventDispatcher虚方发创建不同平台的事件分发者。
Step 3
// src\widgets\kernel\qapplication.cpp
void QApplicationPrivate::createEventDispatcher()
{
QGuiApplicationPrivate::createEventDispatcher();
}
// src\gui\kernel\qguiapplication.cpp
void QGuiApplicationPrivate::createEventDispatcher()
{
Q_ASSERT(!eventDispatcher);
if (platform_integration == nullptr)
createPlatformIntegration(); // 创建平台
// The platform integration should not mess with the event dispatcher
Q_ASSERT(!eventDispatcher);
eventDispatcher = platform_integration->createEventDispatcher(); // 通过对应平台的事件分发者
}
上段代码需要注意的是createEventDispatcher是一个虚方法,并且在每个类中都有实现。在此处则调用是QApplicationPrivate的该方法。在此处创建QWindowsIntegration以及QWindowsGuiEventDispatcher
Step 4
// src\gui\kernel\qguiapplication.cpp
void QGuiApplicationPrivate::createPlatformIntegration()
{
QHighDpiScaling::initHighDpiScaling();
// Load the platform integration
QString platformPluginPath = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH"));
/**
* 代码省略 .......
* 这部分代码通过从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称
*/
init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); // 创建平台插件
if (!icon.isEmpty())
forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon);
}
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'));
QStringList platformArguments;
QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath);
for (const auto &pluginArgument : plugins) {
// Split into platform name and arguments
QStringList arguments = pluginArgument.split(QLatin1Char(':'));
const QString name = arguments.takeFirst().toLower();
QString argumentsKey = name;
argumentsKey[0] = argumentsKey.at(0).toUpper();
arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey));
// Create the platform integration.
QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); // 通过插件工具,创建平台插件
if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) {
if (availablePlugins.contains(name)) {
qCInfo(lcQpaPluginLoading).nospace().noquote()
<< "Could not load the Qt platform plugin \"" << name << "\" in \""
<< QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found.";
} else {
qCWarning(lcQpaPluginLoading).nospace().noquote()
<< "Could not find the Qt platform plugin \"" << name << "\" in \""
<< QDir::toNativeSeparators(platformPluginPath) << "\"";
}
} else {
QGuiApplicationPrivate::platform_name = new QString(name);
platformArguments = arguments;
break;
}
}
/**
* 代码省略 .......
*/
}
从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称。将插件路径下的所有插件放入到列表中,遍历插件列表加载对应平台的插件。如在windows上则加载的是qwindows.dll
插件位于安装目录下如:C:\Qt\5.15.2\msvc2019_64\plugins\platforms
windows下qt程序包插件位于执行目录下的platforms目录下:
在linux平台下,通过QT_QPA_PLATFORM_PLUGIN_PATH环境变量加载平台插件(因为我是在交叉编译环境上运行,所以是qeglfs):
Step 5
// src\gui\kernel\qplatformintegrationfactory.cpp
QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList ¶mList, int &argc, char **argv, const QString &platformPluginPath)
{
#if QT_CONFIG(library)
// Try loading the plugin from platformPluginPath first:
if (!platformPluginPath.isEmpty()) {
QCoreApplication::addLibraryPath(platformPluginPath);
if (QPlatformIntegration *ret = qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(directLoader(), platform, paramList, argc, argv))
return ret;
}
#else
Q_UNUSED(platformPluginPath);
#endif
return qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(loader(), platform, paramList, argc, argv);
}
// src\corelib\plugin\qfactoryloader_p.h
template <class PluginInterface, class FactoryInterface, typename ...Args>
PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key, Args &&...args)
{
const int index = loader->indexOf(key);
if (index != -1) {
QObject *factoryObject = loader->instance(index); // 实例化平台的插件对象。此处在windows平台下,实际上是实例化QWindowsIntegrationPlugin对象
if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject))
if (PluginInterface *result = factory->create(key, std::forward<Args>(args)...))
return result;
}
return nullptr;
}
// src\plugins\platforms\windows\main.cpp
class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "windows.json") // 很关键,提供插件的导出符号。
public:
QPlatformIntegration *create(const QString&, const QStringList&, int &, char **);
};
QPlatformIntegration *QWindowsIntegrationPlugin::create(const QString& system, const QStringList& paramList, int &, char **)
{
if (system.compare(system, QLatin1String("windows"), Qt::CaseInsensitive) == 0)
return new QWindowsGdiIntegration(paramList); // 创建QWindowsGdiIntegration
return nullptr;
}
通过qt的插件机制,加载插件。通过插件提供的qt_plugin_instance接口实例化QWindowsIntegrationPlugin,然后创建QWindowsGdiIntegration
通过depends分析qwindows.dll插件,向外提供两个接口qt_plugin_instance和qt_plugin_query_metadata
Step 6
// rc\plugins\platforms\windows\qwindowsintegration.cpp
QAbstractEventDispatcher * QWindowsIntegration::createEventDispatcher() const
{
return new QWindowsGuiEventDispatcher;
}
QWindowsGdiIntegration继承QWindowsIntegration,因此调用createEventDispatcher创建事件分发者,创建的是QWindowsGuiEventDispatcher
三、启动事件循环
Step 1
// src\corelib\kernel\qcoreapplication.cpp
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -1;
QThreadData *threadData = self->d_func()->threadData;
if (threadData != QThreadData::current()) { // 如果不是主线程,则退出
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -1;
}
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
threadData->quitNow = false;
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
int returnCode = eventLoop.exec(); // 进入事件循环
threadData->quitNow = false;
if (self)
self->d_func()->execCleanup();
return returnCode;
}
在main函数中调用QApplication的exec函数,最终调用的是QCoreApplication的exec函数,在QCoreApplication的exec函数中,实例化QEventLoop对象,通过QEventLoop的exec进入到事件循环
Step 2
// src\corelib\kernel\qeventloop.cpp
int QEventLoop::exec(ProcessEventsFlags flags)
{
/**
* 省略 .......
*/
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec); // 开始处理事件
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
auto threadData = d->threadData.loadRelaxed();
if (!threadData->hasEventDispatcher()) // 事件分发者没有被创建则返回
return false;
return threadData->eventDispatcher.loadRelaxed()->processEvents(flags); // 调用事件分发者的processEvents,(本例事件分发者为QEventDispatcherWin32)
}
d->exit.loadAcquire()为事件循环的状态,在不退出事件循环时,此变量一直为false。通过while循环调用事件分发者的processEvents去处理事件
Step 3
// src\corelib\kernel\qeventdispatcher_win.cpp
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd) {
createInternalHwnd(); // d->internalHwnd作为接收windows消息的窗体,如果窗体不存在则新创建一个
wakeUp(); // trigger a call to sendPostedEvents()
}
/**
* 省略 .......
*/
}
void QEventDispatcherWin32::createInternalHwnd()
{
Q_D(QEventDispatcherWin32);
if (d->internalHwnd)
return;
d->internalHwnd = qt_create_internal_window(this); // 创建窗体并赋值给d->internalHwnd句柄
// setup GetMessage hook needed to drive our posted events
d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
if (Q_UNLIKELY(!d->getMessageHook)) {
int errorCode = GetLastError();
qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls",
errorCode, qUtf16Printable(qt_error_string(errorCode)));
}
// start all normal timers
for (int i = 0; i < d->timerVec.count(); ++i)
d->registerTimer(d->timerVec.at(i));
}
事件分发者第一次调用processEvents处理事件时,d->internalHwnd窗口句柄为空。接下来准备调用操作系统API创建窗口,注册窗口过程处理函数。
Step 4
// step 5 调用qWindowsMessageWindowClassContext注册消息过程处理函数,并且通过WIN API CreateWindow创建窗体
// src\corelib\kernel\qeventdispatcher_win.cpp
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext(); // 注册消息过程处理函数
if (!ctx->atom)
return 0;
// 创建窗体
HWND wnd = CreateWindow(ctx->className, // classname
ctx->className, // window name
0, // style
0, 0, 0, 0, // geometry
HWND_MESSAGE, // parent
0, // menu handle
GetModuleHandle(0), // application
0); // windows creation data.
if (!wnd) {
qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
return 0;
}
#ifdef GWLP_USERDATA
SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
#else
SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
#endif
return wnd;
}
在qWindowsMessageWindowClassContext中注册窗口过程处理函数,调用windows API CreateWindow创建窗口
Step 5
// src\corelib\kernel\qeventdispatcher_win.cpp
Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext) // Q_GLOBAL_STATIC宏通过QGlobalStatic将QWindowsMessageWindowClassContext包装,并创建一个名为qWindowsMessageWindowClassContext的QGlobalStatic对象
#define Q_GLOBAL_STATIC(TYPE, NAME) \
Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
// 通过宏Q_GLOBAL_STATIC_WITH_ARGS创建一个名为NAME全局变量
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
namespace { namespace Q_QGS_ ## NAME { \
typedef TYPE Type; \
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
Q_GLOBAL_STATIC_INTERNAL(ARGS) \
} } \
static QGlobalStatic<TYPE, \
Q_QGS_ ## NAME::innerFunction, \
Q_QGS_ ## NAME::guard> NAME;
// 通过Q_GLOBAL_STATIC_INTERNAL定义一个返回值为Type* 名为innerFunction的函数,该函数通过guard判断静态变量d(使用静态变量的好处是延长变量生命周期;在下次进入该函数时,变量仍为上次的值)是否被初始化过,如果没有被初始化则创建一个新的,反之直接return变量d
#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
Q_DECL_HIDDEN inline Type *innerFunction() \
{ \
static Type *d; \
static QBasicMutex mutex; \
int x = guard.loadAcquire(); \
if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \
const std::lock_guard<QBasicMutex> locker(mutex); \
if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \
d = new Type ARGS; \
static struct Cleanup { \
~Cleanup() { \
delete d; \
guard.storeRelaxed(QtGlobalStatic::Destroyed); \
} \
} cleanup; \
guard.storeRelease(QtGlobalStatic::Initialized); \
} \
} \
return d; \
}
// 声明类型为T,通过innerFunction函数指针创建T类型静态变量,并通过guard来判断静态变量的状态
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
struct QGlobalStatic
{
typedef T Type;
bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); }
Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); }// 重载(),并通过innerFunction函数指针获取静态变量
Type *operator->()
{
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
return innerFunction();
}
Type &operator*()
{
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
return *innerFunction();
}
};
/********************************************************************************************************/
// 综上step 4的QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext(); 则为:创建类型为QWindowsMessageWindowClassContext的静态变量,并通过qWindowsMessageWindowClassContext()返回其指针赋值给ctx变量
// src\corelib\kernel\qeventdispatcher_win.cpp
QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext()
: atom(0), className(0)
{
/**
* 省略 .......
*/
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = qt_internal_proc; // 消息处理回调函数
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
atom = RegisterClass(&wc); // 注册消息过程处理函数
if (!atom) {
qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName));
delete [] className;
className = 0;
}
}
通过实例化QWindowsMessageWindowClassContext注册消息过程处理函数
Step 6
// src\corelib\kernel\qeventdispatcher_win.cpp
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
if (message == WM_NCCREATE)
return true;
MSG msg;
msg.hwnd = hwnd;
msg.message = message;
msg.wParam = wp;
msg.lParam = lp;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qintptr result;
#else
long result;
#endif
if (!dispatcher) {
if (message == WM_TIMER)
KillTimer(hwnd, wp);
return 0;
}
if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) // 系统原生消息的过滤器
return result;
#ifdef GWLP_USERDATA
auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
#else
auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA));
#endif
QEventDispatcherWin32Private *d = 0;
if (q != 0)
d = q->d_func();
switch (message) {
case WM_QT_SOCKETNOTIFIER:
/**
* 省略 .......
*/
case WM_QT_ACTIVATENOTIFIERS: {
/**
* 省略 .......
*/
}
case WM_TIMER:
/**
* 省略 .......
*/
case WM_QT_SENDPOSTEDEVENTS: // 通过调用PostEvent投递的事件会被直接处理
Q_ASSERT(d != 0);
// We send posted events manually, if the window procedure was invoked
// by the foreign event loop (e.g. from the native modal dialog).
// Skip sending, if the message queue is not empty.
// sendPostedEventsTimer will deliver posted events later.
static const UINT mask = inputQueueMask();
if (HIWORD(GetQueueStatus(mask)) == 0)
q->sendPostedEvents();
return 0;
} // switch (message)
return DefWindowProc(hwnd, message, wp, lp);
}
在窗口过程处理函数中,先通过filterNativeEvent对原生消息处理,然后再对不同的消息进行处理。
Step 7
// step 9通过PeekMessage获取系统消息,通过TranslateMessage、DispatchMessage分发消息
// src\corelib\kernel\qeventdispatcher_win.cpp
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd) {
createInternalHwnd();
wakeUp(); // trigger a call to sendPostedEvents()
}
d->interrupt.storeRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents(); // 处理postEvent队列的事件和用户输入事件
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
do {
DWORD waitRet = 0;
DWORD nCount = 0;
HANDLE *pHandles = nullptr;
if (d->winEventNotifierActivatedEvent) {
nCount = 1;
pHandles = &d->winEventNotifierActivatedEvent;
}
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt.loadRelaxed()) {
MSG msg;
bool haveMessage;
// 队列中有消息则取一个MSG
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
// 队列中没有消息,读取消息,并将消息放入队列
haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
if (haveMessage) {
if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
&& isUserInputMessage(msg.message)) {
// queue user input events for later processing
d->queuedUserInputEvents.append(msg);
continue;
}
if ((flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// queue socket events for later processing
d->queuedSocketEvents.append(msg);
continue;
}
}
}
if (!haveMessage) {
// no message - check for signalled objects
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);// 设置超时时间等待消息
if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
// a new message has arrived, process it
continue;
}
}
if (haveMessage) {
// 如果消息是内部窗口句柄并且消息号是WM_QT_SENDPOSTEDEVENTS(wakeUp()通过调用PostMessage投递给d->internalHwnd窗口WM_QT_SENDPOSTEDEVENTS消息)
// 存在一个疑问:如果一直在往系统中投递WM_QT_SENDPOSTEDEVENTS消息,那么这个循环就永远跳不出去,会不会存在投递的事件一直没有被处理???
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
// Set result to 'true' because the message was sent by wakeUp().
retVal = true;
continue;
}
if (msg.message == WM_TIMER) {
// Skip timer event intended for use inside foreign loop.
if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
continue;
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = 0; !found && i < processedTimers.count(); ++i) {
const MSG processed = processedTimers.constData()[i];
found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
}
if (found)
continue;
processedTimers.append(msg);
} else if (msg.message == WM_QUIT) {
if (QCoreApplication::instance())
QCoreApplication::instance()->quit();
return false;
}
if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg); // 分发消息至窗口过程处理函数
}
} else if (waitRet - WAIT_OBJECT_0 < nCount) {
activateEventNotifiers();
} else {
// nothing todo so break
break;
}
retVal = true;
}
// still nothing - wait for message or signalled objects
canWait = (!retVal
&& !d->interrupt.loadRelaxed()
&& flags.testFlag(QEventLoop::WaitForMoreEvents)
&& threadData->canWaitLocked());
if (canWait) {
emit aboutToBlock();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
if (waitRet - WAIT_OBJECT_0 < nCount) {
activateEventNotifiers();
retVal = true;
}
}
} while (canWait);
return retVal;
}
至此对于一个Qt GUI程序的事件循环已经启动完毕
四、sendEvent
用户通过sendEvent投递一个自定义事件,sendEvent的工作原理又是怎样的
Step 1
// src\corelib\kernel\qcoreapplication.cpp
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type());
if (event)
event->spont = false;
return notifyInternal2(receiver, event);
}
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
if (!self && selfRequired)
return false;
// Make it possible for Qt Script to hook into events even
// though QApplication is subclassed...
bool result = false;
void *cbdata[] = { receiver, event, &result };
if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
return result;
}
// Qt enforces the rule that events can only be sent to objects in
// the current thread, so receiver->d_func()->threadData is
// equivalent to QThreadData::current(), just without the function
// call overhead.
QObjectPrivate *d = receiver->d_func();
QThreadData *threadData = d->threadData;
QScopedScopeLevelCounter scopeLevelCounter(threadData);
if (!selfRequired)
return doNotify(receiver, event);
return self->notify(receiver, event);
}
通过QApplication::sendEvent投递一个事件,实际上调用QCoreApplication::sendEvent,QApplication继承QCoreApplication。在notifyInternal2中self->notify(receiver, event),实际上调用的是QApplication的notify,notify为虚函数在QApplication中重写
Step 2
// src\widgets\kernel\qapplication.cpp
bool QApplication::notify(QObject *receiver, QEvent *e)
{
/**
* 代码省略 .......
* 如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理
*/
res = d->notify_helper(receiver, e);
return res;
}
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
/**
* 代码省略 .......
*/
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, e)) { // 先给事件过滤器处理事件
filtered = true;
return filtered;
}
// deliver the event
consumed = receiver->event(e); // 交给event去处理
QCoreApplicationPrivate::setEventSpontaneous(e, false);
return consumed;
}
在QApplication::notify中如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理。在notify_helper中处理事件是,先通过事件处理器处理,最后交给目标对象的event处理。
在此可以看见事件的处理顺序为:QApplication::notify > eventFilter > event
五、postEvent
Step 1
// src\corelib\kernel\qcoreapplication.cpp
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
/**
* 代码省略 .......
*/
// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
QScopedPointer<QEvent> eventDeleter(event); // 在栈上创建一个智能指针,eventDeleter销毁时自动释放event对象,防止内存泄露
Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
data->postEventList.addEvent(QPostEvent(receiver, event, priority)); // 将投递的事件添加postEventList列表中
eventDeleter.take();
event->posted = true;
++receiver->d_func()->postedEvents;
data->canWait = false;
locker.unlock();
QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
if (dispatcher)
dispatcher->wakeUp(); // 通知postEventList队列里面添加了一个新的事件
}
// src\corelib\kernel\qeventdispatcher_win.cpp
void QEventDispatcherWin32::wakeUp()
{
Q_D(QEventDispatcherWin32);
if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) {
// post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0)) // 调用系统接口,投递一个WM_QT_SENDPOSTEDEVENTS消息
qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message");
}
}
调用postEvent将投递的事件放入到postEventList队列中,并通过PostMessage向系统投递一个WM_QT_SENDPOSTEDEVENTS消息,用于通知有新的事件添加到队列中。事件循环会从系统获取到一个WM_QT_SENDPOSTEDEVENTS消息,然后去处理postEventList队列的消息。
Step 2
// src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
QEventDispatcherWin32::sendPostedEvents();
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
// src\corelib\kernel\qeventdispatcher_win.cpp
void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32);
if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0;
// Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0);
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}
//
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
/**
* 代码省略 .......
*/
while (i < data->postEventList.size()) { // 遍历postEventList
// avoid live-lock
if (i >= data->postEventList.insertionOffset)
break;
const QPostEvent &pe = data->postEventList.at(i); // 从postEventList队列中取一个事件
++i;
if (!pe.event)
continue;
if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) {
data->canWait = false;
continue;
}
if (pe.event->type() == QEvent::DeferredDelete) {
// DeferredDelete events are sent either
// 1) when the event loop that posted the event has returned; or
// 2) if explicitly requested (with QEvent::DeferredDelete) for
// events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop.
int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
int loopLevel = data->loopLevel + data->scopeLevel;
const bool allowDeferredDelete =
(eventLevel > loopLevel
|| (!eventLevel && loopLevel > 0)
|| (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel));
if (!allowDeferredDelete) {
// cannot send deferred delete
if (!event_type && !receiver) {
// we must copy it first; we want to re-post the event
// with the event pointer intact, but we can't delay
// nulling the event ptr until after re-posting, as
// addEvent may invalidate pe.
QPostEvent pe_copy = pe;
// null out the event so if sendPostedEvents recurses, it
// will ignore this one, as it's been re-posted.
const_cast<QPostEvent &>(pe).event = nullptr;
// re-post the copied event so it isn't lost
data->postEventList.addEvent(pe_copy);
}
continue;
}
}
// first, we diddle the event so that we can deliver
// it, and that no one will try to touch it later.
pe.event->posted = false;
QEvent *e = pe.event;
QObject * r = pe.receiver;
--r->d_func()->postedEvents;
Q_ASSERT(r->d_func()->postedEvents >= 0);
// next, update the data structure so that we're ready
// for the next event.
const_cast<QPostEvent &>(pe).event = nullptr;
locker.unlock();
const auto relocker = qScopeGuard([&locker] { locker.lock(); });
QScopedPointer<QEvent> event_deleter(e); // will delete the event (with the mutex unlocked)
// after all that work, it's time to deliver the event.
QCoreApplication::sendEvent(r, e); // 最后按照sendEvent的方式将事件处理掉
// careful when adding anything below this point - the
// sendEvent() call might invalidate any invariants this
// function depends on.
}
}
事件循环调用sendPostedEvents从而遍历整个队列的事件,将所有的事件按照sendEvent的方式处理掉。
六、总结
1.sendEvent为同步接口,从源码上看,sendEvent整个过程都为函数调用
2.postEvent为异步接口,因为postEevent先将事件添加到postevent队列中,然后再调用操作系统的异步接口PostMessage向系统投递一个消息,最后在消息循环中拿到此消息,才会去从postEevent队列中取出一个事件进行处理
至此,针对本文开头所提出的四个问题:
1.第一个问题,在事件循环初始化,启动事件循环(其实只完成了一半,因为处理用户输入事件的消息还未讲)已给出答案
2.第二个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
3.第三个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
4.第四个问题,sendEvent和postEvent已给出答案