summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows/qwindowsintegration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/windows/qwindowsintegration.cpp')
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp270
1 files changed, 202 insertions, 68 deletions
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index a281172651..6415c9ac50 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -48,6 +48,11 @@
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/private/qfunctions_win_p.h>
+
+#include <wrl.h>
+
#include <limits.h>
#if !defined(QT_NO_OPENGL)
@@ -56,6 +61,14 @@
#include "qwindowsopengltester.h"
+#if QT_CONFIG(cpp_winrt)
+# include <QtCore/private/qt_winrtbase_p.h>
+# include <winrt/Windows.UI.Notifications.h>
+# include <winrt/Windows.Data.Xml.Dom.h>
+# include <winrt/Windows.Foundation.h>
+# include <winrt/Windows.UI.ViewManagement.h>
+#endif
+
#include <memory>
static inline void initOpenGlBlacklistResources()
@@ -67,31 +80,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-/*!
- \class QWindowsIntegration
- \brief QPlatformIntegration implementation for Windows.
- \internal
-
- \section1 Programming Considerations
-
- The platform plugin should run on Desktop Windows from Windows XP onwards
- and Windows Embedded.
-
- It should compile with:
- \list
- \li Microsoft Visual Studio 2013 or later (using the Microsoft Windows SDK,
- (\c Q_CC_MSVC).
- \li Stock \l{http://mingw.org/}{MinGW} (\c Q_CC_MINGW).
- This version ships with headers that are missing a lot of WinAPI.
- \li MinGW distributions using GCC 4.7 or higher and a recent MinGW-w64 runtime API,
- such as \l{http://tdm-gcc.tdragon.net/}{TDM-GCC}, or
- \l{http://mingwbuilds.sourceforge.net/}{MinGW-builds}
- (\c Q_CC_MINGW and \c __MINGW64_VERSION_MAJOR indicating the version).
- MinGW-w64 provides more complete headers (compared to stock MinGW from mingw.org),
- including a considerable part of the Windows SDK.
- \endlist
-*/
-
struct QWindowsIntegrationPrivate
{
Q_DISABLE_COPY_MOVE(QWindowsIntegrationPrivate)
@@ -131,7 +119,7 @@ bool parseIntOption(const QString &parameter,const QLatin1StringView &option,
const auto valueRef = QStringView{parameter}.right(valueLength);
const int value = valueRef.toInt(&ok);
if (ok) {
- if (value >= minimumValue && value <= maximumValue)
+ if (value >= int(minimumValue) && value <= int(maximumValue))
*target = static_cast<IntType>(value);
else {
qWarning() << "Value" << value << "for option" << option << "out of range"
@@ -148,14 +136,14 @@ using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkMod
static inline unsigned parseOptions(const QStringList &paramList,
int *tabletAbsoluteRange,
- QtWindows::ProcessDpiAwareness *dpiAwareness,
+ QtWindows::DpiAwareness *dpiAwareness,
DarkModeHandling *darkModeHandling)
{
unsigned options = 0;
for (const QString &param : paramList) {
if (param.startsWith(u"fontengine=")) {
- if (param.endsWith(u"directwrite")) {
- options |= QWindowsIntegration::FontDatabaseDirectWrite;
+ if (param.endsWith(u"gdi")) {
+ options |= QWindowsIntegration::FontDatabaseGDI;
} else if (param.endsWith(u"freetype")) {
options |= QWindowsIntegration::FontDatabaseFreeType;
} else if (param.endsWith(u"native")) {
@@ -179,7 +167,8 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch;
} else if (parseIntOption(param, "verbose"_L1, 0, INT_MAX, &QWindowsContext::verbose)
|| parseIntOption(param, "tabletabsoluterange"_L1, 0, INT_MAX, tabletAbsoluteRange)
- || parseIntOption(param, "dpiawareness"_L1, QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorV2DpiAware, dpiAwareness)) {
+ || parseIntOption(param, "dpiawareness"_L1, QtWindows::DpiAwareness::Invalid,
+ QtWindows::DpiAwareness::PerMonitorVersion2, dpiAwareness)) {
} else if (param == u"menus=native") {
options |= QWindowsIntegration::AlwaysUseNativeMenus;
} else if (param == u"menus=none") {
@@ -188,8 +177,11 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::DontUseWMPointer;
} else if (param == u"reverse") {
options |= QWindowsIntegration::RtlEnabled;
+ } else if (param == u"darkmode=0") {
+ *darkModeHandling = {};
} else if (param == u"darkmode=1") {
darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames);
+ darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle, false);
} else if (param == u"darkmode=2") {
darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames);
darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle);
@@ -206,10 +198,11 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
static bool dpiAwarenessSet = false;
// Default to per-monitor-v2 awareness (if available)
- QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorV2DpiAware;
+ QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2;
int tabletAbsoluteRange = -1;
- DarkModeHandling darkModeHandling;
+ DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames
+ | DarkModeHandlingFlag::DarkModeStyle;
m_options = ::parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness, &darkModeHandling);
q->setDarkModeHandling(darkModeHandling);
QWindowsFontDatabase::setFontOptions(m_options);
@@ -220,21 +213,13 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
else
m_context.initTablet();
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication.
if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
-
- // DpiAwareV2 requires using new API
- if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware) {
- m_context.setProcessDpiV2Awareness();
- qCDebug(lcQpaWindows)
- << __FUNCTION__ << "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2";
- } else {
- m_context.setProcessDpiAwareness(dpiAwareness);
- qCDebug(lcQpaWindows)
- << __FUNCTION__ << "DpiAwareness=" << dpiAwareness
- << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
- }
+ m_context.setProcessDpiAwareness(dpiAwareness);
+ qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
+ << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
}
dpiAwarenessSet = true;
}
@@ -260,7 +245,7 @@ QWindowsIntegration::QWindowsIntegration(const QStringList &paramList) :
#if QT_CONFIG(clipboard)
d->m_clipboard.registerViewer();
#endif
- d->m_context.screenManager().handleScreenChanges();
+ d->m_context.screenManager().initialize();
d->m_context.setDetectAltGrModifier((d->m_options & DetectAltGrModifier) != 0);
}
@@ -271,9 +256,9 @@ QWindowsIntegration::~QWindowsIntegration()
void QWindowsIntegration::initialize()
{
- QString icStr = QPlatformInputContextFactory::requested();
- icStr.isNull() ? d->m_inputContext.reset(new QWindowsInputContext)
- : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
+ auto icStrs = QPlatformInputContextFactory::requested();
+ icStrs.isEmpty() ? d->m_inputContext.reset(new QWindowsInputContext)
+ : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
}
bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -301,6 +286,8 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co
return true;
case SwitchableWidgetComposition:
return false; // QTBUG-68329 QTBUG-53515 QTBUG-54734
+ case BackingStoreStaticContents:
+ return true;
default:
return QPlatformIntegration::hasCapability(cap);
}
@@ -311,7 +298,7 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
{
if (window->type() == Qt::Desktop) {
auto *result = new QWindowsDesktopWindow(window);
- qCDebug(lcQpaWindows) << "Desktop window:" << window
+ qCDebug(lcQpaWindow) << "Desktop window:" << window
<< Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry();
return result;
}
@@ -321,15 +308,17 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
requested.geometry = window->isTopLevel()
? QHighDpi::toNativePixels(window->geometry(), window)
: QHighDpi::toNativeLocalPosition(window->geometry(), window);
- // Apply custom margins (see QWindowsWindow::setCustomMargins())).
- const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
- if (customMarginsV.isValid())
- requested.customMargins = qvariant_cast<QMargins>(customMarginsV);
+ if (!(requested.flags & Qt::FramelessWindowHint)) {
+ // Apply custom margins (see QWindowsWindow::setCustomMargins())).
+ const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
+ if (customMarginsV.isValid())
+ requested.customMargins = qvariant_cast<QMargins>(customMarginsV);
+ }
QWindowsWindowData obtained =
QWindowsWindowData::create(window, requested,
QWindowsWindow::formatWindowTitle(window->title()));
- qCDebug(lcQpaWindows).nospace()
+ qCDebug(lcQpaWindow).nospace()
<< __FUNCTION__ << ' ' << window
<< "\n Requested: " << requested.geometry << " frame incl.="
<< QWindowsGeometryHint::positionIncludesFrame(window)
@@ -366,7 +355,7 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n
screen = pScreen->screen();
if (screen && screen != window->screen())
window->setScreen(screen);
- qCDebug(lcQpaWindows) << "Foreign window:" << window << Qt::showbase << Qt::hex
+ qCDebug(lcQpaWindow) << "Foreign window:" << window << Qt::showbase << Qt::hex
<< result->winId() << Qt::noshowbase << Qt::dec << obtainedGeometry << screen;
return result;
}
@@ -495,17 +484,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext()
QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const
{
if (!d->m_fontDatabase) {
-#if QT_CONFIG(directwrite3)
- if (d->m_options & QWindowsIntegration::FontDatabaseDirectWrite)
- d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
- else
-#endif
#ifndef QT_NO_FREETYPE
if (d->m_options & QWindowsIntegration::FontDatabaseFreeType)
d->m_fontDatabase = new QWindowsFontDatabaseFT;
else
#endif // QT_NO_FREETYPE
- d->m_fontDatabase = new QWindowsFontDatabase();
+#if QT_CONFIG(directwrite3)
+ if (!(d->m_options & (QWindowsIntegration::FontDatabaseGDI | QWindowsIntegration::DontUseDirectWriteFonts)))
+ d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
+ else
+#endif
+ d->m_fontDatabase = new QWindowsFontDatabase;
}
return d->m_fontDatabase;
}
@@ -553,14 +542,9 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co
return QPlatformIntegration::styleHint(hint);
}
-Qt::KeyboardModifiers QWindowsIntegration::queryKeyboardModifiers() const
-{
- return QWindowsKeyMapper::queryKeyboardModifiers();
-}
-
-QList<int> QWindowsIntegration::possibleKeys(const QKeyEvent *e) const
+QPlatformKeyMapper *QWindowsIntegration::keyMapper() const
{
- return d->m_context.possibleKeys(e);
+ return d->m_context.keyMapper();
}
#if QT_CONFIG(clipboard)
@@ -627,6 +611,156 @@ void QWindowsIntegration::beep() const
MessageBeep(MB_OK); // For QApplication
}
+void QWindowsIntegration::setApplicationBadge(qint64 number)
+{
+ // Clamp to positive numbers, as the Windows API doesn't support negative numbers
+ number = qMax(0, number);
+
+ // Persist, so we can re-apply it on setting changes and Explorer restart
+ m_applicationBadgeNumber = number;
+
+ static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
+
+#if QT_CONFIG(cpp_winrt)
+ // We prefer the native BadgeUpdater API, that allows us to set a number directly,
+ // but it requires that the application has a package identity, and also doesn't
+ // seem to work in all cases on < Windows 11.
+ if (isWindows11 && qt_win_hasPackageIdentity()) {
+ using namespace winrt::Windows::UI::Notifications;
+ auto badgeXml = BadgeUpdateManager::GetTemplateContent(BadgeTemplateType::BadgeNumber);
+ badgeXml.SelectSingleNode(L"//badge/@value").NodeValue(winrt::box_value(winrt::to_hstring(number)));
+ BadgeUpdateManager::CreateBadgeUpdaterForApplication().Update(BadgeNotification(badgeXml));
+ return;
+ }
+#endif
+
+ // Fallback for non-packaged apps, Windows 10, or Qt builds without WinRT/C++ support
+
+ if (!number) {
+ // Clear badge
+ setApplicationBadge(QImage());
+ return;
+ }
+
+ const bool isDarkMode = QWindowsContext::isDarkMode();
+
+ QColor badgeColor;
+ QColor textColor;
+
+#if QT_CONFIG(cpp_winrt)
+ if (isWindows11) {
+ // Match colors used by BadgeUpdater
+ static const auto fromUIColor = [](winrt::Windows::UI::Color &&color) {
+ return QColor(color.R, color.G, color.B, color.A);
+ };
+ using namespace winrt::Windows::UI::ViewManagement;
+ const auto settings = UISettings();
+ badgeColor = fromUIColor(settings.GetColorValue(isDarkMode ?
+ UIColorType::AccentLight2 : UIColorType::Accent));
+ textColor = fromUIColor(settings.GetColorValue(UIColorType::Background));
+ }
+#endif
+
+ if (!badgeColor.isValid()) {
+ // Fall back to basic badge colors, based on Windows 10 look
+ badgeColor = isDarkMode ? Qt::black : QColor(220, 220, 220);
+ badgeColor.setAlphaF(0.5f);
+ textColor = isDarkMode ? Qt::white : Qt::black;
+ }
+
+ const auto devicePixelRatio = qApp->devicePixelRatio();
+
+ static const QSize iconBaseSize(16, 16);
+ QImage image(iconBaseSize * devicePixelRatio,
+ QImage::Format_ARGB32_Premultiplied);
+ image.fill(Qt::transparent);
+
+ QPainter painter(&image);
+
+ QRect badgeRect = image.rect();
+ QPen badgeBorderPen = Qt::NoPen;
+ if (!isWindows11) {
+ QColor badgeBorderColor = textColor;
+ badgeBorderColor.setAlphaF(0.5f);
+ badgeBorderPen = badgeBorderColor;
+ badgeRect.adjust(1, 1, -1, -1);
+ }
+ painter.setBrush(badgeColor);
+ painter.setPen(badgeBorderPen);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.drawEllipse(badgeRect);
+
+ auto pixelSize = qCeil(10.5 * devicePixelRatio);
+ // Unlike the BadgeUpdater API we're limited by a square
+ // badge, so adjust the font size when above two digits.
+ const bool textOverflow = number > 99;
+ if (textOverflow)
+ pixelSize *= 0.8;
+
+ QFont font = painter.font();
+ font.setPixelSize(pixelSize);
+ font.setWeight(isWindows11 ? QFont::Medium : QFont::DemiBold);
+ painter.setFont(font);
+
+ painter.setRenderHint(QPainter::TextAntialiasing, devicePixelRatio > 1);
+ painter.setPen(textColor);
+
+ auto text = textOverflow ? u"99+"_s : QString::number(number);
+ painter.translate(textOverflow ? 1 : 0, textOverflow ? 0 : -1);
+ painter.drawText(image.rect(), Qt::AlignCenter, text);
+
+ painter.end();
+
+ setApplicationBadge(image);
+}
+
+void QWindowsIntegration::setApplicationBadge(const QImage &image)
+{
+ QComHelper comHelper;
+
+ using Microsoft::WRL::ComPtr;
+
+ ComPtr<ITaskbarList3> taskbarList;
+ CoCreateInstance(CLSID_TaskbarList, nullptr,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));
+ if (!taskbarList) {
+ // There may not be any windows with a task bar button yet,
+ // in which case we'll apply the badge once a window with
+ // a button has been created.
+ return;
+ }
+
+ const auto hIcon = image.toHICON();
+
+ // Apply the icon to all top level windows, since the badge is
+ // set on an application level. If one of the windows go away
+ // the other windows will take over in showing the badge.
+ const auto topLevelWindows = QGuiApplication::topLevelWindows();
+ for (auto *topLevelWindow : topLevelWindows) {
+ if (!topLevelWindow->handle())
+ continue;
+ auto hwnd = reinterpret_cast<HWND>(topLevelWindow->winId());
+ taskbarList->SetOverlayIcon(hwnd, hIcon, L"");
+ }
+
+ DestroyIcon(hIcon);
+
+ // FIXME: Update icon when the application scale factor changes.
+ // Doing so in response to screen DPI changes is too soon, as the
+ // task bar is not yet ready for an updated icon, and will just
+ // result in a blurred icon even if our icon is high-DPI.
+}
+
+void QWindowsIntegration::updateApplicationBadge()
+{
+ // The system color settings have changed, or we are reacting
+ // to a task bar button being created for the fist time or after
+ // Explorer had crashed and re-started. In any case, re-apply the
+ // badge so that everything is up to date.
+ if (m_applicationBadgeNumber)
+ setApplicationBadge(m_applicationBadgeNumber);
+}
+
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{