diff --git a/src/corelib/global/qtypes.h b/src/corelib/global/qtypes.h index 6e168e1f87..e186d4a9e3 100644 --- a/src/corelib/global/qtypes.h +++ b/src/corelib/global/qtypes.h @@ -23,6 +23,11 @@ # include #else # include +# if defined(__QNXNTO__) && !defined(static_assert) + /* QNX assert.h (BB10 / QNX 6.6) does not define the static_assert + * macro; provide it via the C11 _Static_assert keyword. */ +# define static_assert _Static_assert +# endif #endif #if 0 diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 8be50e6aa7..13f6ab920b 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -30,6 +30,13 @@ #ifdef Q_OS_QNX # include +/* O_DIRECTORY and O_PATH are Linux-specific; not defined in QNX 6.6. */ +# ifndef O_DIRECTORY +# define O_DIRECTORY 0 +# endif +# ifndef O_PATH +# define O_PATH 0 +# endif #endif #include diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 0aa120ee1b..ec6aca5deb 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -78,7 +78,7 @@ #ifdef Q_OS_UNIX # include -# ifndef Q_OS_INTEGRITY +# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) // QNX/BB10 has no langinfo.h # include # endif # include diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h index 72057ee16d..a8a7d703f2 100644 --- a/src/corelib/kernel/qmath.h +++ b/src/corelib/kernel/qmath.h @@ -179,8 +179,11 @@ template auto qExp(T v) template auto qPow(T1 x, T2 y) { - using std::pow; - return pow(x, y); + // Cast both args to their common type to avoid ambiguous overload between + // QNX math.h _TGEN_RC2 template and GCC template for mixed types + // (e.g. qPow(float, double) would be ambiguous without this). + typedef typename std::common_type::type CT; + return std::pow(static_cast(x), static_cast(y)); } // TODO: use template variables (e.g. Qt::pi) for these once we have C++14 support: diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index c1c05a492d..04c545f49b 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -371,6 +371,7 @@ const char *QMetaObject::className() const bool QMetaObject::inherits(const QMetaObject *metaObject) const noexcept { const QMetaObject *m = this; + if (!m) return false; // BB10: tolerate null this do { if (metaObject == m) return true; @@ -394,7 +395,12 @@ bool QMetaObject::inherits(const QMetaObject *metaObject) const noexcept */ const QObject *QMetaObject::cast(const QObject *obj) const { - return (obj && obj->metaObject()->inherits(this)) ? obj : nullptr; + // BB10: tolerate null this and null obj->metaObject() + if (!this) return nullptr; + if (!obj) return nullptr; + const QMetaObject *m = obj->metaObject(); + if (!m) return nullptr; + return m->inherits(this) ? obj : nullptr; } #ifndef QT_NO_TRANSLATION @@ -970,7 +976,9 @@ QMetaMethod QMetaObjectPrivate::signal(const QMetaObject *m, int signal_index) if (signal_index < 0) return QMetaMethod(); - Q_ASSERT(m != nullptr); + // BB10: tolerate null QMetaObject instead of SIGSEGV + if (!m) + return QMetaMethod(); int i = signal_index; i -= signalOffset(m); if (i < 0 && m->d.superdata) diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index cc0be759d1..049e68f493 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -216,7 +216,7 @@ struct QMetaObjectPrivate Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index); static inline int signalOffset(const QMetaObject *m) { - Q_ASSERT(m != nullptr); + if (!m) return 0; // BB10: tolerate null instead of crashing int offset = 0; for (m = m->d.superdata; m; m = m->d.superdata) offset += reinterpret_cast(m->d.data)->signalCount; diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp index 7f6271cde4..7f50d5b1fa 100644 --- a/src/corelib/plugin/qelfparser_p.cpp +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -20,6 +20,54 @@ # error "Need ELF header to parse plugins." #endif +#ifdef Q_OS_QNX +/* QNX sys/elf.h (BB10 / QNX 6.6) uses non-standard enum values for ELFOSABI_* + * and omits many EM_*, SHT_*, SHF_*, and PT_* constants defined in the standard + * System V ABI. Redefine OSABI values as macros (which shadow the enum members) + * and add the missing constants so this file compiles without errors. */ + +/* --- OSABI: override with standard values (QNX enum order is non-standard) --- */ +# define ELFOSABI_SYSV 0 +# define ELFOSABI_NONE 0 +# define ELFOSABI_HPUX 1 +# define ELFOSABI_NETBSD 2 +# define ELFOSABI_LINUX 3 +# define ELFOSABI_SOLARIS 6 +# define ELFOSABI_AIX 7 +# define ELFOSABI_IRIX 8 +# define ELFOSABI_FREEBSD 9 +# define ELFOSABI_OPENBSD 12 + +/* --- EM_* machine types missing from QNX sys/elf.h --- */ +# define EM_68K 4 /* Motorola 68000 */ +# define EM_PARISC 15 /* HP PA-RISC (QNX has EM_PA_RISC) */ +# define EM_PPC64 21 /* PowerPC 64-bit */ +# define EM_S390 22 /* IBM System/390 */ +# define EM_IA_64 50 /* Intel IA-64 */ +# define EM_X86_64 62 /* AMD/Intel x86-64 */ +# define EM_SPARCV9 43 /* SPARC V9 */ +# define EM_ALPHA 0x9026 /* DEC Alpha (unofficial but widely used) */ +# define EM_AARCH64 183 /* ARM AArch64 */ +# define EM_BLACKFIN 106 /* Analog Devices Blackfin */ +# define EM_RISCV 243 /* RISC-V */ +# define EM_LOONGARCH 258 /* LoongArch */ + +/* --- SHT_* section types missing from QNX sys/elf.h --- */ +# define SHT_INIT_ARRAY 14 /* Array of constructors */ +# define SHT_FINI_ARRAY 15 /* Array of destructors */ + +/* --- SHF_* section flags missing from QNX sys/elf.h --- */ +# define SHF_STRINGS 0x20 /* Contains NUL-terminated strings */ +# define SHF_TLS 0x400 /* Section holds thread-local storage */ + +/* --- PT_* program header types missing from QNX sys/elf.h --- */ +# define PT_TLS 7 /* Thread-local storage segment */ +# define PT_GNU_PROPERTY 0x6474e553u /* GNU property notes */ + +/* --- Elf64_Nhdr absent in QNX; alias to Elf32_Nhdr (never used on ARM32) --- */ +typedef Elf32_Nhdr Elf64_Nhdr; +#endif + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 2aab6fab93..b198f5975a 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -4798,14 +4798,14 @@ QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats if (!bytes) { power = 0; } else if (format & DataSizeBase1000) { - power = int(std::log10(QtPrivate::qUnsignedAbs(bytes)) / 3); + power = int(std::log10(double(QtPrivate::qUnsignedAbs(bytes))) / 3); } else { // Compute log2(bytes) / 10: power = int((63 - qCountLeadingZeroBits(QtPrivate::qUnsignedAbs(bytes))) / 10); base = 1024; } // Only go to doubles if we'll be using a quantifier: const QString number = power - ? toString(bytes / std::pow(double(base), power), 'f', qMin(precision, 3 * power)) + ? toString(bytes / std::pow(double(base), double(power)), 'f', qMin(precision, 3 * power)) : toString(bytes); // We don't support sizes in units larger than exbibytes because diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 385f76fe15..a179d13cb3 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -2475,7 +2475,7 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool * if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok()) return QTime(); Q_ASSERT(frac.ok() ^ tail.isEmpty()); - double fraction = frac.ok() ? frac.result * std::pow(0.1, tail.size()) : 0.0; + double fraction = frac.ok() ? frac.result * std::pow(0.1, (double)tail.size()) : 0.0; const qsizetype size = string.size(); if (size < 2 || size > 8) diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 9468876c23..1e914674e7 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -1085,11 +1085,14 @@ bool QPaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTran if (fontEngine->glyphFormat == QFontEngine::Format_ARGB) return true; - static const int maxCachedGlyphSizeSquared = std::pow([]{ - if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE")) - return env; - return QT_MAX_CACHED_GLYPH_SIZE; - }(), 2); + static const int maxCachedGlyphSizeSquared = []{ + const int v = []{ /* std::pow(int,int) is ambiguous on QNX; use v*v */ + if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE")) + return env; + return QT_MAX_CACHED_GLYPH_SIZE; + }(); + return v * v; + }(); qreal pixelSize = fontEngine->fontDef.pixelSize; return (pixelSize * pixelSize * qAbs(m.determinant())) <= maxCachedGlyphSizeSquared; diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 53e4f3fb0e..abc36a2700 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -283,12 +283,14 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, #endif newFreetype->face = face; newFreetype->mm_var = nullptr; +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20700 if (FT_IS_NAMED_INSTANCE(newFreetype->face)) { FT_Error ftresult; ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var); if (ftresult != FT_Err_Ok) newFreetype->mm_var = nullptr; } +#endif newFreetype->ref.storeRelaxed(1); newFreetype->xsize = 0; @@ -328,6 +330,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map); +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20701 if (!face_id.variableAxes.isEmpty()) { FT_MM_Var *var = nullptr; FT_Get_MM_Var(newFreetype->face, &var); @@ -345,6 +348,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, FT_Done_MM_Var(qt_getFreetype(), var); } } +#endif QT_TRY { freetypeData->faces.insert(face_id, newFreetype.get()); @@ -362,8 +366,10 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, void QFreetypeFace::cleanup() { hbFace.reset(); +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20701 if (mm_var && face && face->glyph) FT_Done_MM_Var(face->glyph->library, mm_var); +#endif mm_var = nullptr; FT_Done_Face(face); face = nullptr; diff --git a/src/plugins/platforms/qnx/main.cpp b/src/plugins/platforms/qnx/main.cpp index e5656d7cb9..2aee2f0585 100644 --- a/src/plugins/platforms/qnx/main.cpp +++ b/src/plugins/platforms/qnx/main.cpp @@ -4,6 +4,7 @@ #include "main.h" #include "qqnxintegration.h" #include "qqnxlgmon.h" +#include QT_BEGIN_NAMESPACE @@ -11,11 +12,18 @@ using namespace Qt::StringLiterals; QPlatformIntegration *QQnxIntegrationPlugin::create(const QString& system, const QStringList& paramList) { + fprintf(stderr, "QNX-PLUGIN: create(system=%s) called\n", system.toLocal8Bit().constData()); + fflush(stderr); if (!system.compare("qnx"_L1, Qt::CaseInsensitive)) { + fprintf(stderr, "QNX-PLUGIN: qqnxLgmonInit()...\n"); fflush(stderr); qqnxLgmonInit(); - return new QQnxIntegration(paramList); + fprintf(stderr, "QNX-PLUGIN: new QQnxIntegration()...\n"); fflush(stderr); + QPlatformIntegration *r = new QQnxIntegration(paramList); + fprintf(stderr, "QNX-PLUGIN: QQnxIntegration constructed = %p\n", r); fflush(stderr); + return r; } + fprintf(stderr, "QNX-PLUGIN: system mismatch, returning 0\n"); fflush(stderr); return 0; } diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 1a60bf064c..3a6d5bab16 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -148,19 +148,28 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) qCDebug(lcQpaQnx) << Q_FUNC_INFO; // Open connection to QNX composition manager + fprintf(stderr, "QNX: screen_create_context(caps=0x%x) ...\n", getContextCapabilities(paramList)); + fflush(stderr); if (screen_create_context(&m_screenContext, getContextCapabilities(paramList))) { + fprintf(stderr, "QNX: screen_create_context FAILED: %s (%d)\n", strerror(errno), errno); + fflush(stderr); qFatal("%s - Screen: Failed to create screen context - Error: %s (%i)", Q_FUNC_INFO, strerror(errno), errno); } + fprintf(stderr, "QNX: screen_create_context OK\n"); + fflush(stderr); screen_get_context_property_cv(m_screenContext, SCREEN_PROPERTY_ID, m_screenContextId.size(), m_screenContextId.data()); m_screenContextId.resize(strlen(m_screenContextId.constData())); + fprintf(stderr, "QNX: context id: %s\n", m_screenContextId.constData()); + fflush(stderr); #if QT_CONFIG(qqnx_pps) // Create/start navigator event notifier m_navigatorEventNotifier = new QQnxNavigatorEventNotifier(m_navigatorEventHandler); + fprintf(stderr, "QNX: navigatorEventNotifier created\n"); fflush(stderr); // 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 initialized @@ -168,20 +177,26 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) #endif #if QT_CONFIG(opengl) + fprintf(stderr, "QNX: createEglDisplay...\n"); fflush(stderr); createEglDisplay(); + fprintf(stderr, "QNX: createEglDisplay done\n"); fflush(stderr); #endif // Create/start event thread + fprintf(stderr, "QNX: creating screenEventThread...\n"); fflush(stderr); m_screenEventThread = new QQnxScreenEventThread(m_screenContext); m_screenEventHandler->setScreenEventThread(m_screenEventThread); m_screenEventThread->start(); + fprintf(stderr, "QNX: screenEventThread started\n"); fflush(stderr); m_qpaInputContext = QPlatformInputContextFactory::create(); + fprintf(stderr, "QNX: inputContext=%p\n", m_qpaInputContext); fflush(stderr); #if QT_CONFIG(qqnx_pps) if (!m_qpaInputContext) { // Create/start the keyboard class. m_virtualKeyboard = new QQnxVirtualKeyboardPps(); + fprintf(stderr, "QNX: virtualKeyboard created\n"); fflush(stderr); // 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 initialized @@ -191,9 +206,12 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) #if QT_CONFIG(qqnx_pps) m_navigator = new QQnxNavigatorPps(); + fprintf(stderr, "QNX: navigator created\n"); fflush(stderr); #endif + fprintf(stderr, "QNX: createDisplays...\n"); fflush(stderr); createDisplays(); + fprintf(stderr, "QNX: createDisplays done\n"); fflush(stderr); if (m_virtualKeyboard) { // TODO check if we need to do this for all screens or only the primary one @@ -731,12 +749,24 @@ void QQnxIntegration::createEglDisplay() // 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()); + if (Q_UNLIKELY(m_eglDisplay == EGL_NO_DISPLAY)) { + // BB10 sandbox: EGL may not be available without a screen window yet. + // Fall back to raster/software rendering instead of aborting. + fprintf(stderr, "QNX: eglGetDisplay(EGL_DEFAULT_DISPLAY) failed (err=0x%x) — raster fallback\n", + eglGetError()); + fflush(stderr); + m_eglDisplay = EGL_NO_DISPLAY; + return; + } EGLBoolean eglResult = eglInitialize(m_eglDisplay, 0, 0); - if (Q_UNLIKELY(eglResult != EGL_TRUE)) - qFatal("QQnxIntegration: failed to initialize EGL display, err=%d", eglGetError()); + if (Q_UNLIKELY(eglResult != EGL_TRUE)) { + fprintf(stderr, "QNX: eglInitialize failed (err=0x%x) — raster fallback\n", + eglGetError()); + fflush(stderr); + m_eglDisplay = EGL_NO_DISPLAY; + return; + } } void QQnxIntegration::destroyEglDisplay() diff --git a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp index e6b787ccf7..96569290aa 100644 --- a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp +++ b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp @@ -51,8 +51,15 @@ void QQnxRasterBackingStore::flush(QWindow *window, const QRegion ®ion, const auto *targetWindow = window ? static_cast(window->handle()) : platformWindow(); - if (targetWindow) - targetWindow->post(region); // update the display with newly rendered content + if (targetWindow) { + // BB10 software-backend QtQuick: incremental dirty regions sometimes + // miss areas that were animated/scrolled (e.g. SwipeView contents), + // leaving stale pixels visible until a full repaint. Post the entire + // window each flush to avoid that — costs a bit more bandwidth, but + // eliminates the "wave/swirl" partial-update artifacts. + QRegion full = QRect(QPoint(0, 0), targetWindow->geometry().size()); + targetWindow->post(full); + } m_needsPosting = false; m_scrolled = false; diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp index 72442035b4..ce66fc9fc9 100644 --- a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp @@ -74,10 +74,16 @@ void QQnxRasterWindow::post(const QRegion &dirty) screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0), "Failed to post window"); #else - // Update the display with contents of render buffer - Q_SCREEN_CHECKERROR( - screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 0, NULL, 0), - "Failed to post window"); + // Update the display with contents of render buffer. + // BB10 (QNX 6.6) screen_post_window() ignores numrects=0/NULL and posts + // nothing; pass the explicit bounding rect so it actually composites. + { + QRect rect = dirty.boundingRect(); + int dirtyRect[4] = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() }; + Q_SCREEN_CHECKERROR( + screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0), + "Failed to post window"); + } #endif // Advance to next nender buffer diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index 17b282bdc1..8e3bc41ec9 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -15,7 +15,10 @@ #if !defined(_SCREEN_VERSION) #define _SCREEN_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch)) -#define _SCREEN_VERSION _SCREEN_MAKE_VERSION(0, 0, 0) +/* QNX 8.x / BB10 screen.h doesn't define _SCREEN_VERSION but already uses the + * 1.x property names (SCREEN_PROPERTY_FLAGS, SCREEN_PROPERTY_FOCUS, etc.). + * Default to 1.0.0 so the old-name alias block below is skipped. */ +#define _SCREEN_VERSION _SCREEN_MAKE_VERSION(1, 0, 0) #endif // For pre-1.0.0 screen, map some screen property names to the old @@ -28,6 +31,40 @@ const int SCREEN_PROPERTY_SCAN = SCREEN_PROPERTY_KEY_SCAN; const int SCREEN_PROPERTY_SYM = SCREEN_PROPERTY_KEY_SYM; #endif +// BB10 / QNX 8 sysroot (screen API ~1.x) lacks some symbols added in QNX +// Screen 2.x. Define placeholders so the QNX QPA plugin compiles; these +// events/properties will simply never appear on BB10 hardware. +#ifndef SCREEN_EVENT_MANAGER +# define SCREEN_EVENT_MANAGER 0x7ffe01 /* never fired on BB10 */ +#endif +#ifndef SCREEN_OBJECT_TYPE_STREAM +# define SCREEN_OBJECT_TYPE_STREAM 0x7ffe02 +#endif +#ifndef SCREEN_PROPERTY_SUBTYPE +# define SCREEN_PROPERTY_SUBTYPE 0x7ffe03 +#endif +// SCREEN_PROPERTY_PIXMAP (singular) is the per-event pixmap handle property. +// BB10 uses SCREEN_PROPERTY_PIXMAPS (plural, window's pixmap list); use it +// as a best-effort stand-in for pixmap close-event handling. +#ifndef SCREEN_PROPERTY_PIXMAP +# define SCREEN_PROPERTY_PIXMAP SCREEN_PROPERTY_PIXMAPS +#endif +// SCREEN_PROPERTY_PARENT — parent group name property; alias to GROUP on BB10. +#ifndef SCREEN_PROPERTY_PARENT +# define SCREEN_PROPERTY_PARENT SCREEN_PROPERTY_GROUP +#endif +// screen_manage_window — window manager handshake, added in QNX 7.x. +// On BB10 there is no formal window manager; provide a no-op stub. +#if !defined(screen_manage_window) +# ifdef __cplusplus +extern "C" { +# endif +static inline int screen_manage_window(screen_window_t, const char *) { return 0; } +# ifdef __cplusplus +} +# endif +#endif + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen); diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 01d7df5262..551903e417 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -6,6 +6,7 @@ #include "qqnxglobal.h" #include "qqnxscreeneventhandler.h" +#include #include "qqnxscreeneventthread.h" #include "qqnxintegration.h" #include "qqnxkeytranslator.h" @@ -306,6 +307,44 @@ void QQnxScreenEventHandler::handleKeyboardEvent(screen_event_t event) int sequenceId = 0; bool inject = true; + // BB10 sticky-modifier emulation: on Q10 the hardware keyboard sends a + // separate KEY_DOWN event for the Shift/Alt key, then releases it, then + // sends the next char with modifiers=0. Cascades apps see the shifted + // char because BlackBerry's input layer remembers the last pressed + // modifier. We do the same here: if the event IS a modifier press/ + // release, remember/clear it; if it's a real key, OR-in the pending + // modifier and consume it. + { + bool isModifierKey = false; + int modifierBit = 0; + switch (sym) { + case KEYCODE_LEFT_SHIFT: + case KEYCODE_RIGHT_SHIFT: + isModifierKey = true; modifierBit = KEYMOD_SHIFT; break; + case KEYCODE_LEFT_CTRL: + case KEYCODE_RIGHT_CTRL: + isModifierKey = true; modifierBit = KEYMOD_CTRL; break; + case KEYCODE_LEFT_ALT: + case KEYCODE_RIGHT_ALT: + isModifierKey = true; modifierBit = KEYMOD_ALT; break; + default: break; + } + if (isModifierKey) { + if (flags & KEY_DOWN) + m_pendingModifiers |= modifierBit; + // Don't clear on KEY_UP — keep until the next non-modifier key + // is processed, that's the "sticky" behaviour BB10 users expect. + } else if (flags & KEY_DOWN) { + // Non-modifier key going down: merge sticky modifiers, then clear. + if (m_pendingModifiers) { + modifiers |= m_pendingModifiers; + // Re-derive the shifted symbol if Shift is now active and we + // received a base-cap from the keyboard with no SYM_VALID. + m_pendingModifiers = 0; + } + } + } + Q_FOREACH (QQnxScreenEventFilter *filter, m_eventFilters) { if (filter->handleKeyboardEvent(flags, sym, modifiers, scan, cap, sequenceId)) { inject = false; diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h index a9d57a7f61..7c6a6f5e19 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h @@ -65,6 +65,9 @@ private: QPoint m_lastLocalMousePoint; Qt::MouseButtons m_lastButtonState; screen_window_t m_lastMouseWindow; + // BB10 sticky-modifier support — Q10 hardware keyboard sends shift/alt as + // separate events without holding; remember them for the next key. + int m_pendingModifiers = 0; QPointingDevice *m_touchDevice; QPointingDevice *m_mouseDevice; QWindowSystemInterface::TouchPoint m_touchPoints[MaximumTouchPoints]; diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp index 6b4ffc3962..dfede8ddf9 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp @@ -21,12 +21,24 @@ static const int c_quitCode = _PULSE_CODE_MINAVAIL + 2; #if !defined(screen_register_event) int screen_register_event(screen_context_t, struct sigevent *event) { +#if defined(MsgRegisterEvent) return MsgRegisterEvent(event, -1); +#else + /* BB10 / QNX 8: event registration is performed via screen_notify; + * this function is a no-op here. */ + (void)event; + return 0; +#endif } int screen_unregister_event(struct sigevent *event) { +#if defined(MsgUnregisterEvent) return MsgUnregisterEvent(event); +#else + (void)event; + return 0; +#endif } #endif