summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/qnx/qqnxintegration.cpp
blob: 1a60bf064ccd52ee42ccb1f7bb33b78b724d58fe (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
// Copyright (C) 2013 BlackBerry Limited. All rights reserved.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#undef QT_NO_FOREACH// this file contains unported legacy Q_FOREACH uses#include"qqnxglobal.h"#include"qqnxintegration.h"#include"qqnxscreeneventthread.h"#include"qqnxnativeinterface.h"#include"qqnxrasterbackingstore.h"#include"qqnxscreen.h"#include"qqnxscreeneventhandler.h"#include"qqnxwindow.h"#include"qqnxnavigatoreventhandler.h"#include"qqnxabstractnavigator.h"#include"qqnxabstractvirtualkeyboard.h"#include"qqnxservices.h"#include"qqnxforeignwindow.h"#include"qqnxrasterwindow.h"#if !defined(QT_NO_OPENGL)#include"qqnxeglwindow.h"#endif#if QT_CONFIG(qqnx_pps)#include"qqnxnavigatorpps.h"#include"qqnxnavigatoreventnotifier.h"#include"qqnxvirtualkeyboardpps.h"#endif#if QT_CONFIG(qqnx_pps)# include"qqnxbuttoneventnotifier.h"# include"qqnxclipboard.h"#endif#if QT_CONFIG(qqnx_imf)# include"qqnxinputcontext_imf.h"#else# include"qqnxinputcontext_noimf.h"#endif#include <qpa/qplatforminputcontextfactory_p.h>#include <qpa/qplatforminputcontext.h>#include"private/qgenericunixfontdatabase_p.h"#include"private/qgenericunixeventdispatcher_p.h"#include <qpa/qplatformwindow.h>#include <qpa/qwindowsysteminterface.h>#include <QtGui/private/qguiapplication_p.h>#include <QtGui/private/qrhibackingstore_p.h>#if !defined(QT_NO_OPENGL)#include"qqnxglcontext.h"#include <QtGui/QOpenGLContext>#endif#include <private/qsimpledrag_p.h>#include <QtCore/QDebug>#include <QtCore/QJsonDocument>#include <QtCore/QJsonObject>#include <QtCore/QJsonArray>#include <QtCore/QFile>#include <errno.h> QT_BEGIN_NAMESPACE // Q_LOGGING_CATEGORY(lcQpaQnx, "qt.qpa.qnx");using namespaceQt::StringLiterals; QQnxIntegration *QQnxIntegration::ms_instance;staticinline QQnxIntegration::Options parseOptions(const QStringList &paramList){QQnxIntegration::Options options =QQnxIntegration::NoOptions;if(!paramList.contains("no-fullscreen"_L1)) { options |=QQnxIntegration::FullScreenApplication;}if(paramList.contains("flush-screen-context"_L1)) { options |=QQnxIntegration::AlwaysFlushScreenContext;}if(paramList.contains("rootwindow"_L1)) { options |=QQnxIntegration::RootWindow;}if(!paramList.contains("disable-EGL_KHR_surfaceless_context"_L1)) { options |=QQnxIntegration::SurfacelessEGLContext;}if(paramList.contains("desktop"_L1)) { options |=QQnxIntegration::Desktop;}return options;}staticinlineintgetContextCapabilities(const QStringList &paramList){constexpr auto contextCapabilitiesPrefix ="screen-context-capabilities="_L1;int contextCapabilities = SCREEN_APPLICATION_CONTEXT;for(const QString &param : paramList) {if(param.startsWith(contextCapabilitiesPrefix)) {auto value = QStringView{param}.mid(contextCapabilitiesPrefix.length());bool ok =false; contextCapabilities = value.toInt(&ok,0);if(!ok) contextCapabilities = SCREEN_APPLICATION_CONTEXT;}}return contextCapabilities;}QQnxIntegration::QQnxIntegration(const QStringList &paramList):QPlatformIntegration(),m_screenContextId(256,0),m_screenEventThread(0),m_navigatorEventHandler(newQQnxNavigatorEventHandler()),m_virtualKeyboard(0),m_inputContext(0)#if QT_CONFIG(qqnx_pps),m_navigatorEventNotifier(0),m_buttonsNotifier(newQQnxButtonEventNotifier())#endif,m_qpaInputContext(0),m_fontDatabase(newQGenericUnixFontDatabase()),m_eventDispatcher(createUnixEventDispatcher()),m_nativeInterface(newQQnxNativeInterface(this)),m_screenEventHandler(newQQnxScreenEventHandler(this))#if !defined(QT_NO_CLIPBOARD),m_clipboard(0)#endif,m_navigator(0)#if QT_CONFIG(draganddrop),m_drag(newQSimpleDrag())#endif#if QT_CONFIG(opengl),m_eglDisplay(EGL_NO_DISPLAY)#endif{ ms_instance =this; m_options =parseOptions(paramList);qCDebug(lcQpaQnx) << Q_FUNC_INFO;// Open connection to QNX composition managerif(screen_create_context(&m_screenContext,getContextCapabilities(paramList))) {qFatal("%s - Screen: Failed to create screen context - Error: %s (%i)", Q_FUNC_INFO,strerror(errno), errno);}screen_get_context_property_cv(m_screenContext, SCREEN_PROPERTY_ID, m_screenContextId.size(), m_screenContextId.data()); m_screenContextId.resize(strlen(m_screenContextId.constData()));#if QT_CONFIG(qqnx_pps)// Create/start navigator event notifier m_navigatorEventNotifier =newQQnxNavigatorEventNotifier(m_navigatorEventHandler);// delay invocation of start() to the time the event loop is up and running// needed to have the QThread internals of the main thread properly initializedQMetaObject::invokeMethod(m_navigatorEventNotifier,"start",Qt::QueuedConnection);#endif#if QT_CONFIG(opengl)createEglDisplay();#endif// Create/start event thread m_screenEventThread =newQQnxScreenEventThread(m_screenContext); m_screenEventHandler->setScreenEventThread(m_screenEventThread); m_screenEventThread->start(); m_qpaInputContext =QPlatformInputContextFactory::create();#if QT_CONFIG(qqnx_pps)if(!m_qpaInputContext) {// Create/start the keyboard class. m_virtualKeyboard =newQQnxVirtualKeyboardPps();// delay invocation of start() to the time the event loop is up and running// needed to have the QThread internals of the main thread properly initializedQMetaObject::invokeMethod(m_virtualKeyboard,"start",Qt::QueuedConnection);}#endif#if QT_CONFIG(qqnx_pps) m_navigator =newQQnxNavigatorPps();#endifcreateDisplays();if(m_virtualKeyboard) {// TODO check if we need to do this for all screens or only the primary oneQObject::connect(m_virtualKeyboard,SIGNAL(heightChanged(int)),primaryDisplay(),SLOT(keyboardHeightChanged(int)));#if QT_CONFIG(qqnx_pps)// Set up the input context m_inputContext =newQQnxInputContext(this, *m_virtualKeyboard);#if QT_CONFIG(qqnx_imf) m_screenEventHandler->addScreenEventFilter(m_inputContext);#endif#endif}#if QT_CONFIG(qqnx_pps)// delay invocation of start() to the time the event loop is up and running// needed to have the QThread internals of the main thread properly initializedQMetaObject::invokeMethod(m_buttonsNotifier,"start",Qt::QueuedConnection);#endif}QQnxIntegration::~QQnxIntegration(){qCDebug(lcQpaQnx) <<"Platform plugin shutdown begin";delete m_nativeInterface;#if QT_CONFIG(draganddrop)// Destroy the drag objectdelete m_drag;#endif#if !defined(QT_NO_CLIPBOARD)// Delete the clipboarddelete m_clipboard;#endif// Stop/destroy navigator event notifier#if QT_CONFIG(qqnx_pps)delete m_navigatorEventNotifier;#endifdelete m_navigatorEventHandler;// Stop/destroy screen event threaddelete m_screenEventThread;// In case the event-dispatcher was never transferred to QCoreApplicationdelete m_eventDispatcher;delete m_screenEventHandler;// Destroy all displaysdestroyDisplays();// Close connection to QNX composition managerscreen_destroy_context(m_screenContext);#if QT_CONFIG(opengl)destroyEglDisplay();#endif#if QT_CONFIG(qqnx_pps)// Destroy the hardware button notifierdelete m_buttonsNotifier;// Destroy input contextdelete m_inputContext;#endifdelete m_qpaInputContext;// Destroy the keyboard class.delete m_virtualKeyboard;// Destroy services classdelete m_services;// Destroy navigator interfacedelete m_navigator; ms_instance =nullptr;qCDebug(lcQpaQnx) <<"Platform plugin shutdown end";}boolQQnxIntegration::hasCapability(QPlatformIntegration::Capability cap)const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;switch(cap) {case MultipleWindows:case ForeignWindows:case ThreadedPixmaps:return true;#if !defined(QT_NO_OPENGL)case OpenGL:case ThreadedOpenGL:case BufferQueueingOpenGL:return true;#endifdefault:returnQPlatformIntegration::hasCapability(cap);}} QPlatformWindow *QQnxIntegration::createForeignWindow(QWindow *window, WId nativeHandle)const{ screen_window_t screenWindow =reinterpret_cast<screen_window_t>(nativeHandle);if(this->window(screenWindow)) {qWarning() <<"QWindow already created for foreign window"<< screenWindow;returnnullptr;}return newQQnxForeignWindow(window, m_screenContext, screenWindow);} QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window)const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;QSurface::SurfaceType surfaceType = window->surfaceType();const bool needRootWindow =options() & RootWindow;switch(surfaceType) {caseQSurface::RasterSurface:return newQQnxRasterWindow(window, m_screenContext, needRootWindow);#if !defined(QT_NO_OPENGL)caseQSurface::OpenGLSurface:return newQQnxEglWindow(window, m_screenContext, needRootWindow);#endifdefault:qFatal("QQnxWindow: unsupported window API");}return0;} QPlatformBackingStore *QQnxIntegration::createPlatformBackingStore(QWindow *window)const{QSurface::SurfaceType surfaceType = window->surfaceType();qCDebug(lcQpaQnx) << Q_FUNC_INFO << surfaceType;switch(surfaceType) {caseQSurface::RasterSurface:return newQQnxRasterBackingStore(window);#if !defined(QT_NO_OPENGL)// Return a QRhiBackingStore for non-raster surface windowscaseQSurface::OpenGLSurface:return newQRhiBackingStore(window);#endifdefault:returnnullptr;}}#if !defined(QT_NO_OPENGL) QPlatformOpenGLContext *QQnxIntegration::createPlatformOpenGLContext(QOpenGLContext *context)const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;// Get color channel sizes from window format QSurfaceFormat format = context->format();int alphaSize = format.alphaBufferSize();int redSize = format.redBufferSize();int greenSize = format.greenBufferSize();int blueSize = format.blueBufferSize();// Check if all channels are don't careif(alphaSize == -1&& redSize == -1&& greenSize == -1&& blueSize == -1) {// Set color channels based on depth of window's screen QQnxScreen *screen =static_cast<QQnxScreen*>(context->screen()->handle());int depth = screen->depth();if(depth ==32) {// SCREEN_FORMAT_RGBA8888 alphaSize =8; redSize =8; greenSize =8; blueSize =8;}else{// SCREEN_FORMAT_RGB565 alphaSize =0; redSize =5; greenSize =6; blueSize =5;}}else{// Choose best match based on supported pixel formatsif(alphaSize <=0&& redSize <=5&& greenSize <=6&& blueSize <=5) {// SCREEN_FORMAT_RGB565 alphaSize =0; redSize =5; greenSize =6; blueSize =5;}else{// SCREEN_FORMAT_RGBA8888 alphaSize =8; redSize =8; greenSize =8; blueSize =8;}}// Update color channel sizes in window format format.setAlphaBufferSize(alphaSize); format.setRedBufferSize(redSize); format.setGreenBufferSize(greenSize); format.setBlueBufferSize(blueSize); context->setFormat(format); QQnxGLContext *ctx =newQQnxGLContext(context->format(), context->shareHandle());return ctx;}#endif QPlatformInputContext *QQnxIntegration::inputContext()const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;if(m_qpaInputContext)return m_qpaInputContext;return m_inputContext;}voidQQnxIntegration::moveToScreen(QWindow *window,int screen){qCDebug(lcQpaQnx) << Q_FUNC_INFO <<"w ="<< window <<", s ="<< screen;// get platform window used by widget QQnxWindow *platformWindow =static_cast<QQnxWindow *>(window->handle());// lookup platform screen by index QQnxScreen *platformScreen = m_screens.at(screen);// move the platform window to the platform screen platformWindow->setScreen(platformScreen);} QAbstractEventDispatcher *QQnxIntegration::createEventDispatcher()const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;// We transfer ownersip of the event-dispatcher to QtCoreApplication QAbstractEventDispatcher *eventDispatcher = m_eventDispatcher; m_eventDispatcher =0;return eventDispatcher;} QPlatformNativeInterface *QQnxIntegration::nativeInterface()const{return m_nativeInterface;}#if !defined(QT_NO_CLIPBOARD) QPlatformClipboard *QQnxIntegration::clipboard()const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;#if QT_CONFIG(qqnx_pps)if(!m_clipboard) m_clipboard =new QQnxClipboard;#endifreturn m_clipboard;}#endif#if QT_CONFIG(draganddrop) QPlatformDrag *QQnxIntegration::drag()const{return m_drag;}#endif QVariant QQnxIntegration::styleHint(QPlatformIntegration::StyleHint hint)const{qCDebug(lcQpaQnx) << Q_FUNC_INFO;if((hint == ShowIsFullScreen) && (m_options & FullScreenApplication))return true;returnQPlatformIntegration::styleHint(hint);} QPlatformServices *QQnxIntegration::services()const{// Create services handling classif(m_navigator && !m_services) m_services =newQQnxServices(m_navigator);return m_services;} QWindow *QQnxIntegration::window(screen_window_t qnxWindow)const{qCDebug(lcQpaQnx) << Q_FUNC_INFO; QMutexLocker locker(&m_windowMapperMutex);Q_UNUSED(locker);return m_windowMapper.value(qnxWindow,0);}voidQQnxIntegration::addWindow(screen_window_t qnxWindow, QWindow *window){qCDebug(lcQpaQnx) << Q_FUNC_INFO; QMutexLocker locker(&m_windowMapperMutex);Q_UNUSED(locker); m_windowMapper.insert(qnxWindow, window);}voidQQnxIntegration::removeWindow(screen_window_t qnxWindow){qCDebug(lcQpaQnx) << Q_FUNC_INFO; QMutexLocker locker(&m_windowMapperMutex);Q_UNUSED(locker); m_windowMapper.remove(qnxWindow);}/*! Get display ID for given \a display Returns -1 for failure, otherwise returns display ID */static intgetIdOfDisplay(screen_display_t display){int displayId;if(screen_get_display_property_iv(display, SCREEN_PROPERTY_ID,&displayId) ==0) {return displayId;}return-1;}/*! Read JSON configuration file for the QNX display order Returns true if file was read successfully and fills \a requestedDisplays */static boolgetRequestedDisplays(QJsonArray &requestedDisplays){// Check if display configuration file is provided QByteArray json =qgetenv("QT_QPA_QNX_DISPLAY_CONFIG");if(json.isEmpty())return false;// Check if configuration file exists QFile file(QString::fromUtf8(json));if(!file.open(QFile::ReadOnly)) {qWarning() <<"Could not open config file"<< json <<"for reading";return false;}// Read config file and check it's jsonconst QJsonDocument doc =QJsonDocument::fromJson(file.readAll());if(!doc.isObject()) {qWarning() <<"Invalid config file"<< json <<"- no top-level JSON object";return false;}// Read the requested display orderconst QJsonObject object = doc.object(); requestedDisplays = object.value("displayOrder"_L1).toArray();return true;}/*! Match \a availableDisplays with display order defined in a json file pointed to by QT_QPA_QNX_DISPLAY_CONFIG. Display order must use same identifiers as defined for displays in graphics.conf. Number of available displays must be specified in \a displayCount An example configuration is below: \badcode { "displayOrder": [ 3, 1 ] } \endcode Returns ordered list of displays. If no order was specified, returns displays in the same order as in the original list.*/ QList<screen_display_t *>QQnxIntegration::sortDisplays(screen_display_t *availableDisplays,int displayCount){// Intermediate list for sorting QList<screen_display_t *> allDisplays;for(int i =0; i < displayCount; i++) allDisplays.append(&availableDisplays[i]);// Read requested display order if available QJsonArray requestedDisplays;if(!getRequestedDisplays(requestedDisplays))return allDisplays;// Go through all the requested displays IDs QList<screen_display_t *> orderedDisplays;for(const QJsonValue &value :std::as_const(requestedDisplays)) {int requestedValue = value.toInt();// Move all displays with matching ID from the intermediate list// to the beginning of the ordered listfor(auto it = allDisplays.begin(), end = allDisplays.end(); it != end; ++it) { screen_display_t *display = *it;if(getIdOfDisplay(*display) == requestedValue) { orderedDisplays.append(display); allDisplays.erase(it);break;}}}// Place all unordered displays to the end of list orderedDisplays.append(allDisplays);return orderedDisplays;}voidQQnxIntegration::createDisplays(){qCDebug(lcQpaQnx) << Q_FUNC_INFO;// Query number of displaysint displayCount =0;int result =screen_get_context_property_iv(m_screenContext, SCREEN_PROPERTY_DISPLAY_COUNT,&displayCount);Q_SCREEN_CRITICALERROR(result,"Failed to query display count");if(Q_UNLIKELY(displayCount <1)) {// Never happens, even if there's no display, libscreen returns 1qFatal("QQnxIntegration: displayCount=%d", displayCount);}// Get all displays screen_display_t *displays = (screen_display_t *)alloca(sizeof(screen_display_t) * displayCount); result =screen_get_context_property_pv(m_screenContext, SCREEN_PROPERTY_DISPLAYS,(void**)displays); QList<screen_display_t *> orderedDisplays =sortDisplays(displays, displayCount);Q_SCREEN_CRITICALERROR(result,"Failed to query displays");// If it's primary, we create a QScreen for it even if it's not attached// since Qt will dereference QGuiApplication::primaryScreen()createDisplay(*orderedDisplays[0],/*isPrimary=*/true);for(int i=1; i<displayCount; i++) {int isAttached =1; result =screen_get_display_property_iv(*orderedDisplays[i], SCREEN_PROPERTY_ATTACHED,&isAttached);Q_SCREEN_CHECKERROR(result,"Failed to query display attachment");if(!isAttached) {qCDebug(lcQpaQnx) <<"Skipping non-attached display "<< i;continue;}qCDebug(lcQpaQnx) <<"Creating screen for display "<< i;createDisplay(*orderedDisplays[i],/*isPrimary=*/false);}// of displays iteration}voidQQnxIntegration::createDisplay(screen_display_t display,bool isPrimary){ QQnxScreen *screen =newQQnxScreen(m_screenContext, display, isPrimary); m_screens.append(screen);QWindowSystemInterface::handleScreenAdded(screen); screen->adjustOrientation();QObject::connect(m_screenEventHandler,SIGNAL(newWindowCreated(void*)), screen,SLOT(newWindowCreated(void*)));QObject::connect(m_screenEventHandler,SIGNAL(windowClosed(void*)), screen,SLOT(windowClosed(void*)));QObject::connect(m_navigatorEventHandler,SIGNAL(rotationChanged(int)), screen,SLOT(setRotation(int)));QObject::connect(m_navigatorEventHandler,SIGNAL(windowGroupActivated(QByteArray)), screen,SLOT(activateWindowGroup(QByteArray)));QObject::connect(m_navigatorEventHandler,SIGNAL(windowGroupDeactivated(QByteArray)), screen,SLOT(deactivateWindowGroup(QByteArray)));QObject::connect(m_navigatorEventHandler,SIGNAL(windowGroupStateChanged(QByteArray,Qt::WindowState)), screen,SLOT(windowGroupStateChanged(QByteArray,Qt::WindowState)));}voidQQnxIntegration::removeDisplay(QQnxScreen *screen){Q_CHECK_PTR(screen);Q_ASSERT(m_screens.contains(screen)); m_screens.removeAll(screen);QWindowSystemInterface::handleScreenRemoved(screen);}voidQQnxIntegration::destroyDisplays(){qCDebug(lcQpaQnx) << Q_FUNC_INFO;Q_FOREACH(QQnxScreen *screen, m_screens) {QWindowSystemInterface::handleScreenRemoved(screen);} m_screens.clear();} QQnxScreen *QQnxIntegration::screenForNative(screen_display_t qnxScreen)const{Q_FOREACH(QQnxScreen *screen, m_screens) {if(screen->nativeDisplay() == qnxScreen)return screen;}return0;} QQnxScreen *QQnxIntegration::primaryDisplay()const{return m_screens.first();}QQnxIntegration::Options QQnxIntegration::options()const{return m_options;} screen_context_t QQnxIntegration::screenContext(){return m_screenContext;} QByteArray QQnxIntegration::screenContextId(){return m_screenContextId;} QQnxNavigatorEventHandler *QQnxIntegration::navigatorEventHandler(){return m_navigatorEventHandler;}boolQQnxIntegration::supportsNavigatorEvents()const{// If QQNX_PPS is defined then we have navigatorreturn m_navigator !=0;}#if QT_CONFIG(opengl)voidQQnxIntegration::createEglDisplay(){qCDebug(lcQpaQnx) << Q_FUNC_INFO;// Initialize connection to EGL m_eglDisplay =eglGetDisplay(EGL_DEFAULT_DISPLAY);if(Q_UNLIKELY(m_eglDisplay == EGL_NO_DISPLAY))qFatal("QQnxiIntegration: failed to obtain EGL display: %x",eglGetError()); EGLBoolean eglResult =eglInitialize(m_eglDisplay,0,0);if(Q_UNLIKELY(eglResult != EGL_TRUE))qFatal("QQnxIntegration: failed to initialize EGL display, err=%d",eglGetError());}voidQQnxIntegration::destroyEglDisplay(){qCDebug(lcQpaQnx) << Q_FUNC_INFO;// Close connection to EGLeglTerminate(m_eglDisplay);}#endif QT_END_NAMESPACE 
close