From ea757da43697787cfcd1c01f11bd2b0288c679fe Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 22 Feb 2016 09:42:39 +0100 Subject: QWindowsTheme: Run SHGetFileInfo() in a thread. Windows 10: SHGetFileInfo() (as called by item views on file system models has been observed to trigger a WM_PAINT on the mainwindow for totally obscure reasons, causing a recursive repaint. Suppress this by running it via QThreadPool. Task-number: QTBUG-45298 Task-number: QTBUG-48823 Task-number: QTCREATORBUG-14888 Change-Id: I7479102b9b8fb0771681260298c3d735e66f220f Reviewed-by: Joerg Bornemann --- src/plugins/platforms/windows/qwindowstheme.cpp | 56 ++++++++-- src/plugins/platforms/windows/qwindowstheme.h | 2 + .../platforms/windows/qwindowsthreadpoolrunner.h | 116 +++++++++++++++++++++ src/plugins/platforms/windows/windows.pri | 3 +- 4 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 src/plugins/platforms/windows/qwindowsthreadpoolrunner.h (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 887a217b06..ef652e6f98 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -122,6 +122,41 @@ static inline QColor getSysColor(int index) return COLORREFToQColor(GetSysColor(index)); } +#ifndef QT_NO_WINCE_SHELLSDK +// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system +// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the +// behavior by running it in a thread. +class ShGetFileInfoFunction +{ +public: + explicit ShGetFileInfoFunction(const wchar_t *fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) : + m_fileName(fn), m_attributes(a), m_flags(f), m_info(i), m_result(r) {} + + void operator()() const { *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); } + +private: + const wchar_t *m_fileName; + const DWORD m_attributes; + const UINT m_flags; + SHFILEINFO *const m_info; + bool *m_result; +}; + +static bool shGetFileInfoBackground(QWindowsThreadPoolRunner &r, + const wchar_t *fileName, DWORD attributes, + SHFILEINFO *info, UINT flags, + unsigned long timeOutMSecs = 5000) +{ + bool result = false; + if (!r.run(ShGetFileInfoFunction(fileName, attributes, info, flags, &result), timeOutMSecs)) { + qWarning().noquote() << "ShGetFileInfoBackground() timed out for " + << QString::fromWCharArray(fileName); + return false; + } + return result; +} +#endif // !QT_NO_WINCE_SHELLSDK + // from QStyle::standardPalette static inline QPalette standardPalette() { @@ -690,23 +725,22 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s } SHFILEINFO info; - unsigned int flags = + const unsigned int flags = #ifndef Q_OS_WINCE SHGFI_ICON|iconSize|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX; #else iconSize|SHGFI_SYSICONINDEX; #endif // Q_OS_WINCE - unsigned long val = 0; + + #if !defined(QT_NO_WINCE_SHELLSDK) - if (cacheableDirIcon && useDefaultFolderIcon) { - flags |= SHGFI_USEFILEATTRIBUTES; - val = SHGetFileInfo(L"dummy", - FILE_ATTRIBUTE_DIRECTORY, - &info, sizeof(SHFILEINFO), flags); - } else { - val = SHGetFileInfo(reinterpret_cast(filePath.utf16()), 0, - &info, sizeof(SHFILEINFO), flags); - } + const bool val = cacheableDirIcon && useDefaultFolderIcon + ? shGetFileInfoBackground(m_threadPoolRunner, L"dummy", FILE_ATTRIBUTE_DIRECTORY, + &info, flags | SHGFI_USEFILEATTRIBUTES) + : shGetFileInfoBackground(m_threadPoolRunner, reinterpret_cast(filePath.utf16()), 0, + &info, flags); +#else + const bool val = false; #endif // !QT_NO_WINCE_SHELLSDK // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h index fc004b68c8..cacde98601 100644 --- a/src/plugins/platforms/windows/qwindowstheme.h +++ b/src/plugins/platforms/windows/qwindowstheme.h @@ -34,6 +34,7 @@ #ifndef QWINDOWSTHEME_H #define QWINDOWSTHEME_H +#include "qwindowsthreadpoolrunner.h" #include QT_BEGIN_NAMESPACE @@ -74,6 +75,7 @@ private: static QWindowsTheme *m_instance; QPalette *m_palettes[NPalettes]; QFont *m_fonts[NFonts]; + mutable QWindowsThreadPoolRunner m_threadPoolRunner; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h b/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h new file mode 100644 index 0000000000..0361aa90f5 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSTHREADPOOLRUNNER_H +#define QWINDOWSTHREADPOOLRUNNER_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsThreadPoolRunner + \brief Runs a task in the global instance of QThreadPool + + QThreadPool does not provide a method to wait on a single task, so this needs + to be done by using QWaitCondition/QMutex. + + \internal + \ingroup qt-lighthouse-win +*/ +class QWindowsThreadPoolRunner +{ + Q_DISABLE_COPY(QWindowsThreadPoolRunner) + +#ifndef QT_NO_THREAD + template // nested class implementing QRunnable to execute a function. + class Runnable : public QRunnable + { + public: + explicit Runnable(QMutex *m, QWaitCondition *c, RunnableFunction f) + : m_mutex(m), m_condition(c), m_function(f) {} + + void run() Q_DECL_OVERRIDE + { + m_function(); + m_mutex->lock(); + m_condition->wakeAll(); + m_mutex->unlock(); + } + + private: + QMutex *m_mutex; + QWaitCondition *m_condition; + RunnableFunction m_function; + }; // class Runnable + +public: + QWindowsThreadPoolRunner() {} + + template + bool run(Function f, unsigned long timeOutMSecs = 5000) + { + QThreadPool *pool = QThreadPool::globalInstance(); + Q_ASSERT(pool); + Runnable *runnable = new Runnable(&m_mutex, &m_condition, f); + m_mutex.lock(); + pool->start(runnable); + const bool ok = m_condition.wait(&m_mutex, timeOutMSecs); + m_mutex.unlock(); + if (!ok) + pool->cancel(runnable); + return ok; + } + +private: + QMutex m_mutex; + QWaitCondition m_condition; +#else // !QT_NO_THREAD +public: + QWindowsThreadPoolRunner() {} + + template + bool run(Function f, unsigned long /* timeOutMSecs */ = 5000) + { + f(); + return true; + } +#endif // QT_NO_THREAD +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSTHREADPOOLRUNNER_H diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri index 67af5c03ef..eba0593d75 100644 --- a/src/plugins/platforms/windows/windows.pri +++ b/src/plugins/platforms/windows/windows.pri @@ -63,7 +63,8 @@ HEADERS += \ $$PWD/qplatformfunctions_wince.h \ $$PWD/qwindowsnativeimage.h \ $$PWD/qwindowsnativeinterface.h \ - $$PWD/qwindowsopengltester.h + $$PWD/qwindowsopengltester.h \ + $$PWD/qwindowsthreadpoolrunner.h INCLUDEPATH += $$PWD -- cgit v1.2.3