diff options
Diffstat (limited to 'src/plugins/platforms')
23 files changed, 482 insertions, 226 deletions
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 2bc3b0dc7d..7045533fca 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -34,6 +34,7 @@ #include <dlfcn.h> #include <pthread.h> +#include <semaphore.h> #include <qplugin.h> #include <qdebug.h> @@ -91,6 +92,8 @@ extern "C" typedef int (*Main)(int, char **); //use the standard main method to static Main m_main = nullptr; static void *m_mainLibraryHnd = nullptr; static QList<QByteArray> m_applicationParams; +pthread_t m_qtAppThread = 0; +static sem_t m_exitSemaphore, m_terminateSemaphore; struct SurfaceData { @@ -454,6 +457,10 @@ static void *startMainMethod(void */*data*/) if (vm != 0) vm->DetachCurrentThread(); + sem_post(&m_terminateSemaphore); + sem_wait(&m_exitSemaphore); + sem_destroy(&m_exitSemaphore); + // We must call exit() to ensure that all global objects will be destructed exit(ret); return 0; @@ -503,8 +510,13 @@ static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring para return false; } - pthread_t appThread; - return pthread_create(&appThread, nullptr, startMainMethod, nullptr) == 0; + if (sem_init(&m_exitSemaphore, 0, 0) == -1) + return false; + + if (sem_init(&m_terminateSemaphore, 0, 0) == -1) + return false; + + return pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr) == 0; } @@ -518,6 +530,8 @@ static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) static void terminateQt(JNIEnv *env, jclass /*clazz*/) { + sem_wait(&m_terminateSemaphore); + sem_destroy(&m_terminateSemaphore); env->DeleteGlobalRef(m_applicationClass); env->DeleteGlobalRef(m_classLoaderObject); if (m_resourcesObj) @@ -535,6 +549,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/) m_androidPlatformIntegration = nullptr; delete m_androidAssetsFileEngineHandler; m_androidAssetsFileEngineHandler = nullptr; + sem_post(&m_exitSemaphore); + pthread_join(m_qtAppThread, nullptr); } static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h) diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp index 835ca8a10a..a21aac51b4 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp @@ -74,7 +74,7 @@ QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &fami result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(QLatin1Char(';'))); else result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(QLatin1Char(';'))); - result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); return result; } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 18abaddfd7..2d8abba189 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -502,7 +502,11 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect) QMacAutoReleasePool pool; if (m_contentViewIsEmbedded) { - QPlatformWindow::setGeometry(rect); + if (m_qtView) { + [m_qtView setFrame:NSMakeRect(0, 0, rect.width(), rect.height())]; + } else { + QPlatformWindow::setGeometry(rect); + } return; } diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index fe0ca33c13..8bdb4bcdb3 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -45,12 +45,15 @@ QIOSContext::QIOSContext(QOpenGLContext *context) : QPlatformOpenGLContext() , m_sharedContext(static_cast<QIOSContext *>(context->shareHandle())) + , m_eaglContext(0) , m_format(context->format()) { m_format.setRenderableType(QSurfaceFormat::OpenGLES); - m_eaglContext = [[EAGLContext alloc] - initWithAPI:EAGLRenderingAPI(m_format.majorVersion()) - sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil]; + + EAGLSharegroup *shareGroup = m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil; + const int preferredVersion = m_format.majorVersion() == 1 ? kEAGLRenderingAPIOpenGLES1 : kEAGLRenderingAPIOpenGLES3; + for (int version = preferredVersion; !m_eaglContext && version >= m_format.majorVersion(); --version) + m_eaglContext = [[EAGLContext alloc] initWithAPI:EAGLRenderingAPI(version) sharegroup:shareGroup]; if (m_eaglContext != nil) { EAGLContext *originalContext = [EAGLContext currentContext]; diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index f68321fb3b..5f9346816a 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -62,11 +62,23 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; { if (self = [super init]) { [self setVisibleMenuItems:visibleMenuItems]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(menuClosed) + name:UIMenuControllerDidHideMenuNotification object:nil]; } return self; } +-(void)dealloc +{ + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:UIMenuControllerDidHideMenuNotification object:nil]; + [super dealloc]; +} + - (void)setVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems { m_visibleMenuItems = visibleMenuItems; @@ -86,6 +98,11 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; [[UIMenuController sharedMenuController] setMenuVisible:YES animated:NO]; } +-(void)menuClosed +{ + QIOSMenu::currentMenu()->dismiss(); +} + - (id)targetForAction:(SEL)action withSender:(id)sender { Q_UNUSED(sender); diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 9ca5e22b90..320b1cac61 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -318,7 +318,11 @@ // a regular responder transfer to another window. In the former case, iOS // will set the new first-responder to our next-responder, and in the latter // case we'll have an active responder candidate. - if ([UIResponder currentFirstResponder] == [self nextResponder]) { + if (![UIResponder currentFirstResponder]) { + // No first responder set anymore, sync this with Qt by clearing the + // focus object. + m_inputContext->clearCurrentFocusObject(); + } else if ([UIResponder currentFirstResponder] == [self nextResponder]) { // We have resigned the keyboard, and transferred first responder back to the parent view Q_ASSERT(!FirstResponderCandidate::currentCandidate()); if ([self currentImeState:Qt::ImEnabled].toBool()) { @@ -364,6 +368,32 @@ [self sendKeyPressRelease:key modifiers:modifiers]; } +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender +{ + bool isEditAction = (action == @selector(cut:) + || action == @selector(copy:) + || action == @selector(paste:) + || action == @selector(delete:) + || action == @selector(toggleBoldface:) + || action == @selector(toggleItalics:) + || action == @selector(toggleUnderline:) + || action == @selector(undo) + || action == @selector(redo)); + + bool isSelectAction = (action == @selector(select:) + || action == @selector(selectAll:) + || action == @selector(paste:) + || action == @selector(undo) + || action == @selector(redo)); + + const bool unknownAction = !isEditAction && !isSelectAction; + const bool hasSelection = ![self selectedTextRange].empty; + + if (unknownAction) + return [super canPerformAction:action withSender:sender]; + return (hasSelection && isEditAction) || (!hasSelection && isSelectAction); +} + - (void)cut:(id)sender { Q_UNUSED(sender); @@ -382,6 +412,13 @@ [self sendShortcut:QKeySequence::Paste]; } +- (void)select:(id)sender +{ + Q_UNUSED(sender); + [self sendShortcut:QKeySequence::MoveToPreviousWord]; + [self sendShortcut:QKeySequence::SelectNextWord]; +} + - (void)selectAll:(id)sender { Q_UNUSED(sender); @@ -580,7 +617,8 @@ - (UITextPosition *)endOfDocument { - int endPosition = [self currentImeState:Qt::ImSurroundingText].toString().length(); + QString surroundingText = [self currentImeState:Qt::ImSurroundingText].toString(); + int endPosition = surroundingText.length() + m_markedText.length(); return [QUITextPosition positionWithIndex:endPosition]; } @@ -611,9 +649,18 @@ - (NSString *)textInRange:(UITextRange *)range { + QString text = [self currentImeState:Qt::ImSurroundingText].toString(); + if (!m_markedText.isEmpty()) { + // [UITextInput textInRange] is sparsely documented, but it turns out that unconfirmed + // marked text should be seen as a part of the text document. This is different from + // ImSurroundingText, which excludes it. + int cursorPos = [self currentImeState:Qt::ImCursorPosition].toInt(); + text = text.left(cursorPos) + m_markedText + text.mid(cursorPos); + } + int s = static_cast<QUITextPosition *>([range start]).index; int e = static_cast<QUITextPosition *>([range end]).index; - return [self currentImeState:Qt::ImSurroundingText].toString().mid(s, e - s).toNSString(); + return text.mid(s, e - s).toNSString(); } - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange @@ -882,6 +929,14 @@ if ([text isEqualToString:@"\n"]) { [self sendKeyPressRelease:Qt::Key_Return modifiers:Qt::NoModifier]; + // An onEnter handler of a TextInput might move to the next input by calling + // nextInput.forceActiveFocus() which changes the focusObject. + // In that case we don't want to hide the VKB. + if (focusObject != QGuiApplication::focusObject()) { + qImDebug() << "focusObject already changed, not resigning first responder."; + return; + } + if (self.returnKeyType == UIReturnKeyDone || self.returnKeyType == UIReturnKeyGo || self.returnKeyType == UIReturnKeySend || self.returnKeyType == UIReturnKeySearch) [self resignFirstResponder]; diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index bd5c35037d..135c9eb601 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -96,7 +96,12 @@ void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, RECT dirty = {dirtyRect.x(), dirtyRect.y(), dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()}; UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, m_image->hdc(), &ptSrc, 0, &blend, ULW_ALPHA, &dirty}; - QWindowsContext::user32dll.updateLayeredWindowIndirect(rw->handle(), &info); + const BOOL result = QWindowsContext::user32dll.updateLayeredWindowIndirect(rw->handle(), &info); + if (!result) + qErrnoWarning("UpdateLayeredWindowIndirect failed for ptDst=(%d, %d)," + " size=(%dx%d), dirty=(%dx%d %d, %d)", r.x(), r.y(), + r.width(), r.height(), dirtyRect.width(), dirtyRect.height(), + dirtyRect.x(), dirtyRect.y()); } else { QWindowsContext::user32dll.updateLayeredWindow(rw->handle(), NULL, &ptDst, &size, m_image->hdc(), &ptSrc, 0, &blend, ULW_ALPHA); } diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 361e7f4445..183c198806 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -192,7 +192,14 @@ typedef struct { quint16 stringOffset; } NAME_RECORD; -static QString fontNameFromTTFile(const QString &filename) +typedef struct { + quint32 tag; + quint16 majorVersion; + quint16 minorVersion; + quint32 numFonts; +} TTC_TABLE_HEADER; + +static QString fontNameFromTTFile(const QString &filename, int startPos = 0) { QFile f(filename); QString retVal; @@ -200,6 +207,7 @@ static QString fontNameFromTTFile(const QString &filename) qint64 bytesToRead; if (f.open(QIODevice::ReadOnly)) { + f.seek(startPos); OFFSET_TABLE ttOffsetTable; bytesToRead = sizeof(OFFSET_TABLE); bytesRead = f.read((char*)&ttOffsetTable, bytesToRead); @@ -280,6 +288,37 @@ static QString fontNameFromTTFile(const QString &filename) return retVal; } +static QStringList fontNamesFromTTCFile(const QString &filename) +{ + QFile f(filename); + QStringList retVal; + qint64 bytesRead; + qint64 bytesToRead; + + if (f.open(QIODevice::ReadOnly)) { + TTC_TABLE_HEADER ttcTableHeader; + bytesToRead = sizeof(TTC_TABLE_HEADER); + bytesRead = f.read((char*)&ttcTableHeader, bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + ttcTableHeader.majorVersion = qFromBigEndian(ttcTableHeader.majorVersion); + ttcTableHeader.minorVersion = qFromBigEndian(ttcTableHeader.minorVersion); + ttcTableHeader.numFonts = qFromBigEndian(ttcTableHeader.numFonts); + + if (ttcTableHeader.majorVersion < 1 || ttcTableHeader.majorVersion > 2) + return retVal; + QVarLengthArray<quint32> offsetTable(ttcTableHeader.numFonts); + bytesToRead = sizeof(offsetTable) * ttcTableHeader.numFonts; + bytesRead = f.read((char*)offsetTable.data(), bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + f.close(); + for (int i = 0; i < (int)ttcTableHeader.numFonts; ++i) + retVal << fontNameFromTTFile(filename, qFromBigEndian(offsetTable[i])); + } + return retVal; +} + static inline QString fontSettingsOrganization() { return QStringLiteral("Qt-Project"); } static inline QString fontSettingsApplication() { return QStringLiteral("Qtbase"); } static inline QString fontSettingsGroup() { return QStringLiteral("CEFontCache"); } @@ -306,20 +345,28 @@ static QString findFontFile(const QString &faceName) //empty the cache first, as it seems that it is dirty settings.remove(QString()); - QDirIterator it(QStringLiteral("/Windows"), QStringList(QStringLiteral("*.ttf")), QDir::Files | QDir::Hidden | QDir::System); - + QDirIterator it(QStringLiteral("/Windows"), QStringList() << QStringLiteral("*.ttf") << QStringLiteral("*.ttc"), QDir::Files | QDir::Hidden | QDir::System); + const QLatin1Char lowerF('f'); + const QLatin1Char upperF('F'); while (it.hasNext()) { const QString fontFile = it.next(); - const QString fontName = fontNameFromTTFile(fontFile); - if (fontName.isEmpty()) - continue; - fontCache.insert(fontName, fontFile); - settings.setValue(fontName, fontFile); - - if (localizedName(fontName)) { - QString englishFontName = getEnglishName(fontName); - fontCache.insert(englishFontName, fontFile); - settings.setValue(englishFontName, fontFile); + QStringList fontNames; + const QChar c = fontFile[fontFile.size() - 1]; + if (c == lowerF || c == upperF) + fontNames << fontNameFromTTFile(fontFile); + else + fontNames << fontNamesFromTTCFile(fontFile); + foreach (const QString fontName, fontNames) { + if (fontName.isEmpty()) + continue; + fontCache.insert(fontName, fontFile); + settings.setValue(fontName, fontFile); + + if (localizedName(fontName)) { + QString englishFontName = getEnglishName(fontName); + fontCache.insert(englishFontName, fontFile); + settings.setValue(englishFontName, fontFile); + } } } settings.endGroup(); @@ -675,7 +722,7 @@ QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QF result.append(QWindowsFontDatabase::extraTryFontsForFamily(family)); - result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint << script << result; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 593ac7d810..5e2e9f6454 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -485,9 +485,9 @@ qreal QWindowsFontEngineDirectWrite::maxCharWidth() const return 0; } -QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) { - QImage im = imageForGlyph(glyph, subPixelPosition, 0, QTransform()); + QImage im = alphaRGBMapForGlyph(glyph, subPixelPosition, t); QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8); @@ -504,6 +504,11 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed sub return alphaMap; } +QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + return alphaMapForGlyph(glyph, subPixelPosition, QTransform()); +} + bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const { return true; @@ -709,9 +714,9 @@ QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyN glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph, QFixed pos, const QTransform &matrix, GlyphFormat format) { Q_UNUSED(pos); - int margin = 0; - if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) - margin = glyphMargin(QFontEngine::Format_A32); + Q_UNUSED(format); + + int margin = glyphMargin(QFontEngine::Format_A32); glyph_metrics_t gm = QFontEngine::boundingBox(glyph, matrix); gm.width += margin * 2; gm.height += margin * 2; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h index da772469e9..cee8691d69 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h @@ -86,6 +86,7 @@ public: bool supportsSubPixelPositions() const; QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition); + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t); QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform); QFontEngine *cloneWithSize(qreal pixelSize) const; diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 4c0b94e6e7..c5dff60114 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -86,10 +86,15 @@ QT_BEGIN_NAMESPACE The code originates from \c qkeymapper_win.cpp. */ +static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state); + QWindowsKeyMapper::QWindowsKeyMapper() : m_useRTLExtensions(false), m_keyGrabber(0) { memset(keyLayout, 0, sizeof(keyLayout)); + QGuiApplication *app = static_cast<QGuiApplication *>(QGuiApplication::instance()); + QObject::connect(app, &QGuiApplication::applicationStateChanged, + app, clearKeyRecorderOnApplicationInActive); } QWindowsKeyMapper::~QWindowsKeyMapper() @@ -144,6 +149,12 @@ struct KeyRecorder }; static KeyRecorder key_recorder; +static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state) +{ + if (state == Qt::ApplicationInactive) + key_recorder.clearKeys(); +} + KeyRecord *KeyRecorder::findKey(int code, bool remove) { KeyRecord *result = 0; diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index 5b0f6f637e..c5c60aa882 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -245,6 +245,8 @@ QFunctionPointer QWindowsNativeInterface::platformFunction(const QByteArray &fun { if (function == QWindowsWindowFunctions::setTouchWindowTouchTypeIdentifier()) return QFunctionPointer(QWindowsWindow::setTouchWindowTouchTypeStatic); + else if (function == QWindowsWindowFunctions::setHasBorderInFullScreenIdentifier()) + return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenStatic); return Q_NULLPTR; } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 611d586b19..cac8ec5ecc 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1720,7 +1720,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) if (!m_savedStyle) { m_savedStyle = style(); #ifndef Q_OS_WINCE - if (oldState == Qt::WindowMinimized) { + if (oldState == Qt::WindowMinimized || oldState == Qt::WindowMaximized) { const QRect nf = normalFrameGeometry(m_data.hwnd); if (nf.isValid()) m_savedFrameGeometry = nf; @@ -1735,6 +1735,8 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) newStyle |= WS_SYSMENU; if (visible) newStyle |= WS_VISIBLE; + if (testFlag(HasBorderInFullScreen)) + newStyle |= WS_BORDER; setStyle(newStyle); // Use geometry of QWindow::screen() within creation or the virtual screen the // window is in (QTBUG-31166, QTBUG-30724). @@ -1769,7 +1771,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) // preserve maximized state if (visible) { setFlag(WithinMaximize); - ShowWindow(m_data.hwnd, (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE); + ShowWindow(m_data.hwnd, (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA); clearFlag(WithinMaximize); } m_savedStyle = 0; @@ -2371,4 +2373,19 @@ void QWindowsWindow::aboutToMakeCurrent() #endif } +void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border) +{ + if (!window->handle()) + return; + static_cast<QWindowsWindow *>(window->handle())->setHasBorderInFullScreen(border); +} + +void QWindowsWindow::setHasBorderInFullScreen(bool border) +{ + if (border) + setFlag(HasBorderInFullScreen); + else + clearFlag(HasBorderInFullScreen); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 4172a3d850..710cab8597 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -137,7 +137,8 @@ public: WithinMaximize = 0x40000, MaximizeToFullScreen = 0x80000, InputMethodDisabled = 0x100000, - Compositing = 0x200000 + Compositing = 0x200000, + HasBorderInFullScreen = 0x400000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); @@ -251,7 +252,8 @@ public: static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); - + static void setHasBorderInFullScreenStatic(QWindow *window, bool border); + void setHasBorderInFullScreen(bool border); private: inline void show_sys() const; inline void hide_sys() const; diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp index c348faf015..793256a83f 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp @@ -428,6 +428,21 @@ QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handl return engine; } +QStringList QWinRTFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const +{ + Q_UNUSED(style) + Q_UNUSED(styleHint) + Q_UNUSED(script) + + QStringList result; + if (family == QLatin1String("Helvetica")) + result.append(QStringLiteral("Arial")); + result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + return result; +} + void QWinRTFontDatabase::releaseHandle(void *handle) { if (!handle) diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h index a88092e432..41619f5bd8 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.h +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h @@ -60,6 +60,8 @@ public: void populateFontDatabase() Q_DECL_OVERRIDE; void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, + QFont::StyleHint styleHint, QChar::Script script) const Q_DECL_OVERRIDE; void releaseHandle(void *handle) Q_DECL_OVERRIDE; private: QHash<IDWriteFontFile *, FontDescription> m_fonts; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 1d20432bd4..e28d84cf92 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -179,42 +179,6 @@ QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_ran return 0; } -QXcbScreen* QXcbConnection::createScreen(QXcbVirtualDesktop* virtualDesktop, - xcb_randr_output_t outputId, - xcb_randr_get_output_info_reply_t *output) -{ - QString name; - if (output) - name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output), - xcb_randr_get_output_info_name_length(output)); - else { - QByteArray displayName = m_displayName; - int dotPos = displayName.lastIndexOf('.'); - if (dotPos != -1) - displayName.truncate(dotPos); - name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + QString::number(virtualDesktop->number()); - } - - return new QXcbScreen(this, virtualDesktop, outputId, output, name); -} - -bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) -{ - xcb_generic_error_t *error = 0; - xcb_randr_get_output_primary_cookie_t primaryCookie = - xcb_randr_get_output_primary(xcb_connection(), rootWindow); - QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary ( - xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); - if (!primary || error) { - qWarning("failed to get the primary output of the screen"); - free(error); - error = NULL; - } - const bool isPrimary = primary ? (primary->output == output) : false; - - return isPrimary; -} - QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) { foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) { @@ -237,8 +201,9 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) // Not for us return; - qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc; QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc); + qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc + << "mode" << crtc.mode << "relevant screen" << screen; // Only update geometry when there's a valid mode on the CRTC // CRTC with node mode could mean that output has been disabled, and we'll // get RRNotifyOutputChange notification for that. @@ -260,16 +225,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) { qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected"; - - // Known screen removed -> delete it - m_screens.removeOne(screen); - virtualDesktop->removeScreen(screen); - - QXcbIntegration::instance()->destroyScreen(screen); - - // QTBUG-40174, QTBUG-42985: If all screens are removed, wait - // and start rendering again later if a screen becomes available. - + destroyScreen(screen); } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) { // New XRandR output is available and it's enabled if (output.crtc != XCB_NONE && output.mode != XCB_NONE) { @@ -278,59 +234,142 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo( xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); - screen = createScreen(virtualDesktop, output.output, outputInfo.data()); - qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; - - screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); - virtualDesktop->addScreen(screen); - m_screens << screen; - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + // Find a fake screen + foreach (QPlatformScreen *scr, virtualDesktop->screens()) { + QXcbScreen *xcbScreen = (QXcbScreen *)scr; + if (xcbScreen->output() == XCB_NONE) { + screen = xcbScreen; + break; + } + } - // Windows which had null screens have already had expose events by now. - // They need to be told the screen is back, it's OK to render. - foreach (QWindow *window, QGuiApplication::topLevelWindows()) { - QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(screen); + if (screen) { + QString nameWas = screen->name(); + // Transform the fake screen into a physical screen + screen->setOutput(output.output, outputInfo.data()); + updateScreen(screen, output); + qCDebug(lcQpaScreen) << "output" << screen->name() + << "is connected and enabled; was fake:" << nameWas; + } else { + screen = createScreen(virtualDesktop, output, outputInfo.data()); + qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; } } - // else ignore disabled screens } else if (screen) { - // Screen has been disabled -> remove if (output.crtc == XCB_NONE && output.mode == XCB_NONE) { + // Screen has been disabled xcb_randr_get_output_info_cookie_t outputInfoCookie = xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp); QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo( xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); if (outputInfo->crtc == XCB_NONE) { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; - m_screens.removeOne(screen); - virtualDesktop->removeScreen(screen); - QXcbIntegration::instance()->destroyScreen(screen); + destroyScreen(screen); } else { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch"; + // Reset crtc to skip RRCrtcChangeNotify events, + // because they may be invalid in the middle of the mode switch + screen->setCrtc(XCB_NONE); } } else { - // Just update existing screen - screen->updateGeometry(output.config_timestamp); - const bool wasPrimary = screen->isPrimary(); - screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); - if (screen->mode() != output.mode) - screen->updateRefreshRate(output.mode); - - // If the screen became primary, reshuffle the order in QGuiApplicationPrivate - if (!wasPrimary && screen->isPrimary()) { - const int idx = m_screens.indexOf(screen); - m_screens.swap(0, idx); - QXcbIntegration::instance()->setPrimaryScreen(screen); - } + updateScreen(screen, output); qCDebug(lcQpaScreen) << "output has changed" << screen; } } + + qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + } +} + +bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) +{ + xcb_generic_error_t *error = 0; + xcb_randr_get_output_primary_cookie_t primaryCookie = + xcb_randr_get_output_primary(xcb_connection(), rootWindow); + QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary ( + xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); + if (!primary || error) { + qWarning("failed to get the primary output of the screen"); + free(error); + error = NULL; + } + const bool isPrimary = primary ? (primary->output == output) : false; + + return isPrimary; +} + +void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange) +{ + screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid + screen->updateGeometry(outputChange.config_timestamp); + if (screen->mode() != outputChange.mode) + screen->updateRefreshRate(outputChange.mode); + // Only screen which belongs to the primary virtual desktop can be a primary screen + if (screen->screenNumber() == m_primaryScreenNumber) { + if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) { + screen->setPrimary(true); + + // If the screen became primary, reshuffle the order in QGuiApplicationPrivate + const int idx = m_screens.indexOf(screen); + if (idx > 0) { + m_screens.first()->setPrimary(false); + m_screens.swap(0, idx); + } + screen->virtualDesktop()->setPrimaryScreen(screen); + QXcbIntegration::instance()->setPrimaryScreen(screen); + } + } +} + +QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, + const xcb_randr_output_change_t &outputChange, + xcb_randr_get_output_info_reply_t *outputInfo) +{ + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo); + // Only screen which belongs to the primary virtual desktop can be a primary screen + if (screen->screenNumber() == m_primaryScreenNumber) + screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output)); + + if (screen->isPrimary()) { if (!m_screens.isEmpty()) - qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); - else - qCDebug(lcQpaScreen) << "no outputs"; + m_screens.first()->setPrimary(false); + + m_screens.prepend(screen); + } else { + m_screens.append(screen); + } + virtualDesktop->addScreen(screen); + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + + return screen; +} + +void QXcbConnection::destroyScreen(QXcbScreen *screen) +{ + QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop(); + if (virtualDesktop->screens().count() == 1) { + // If there are no other screens on the same virtual desktop, + // then transform the physical screen into a fake screen. + const QString nameWas = screen->name(); + screen->setOutput(XCB_NONE, Q_NULLPTR); + qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen; + } else { + // There is more than one screen on the same virtual desktop, remove the screen + m_screens.removeOne(screen); + virtualDesktop->removeScreen(screen); + + // When primary screen is removed, set the new primary screen + // which belongs to the primary virtual desktop. + if (screen->isPrimary()) { + QXcbScreen *newPrimary = (QXcbScreen *)virtualDesktop->screens().at(0); + newPrimary->setPrimary(true); + const int idx = m_screens.indexOf(newPrimary); + if (idx > 0) + m_screens.swap(0, idx); + QXcbIntegration::instance()->setPrimaryScreen(newPrimary); + } + + QXcbIntegration::instance()->destroyScreen(screen); } } @@ -338,8 +377,7 @@ void QXcbConnection::initializeScreens() { xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int xcbScreenNumber = 0; // screen number in the xcb sense - QXcbScreen* primaryScreen = Q_NULLPTR; - bool hasOutputs = false; + QXcbScreen *primaryScreen = Q_NULLPTR; while (it.rem) { // Each "screen" in xcb terminology is a virtual desktop, // potentially a collection of separate juxtaposed monitors. @@ -348,8 +386,6 @@ void QXcbConnection::initializeScreens() xcb_screen_t *xcbScreen = it.data; QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); m_virtualDesktops.append(virtualDesktop); - QList<QPlatformScreen *> siblings; - int outputCount = 0; if (has_randr_extension) { xcb_generic_error_t *error = NULL; // RRGetScreenResourcesCurrent is fast but it may return nothing if the @@ -366,7 +402,7 @@ void QXcbConnection::initializeScreens() } else { xcb_timestamp_t timestamp; xcb_randr_output_t *outputs = Q_NULLPTR; - outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data()); + int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data()); if (outputCount) { timestamp = resources_current->config_timestamp; outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data()); @@ -393,6 +429,7 @@ void QXcbConnection::initializeScreens() qWarning("failed to get the primary output of the screen"); free(error); } else { + QList<QPlatformScreen *> siblings; for (int i = 0; i < outputCount; i++) { QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output( xcb_randr_get_output_info_reply(xcb_connection(), @@ -416,9 +453,8 @@ void QXcbConnection::initializeScreens() continue; } - QXcbScreen *screen = createScreen(virtualDesktop, outputs[i], output.data()); + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.data()); siblings << screen; - hasOutputs = true; m_screens << screen; // There can be multiple outputs per screen, use either @@ -435,11 +471,23 @@ void QXcbConnection::initializeScreens() } } } + virtualDesktop->setScreens(siblings); } } } } - virtualDesktop->setScreens(siblings); + if (virtualDesktop->screens().isEmpty()) { + // If there are no XRandR outputs or XRandR extension is missing, + // then create a fake/legacy screen. + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR); + qCDebug(lcQpaScreen) << "created fake screen" << screen; + m_screens << screen; + if (m_primaryScreenNumber == xcbScreenNumber) { + primaryScreen = screen; + primaryScreen->setPrimary(true); + } + virtualDesktop->addScreen(screen); + } xcb_screen_next(&it); ++xcbScreenNumber; } // for each xcb screen @@ -447,39 +495,25 @@ void QXcbConnection::initializeScreens() foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) virtualDesktop->subscribeToXFixesSelectionNotify(); - // If there's no randr extension, or there was some error above, or we found a - // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X), - // but the dimensions are known anyway, and we don't already have any lingering - // (possibly disconnected) screens, then showing windows should be possible, - // so create one screen. (QTBUG-31389) - QXcbVirtualDesktop *virtualDesktop = m_virtualDesktops.value(0); - if (virtualDesktop && !hasOutputs && !virtualDesktop->size().isEmpty() && m_screens.isEmpty()) { - QXcbScreen *screen = createScreen(virtualDesktop, 0, Q_NULLPTR); - virtualDesktop->setScreens(QList<QPlatformScreen *>() << screen); - m_screens << screen; - primaryScreen = screen; - primaryScreen->setPrimary(true); - qCDebug(lcQpaScreen) << "found a screen with zero outputs" << screen; - } - - // Ensure the primary screen is first in the list - if (primaryScreen) { - Q_ASSERT(!m_screens.isEmpty()); - if (m_screens.first() != primaryScreen) { - m_screens.removeOne(primaryScreen); - m_screens.prepend(primaryScreen); + if (m_virtualDesktops.isEmpty()) { + qFatal("QXcbConnection: no screens available"); + } else { + // Ensure the primary screen is first on the list + if (primaryScreen) { + if (m_screens.first() != primaryScreen) { + m_screens.removeOne(primaryScreen); + m_screens.prepend(primaryScreen); + } } - } - // Push the screens to QApplication - QXcbIntegration *integration = QXcbIntegration::instance(); - foreach (QXcbScreen* screen, m_screens) { - qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ')'; - integration->screenAdded(screen, screen->isPrimary()); - } + // Push the screens to QGuiApplication + foreach (QXcbScreen *screen, m_screens) { + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + } - if (!m_screens.isEmpty()) qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + } } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) @@ -553,9 +587,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXFixes(); initializeScreens(); - if (Q_UNLIKELY(m_screens.isEmpty())) - qFatal("QXcbConnection: no screens available"); - initializeXRender(); m_xi2Enabled = false; #if defined(XCB_USE_XINPUT2) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index cf50076f98..4acca7d374 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -518,15 +518,17 @@ private: void initializeXShape(); void initializeXKB(); void handleClientMessageEvent(const xcb_client_message_event_t *event); - QXcbScreen* createScreen(QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId = XCB_NONE, - xcb_randr_get_output_info_reply_t *output = 0); QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc); QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output); QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow); + void updateScreens(const xcb_randr_notify_event_t *event); bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); + void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange); + QXcbScreen *createScreen(QXcbVirtualDesktop *virtualDesktop, + const xcb_randr_output_change_t &outputChange, + xcb_randr_get_output_info_reply_t *outputInfo); + void destroyScreen(QXcbScreen *screen); void initializeScreens(); - void updateScreens(const xcb_randr_notify_event_t *event); bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const; bool m_xi2Enabled; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 8097cce709..1a123703a5 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -406,6 +406,9 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) #endif // XCB_USE_XINPUT22 case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo); + // Some devices (mice) report a resolution of 0; they will be excluded later, + // for now just prevent a division by zero + const int vciResolution = vci->resolution ? vci->resolution : 1; if (vci->label == atom(QXcbAtom::AbsMTPositionX)) caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition; else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) @@ -414,16 +417,16 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) caps |= QTouchDevice::Pressure; else if (vci->label == atom(QXcbAtom::RelX)) { hasRelativeCoords = true; - dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::RelY)) { hasRelativeCoords = true; - dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::AbsX)) { caps |= QTouchDevice::Position; - dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::AbsY)) { caps |= QTouchDevice::Position; - dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution); } break; } diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index db7d837e01..9b1b9fcbb0 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -81,6 +81,13 @@ void QXcbVirtualDesktop::addScreen(QPlatformScreen *s) ((QXcbScreen *) s)->isPrimary() ? m_screens.prepend(s) : m_screens.append(s); } +void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s) +{ + const int idx = m_screens.indexOf(s); + Q_ASSERT(idx > -1); + m_screens.swap(0, idx); +} + QXcbXSettings *QXcbVirtualDesktop::xSettings() const { if (!m_xSettings) { @@ -149,16 +156,15 @@ void QXcbVirtualDesktop::updateWorkArea() } QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, - QString outputName) + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output) : QXcbObject(connection) , m_virtualDesktop(virtualDesktop) , m_output(outputId) - , m_crtc(output ? output->crtc : 0) + , m_crtc(output ? output->crtc : XCB_NONE) , m_mode(XCB_NONE) , m_primary(false) , m_rotation(XCB_RANDR_ROTATION_ROTATE_0) - , m_outputName(outputName) + , m_outputName(getOutputName(output)) , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize()) , m_virtualSize(virtualDesktop->size()) , m_virtualSizeMillimeters(virtualDesktop->physicalSize()) @@ -266,6 +272,22 @@ QXcbScreen::~QXcbScreen() delete m_cursor; } +QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo) +{ + QString name; + if (outputInfo) { + name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(outputInfo), + xcb_randr_get_output_info_name_length(outputInfo)); + } else { + QByteArray displayName = connection()->displayName(); + int dotPos = displayName.lastIndexOf('.'); + if (dotPos != -1) + displayName.truncate(dotPos); + name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + + QString::number(m_virtualDesktop->number()); + } + return name; +} QWindow *QXcbScreen::topLevelAt(const QPoint &p) const { @@ -390,6 +412,16 @@ QPlatformCursor *QXcbScreen::cursor() const return m_cursor; } +void QXcbScreen::setOutput(xcb_randr_output_t outputId, + xcb_randr_get_output_info_reply_t *outputInfo) +{ + m_output = outputId; + m_crtc = outputInfo ? outputInfo->crtc : XCB_NONE; + m_mode = XCB_NONE; + m_outputName = getOutputName(outputInfo); + // TODO: Send an event to the QScreen instance that the screen changed its name +} + /*! \brief handle the XCB screen change event and update properties @@ -458,19 +490,10 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan updateGeometry(change_event->timestamp); - QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation); QDpi ldpi = logicalDpi(); QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(), ldpi.first, ldpi.second); - - // Windows which had null screens have already had expose events by now. - // They need to be told the screen is back, it's OK to render. - foreach (QWindow *window, QGuiApplication::topLevelWindows()) { - QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(this); - } } void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index c68c290791..79620f40ec 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -72,6 +72,7 @@ public: void setScreens(QList<QPlatformScreen *> sl) { m_screens = sl; } void removeScreen(QPlatformScreen *s) { m_screens.removeOne(s); } void addScreen(QPlatformScreen *s); + void setPrimaryScreen(QPlatformScreen *s); QXcbXSettings *xSettings() const; @@ -101,10 +102,11 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen { public: QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, - QString outputName); + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo); ~QXcbScreen(); + QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo); + QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE; QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE; @@ -137,6 +139,10 @@ public: xcb_randr_crtc_t crtc() const { return m_crtc; } xcb_randr_mode_t mode() const { return m_mode; } + void setOutput(xcb_randr_output_t outputId, + xcb_randr_get_output_info_reply_t *outputInfo); + void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; } + void windowShown(QXcbWindow *window); QString windowManagerName() const { return m_windowManagerName; } bool syncRequestSupported() const { return m_syncRequestSupported; } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 3e01a90402..ae81674eca 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -143,7 +143,7 @@ const quint32 XEMBED_VERSION = 0; QXcbScreen *QXcbWindow::parentScreen() { - return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : m_xcbScreen; + return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : xcbScreen(); } // Returns \c true if we should set WM_TRANSIENT_FOR on \a w @@ -266,7 +266,6 @@ static const char *wm_window_type_property_id = "_q_xcb_wm_window_type"; QXcbWindow::QXcbWindow(QWindow *window) : QPlatformWindow(window) , m_window(0) - , m_xcbScreen(0) , m_syncCounter(0) , m_gravity(XCB_GRAVITY_STATIC) , m_mapped(false) @@ -322,7 +321,6 @@ void QXcbWindow::create() QRect rect = windowGeometry(); QXcbScreen *platformScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); - m_xcbScreen = platformScreen; if (type == Qt::Desktop) { m_window = platformScreen->root(); m_depth = platformScreen->screen()->root_depth; @@ -624,27 +622,18 @@ void QXcbWindow::destroy() m_pendingSyncRequest->invalidate(); } -void QXcbWindow::maybeSetScreen(QXcbScreen *screen) -{ - if (!window()->screen() && screen->geometry().contains(geometry().topLeft())) { - QWindowSystemInterface::handleWindowScreenChanged(window(), static_cast<QPlatformScreen *>(screen)->screen()); - QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size()))); - } -} - void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); propagateSizeHints(); - QXcbScreen *currentScreen = m_xcbScreen; + QXcbScreen *currentScreen = xcbScreen(); QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); if (!newScreen) newScreen = xcbScreen(); - m_xcbScreen = newScreen; const QRect wmGeometry = windowToWmGeometry(rect); if (newScreen && newScreen != currentScreen) @@ -848,15 +837,13 @@ void QXcbWindow::hide() Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); // send synthetic UnmapNotify event according to icccm 4.1.4 - if (xcbScreen()) { - xcb_unmap_notify_event_t event; - event.response_type = XCB_UNMAP_NOTIFY; - event.event = xcbScreen()->root(); - event.window = m_window; - event.from_configure = false; - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(), - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); - } + xcb_unmap_notify_event_t event; + event.response_type = XCB_UNMAP_NOTIFY; + event.event = xcbScreen()->root(); + event.window = m_window; + event.from_configure = false; + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); xcb_flush(xcb_connection()); @@ -1185,8 +1172,6 @@ void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two) event.data.data32[3] = 0; event.data.data32[4] = 0; - if (!xcbScreen()) - return; Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); } @@ -1448,8 +1433,6 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) xcb_parent_id = qXcbParent->xcb_window(); m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow; } else { - if (!xcbScreen()) - return; xcb_parent_id = xcbScreen()->root(); m_embedded = false; } @@ -2005,7 +1988,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * { bool fromSendEvent = (event->response_type & 0x80); QPoint pos(event->x, event->y); - if (!parent() && !fromSendEvent && xcbScreen()) { + if (!parent() && !fromSendEvent) { // Do not trust the position, query it instead. xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(), 0, 0); @@ -2017,21 +2000,38 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } } - const QRect rect = QRect(pos, QSize(event->width, event->height)); - QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(rect); + // The original geometry requested by setGeometry() might be different + // from what we end up with after applying window constraints. + QRect requestedGeometry = geometry(); - QXcbScreen *currentScreen = m_xcbScreen; - m_xcbScreen = static_cast<QXcbScreen*>(newScreen); + const QRect actualGeometry = QRect(pos, QSize(event->width, event->height)); + QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(actualGeometry); if (!newScreen) return; - QPlatformWindow::setGeometry(rect); - QWindowSystemInterface::handleGeometryChange(window(), rect); - if (newScreen != currentScreen) - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + // Persist the actual geometry so that QWindow::geometry() can + // be queried in the resize event. + QPlatformWindow::setGeometry(actualGeometry); - if (m_mapped) - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); + // FIXME: In the case of the requestedGeometry not matching the actualGeometry due + // to e.g. the window manager applying restrictions to the geometry, the application + // will never see a move/resize event if the actualGeometry is the same as the current + // geometry, and may think the requested geometry was fulfilled. + QWindowSystemInterface::handleGeometryChange(window(), actualGeometry); + + // QPlatformScreen::screen() is updated asynchronously, so we can't compare it + // with the newScreen. Just send the WindowScreenChanged event and QGuiApplication + // will make the comparison later. + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + + // For expose events we have no way of telling QGuiApplication to used the locally + // cached version of the previous state, so we may in some situations end up with + // an additional expose event. + QRect previousGeometry = requestedGeometry != actualGeometry ? + requestedGeometry : qt_window_private(window())->geometry; + + if (m_mapped && actualGeometry.size() != previousGeometry.size()) + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), actualGeometry.size())); if (m_usingSyncProtocol && m_syncState == SyncReceived) m_syncState = SyncAndConfigureReceived; @@ -2298,8 +2298,6 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) return; const QPoint local(event->event_x, event->event_y); - if (!xcbScreen()) - return; QPoint global = QPoint(event->root_x, event->root_y); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -2317,8 +2315,6 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) if (enterWindow) { QPoint local(enter->event_x, enter->event_y); - if (!xcbScreen()) - return; QPoint global = QPoint(event->root_x, event->root_y); QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global); @@ -2334,8 +2330,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev connection()->setTime(event->time); const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - if (!xcbScreen()) - return; if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { if (propertyDeleted) @@ -2655,8 +2649,6 @@ bool QXcbWindow::needsSync() const void QXcbWindow::postSyncWindowRequest() { - if (!xcbScreen()) - return; if (!m_pendingSyncRequest) { QXcbSyncWindowRequest *e = new QXcbSyncWindowRequest(this); m_pendingSyncRequest = e; diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index c42c98c205..587be22915 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -167,7 +167,6 @@ public: virtual void create(); virtual void destroy(); - void maybeSetScreen(QXcbScreen *screen); QXcbScreen *screenForNativeGeometry(const QRect &newGeometry) const; public Q_SLOTS: @@ -215,8 +214,6 @@ protected: xcb_window_t m_window; - QXcbScreen *m_xcbScreen; - uint m_depth; QImage::Format m_imageFormat; bool m_imageRgbSwap; |