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 <joerg.bornemann@theqtcompany.com>
This commit is contained in:
parent
c0963486ce
commit
ea757da436
@ -122,6 +122,41 @@ static inline QColor getSysColor(int index)
|
|||||||
return COLORREFToQColor(GetSysColor(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
|
// from QStyle::standardPalette
|
||||||
static inline QPalette standardPalette()
|
static inline QPalette standardPalette()
|
||||||
{
|
{
|
||||||
@ -690,23 +725,22 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s
|
|||||||
}
|
}
|
||||||
|
|
||||||
SHFILEINFO info;
|
SHFILEINFO info;
|
||||||
unsigned int flags =
|
const unsigned int flags =
|
||||||
#ifndef Q_OS_WINCE
|
#ifndef Q_OS_WINCE
|
||||||
SHGFI_ICON|iconSize|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX;
|
SHGFI_ICON|iconSize|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX;
|
||||||
#else
|
#else
|
||||||
iconSize|SHGFI_SYSICONINDEX;
|
iconSize|SHGFI_SYSICONINDEX;
|
||||||
#endif // Q_OS_WINCE
|
#endif // Q_OS_WINCE
|
||||||
unsigned long val = 0;
|
|
||||||
|
|
||||||
#if !defined(QT_NO_WINCE_SHELLSDK)
|
#if !defined(QT_NO_WINCE_SHELLSDK)
|
||||||
if (cacheableDirIcon && useDefaultFolderIcon) {
|
const bool val = cacheableDirIcon && useDefaultFolderIcon
|
||||||
flags |= SHGFI_USEFILEATTRIBUTES;
|
? shGetFileInfoBackground(m_threadPoolRunner, L"dummy", FILE_ATTRIBUTE_DIRECTORY,
|
||||||
val = SHGetFileInfo(L"dummy",
|
&info, flags | SHGFI_USEFILEATTRIBUTES)
|
||||||
FILE_ATTRIBUTE_DIRECTORY,
|
: shGetFileInfoBackground(m_threadPoolRunner, reinterpret_cast<const wchar_t *>(filePath.utf16()), 0,
|
||||||
&info, sizeof(SHFILEINFO), flags);
|
&info, flags);
|
||||||
} else {
|
#else
|
||||||
val = SHGetFileInfo(reinterpret_cast<const wchar_t *>(filePath.utf16()), 0,
|
const bool val = false;
|
||||||
&info, sizeof(SHFILEINFO), flags);
|
|
||||||
}
|
|
||||||
#endif // !QT_NO_WINCE_SHELLSDK
|
#endif // !QT_NO_WINCE_SHELLSDK
|
||||||
|
|
||||||
// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
|
// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#ifndef QWINDOWSTHEME_H
|
#ifndef QWINDOWSTHEME_H
|
||||||
#define QWINDOWSTHEME_H
|
#define QWINDOWSTHEME_H
|
||||||
|
|
||||||
|
#include "qwindowsthreadpoolrunner.h"
|
||||||
#include <qpa/qplatformtheme.h>
|
#include <qpa/qplatformtheme.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@ -74,6 +75,7 @@ private:
|
|||||||
static QWindowsTheme *m_instance;
|
static QWindowsTheme *m_instance;
|
||||||
QPalette *m_palettes[NPalettes];
|
QPalette *m_palettes[NPalettes];
|
||||||
QFont *m_fonts[NFonts];
|
QFont *m_fonts[NFonts];
|
||||||
|
mutable QWindowsThreadPoolRunner m_threadPoolRunner;
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
116
src/plugins/platforms/windows/qwindowsthreadpoolrunner.h
Normal file
116
src/plugins/platforms/windows/qwindowsthreadpoolrunner.h
Normal file
@ -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 <QtCore/QMutex>
|
||||||
|
#include <QtCore/QRunnable>
|
||||||
|
#include <QtCore/QThreadPool>
|
||||||
|
#include <QtCore/QWaitCondition>
|
||||||
|
|
||||||
|
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 <class RunnableFunction> // 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 <class Function>
|
||||||
|
bool run(Function f, unsigned long timeOutMSecs = 5000)
|
||||||
|
{
|
||||||
|
QThreadPool *pool = QThreadPool::globalInstance();
|
||||||
|
Q_ASSERT(pool);
|
||||||
|
Runnable<Function> *runnable = new Runnable<Function>(&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 <class Function>
|
||||||
|
bool run(Function f, unsigned long /* timeOutMSecs */ = 5000)
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // QT_NO_THREAD
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QWINDOWSTHREADPOOLRUNNER_H
|
@ -63,7 +63,8 @@ HEADERS += \
|
|||||||
$$PWD/qplatformfunctions_wince.h \
|
$$PWD/qplatformfunctions_wince.h \
|
||||||
$$PWD/qwindowsnativeimage.h \
|
$$PWD/qwindowsnativeimage.h \
|
||||||
$$PWD/qwindowsnativeinterface.h \
|
$$PWD/qwindowsnativeinterface.h \
|
||||||
$$PWD/qwindowsopengltester.h
|
$$PWD/qwindowsopengltester.h \
|
||||||
|
$$PWD/qwindowsthreadpoolrunner.h
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user