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