QT(7)—— 事件系统之窗口系统事件 Unix 操作系统、XCB平台、Glib函数库

理解的关键节点是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);
   

加载平台集成环境 即XCB

QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList &paramList, 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平台集成环境的生成,在编译时通过指定参数进行编译。

QPlatformIntegration

class QXcbIntegration : public QPlatformIntegration
{
public:
    QXcbIntegration(const QStringList &parameters, 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;
};

QPlatformIntegration

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是一个窗口系统的支撑。不用再往下追踪了,以下就是不同窗口系统的实现机制。

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

一) 基本运行原理
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即一个窗口程序的底层建筑,用于窗口系统的输入输出之间的通信。

EventDispatcher 事件分发系统

窗口系统的平台集成环境已经匹配了,接下来就是创建一个事件分发系统,将窗口事件信息传输到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系统的通信链路添加事件分发器。

UnixEventDispatcher

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的分发器。

QPAEventDispatcherGlib

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事件。

GUserEventSource

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中的系统事件处理函数

processWindowSystemEvent

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);
  }

通过接收者和事件在QT系统内部进行传输

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函数,被个性化处理。

你可能感兴趣的:(QT)