理解的关键节点是windowSystemEventsQueued的添加与减少
static gboolean userEventSourcePrepare(GSource *s, gint *timeout)
{
return QWindowSystemInterface::windowSystemEventsQueued() > 0;
}
从QApplication开始追踪
void QApplicationPrivate::createEventDispatcher()
{
QGuiApplicationPrivate::createEventDispatcher();
}
void QGuiApplicationPrivate::createEventDispatcher()
{
Q_ASSERT(!eventDispatcher);
if (platform_integration == 0)
createPlatformIntegration();
// The platform integration should not mess with the event dispatcher
Q_ASSERT(!eventDispatcher);
eventDispatcher = platform_integration->createEventDispatcher();
}
void QGuiApplicationPrivate::createPlatformIntegration()
{
// Load the platform integration
QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH"));
QByteArray platformName;
platformName = QT_QPA_DEFAULT_PLATFORM_NAME;
const bool isXcb = platformName == "xcb";
if (arg == "-qwindowtitle" || (isXcb && arg == "-title")) {
if (++i < argc)
firstWindowTitle = QString::fromLocal8Bit(argv[i]);
}
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 &pluginArgument, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
// Create the platform integration.
QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath);
QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList ¶mList, int &argc, char **argv, const QString &platformPluginPath)
{
if (QPlatformIntegration *ret = loadIntegration(loader(), platform, paramList, argc, argv))
return ret;
}
在qtbase\src\plugins\platforms\xcb\qxcbmain.cpp中的插件
class QXcbIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "xcb.json")
public:
QPlatformIntegration *create(const QString&, const QStringList&, int &, char **);
};
QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters, int &argc, char **argv)
{
if (!system.compare(QLatin1String("xcb"), Qt::CaseInsensitive))
return new QXcbIntegration(parameters, argc, argv);
return 0;
}
完成QPlatformIntegration平台集成环境的生成,在编译时通过指定参数进行编译。
class QXcbIntegration : public QPlatformIntegration
{
public:
QXcbIntegration(const QStringList ¶meters, int &argc, char **argv);
~QXcbIntegration();
QPlatformWindow *createPlatformWindow(QWindow *window) const;
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
#endif
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const;
QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const;
bool hasCapability(Capability cap) const;
QAbstractEventDispatcher *createEventDispatcher() const;
void initialize();
void moveToScreen(QWindow *window, int screen);
QPlatformFontDatabase *fontDatabase() const;
QPlatformNativeInterface *nativeInterface()const;
QPlatformInputContext *inputContext() const;
QPlatformServices *services() const;
Qt::KeyboardModifiers queryKeyboardModifiers() const;
QList<int> possibleKeys(const QKeyEvent *e) const;
QStringList themeNames() const;
QPlatformTheme *createPlatformTheme(const QString &name) const;
QVariant styleHint(StyleHint hint) const;
QXcbConnection *defaultConnection() const { return m_connections.first(); }
QByteArray wmClass() const;
#if !defined(QT_NO_SESSIONMANAGER) && defined(XCB_USE_SM)
QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const Q_DECL_OVERRIDE;
#endif
void sync();
private:
QList<QXcbConnection *> m_connections;
QScopedPointer<QPlatformFontDatabase> m_fontDatabase;
QScopedPointer<QXcbNativeInterface> m_nativeInterface;
QScopedPointer<QPlatformInputContext> m_inputContext;
#ifndef QT_NO_ACCESSIBILITY
mutable QScopedPointer<QPlatformAccessibility> m_accessibility;
#endif
QScopedPointer<QPlatformServices> m_services;
friend class QXcbConnection; // access QPlatformIntegration::screenAdded()
mutable QByteArray m_wmClass;
const char *m_instanceName;
};
class Q_GUI_EXPORT QPlatformIntegration
{
public:
enum Capability {
ThreadedPixmaps = 1,
OpenGL,
ThreadedOpenGL,
SharedGraphicsCache,
BufferQueueingOpenGL,
WindowMasks,
MultipleWindows,
ApplicationState,
ForeignWindows,
NonFullScreenWindows,
NativeWidgets,
WindowManagement,
SyncState,
RasterGLSurface,
AllGLFunctionsQueryable
};
virtual ~QPlatformIntegration() { }
virtual bool hasCapability(Capability cap) const;
virtual QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const;
virtual QPlatformWindow *createPlatformWindow(QWindow *window) const = 0;
virtual QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const = 0;
#ifndef QT_NO_OPENGL
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
#endif
virtual QPlatformSharedGraphicsCache *createPlatformSharedGraphicsCache(const char *cacheId) const;
virtual QPaintEngine *createImagePaintEngine(QPaintDevice *paintDevice) const;
// Event dispatcher:
virtual QAbstractEventDispatcher *createEventDispatcher() const = 0;
virtual void initialize();
//Deeper window system integrations
virtual QPlatformFontDatabase *fontDatabase() const;
#ifndef QT_NO_CLIPBOARD
virtual QPlatformClipboard *clipboard() const;
#endif
#ifndef QT_NO_DRAGANDDROP
virtual QPlatformDrag *drag() const;
#endif
virtual QPlatformInputContext *inputContext() const;
#ifndef QT_NO_ACCESSIBILITY
virtual QPlatformAccessibility *accessibility() const;
#endif
// Access native handles. The window handle is already available from Wid;
virtual QPlatformNativeInterface *nativeInterface() const;
virtual QPlatformServices *services() const;
enum StyleHint {
CursorFlashTime,
KeyboardInputInterval,
MouseDoubleClickInterval,
StartDragDistance,
StartDragTime,
KeyboardAutoRepeatRate,
ShowIsFullScreen,
PasswordMaskDelay,
FontSmoothingGamma,
StartDragVelocity,
UseRtlExtensions,
SynthesizeMouseFromTouchEvents,
PasswordMaskCharacter,
SetFocusOnTouchRelease,
ShowIsMaximized,
MousePressAndHoldInterval
};
virtual QVariant styleHint(StyleHint hint) const;
virtual Qt::WindowState defaultWindowState(Qt::WindowFlags) const;
virtual Qt::KeyboardModifiers queryKeyboardModifiers() const;
virtual QList<int> possibleKeys(const QKeyEvent *) const;
virtual QStringList themeNames() const;
virtual QPlatformTheme *createPlatformTheme(const QString &name) const;
virtual QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const;
#ifndef QT_NO_SESSIONMANAGER
virtual QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const;
#endif
virtual void sync();
#ifndef QT_NO_OPENGL
virtual QOpenGLContext::OpenGLModuleType openGLModuleType();
#endif
protected:
void screenAdded(QPlatformScreen *screen);
};
通过以上子类父类的定义可以看出XCB是一个窗口系统的支撑。不用再往下追踪了,以下就是不同窗口系统的实现机制。
The X protocol C-language Binding (XCB) is a replacement for Xlib featuring a small footprint, latency hiding, direct access to the protocol, improved threading support, and extensibility.
X协议的c语言绑定(XCB) 是一种替代Xlib具有占用空间小,延迟隐藏,直接访问协议,提高线程的支持,和可扩展性。
XCB(“X C Bing”)是一个低级的api给X window server。 XCB是Xlib的一个替代产物,Xlib曾是多年以来标准的C绑定的给X Window System 协议的库。Xlib表现得很优秀,但是对于应用程序来说也有很多不理想的地方,例如:
小平台: Xlib包含在大段的代码,精简起来十分困难
延迟隐藏:Xlib需要有效的同步回复:他们会一直阻塞直到获取到结果,不管这个结果是否立即需要
直接访问协议:Xlib需要大量的缓存,层和相似的优化。由于这个通性,它很难简单的发出特定的X 协议请求和处理特定的响应
线程化应用程序:Xlib尽量会支持多线程,它的api的容错性较差
新的扩展:Xlib的底部架构提供了有限的支持给新的X的扩展客户端代码。
XCB被设计用来解决上述的问题,目前已经解决的有:
工具包的实现
直接进行协议级的编程
轻量级的调用常用的Xlib api.
一) 基本运行原理
X Window System采用C/S结构,但和我们常见的C/S不同。常见的C/S结构中,称提供服务的一方为server,即服务器端(如HTTP服务,FTP服务 等),使用服务的称为client,即客户端。但在X Window System中,client是执行程序的一方,在上面执行各种X程序,而server则是负责显示client运行程序的窗口的一方。
X Window System的组成可以分为X server,X client,X protocol三部分。X server主要控制输入输出,维护字体,颜色等相关资源。它接受输入设备的输入信息并传递给X client,X client将这些信息处理后所返回的信息,也由X server负责输出到输出设备(即我们所见的显示器)上。X server传递给X client的信息称为Event,主要是键盘鼠标输入和窗口状态的信息。X client传递给X server的信息则称为Request,主要是要求X server建立窗口,更改窗口大小位置或在窗口上绘图输出文字等。X client主要是完成应用程序计算处理的部分,并不接受用户的输入信息,输入信息都是输入给X server,然后由X server以Event的形式传递给X client(这里感觉类似Windows的消息机制,系统接收到用户的输入信息,然后以消息的形式传递给窗口,再由窗口的消息处理过程处理)。X client对收到的Event进行相应的处理后,如果需要输出到屏幕上或更改画面的外观等,则发出Request给X server,由X server负责显示。
常见的情况是X server与X client都在同一台电脑上运行,但他们也可分别位于网络上不同的电脑上。在X Window System中,X client是与硬件无关的,它并不关心你使用的是什么显卡什么显示器什么键盘鼠标,这些只与X server相关。我们平常安装完XFree86后运行xf86config或xf86cfg进行的配置实际上只是与X server有关,可以说就是配置X server吧,不配置照样可以运行X client程序(如:xeyes -display xserver:0就可以在xserver这台机器上的0号屏幕(屏幕编号displaynumber为0)上显示那对大眼睛了)。
X protocol就是X server于X client之间通信的协议了。X protocol支持现在常用的网络通信协议。我只能测试TCP/IP,可以看到X server侦听在tcp 6000端口上。那X protocol就是位于运输层以上了,应该属于应用层吧?。
总结下运行过程吧:
(1) 用户通过鼠标键盘对X server下达操作命令
(2) X server利用Event传递用户操作信息给X client
(3) X client进行程序运算
(4) X client利用Request传回所要显示的结果
(5) X server将结果显示在屏幕上
可见XCB即一个窗口程序的底层建筑,用于窗口系统的输入输出之间的通信。
窗口系统的平台集成环境已经匹配了,接下来就是创建一个事件分发系统,将窗口事件信息传输到QT系统的内部。
class QXcbIntegration : public QPlatformIntegration
{
QList<QXcbConnection *> m_connections;
}
QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const
{
QAbstractEventDispatcher *dispatcher = createUnixEventDispatcher();
for (int i = 0; i < m_connections.size(); i++)
m_connections[i]->eventReader()->registerEventDispatcher(dispatcher);
return dispatcher;
}
可以看出是给某一个C/S系统的通信链路添加事件分发器。
class QAbstractEventDispatcher *createUnixEventDispatcher()
{
#if !defined(QT_NO_GLIB) && !defined(Q_OS_WIN)
if (qEnvironmentVariableIsEmpty("QT_NO_GLIB") && QEventDispatcherGlib::versionSupported())
return new QPAEventDispatcherGlib();
else
#endif
return new QUnixEventDispatcherQPA();
}
Unix拥有两种事件分发器,一种是有glib的分发器,另一种是纯Unix平台的事件分发器。看glib的分发器。
class QPAEventDispatcherGlib : public QEventDispatcherGlib
{
Q_OBJECT
Q_DECLARE_PRIVATE(QPAEventDispatcherGlib)
public:
explicit QPAEventDispatcherGlib(QObject *parent = 0);
~QPAEventDispatcherGlib();
bool processEvents(QEventLoop::ProcessEventsFlags flags);
QEventLoop::ProcessEventsFlags m_flags;
};
继承自QEventDispatcherGlib
QPAEventDispatcherGlib::QPAEventDispatcherGlib(QObject *parent)
: QEventDispatcherGlib(*new QPAEventDispatcherGlibPrivate, parent)
, m_flags(QEventLoop::AllEvents)
{
Q_D(QPAEventDispatcherGlib);
d->userEventSource->q = this;
}
QPAEventDispatcherGlibPrivate::QPAEventDispatcherGlibPrivate(GMainContext *context)
: QEventDispatcherGlibPrivate(context)
{
Q_Q(QPAEventDispatcherGlib);
userEventSource = reinterpret_cast<GUserEventSource *>(g_source_new(&userEventSourceFuncs,
sizeof(GUserEventSource)));
userEventSource->q = q;
g_source_set_can_recurse(&userEventSource->source, true);
g_source_attach(&userEventSource->source, mainContext);
}
同时添加新的事件源,用户事件,对用着窗口系统的事件,例如鼠标,键盘的事件,即自发性的事件,与之对应的是非自发性的事件,较常见的就是QObject的信号槽机制生成的PostEvent ,MetaCall事件。
Glib自定义的事件源
struct GUserEventSource
{
GSource source;
QPAEventDispatcherGlib *q;
};
static gboolean userEventSourcePrepare(GSource *s, gint *timeout)
{
Q_UNUSED(s)
Q_UNUSED(timeout)
return QWindowSystemInterface::windowSystemEventsQueued() > 0;
}
static gboolean userEventSourceCheck(GSource *source)
{
return userEventSourcePrepare(source, 0);
}
static gboolean userEventSourceDispatch(GSource *source, GSourceFunc, gpointer)
{
GUserEventSource *userEventSource = reinterpret_cast<GUserEventSource *>(source);
QPAEventDispatcherGlib *dispatcher = userEventSource->q;
QWindowSystemInterface::sendWindowSystemEvents(dispatcher->m_flags);
return true;
}
static GSourceFuncs userEventSourceFuncs = {
userEventSourcePrepare,
userEventSourceCheck,
userEventSourceDispatch,
NULL,
NULL,
NULL
};
循环处理windowSystemEventsQueued中的窗口事件。
最终到达QGuiApplication中的系统事件处理函数
void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
switch(e->type) {
case QWindowSystemInterfacePrivate::FrameStrutMouse:
case QWindowSystemInterfacePrivate::Mouse:
QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));
break;
}
由 其经过处理,封装成Spontaneous QEvent
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{
QEvent::Type type;
Qt::MouseButtons stateChange = e->buttons ^ buttons;
QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers);
ev.setTimestamp(e->timestamp);
setMouseEventSource(&ev, e->source);
QGuiApplication::sendSpontaneousEvent(window, &ev);
}
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = true; return self ? self->notifyInternal(receiver, event) : false; }
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
{
// 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;
QScopedLoopLevelCounter loopLevelCounter(threadData);
return notify(receiver, event);
}
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
Q_D(QCoreApplication);
// no events are delivered after ~QCoreApplication() has started
if (QCoreApplicationPrivate::is_app_closing)
return true;
if (receiver == 0) { // serious error
qWarning("QCoreApplication::notify: Unexpected null receiver");
return true;
}
#ifndef QT_NO_DEBUG
d->checkReceiverThread(receiver);
#endif
return receiver->isWidgetType() ? false : d->notify_helper(receiver, event);
}
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
// send to all application event filters
if (sendThroughApplicationEventFilters(receiver, event))
return true;
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, event))
return true;
// deliver the event
return receiver->event(event);
}
最终会到达某一个图形对象的event函数,被个性化处理。