Details
-
Bug
-
Resolution: Done
-
P2: Important
-
None
-
5.12.3
-
None
Description
The foreign window support on macos is really in a bad shape, to not say it does not works at all.
There is various issues:
- WId from same process
- Foreign window geometry is modified - it should not
- Deleting the foreign window lead to foreign window corruption (content can no longer be rendered and errors are throws)
- WId from external process
- Crash, the reinterpreted cast from https://code.woboq.org/qt5/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm.html#168 result to a non null pointer but it's invalid - using it crash the application.
I was not able to find Apple documentation on this subject (external process WId usage), either Apple don't support that, or SIP prevent it. Still need to find a way to prevent crashes.
- Crash, the reinterpreted cast from https://code.woboq.org/qt5/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm.html#168 result to a non null pointer but it's invalid - using it crash the application.
#include <QtGui> #include <QtWidgets> // Mimic KwindowSystem::setMainWindow void KWindowSystemSetMainWindow(QWidget *subWidget, WId mainWindowId) { // Set the WA_NativeWindow attribute to force the creation of the QWindow. // Without this QWidget::windowHandle() returns 0. subWidget->setAttribute(Qt::WA_NativeWindow, true); QWindow *subWindow = subWidget->windowHandle(); Q_ASSERT(subWindow); QWindow *mainWindow = QWindow::fromWinId(mainWindowId); if (!mainWindow) { // foreign windows not supported on all platforms return; } // mainWindow is not the child of any object, so make sure it gets deleted at some point QObject::connect(subWidget, &QObject::destroyed, mainWindow, &QObject::deleteLater); subWindow->setTransientParent(mainWindow); } class D : public QDialog { public: explicit D(QWidget *parent = nullptr) : QDialog(parent) { auto closeButton = new QPushButton(QString::fromLatin1("Close")); connect(closeButton, &QPushButton::clicked, this, &QDialog::accept); auto vLayout = new QVBoxLayout(this); vLayout->addWidget(closeButton); } }; class W : public QMainWindow { public: explicit W(QWidget *parent = nullptr) : QMainWindow(parent) { auto autoDelete = new QCheckBox(QString::fromLatin1("Dlg DeleteOnClose")); auto usePointer = new QCheckBox(QString::fromLatin1("Dlg use pointer")); auto inProcess = new QPushButton(QString::fromLatin1("In-Process")); connect(inProcess, &QPushButton::clicked, this, [this, autoDelete, usePointer]() { process(winId(), autoDelete->isChecked(), usePointer->isChecked()); }); auto outProcess = new QPushButton(QString::fromLatin1("Out-Process")); connect(outProcess, &QPushButton::clicked, this, [this, autoDelete, usePointer]() { const QString cmd = QApplication::applicationFilePath(); const QStringList args = { QString::number(winId()), QString::number(autoDelete->isChecked() ? 1 : 0), QString::number(usePointer->isChecked() ? 1 : 0) }; QProcess::startDetached(cmd, args); }); auto centralWidget = new QWidget(this); auto vLayout = new QVBoxLayout(centralWidget); vLayout->addWidget(autoDelete); vLayout->addWidget(usePointer); vLayout->addWidget(inProcess); vLayout->addWidget(outProcess); setCentralWidget(centralWidget); } void process(WId id, bool autoDelete, bool usePointer) { qWarning() << Q_FUNC_INFO << id << autoDelete << usePointer; const auto setup = [id, autoDelete](QDialog *dlg) { dlg->setAttribute(Qt::WA_DeleteOnClose, autoDelete); KWindowSystemSetMainWindow(dlg, id); dlg->exec(); }; if (usePointer) { auto d = new D; setup(d); } else { D d; setup(&d); } } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QApplication::setApplicationName(QFileInfo(QString::fromLatin1(argv[0])).baseName()); QApplication::setApplicationDisplayName(QApplication::applicationName()); const WId id = argc > 1 ? static_cast<quintptr>(QString::fromLatin1(argv[1]).toULongLong()) : 0; const bool deleteOnClose = argc > 2 ? QString::fromLatin1(argv[2]).toInt() == 1 : 0; const bool usePointer = argc > 3 ? QString::fromLatin1(argv[3]).toInt() == 1 : 0; W w; w.setWindowTitle(QApplication::applicationDisplayName()); if (id != 0) { w.process(id, deleteOnClose, usePointer); } else { w.show(); } return app.exec(); }