From ef2200e327fd19d6ef3a0b5c4406aedfbcd61f95 Mon Sep 17 00:00:00 2001 From: Felix Lionardo Date: Thu, 16 Jan 2025 15:52:26 -0500 Subject: [PATCH] QNX desktop support Adds functionality to communicate with a desktop manager. This feature is only enabled if "desktop" is added to the QT_QPA_PLATFORM environment variable (e.g., "QT_QPA_PLATFORM=qnx:no-fullscreen:desktop"). Pick-to: 6.8 Change-Id: If98e0dda43692abce47f2d8f5f340bd7276ec901 Reviewed-by: James McDonnell (cherry picked from commit 70b1db9c0e0a53ec7e47ba296a534d38559c702f) Reviewed-by: Qt Cherry-pick Bot --- src/plugins/platforms/qnx/qqnxintegration.cpp | 4 + src/plugins/platforms/qnx/qqnxintegration.h | 3 +- .../platforms/qnx/qqnxscreeneventhandler.cpp | 35 ++++++++ .../platforms/qnx/qqnxscreeneventhandler.h | 1 + src/plugins/platforms/qnx/qqnxwindow.cpp | 85 ++++++++++++++++--- src/plugins/platforms/qnx/qqnxwindow.h | 10 +++ 6 files changed, 123 insertions(+), 15 deletions(-) diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 8e3c4ee85a8..1a60bf064cc 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -93,6 +93,10 @@ static inline QQnxIntegration::Options parseOptions(const QStringList ¶mList options |= QQnxIntegration::SurfacelessEGLContext; } + if (paramList.contains("desktop"_L1)) { + options |= QQnxIntegration::Desktop; + } + return options; } diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index 2fc762314d8..4de1dccca8f 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -54,7 +54,8 @@ public: FullScreenApplication = 0x1, RootWindow = 0x2, AlwaysFlushScreenContext = 0x4, - SurfacelessEGLContext = 0x8 + SurfacelessEGLContext = 0x8, + Desktop = 0x10 }; Q_DECLARE_FLAGS(Options, Option) explicit QQnxIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index b4c0cd3af72..b451360c955 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -192,6 +192,10 @@ bool QQnxScreenEventHandler::handleEvent(screen_event_t event, int qnxType) handlePropertyEvent(event); break; + case SCREEN_EVENT_MANAGER: + handleManagerEvent(event); + break; + default: // event ignored qCDebug(lcQpaScreenEvents) << Q_FUNC_INFO << "Unknown event" << qnxType; @@ -698,6 +702,11 @@ void QQnxScreenEventHandler::handlePropertyEvent(screen_event_t event) if (Q_UNLIKELY(screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0)) qFatal("QQnx: failed to query window property, errno=%d", errno); + if (window == 0) { + qCDebug(lcQpaScreenEvents) << "handlePositionEvent on NULL window"; + return; + } + errno = 0; int property; if (Q_UNLIKELY(screen_get_event_property_iv(event, SCREEN_PROPERTY_NAME, &property) != 0)) @@ -768,4 +777,30 @@ void QQnxScreenEventHandler::timerEvent(QTimerEvent *event) QT_END_NAMESPACE +void QQnxScreenEventHandler::handleManagerEvent(screen_event_t event) +{ + errno = 0; + int subtype; + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_SUBTYPE, &subtype), + "Failed to query object type property"); + + errno = 0; + screen_window_t window = 0; + if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) + qFatal("QQnx: failed to query window property, errno=%d", errno); + + switch (subtype) { + case SCREEN_EVENT_CLOSE: { + QWindow *closeWindow = QQnxIntegration::instance()->window(window); + closeWindow->close(); + break; + } + + default: + // event ignored + qCDebug(lcQpaScreenEvents) << "Ignore manager event for subtype: " << subtype; + } +} + #include "moc_qqnxscreeneventhandler.cpp" diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h index d8524a1ffb2..1e7ce98713c 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h @@ -54,6 +54,7 @@ private: void handlePropertyEvent(screen_event_t event); void handleKeyboardFocusPropertyEvent(screen_window_t window); void handleGeometryPropertyEvent(screen_window_t window); + void handleManagerEvent(screen_event_t event); private: enum { diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 3384932a8b6..3a09f71bfd3 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -119,7 +119,8 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW m_exposed(true), m_foreign(false), m_windowState(Qt::WindowNoState), - m_firstActivateHandled(false) + m_firstActivateHandled(false), + m_desktopNotify(0) { qCDebug(lcQpaWindow) << "window =" << window << ", size =" << window->size(); @@ -201,6 +202,26 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW } } + // QNX desktop integration. + if (QQnxIntegration::instance()->options() & QQnxIntegration::Desktop) { + // Determine if the window needs a frame. + switch (window->type()) { + case Qt::Popup: + case Qt::ToolTip: + m_desktopNotify = DesktopNotifyPosition | DesktopNotifyVisible; + break; + + default: + m_desktopNotify = DesktopNotifyTitle | DesktopNotifyVisible; + break; + } + + // Wait for the window manager to acknowledge the window's creation. + // The call returns immediately if there is no window manager. + screen_manage_window(m_window, + (m_desktopNotify & DesktopNotifyTitle) ? "Frame=Y" : "Frame=N"); + } + int debug = 0; if (Q_UNLIKELY(debug_fps())) { debug |= SCREEN_DEBUG_GRAPH_FPS; @@ -308,12 +329,17 @@ void QQnxWindow::setGeometryHelper(const QRect &rect) // Call base class method QPlatformWindow::setGeometry(rect); - // Set window geometry equal to widget geometry int val[2]; - val[0] = rect.x(); - val[1] = rect.y(); - Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val), - "Failed to set window position"); + + // Set window geometry equal to widget geometry + if (m_desktopNotify & DesktopNotifyPosition) { + notifyManager(QString::asprintf("Pos=%d,%d", rect.x(), rect.y())); + } else { + val[0] = rect.x(); + val[1] = rect.y(); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val), + "Failed to set window position"); + } val[0] = rect.width(); val[1] = rect.height(); @@ -373,8 +399,12 @@ void QQnxWindow::updateVisibility(bool parentVisible) qCDebug(lcQpaWindow) << "parentVisible =" << parentVisible << "window =" << window(); // Set window visibility int val = (m_visible && parentVisible) ? 1 : 0; - Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val), - "Failed to set window visibility"); + if (m_desktopNotify & DesktopNotifyVisible) { + notifyManager(QString("Visible=") + (val ? "Y" : "N")); + } else { + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val), + "Failed to set window visibility"); + } Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->updateVisibility(m_visible && parentVisible); @@ -421,16 +451,16 @@ void QQnxWindow::setBufferSize(const QSize &size) screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, &format), "Failed to set window format"); - if (m_bufferSize.isValid()) { - // destroy buffers first, if resized - Q_SCREEN_CRITICALERROR(screen_destroy_window_buffers(m_window), - "Failed to destroy window buffers"); - } - int val[2] = { nonEmptySize.width(), nonEmptySize.height() }; Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val), "Failed to set window buffer size"); + if (m_bufferSize.isValid()) { + m_bufferSize = nonEmptySize; + resetBuffers(); + return; + } + Q_SCREEN_CRITICALERROR(screen_create_window_buffers(m_window, MAX_BUFFER_COUNT), "Failed to create window buffers"); @@ -930,6 +960,33 @@ void QQnxWindow::addContextPermission() grantString.data()); } +void QQnxWindow::setWindowTitle(const QString &title) +{ + if (m_desktopNotify & DesktopNotifyTitle) { + QString titleStr = "Title=" + title; + notifyManager(titleStr); + } +} + +void QQnxWindow::notifyManager(const QString &msg) +{ + screen_event_t ev; + screen_create_event(&ev); + + std::string str = msg.toStdString(); + screen_set_event_property_iv(ev, SCREEN_PROPERTY_TYPE, + (const int[]){ SCREEN_EVENT_MANAGER }); + screen_set_event_property_cv(ev, SCREEN_PROPERTY_USER_DATA, str.length(), + str.c_str()); + screen_set_event_property_pv(ev, SCREEN_PROPERTY_WINDOW, + reinterpret_cast(&m_window)); + screen_set_event_property_pv(ev, SCREEN_PROPERTY_CONTEXT, + reinterpret_cast(&m_screenContext)); + + Q_SCREEN_CHECKERROR(screen_inject_event(NULL, ev), + "Failed to send a message to the window manager"); +} + void QQnxWindow::removeContextPermission() { QByteArray revokeString("context:"); diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index 013ea342e48..9538d5cd055 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -75,6 +75,8 @@ public: void windowPosted(); void handleActivationEvent(); + void setWindowTitle(const QString &title); + protected: virtual int pixelFormat() const = 0; virtual void resetBuffers() = 0; @@ -95,6 +97,7 @@ private: void setFocus(screen_window_t newFocusWindow); bool showWithoutActivating() const; bool focusable() const; + void notifyManager(const QString &msg); void addContextPermission(); void removeContextPermission(); @@ -119,6 +122,13 @@ private: bool m_isTopLevel; bool m_firstActivateHandled; + int m_desktopNotify; + + enum { + DesktopNotifyTitle = 0x1, + DesktopNotifyPosition = 0x2, + DesktopNotifyVisible = 0x2 + }; }; QT_END_NAMESPACE