From 514c202da95bbe876c9759961a8032eae4b96bdd Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 8 Jan 2016 10:04:02 +0100 Subject: Add a manual test for foreign windows. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a command line tool that can either take a list of window ids and output information on them using a verbose debug stream or embed foreign windows using QWidget::createWindowContainer(). Task-number: QTBUG-41186 Change-Id: I14e436b5d08828f5b78b29e0701daeffe11367d3 Reviewed-by: Morten Johan Sørvig --- tests/manual/foreignwindows/foreignwindows.pro | 6 + tests/manual/foreignwindows/main.cpp | 329 +++++++++++++++++++++++++ tests/manual/manual.pro | 4 +- 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 tests/manual/foreignwindows/foreignwindows.pro create mode 100644 tests/manual/foreignwindows/main.cpp (limited to 'tests/manual') diff --git a/tests/manual/foreignwindows/foreignwindows.pro b/tests/manual/foreignwindows/foreignwindows.pro new file mode 100644 index 0000000000..6a370a6813 --- /dev/null +++ b/tests/manual/foreignwindows/foreignwindows.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +QT += widgets +CONFIG += console c++11 +CONFIG -= app_bundle +SOURCES += main.cpp +include(../diaglib/diaglib.pri) diff --git a/tests/manual/foreignwindows/main.cpp b/tests/manual/foreignwindows/main.cpp new file mode 100644 index 0000000000..6c722a3f6f --- /dev/null +++ b/tests/manual/foreignwindows/main.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +# include +#endif + +#include // diaglib +#include +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +typedef QSharedPointer WidgetPtr; +typedef QList WidgetPtrList; +typedef QList WIdList; + +// Create some pre-defined Windows controls by class name +static WId createInternalWindow(const QString &name) +{ + WId result = 0; +#ifdef Q_OS_WIN + if (name == QLatin1String("BUTTON") || name == QLatin1String("COMBOBOX") + || name == QLatin1String("EDIT") || name.startsWith(QLatin1String("RICHEDIT"))) { + const HWND hwnd = + CreateWindowEx(0, reinterpret_cast(name.utf16()), + L"NativeCtrl", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, GetModuleHandle(NULL), NULL); + if (hwnd) { + SetWindowText(hwnd, L"Demo"); + result = WId(hwnd); + } else { + qErrnoWarning("Cannot create window \"%s\"", qPrintable(name)); + } + } +#else // Q_OS_WIN + Q_UNUSED(name) +#endif + return result; +} + +// Embed a foreign window using createWindowContainer() providing +// menu actions to dump information. +class EmbeddingWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit EmbeddingWindow(QWindow *window); + +public slots: + void releaseForeignWindow(); + +private: + QWindow *m_window; + QAction *m_releaseAction; +}; + +EmbeddingWindow::EmbeddingWindow(QWindow *window) : m_window(window) +{ + const QString title = QLatin1String("Qt ") + QLatin1String(QT_VERSION_STR) + + QLatin1String(" 0x") + QString::number(window->winId(), 16); + setWindowTitle(title); + setObjectName("MainWindow"); + QWidget *container = QWidget::createWindowContainer(window, Q_NULLPTR, Qt::Widget); + container->setObjectName("Container"); + setCentralWidget(container); + + QMenu *fileMenu = menuBar()->addMenu("File"); + fileMenu->setObjectName("FileMenu"); + QToolBar *toolbar = new QToolBar; + addToolBar(Qt::TopToolBarArea, toolbar); + + // Manipulation + QAction *action = fileMenu->addAction("Visible"); + action->setCheckable(true); + action->setChecked(true); + connect(action, &QAction::toggled, m_window, &QWindow::setVisible); + toolbar->addAction(action); + + m_releaseAction = fileMenu->addAction("Release", this, &EmbeddingWindow::releaseForeignWindow); + toolbar->addAction(m_releaseAction); + + fileMenu->addSeparator(); // Diaglib actions + action = fileMenu->addAction("Dump Widgets", + this, [] () { QtDiag::dumpAllWidgets(); }); + toolbar->addAction(action); + action = fileMenu->addAction("Dump Windows", + this, [] () { QtDiag::dumpAllWindows(); }); + toolbar->addAction(action); + action = fileMenu->addAction("Dump Native Windows", + this, [this] () { QtDiag::dumpNativeWindows(winId()); }); + toolbar->addAction(action); + + fileMenu->addSeparator(); + action = fileMenu->addAction("Quit", qApp, &QCoreApplication::quit); + toolbar->addAction(action); + action->setShortcut(Qt::CTRL + Qt::Key_Q); +} + +void EmbeddingWindow::releaseForeignWindow() +{ + if (m_window) { + m_window->setParent(Q_NULLPTR); + m_window = Q_NULLPTR; + m_releaseAction->setEnabled(false); + } +} + +// Dump information about foreign windows. +class WindowDumper : public QObject { + Q_OBJECT +public: + explicit WindowDumper(const QWindowList &watchedWindows) + : m_watchedWindows(watchedWindows) {} + +public slots: + void dump() const; + +private: + const QWindowList m_watchedWindows; +}; + +void WindowDumper::dump() const +{ + static int n = 0; + QString s; + QDebug debug(&s); + debug.nospace(); + debug.setVerbosity(3); + debug << '#' << n++; + if (m_watchedWindows.size() > 1) + debug << '\n'; + foreach (const QWindow *w, m_watchedWindows) { + const QPoint globalPos = w->mapToGlobal(QPoint()); + debug << " " << w << " pos=" << globalPos.x() << ',' << globalPos.y() << '\n'; + } + + std::cout << qPrintable(s); +} + +static QString description(const QString &appName) +{ + QString result; + QTextStream(&result) + << "\nDumps information about foreign windows passed on the command line or\n" + "tests embedding foreign windows into Qt.\n\nUse cases:\n\n" + << appName << " -a Dump a list of all native window ids.\n" + << appName << " Dump information on the window.\n" + << appName << " -c Dump information on the window continuously.\n" + << appName << " -e Embed window into a Qt widget.\n" + << "\nOn Windows, class names of well known controls (EDIT, BUTTON...) can be\n" + "passed as along with -e, which will create the control.\n"; + return result; +} + +struct EventFilterOption +{ + const char *name; + const char *description; + QtDiag::EventFilter::EventCategories categories; +}; + +EventFilterOption eventFilterOptions[] = { +{"mouse-events", "Dump mouse events.", QtDiag::EventFilter::MouseEvents}, +{"keyboard-events", "Dump keyboard events.", QtDiag::EventFilter::KeyEvents}, +{"state-events", "Dump state/focus change events.", QtDiag::EventFilter::StateChangeEvents | QtDiag::EventFilter::FocusEvents} +}; + +static inline bool isOptionSet(int argc, char *argv[], const char *option) +{ + return (argv + argc) != + std::find_if(argv + 1, argv + argc, + [option] (const char *arg) { return !qstrcmp(arg, option); }); +} + +int main(int argc, char *argv[]) +{ + // Check for no scaling before QApplication is instantiated. + if (isOptionSet(argc, argv, "-s")) + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); + QGuiApplication::setApplicationDisplayName("Foreign window tester"); + + QApplication app(argc, argv); + + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.setApplicationDescription(description(QCoreApplication::applicationName())); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption noScalingDummy(QStringLiteral("s"), + QStringLiteral("Disable High DPI scaling.")); + parser.addOption(noScalingDummy); + QCommandLineOption outputAllOption(QStringList() << QStringLiteral("a") << QStringLiteral("all"), + QStringLiteral("Output all native window ids (requires diaglib).")); + parser.addOption(outputAllOption); + QCommandLineOption continuousOption(QStringList() << QStringLiteral("c") << QStringLiteral("continuous"), + QStringLiteral("Output continuously.")); + parser.addOption(continuousOption); + QCommandLineOption embedOption(QStringList() << QStringLiteral("e") << QStringLiteral("embed"), + QStringLiteral("Embed a foreign window into a Qt widget.")); + parser.addOption(embedOption); + const int eventFilterOptionCount = int(sizeof(eventFilterOptions) / sizeof(eventFilterOptions[0])); + for (int i = 0; i < eventFilterOptionCount; ++i) { + parser.addOption(QCommandLineOption(QLatin1String(eventFilterOptions[i].name), + QLatin1String(eventFilterOptions[i].description))); + } + parser.addPositionalArgument(QStringLiteral("[windows]"), QStringLiteral("Window IDs.")); + + parser.process(QCoreApplication::arguments()); + + if (parser.isSet(outputAllOption)) { + QtDiag::dumpNativeWindows(); + return 0; + } + + QWindowList windows; + foreach (const QString &argument, parser.positionalArguments()) { + bool ok = true; + WId wid = createInternalWindow(argument); + if (!wid) + wid = argument.toULongLong(&ok, 0); + if (!wid || !ok) { + std::cerr << "Invalid window id: \"" << qPrintable(argument) << "\"\n"; + return -1; + } + QWindow *foreignWindow = QWindow::fromWinId(wid); + foreignWindow->setObjectName("ForeignWindow" + QString::number(wid, 16)); + windows.append(foreignWindow); + } + + if (windows.isEmpty()) + parser.showHelp(0); + + int exitCode = 0; + + if (parser.isSet(embedOption)) { + QtDiag::EventFilter::EventCategories eventCategories = 0; + for (int i = 0; i < eventFilterOptionCount; ++i) { + if (parser.isSet(QLatin1String(eventFilterOptions[i].name))) + eventCategories |= eventFilterOptions[i].categories; + } + if (eventCategories) + app.installEventFilter(new QtDiag::EventFilter(eventCategories, &app)); + + const QRect availableGeometry = QApplication::desktop()->availableGeometry(0); + QPoint pos = availableGeometry.topLeft() + QPoint(availableGeometry.width(), availableGeometry.height()) / 3; + + WidgetPtrList mainWindows; + foreach (QWindow *window, windows) { + WidgetPtr mainWindow(new EmbeddingWindow(window)); + mainWindow->move(pos); + mainWindow->resize(availableGeometry.size() / 4); + mainWindow->show(); + pos += QPoint(40, 40); + mainWindows.append(mainWindow); + } + exitCode = app.exec(); + + } else if (parser.isSet(continuousOption)) { + WindowDumper dumper(windows); + dumper.dump(); + QTimer *timer = new QTimer(&dumper); + QObject::connect(timer, &QTimer::timeout, &dumper, &WindowDumper::dump); + timer->start(1000); + exitCode = app.exec(); + + } else { + WindowDumper(windows).dump(); + } + + return exitCode; +} + +#include "main.moc" diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index cffe76b2b4..8777cc6e11 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -2,6 +2,7 @@ TEMPLATE=subdirs SUBDIRS = bearerex \ filetest \ +foreignwindows \ gestures \ inputmethodhints \ keypadnavigation \ @@ -62,4 +63,5 @@ win32 { } lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \ -qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency +qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \ +foreignwindows -- cgit v1.2.3