QPA のお勉強(1)
QPA とは?
Qt Platform Abstraction (通称 QPA) は Qt の GUI まわりのプラットフォームの抽象化レイヤーです。Qt 4.8 で導入され、Qt 5.0 で機能を追加しすべてのプラットフォームで使われるようになりました。
windows, cocoa, xcb, wayland, eglfs, qnx など、実際に使用されているプラグインの他、directfb, kms, linuxfb, openwfd のように試験的に実装されているプラグインもあります。
QPA の勉強を兼ねてなにか面白いプラグインを作りたいなと思ってきたのですが、なかなかいいアイデアも思い浮かばなかったので、今回は proxy プラグインを作ってみました。
QPA プラグインのソースコード
Qt 内の QPA プラグインのソースの位置は qtbase/src/plugins/platforms/ になります。
今回は、最小限のサンプルである minimal を参考にして proxy プラグインを作成しました。
QPA プラグインを作成する
proxy ディレクトリを作成し、proxy.pro, proxy.json, main.cpp と qproxyintegration.{h,cpp} を作成します。
proxy.pro
TARGET = qproxy PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QProxyIntegrationPlugin load(qt_plugin) QT += core-private gui-private platformsupport-private SOURCES = main.cpp \ qproxyintegration.cpp HEADERS = qproxyintegration.h OTHER_FILES += proxy.json
Qt 5 でのプラグインのプロジェクトファイルの典型的な内容になっています。
main.cpp
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include <qpa/qplatformintegrationplugin.h> #include "qproxyintegration.h" QT_BEGIN_NAMESPACE class QProxyIntegrationPlugin : public QPlatformIntegrationPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "proxy.json") public: QPlatformIntegration *create(const QString&, const QStringList&); }; QPlatformIntegration *QProxyIntegrationPlugin::create(const QString& system, const QStringList& paramList) { Q_UNUSED(paramList); if (system.toLower() == "proxy") return new QProxyIntegration(paramList); return 0; } QT_END_NAMESPACE #include "main.moc"
こちらも Qt 5 でのプラグインのソースファイルの典型的な内容です。
proxy.json
{ "Keys": [ "proxy" ] }
Qt 4 では Q_EXPORT_PLUGIN2 マクロを使用してプラグインの登録を行っていましたが、 Qt 5 では、プラグイン自体をロードしなくてもメタデータが取得できるよう プラグインの仕組みが書き換えられました。これにより、プラグインのメタデータは上記のように json ファイルに記述するように変わっています。
qproxyintegration.h
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QPLATFORMINTEGRATION_PROXY_H #define QPLATFORMINTEGRATION_PROXY_H #include <qpa/qplatformintegration.h> QT_BEGIN_NAMESPACE class QProxyIntegration : public QPlatformIntegration { public: QProxyIntegration(const QStringList ¶mList); ~QProxyIntegration(); virtual bool hasCapability(QPlatformIntegration::Capability cap) const; virtual QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const; virtual QPlatformWindow *createPlatformWindow(QWindow *window) const; virtual QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; #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 *guiThreadEventDispatcher() const; //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; virtual QVariant styleHint(StyleHint hint) const; virtual Qt::KeyboardModifiers queryKeyboardModifiers() const; virtual QList<int> possibleKeys(const QKeyEvent *) const; virtual QStringList themeNames() const; virtual QPlatformTheme *createPlatformTheme(const QString &name) const; private: QPlatformIntegration *mPlatformIntegration; }; QT_END_NAMESPACE #endif
QPlatformIntegration のすべての仮想関数をオーバーライドし、参照元の QPlatformIntegration のポインタをメンバ変数として持つようにしています。
qproxyintegration.cpp
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qproxyintegration.h" #include <qpa/qplatformintegrationfactory_p.h> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> QT_BEGIN_NAMESPACE QProxyIntegration::QProxyIntegration(const QStringList ¶mList) { QByteArray platformName; #ifdef QT_QPA_DEFAULT_PLATFORM_NAME platformName = QT_QPA_DEFAULT_PLATFORM_NAME; #endif QByteArray platformNameEnv = qgetenv("QT_QPA_PLATFORM"); if (!platformNameEnv.isEmpty()) { platformName = platformNameEnv; } // Load the platform integration QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); // On Mac, look inside the application bundle for the platform plugin. // TODO (msorvig): Create proper cross-platform solution for loading // deployed platform plugins #ifdef Q_OS_MAC const QString bundlePluginPath = QCoreApplication::applicationDirPath() + QLatin1String("../Plugins/"); if (platformPluginPath.isEmpty() && QDir(bundlePluginPath).exists()) { platformPluginPath = bundlePluginPath; } #endif QStringList argv = QCoreApplication::arguments(); for (int i = 0; i < argv.size(); i++) { if (argv.at(i) == QLatin1String("-platformpluginpath")) { if (++i == argv.size()) { platformPluginPath = argv.at(i); break; } } } QStringList arguments = paramList; if (!arguments.isEmpty()) { QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); if (keys.contains(arguments.first())) { platformName = arguments.takeFirst().toLower().toUtf8(); } } mPlatformIntegration = QPlatformIntegrationFactory::create(QLatin1String(platformName), arguments, platformPluginPath); if (mPlatformIntegration) { } else { QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); QString fatalMessage = QString::fromLatin1("Failed to load platform plugin \"%1\". Available platforms are: \n").arg(QLatin1String(platformName)); foreach(const QString &key, keys) { fatalMessage.append(key + QLatin1Char('\n')); } qFatal("%s", fatalMessage.toLocal8Bit().constData()); return; } } QProxyIntegration::~QProxyIntegration() { delete mPlatformIntegration; } bool QProxyIntegration::hasCapability(QPlatformIntegration::Capability cap) const { return mPlatformIntegration->hasCapability(cap); } QPlatformPixmap *QProxyIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const { return mPlatformIntegration->createPlatformPixmap(type); } QPlatformWindow *QProxyIntegration::createPlatformWindow(QWindow *window) const { return mPlatformIntegration->createPlatformWindow(window); } QPlatformBackingStore *QProxyIntegration::createPlatformBackingStore(QWindow *window) const { return mPlatformIntegration->createPlatformBackingStore(window); } #ifndef QT_NO_OPENGL QPlatformOpenGLContext *QProxyIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { return mPlatformIntegration->createPlatformOpenGLContext(context); } #endif QPlatformSharedGraphicsCache *QProxyIntegration::createPlatformSharedGraphicsCache(const char *cacheId) const { return mPlatformIntegration->createPlatformSharedGraphicsCache(cacheId); } QPaintEngine *QProxyIntegration::createImagePaintEngine(QPaintDevice *paintDevice) const { return mPlatformIntegration->createImagePaintEngine(paintDevice); } QAbstractEventDispatcher *QProxyIntegration::guiThreadEventDispatcher() const { return mPlatformIntegration->guiThreadEventDispatcher(); } QPlatformFontDatabase *QProxyIntegration::fontDatabase() const { return mPlatformIntegration->fontDatabase(); } #ifndef QT_NO_CLIPBOARD QPlatformClipboard *QProxyIntegration::clipboard() const { return mPlatformIntegration->clipboard(); } #endif #ifndef QT_NO_DRAGANDDROP QPlatformDrag *QProxyIntegration::drag() const { return mPlatformIntegration->drag(); } #endif QPlatformInputContext *QProxyIntegration::inputContext() const { return mPlatformIntegration->inputContext(); } #ifndef QT_NO_ACCESSIBILITY QPlatformAccessibility *QProxyIntegration::accessibility() const { return mPlatformIntegration->accessibility(); } #endif QPlatformNativeInterface *QProxyIntegration::nativeInterface() const { return mPlatformIntegration->nativeInterface(); } QPlatformServices *QProxyIntegration::services() const { return mPlatformIntegration->services(); } QVariant QProxyIntegration::styleHint(StyleHint hint) const { return mPlatformIntegration->styleHint(hint); } Qt::KeyboardModifiers QProxyIntegration::queryKeyboardModifiers() const { return mPlatformIntegration->queryKeyboardModifiers(); } QList<int> QProxyIntegration::possibleKeys(const QKeyEvent *e) const { return mPlatformIntegration->possibleKeys(e); } QStringList QProxyIntegration::themeNames() const { return mPlatformIntegration->themeNames(); } QPlatformTheme *QProxyIntegration::createPlatformTheme(const QString &name) const { return mPlatformIntegration->createPlatformTheme(name); } QT_END_NAMESPACE
上書きした仮想関数内で、参照元のプラグインの関数を呼び出しています。参照元のプラグインの確定アルゴリズムは、QGuiApplication のコードを参考にしています。
まとめ
今回は、minimal プラグインを参考に、他の(プラットフォームのデフォルトの) QPA プラグインをただ呼ぶだけのプロキシプラグインを作成しました。コピペだらけで特に難しいところはないはずです。
次回はこれをベースになにかを作る予定。