Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
10#include "qwindowsmenu.h"
12
13#include "qwindowsscreen.h"
14#include "qwindowstheme.h"
16#include <QtGui/private/qtgui-config_p.h>
17#if QT_CONFIG(directwrite3)
18#include <QtGui/private/qwindowsdirectwritefontdatabase_p.h>
19#endif
20#ifndef QT_NO_FREETYPE
21# include <QtGui/private/qwindowsfontdatabase_ft_p.h>
22#endif
23#include <QtGui/private/qwindowsfontdatabase_p.h>
24#if QT_CONFIG(clipboard)
25# include "qwindowsclipboard.h"
26# if QT_CONFIG(draganddrop)
27# include "qwindowsdrag.h"
28# endif
29#endif
32#if QT_CONFIG(accessibility)
33# include "uiautomation/qwindowsuiaaccessibility.h"
34#endif
35
36#include <qpa/qplatformnativeinterface.h>
37#include <qpa/qwindowsysteminterface.h>
38#if QT_CONFIG(sessionmanager)
39# include "qwindowssessionmanager.h"
40#endif
41#include <QtGui/qpointingdevice.h>
42#include <QtGui/private/qguiapplication_p.h>
43#include <QtGui/private/qhighdpiscaling_p.h>
44#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
45#include <QtGui/qpa/qplatformcursor.h>
46
47#include <QtGui/private/qwindowsguieventdispatcher_p.h>
48
49#include <QtCore/qdebug.h>
50#include <QtCore/qvariant.h>
51
52#include <QtCore/qoperatingsystemversion.h>
53#include <QtCore/private/qfunctions_win_p.h>
54#include <QtCore/private/qcomptr_p.h>
55
56#include <limits.h>
57
58#if !defined(QT_NO_OPENGL)
60#endif
61
63
64#if QT_CONFIG(cpp_winrt)
65# include <QtCore/private/qt_winrtbase_p.h>
66# include <winrt/Windows.UI.Notifications.h>
67# include <winrt/Windows.Data.Xml.Dom.h>
68# include <winrt/Windows.Foundation.h>
69# include <winrt/Windows.UI.ViewManagement.h>
70#endif
71
72#include <memory>
73
74static inline void initOpenGlBlacklistResources()
75{
76 Q_INIT_RESOURCE(openglblacklists);
77}
78
79QT_BEGIN_NAMESPACE
80
81using namespace Qt::StringLiterals;
82
110
111template <typename IntType>
112bool parseIntOption(const QString &parameter,const QLatin1StringView &option,
113 IntType minimumValue, IntType maximumValue, IntType *target)
114{
115 const int valueLength = parameter.size() - option.size() - 1;
116 if (valueLength < 1 || !parameter.startsWith(option) || parameter.at(option.size()) != u'=')
117 return false;
118 bool ok;
119 const auto valueRef = QStringView{parameter}.right(valueLength);
120 const int value = valueRef.toInt(&ok);
121 if (ok) {
122 if (value >= int(minimumValue) && value <= int(maximumValue))
123 *target = static_cast<IntType>(value);
124 else {
125 qWarning() << "Value" << value << "for option" << option << "out of range"
126 << minimumValue << ".." << maximumValue;
127 }
128 } else {
129 qWarning() << "Invalid value" << valueRef << "for option" << option;
130 }
131 return true;
132}
133
134using DarkModeHandlingFlag = QNativeInterface::Private::QWindowsApplication::DarkModeHandlingFlag;
135using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkModeHandling;
136
137static inline unsigned parseOptions(const QStringList &paramList,
138 int *tabletAbsoluteRange,
139 QtWindows::DpiAwareness *dpiAwareness,
140 DarkModeHandling *darkModeHandling)
141{
142 unsigned options = 0;
143 for (const QString &param : paramList) {
144 if (param.startsWith(u"fontengine=")) {
145 if (param.endsWith(u"gdi")) {
146 options |= QWindowsIntegration::FontDatabaseGDI;
147 } else if (param.endsWith(u"freetype")) {
148 options |= QWindowsIntegration::FontDatabaseFreeType;
149 } else if (param.endsWith(u"native")) {
150 options |= QWindowsIntegration::FontDatabaseNative;
151 }
152 } else if (param.startsWith(u"dialogs=")) {
153 if (param.endsWith(u"xp")) {
154 options |= QWindowsIntegration::XpNativeDialogs;
155 } else if (param.endsWith(u"none")) {
156 options |= QWindowsIntegration::NoNativeDialogs;
157 }
158 } else if (param == u"altgr") {
159 options |= QWindowsIntegration::DetectAltGrModifier;
160 } else if (param == u"gl=gdi") {
161 options |= QWindowsIntegration::DisableArb;
162 } else if (param == u"nodirectwrite") {
163 options |= QWindowsIntegration::DontUseDirectWriteFonts;
164 } else if (param == u"nocolorfonts") {
165 options |= QWindowsIntegration::DontUseColorFonts;
166 } else if (param == u"nomousefromtouch") {
167 options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch;
168 } else if (parseIntOption(param, "verbose"_L1, 0, INT_MAX, &QWindowsContext::verbose)
169 || parseIntOption(param, "tabletabsoluterange"_L1, 0, INT_MAX, tabletAbsoluteRange)
170 || parseIntOption(param, "dpiawareness"_L1, QtWindows::DpiAwareness::Invalid,
171 QtWindows::DpiAwareness::PerMonitorVersion2, dpiAwareness)) {
172 } else if (param == u"menus=native") {
173 options |= QWindowsIntegration::AlwaysUseNativeMenus;
174 } else if (param == u"menus=none") {
175 options |= QWindowsIntegration::NoNativeMenus;
176 } else if (param == u"reverse") {
177 options |= QWindowsIntegration::RtlEnabled;
178 } else if (param == u"darkmode=0") {
179 *darkModeHandling = {};
180 } else if (param == u"darkmode=1") {
181 darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames);
182 darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle, false);
183 } else if (param == u"darkmode=2") {
184 darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames);
185 darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle);
186 } else {
187 qWarning() << "Unknown option" << param;
188 }
189 }
190 return options;
191}
192
193void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStringList &paramList)
194{
196
197 static bool dpiAwarenessSet = false;
198 // Default to per-monitor-v2 awareness (if available)
199 QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2;
200
201 int tabletAbsoluteRange = -1;
202 DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames
203 | DarkModeHandlingFlag::DarkModeStyle;
204 m_options = ::parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness, &darkModeHandling);
205 q->setDarkModeHandling(darkModeHandling);
206 QWindowsFontDatabase::setFontOptions(m_options);
207 if (tabletAbsoluteRange >= 0)
209
210 QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
211 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
212
213 if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication.
214 if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
215 m_context.setProcessDpiAwareness(dpiAwareness);
216 qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
217 << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
218 }
219 dpiAwarenessSet = true;
220 }
221
222 m_context.initTouch(m_options);
223 QPlatformCursor::setCapability(QPlatformCursor::OverrideCursor);
224
225 m_context.initPowerNotificationHandler();
226}
227
229{
230 delete m_fontDatabase;
231}
232
233QWindowsIntegration *QWindowsIntegration::m_instance = nullptr;
234
235QWindowsIntegration::QWindowsIntegration(const QStringList &paramList) :
237{
238 m_instance = this;
239 d->parseOptions(this, paramList);
240#if QT_CONFIG(clipboard)
241 d->m_clipboard.registerViewer();
242#endif
243 d->m_context.screenManager().initialize();
244 d->m_context.setDetectAltGrModifier((d->m_options & DetectAltGrModifier) != 0);
245}
246
248{
249 m_instance = nullptr;
250}
251
253{
254 auto icStrs = QPlatformInputContextFactory::requested();
255 icStrs.isEmpty() ? d->m_inputContext.reset(new QWindowsInputContext)
256 : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
257}
258
259bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const
260{
261 switch (cap) {
262 case ThreadedPixmaps:
263 return true;
264#ifndef QT_NO_OPENGL
265 case OpenGL:
266#if !QT_CONFIG(run_opengl_tests)
267 // Workaround for build configs on WoA that don't have OpenGL installed
268 // FIXME: Detect at runtime
269 return false;
270#endif
271 return true;
272 case ThreadedOpenGL:
274 return glContext->supportsThreadedOpenGL();
275 return false;
276#endif // !QT_NO_OPENGL
277 case WindowMasks:
278 return true;
279 case MultipleWindows:
280 return true;
281 case ForeignWindows:
282 return true;
283 case ChildWindows:
284 return true;
285 case AllGLFunctionsQueryable:
286 return true;
287 case SwitchableWidgetComposition:
288 return false; // QTBUG-68329 QTBUG-53515 QTBUG-54734
289 case BackingStoreStaticContents:
290 return true;
291 case MouseCursorPositioning:
292 return true;
293 default:
294 return QPlatformIntegration::hasCapability(cap);
295 }
296 return false;
297}
298
299QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const
300{
301 QWindowsWindowData requested;
302 requested.flags = window->flags();
303 requested.geometry = window->isTopLevel()
304 ? QHighDpi::toNativePixels(window->geometry(), window)
305 : QHighDpi::toNativeLocalPosition(window->geometry(), window);
306 if (!(requested.flags & Qt::FramelessWindowHint)) {
307 // Apply custom margins (see QWindowsWindow::setCustomMargins())).
308 const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
309 if (customMarginsV.isValid())
310 requested.customMargins = qvariant_cast<QMargins>(customMarginsV);
311 }
312
313 QWindowsWindowData obtained =
314 QWindowsWindowData::create(window, requested,
315 QWindowsWindow::formatWindowTitle(window->title()));
316 qCDebug(lcQpaWindow).nospace()
317 << __FUNCTION__ << ' ' << window
318 << "\n Requested: " << requested.geometry << " frame incl.="
319 << QWindowsGeometryHint::positionIncludesFrame(window)
320 << ' ' << requested.flags
321 << "\n Obtained : " << obtained.geometry << " margins=" << obtained.fullFrameMargins
322 << " handle=" << obtained.hwnd << ' ' << obtained.flags << '\n';
323
324 if (Q_UNLIKELY(!obtained.hwnd))
325 return nullptr;
326
327 QWindowsWindow *result = createPlatformWindowHelper(window, obtained);
328 Q_ASSERT(result);
329
330 if (window->isTopLevel() && !QWindowsContext::shouldHaveNonClientDpiScaling(window))
332
333 if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
334 menuBarToBeInstalled->install(result);
335
336 return result;
337}
338
339QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
340{
341 const HWND hwnd = reinterpret_cast<HWND>(nativeHandle);
342 if (!IsWindow(hwnd)) {
343 qWarning("Windows QPA: Invalid foreign window ID %p.", hwnd);
344 return nullptr;
345 }
346 auto *result = new QWindowsForeignWindow(window, hwnd);
347 const QRect obtainedGeometry = result->geometry();
348 QScreen *screen = nullptr;
349 if (const QPlatformScreen *pScreen = result->screenForGeometry(obtainedGeometry))
350 screen = pScreen->screen();
351 if (screen && screen != window->screen())
352 window->setScreen(screen);
353 qCDebug(lcQpaWindow) << "Foreign window:" << window << Qt::showbase << Qt::hex
354 << result->winId() << Qt::noshowbase << Qt::dec << obtainedGeometry << screen;
355 return result;
356}
357
358// Overridden to return a QWindowsDirect2DWindow in Direct2D plugin.
359QWindowsWindow *QWindowsIntegration::createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &data) const
360{
361 return new QWindowsWindow(window, data);
362}
363
364#ifndef QT_NO_OPENGL
365
367{
368#if defined(QT_OPENGL_DYNAMIC)
369 QWindowsOpenGLTester::Renderer requestedRenderer = QWindowsOpenGLTester::requestedRenderer();
370 switch (requestedRenderer) {
371 case QWindowsOpenGLTester::DesktopGl:
372 if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) {
373 if ((QWindowsOpenGLTester::supportedRenderers(requestedRenderer) & QWindowsOpenGLTester::DisableRotationFlag)
374 && !QWindowsScreen::setOrientationPreference(Qt::LandscapeOrientation)) {
375 qCWarning(lcQpaGl, "Unable to disable rotation.");
376 }
377 return glCtx;
378 }
379 qCWarning(lcQpaGl, "System OpenGL failed. Falling back to Software OpenGL.");
380 return QOpenGLStaticContext::create(true);
381 case QWindowsOpenGLTester::SoftwareRasterizer:
382 if (QWindowsStaticOpenGLContext *swCtx = QOpenGLStaticContext::create(true))
383 return swCtx;
384 qCWarning(lcQpaGl, "Software OpenGL failed. Falling back to system OpenGL.");
385 if (QWindowsOpenGLTester::supportedRenderers(requestedRenderer) & QWindowsOpenGLTester::DesktopGl)
386 return QOpenGLStaticContext::create();
387 return nullptr;
388 default:
389 break;
390 }
391
392 const QWindowsOpenGLTester::Renderers supportedRenderers = QWindowsOpenGLTester::supportedRenderers(requestedRenderer);
393 if (supportedRenderers.testFlag(QWindowsOpenGLTester::DisableProgramCacheFlag)
394 && !QCoreApplication::testAttribute(Qt::AA_DisableShaderDiskCache)) {
395 QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache);
396 }
397 if (supportedRenderers & QWindowsOpenGLTester::DesktopGl) {
398 if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) {
399 if ((supportedRenderers & QWindowsOpenGLTester::DisableRotationFlag)
400 && !QWindowsScreen::setOrientationPreference(Qt::LandscapeOrientation)) {
401 qCWarning(lcQpaGl, "Unable to disable rotation.");
402 }
403 return glCtx;
404 }
405 }
406 return QOpenGLStaticContext::create(true);
407#else
409#endif
410}
411
416
418{
419 qCDebug(lcQpaGl) << __FUNCTION__ << context->format();
421 std::unique_ptr<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(context));
422 if (result->isValid())
423 return result.release();
424 }
425 return nullptr;
426}
427
429{
430#if !defined(QT_OPENGL_DYNAMIC)
431 return QOpenGLContext::LibGL;
432#else
433 if (const QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
434 return staticOpenGLContext->moduleType();
435 return QOpenGLContext::LibGL;
436#endif
437}
438
440{
442 return static_cast<HMODULE>(staticOpenGLContext->moduleHandle());
443
444 return nullptr;
445}
446
447QOpenGLContext *QWindowsIntegration::createOpenGLContext(HGLRC ctx, HWND window, QOpenGLContext *shareContext) const
448{
449 if (!ctx || !window)
450 return nullptr;
451
453 std::unique_ptr<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(ctx, window));
454 if (result->isValid()) {
455 auto *context = new QOpenGLContext;
456 context->setShareContext(shareContext);
457 auto *contextPrivate = QOpenGLContextPrivate::get(context);
458 contextPrivate->adopt(result.release());
459 return context;
460 }
461 }
462
463 return nullptr;
464}
465
467{
469 if (!integration)
470 return nullptr;
471 QWindowsIntegrationPrivate *d = integration->d.data();
472 QMutexLocker lock(&d->m_staticContextLock);
473 if (d->m_staticOpenGLContext.isNull())
474 d->m_staticOpenGLContext.reset(QWindowsStaticOpenGLContext::create());
475 return d->m_staticOpenGLContext.data();
476}
477#endif // !QT_NO_OPENGL
478
480{
481 if (!d->m_fontDatabase) {
482#ifndef QT_NO_FREETYPE
483 if (d->m_options & QWindowsIntegration::FontDatabaseFreeType)
484 d->m_fontDatabase = new QWindowsFontDatabaseFT;
485 else
486#endif // QT_NO_FREETYPE
487#if QT_CONFIG(directwrite3)
488 if (!(d->m_options & (QWindowsIntegration::FontDatabaseGDI | QWindowsIntegration::DontUseDirectWriteFonts)))
489 d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
490 else
491#endif
492 d->m_fontDatabase = new QWindowsFontDatabase;
493 }
494 return d->m_fontDatabase;
495}
496
497#ifdef SPI_GETKEYBOARDSPEED
498static inline int keyBoardAutoRepeatRateMS()
499{
500 DWORD time = 0;
501 if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &time, 0))
502 return time ? 1000 / static_cast<int>(time) : 500;
503 return 30;
504}
505#endif
506
507QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
508{
509 switch (hint) {
510 case QPlatformIntegration::CursorFlashTime:
511 if (const unsigned timeMS = GetCaretBlinkTime())
512 return QVariant(timeMS != INFINITE ? int(timeMS) * 2 : 0);
513 break;
514#ifdef SPI_GETKEYBOARDSPEED
515 case KeyboardAutoRepeatRate:
516 return QVariant(keyBoardAutoRepeatRateMS());
517#endif
518 case QPlatformIntegration::ShowIsMaximized:
519 case QPlatformIntegration::StartDragTime:
520 case QPlatformIntegration::StartDragDistance:
521 case QPlatformIntegration::KeyboardInputInterval:
522 case QPlatformIntegration::ShowIsFullScreen:
523 case QPlatformIntegration::PasswordMaskDelay:
524 case QPlatformIntegration::StartDragVelocity:
525 break; // Not implemented
526 case QPlatformIntegration::FontSmoothingGamma:
527 return QVariant(QWindowsFontDatabase::fontSmoothingGamma());
528 case QPlatformIntegration::MouseDoubleClickInterval:
529 if (const UINT ms = GetDoubleClickTime())
530 return QVariant(int(ms));
531 break;
532 case QPlatformIntegration::UseRtlExtensions:
533 return QVariant(d->m_context.useRTLExtensions());
534 default:
535 break;
536 }
537 return QPlatformIntegration::styleHint(hint);
538}
539
540QPlatformKeyMapper *QWindowsIntegration::keyMapper() const
541{
542 return d->m_context.keyMapper();
543}
544
545#if QT_CONFIG(clipboard)
546QPlatformClipboard * QWindowsIntegration::clipboard() const
547{
548 return &d->m_clipboard;
549}
550# if QT_CONFIG(draganddrop)
551QPlatformDrag *QWindowsIntegration::drag() const
552{
553 return &d->m_drag;
554}
555# endif // QT_CONFIG(draganddrop)
556#endif // !QT_NO_CLIPBOARD
557
559{
560 return d->m_inputContext.data();
561}
562
563#if QT_CONFIG(accessibility)
564QPlatformAccessibility *QWindowsIntegration::accessibility() const
565{
566 return &d->m_accessibility;
567}
568#endif
569
571{
572 return d->m_options;
573}
574
575#if QT_CONFIG(sessionmanager)
576QPlatformSessionManager *QWindowsIntegration::createPlatformSessionManager(const QString &id, const QString &key) const
577{
578 return new QWindowsSessionManager(id, key);
579}
580#endif
581
583{
584 return new QWindowsGuiEventDispatcher;
585}
586
588{
589 return QStringList(QLatin1StringView(QWindowsTheme::name));
590}
591
593{
594 if (name == QLatin1StringView(QWindowsTheme::name))
595 return new QWindowsTheme;
596 return QPlatformIntegration::createPlatformTheme(name);
597}
598
600{
601 if (d->m_services.isNull())
602 d->m_services.reset(new QWindowsServices);
603
604 return d->m_services.data();
605}
606
608{
609 MessageBeep(MB_OK); // For QApplication
610}
611
613{
614 // Clamp to positive numbers, as the Windows API doesn't support negative numbers
615 number = qMax(0, number);
616
617 // Persist, so we can re-apply it on setting changes and Explorer restart
618 m_applicationBadgeNumber = number;
619
620 static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
621
622#if QT_CONFIG(cpp_winrt)
623 // We prefer the native BadgeUpdater API, that allows us to set a number directly,
624 // but it requires that the application has a package identity, and also doesn't
625 // seem to work in all cases on < Windows 11.
626 QT_TRY {
627 if (isWindows11 && qt_win_hasPackageIdentity()) {
628 using namespace winrt::Windows::UI::Notifications;
629 auto badgeXml = BadgeUpdateManager::GetTemplateContent(BadgeTemplateType::BadgeNumber);
630 badgeXml.SelectSingleNode(L"//badge/@value").NodeValue(winrt::box_value(winrt::to_hstring(number)));
631 BadgeUpdateManager::CreateBadgeUpdaterForApplication().Update(BadgeNotification(badgeXml));
632 return;
633 }
634 } QT_CATCH(...) {
635 // fall back to win32 implementation
636 }
637#endif
638
639 // Fallback for non-packaged apps, Windows 10, or Qt builds without WinRT/C++ support
640
641 if (!number) {
642 // Clear badge
643 setApplicationBadge(QImage());
644 return;
645 }
646
647 const bool isDarkMode = QWindowsTheme::instance()->colorScheme()
648 == Qt::ColorScheme::Dark;
649
650 QColor badgeColor;
651 QColor textColor;
652
653#if QT_CONFIG(cpp_winrt)
654 if (isWindows11) {
655 // Match colors used by BadgeUpdater
656 static const auto fromUIColor = [](winrt::Windows::UI::Color &&color) {
657 return QColor(color.R, color.G, color.B, color.A);
658 };
659 using namespace winrt::Windows::UI::ViewManagement;
660 const auto settings = UISettings();
661 badgeColor = fromUIColor(settings.GetColorValue(isDarkMode ?
662 UIColorType::AccentLight2 : UIColorType::Accent));
663 textColor = fromUIColor(settings.GetColorValue(UIColorType::Background));
664 }
665#endif
666
667 if (!badgeColor.isValid()) {
668 // Fall back to basic badge colors, based on Windows 10 look
669 badgeColor = isDarkMode ? Qt::black : QColor(220, 220, 220);
670 badgeColor.setAlphaF(0.5f);
671 textColor = isDarkMode ? Qt::white : Qt::black;
672 }
673
674 const auto devicePixelRatio = qApp->devicePixelRatio();
675
676 static const QSize iconBaseSize(16, 16);
677 QImage image(iconBaseSize * devicePixelRatio,
678 QImage::Format_ARGB32_Premultiplied);
679 image.fill(Qt::transparent);
680
681 QPainter painter(&image);
682
683 QRect badgeRect = image.rect();
684 QPen badgeBorderPen = Qt::NoPen;
685 if (!isWindows11) {
686 QColor badgeBorderColor = textColor;
687 badgeBorderColor.setAlphaF(0.5f);
688 badgeBorderPen = badgeBorderColor;
689 badgeRect.adjust(1, 1, -1, -1);
690 }
691 painter.setBrush(badgeColor);
692 painter.setPen(badgeBorderPen);
693 painter.setRenderHint(QPainter::Antialiasing);
694 painter.drawEllipse(badgeRect);
695
696 auto pixelSize = qCeil(10.5 * devicePixelRatio);
697 // Unlike the BadgeUpdater API we're limited by a square
698 // badge, so adjust the font size when above two digits.
699 const bool textOverflow = number > 99;
700 if (textOverflow)
701 pixelSize *= 0.8;
702
703 QFont font = painter.font();
704 font.setPixelSize(pixelSize);
705 font.setWeight(isWindows11 ? QFont::Medium : QFont::DemiBold);
706 painter.setFont(font);
707
708 painter.setRenderHint(QPainter::TextAntialiasing, devicePixelRatio > 1);
709 painter.setPen(textColor);
710
711 auto text = textOverflow ? u"99+"_s : QString::number(number);
712 painter.translate(textOverflow ? 1 : 0, textOverflow ? 0 : -1);
713 painter.drawText(image.rect(), Qt::AlignCenter, text);
714
715 painter.end();
716
717 setApplicationBadge(image);
718}
719
720void QWindowsIntegration::setApplicationBadge(const QImage &image)
721{
722 QComHelper comHelper;
723
724 ComPtr<ITaskbarList3> taskbarList;
725 CoCreateInstance(CLSID_TaskbarList, nullptr,
726 CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));
727 if (!taskbarList) {
728 // There may not be any windows with a task bar button yet,
729 // in which case we'll apply the badge once a window with
730 // a button has been created.
731 return;
732 }
733
734 const auto hIcon = image.toHICON();
735
736 // Apply the icon to all top level windows, since the badge is
737 // set on an application level. If one of the windows go away
738 // the other windows will take over in showing the badge.
739 const auto topLevelWindows = QGuiApplication::topLevelWindows();
740 for (auto *topLevelWindow : topLevelWindows) {
741 if (!topLevelWindow->handle())
742 continue;
743 auto hwnd = reinterpret_cast<HWND>(topLevelWindow->winId());
744 taskbarList->SetOverlayIcon(hwnd, hIcon, L"");
745 }
746
747 DestroyIcon(hIcon);
748
749 // FIXME: Update icon when the application scale factor changes.
750 // Doing so in response to screen DPI changes is too soon, as the
751 // task bar is not yet ready for an updated icon, and will just
752 // result in a blurred icon even if our icon is high-DPI.
753}
754
756{
757 // The system color settings have changed, or we are reacting
758 // to a task bar button being created for the fist time or after
759 // Explorer had crashed and re-started. In any case, re-apply the
760 // badge so that everything is up to date.
761 if (m_applicationBadgeNumber)
762 setApplicationBadge(m_applicationBadgeNumber);
763}
764
765#if QT_CONFIG(vulkan)
766QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
767{
768 return new QWindowsVulkanInstance(instance);
769}
770#endif
771
772QT_END_NAMESPACE
Static Open GL context containing version information, extension function pointers,...
static QOpenGLStaticContext * create(bool softwareRendering=false)
Singleton container for all relevant information.
static void setTabletAbsoluteRange(int a)
Window wrapping a foreign native window.
QStringList themeNames() const override
QPlatformKeyMapper * keyMapper() const override
Accessor for the platform integration's key mapper.
static QWindowsStaticOpenGLContext * staticOpenGLContext()
void setApplicationBadge(const QImage &image)
QOpenGLContext * createOpenGLContext(HGLRC context, HWND window, QOpenGLContext *shareContext) const override
QWindowsIntegration(const QStringList &paramList)
QAbstractEventDispatcher * createEventDispatcher() const override
Factory function for the GUI event dispatcher.
QPlatformTheme * createPlatformTheme(const QString &name) const override
bool hasCapability(QPlatformIntegration::Capability cap) const override
QOpenGLContext::OpenGLModuleType openGLModuleType() override
Platform integration function for querying the OpenGL implementation type.
QPlatformFontDatabase * fontDatabase() const override
Accessor for the platform integration's fontdatabase.
void initialize() override
Performs initialization steps that depend on having an event dispatcher available.
void setApplicationBadge(qint64 number) override
static QWindowsIntegration * instance()
HMODULE openGLModuleHandle() const override
void beep() const override
QPlatformInputContext * inputContext() const override
Returns the platforms input context.
QPlatformServices * services() const override
QVariant styleHint(StyleHint hint) const override
QPlatformOpenGLContext * createPlatformOpenGLContext(QOpenGLContext *context) const override
Factory function for QPlatformOpenGLContext.
Windows native menu bar.
void install(QWindowsWindow *window)
virtual void * moduleHandle() const =0
virtual bool supportsThreadedOpenGL() const
static const char * name
Raster or OpenGL Window.
void setFlag(unsigned f) const
static void initOpenGlBlacklistResources()
bool parseIntOption(const QString &parameter, const QLatin1StringView &option, IntType minimumValue, IntType maximumValue, IntType *target)
static unsigned parseOptions(const QStringList &paramList, int *tabletAbsoluteRange, QtWindows::DpiAwareness *dpiAwareness, DarkModeHandling *darkModeHandling)
QScopedPointer< QWindowsStaticOpenGLContext > m_staticOpenGLContext
void parseOptions(QWindowsIntegration *q, const QStringList &paramList)
QScopedPointer< QWindowsServices > m_services
QPlatformFontDatabase * m_fontDatabase
QScopedPointer< QPlatformInputContext > m_inputContext