Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> |
4 | ** Copyright (C) 2016 The Qt Company Ltd. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the plugins of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "qwindowscontext.h" |
42 | #include "qwindowsintegration.h" |
43 | #include "qwindowswindow.h" |
44 | #include "qwindowskeymapper.h" |
45 | #include "qwindowsmousehandler.h" |
46 | #include "qwindowspointerhandler.h" |
47 | #include "qtwindowsglobal.h" |
48 | #include "qwindowsmenu.h" |
49 | #include "qwindowsmime.h" |
50 | #include "qwindowsinputcontext.h" |
51 | #if QT_CONFIG(tabletevent) |
52 | # include "qwindowstabletsupport.h" |
53 | #endif |
54 | #include "qwindowstheme.h" |
55 | #include <private/qguiapplication_p.h> |
56 | #if QT_CONFIG(accessibility) |
57 | # include "uiautomation/qwindowsuiaaccessibility.h" |
58 | #endif |
59 | #if QT_CONFIG(sessionmanager) |
60 | # include <private/qsessionmanager_p.h> |
61 | # include "qwindowssessionmanager.h" |
62 | #endif |
63 | #include "qwindowsscreen.h" |
64 | #include "qwindowstheme.h" |
65 | |
66 | #include <QtGui/qwindow.h> |
67 | #include <qpa/qwindowsysteminterface.h> |
68 | #include <qpa/qwindowsysteminterface_p.h> |
69 | #include <qpa/qplatformnativeinterface.h> |
70 | #include <QtGui/qguiapplication.h> |
71 | #include <QtGui/qopenglcontext.h> |
72 | |
73 | #include <QtCore/qset.h> |
74 | #include <QtCore/qhash.h> |
75 | #include <QtCore/qstringlist.h> |
76 | #include <QtCore/qdebug.h> |
77 | #include <QtCore/qoperatingsystemversion.h> |
78 | #include <QtCore/qsysinfo.h> |
79 | #include <QtCore/qscopedpointer.h> |
80 | #include <QtCore/quuid.h> |
81 | #include <QtCore/private/qsystemlibrary_p.h> |
82 | |
83 | #include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h> |
84 | |
85 | #include <stdlib.h> |
86 | #include <stdio.h> |
87 | #include <windowsx.h> |
88 | #include <comdef.h> |
89 | #include <dbt.h> |
90 | #include <wtsapi32.h> |
91 | |
92 | QT_BEGIN_NAMESPACE |
93 | |
94 | Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows") |
95 | Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") |
96 | Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl") |
97 | Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") |
98 | Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods") |
99 | Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs") |
100 | Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus") |
101 | Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") |
102 | Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility") |
103 | Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation") |
104 | Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon") |
105 | |
106 | int QWindowsContext::verbose = 0; |
107 | |
108 | #if !defined(LANG_SYRIAC) |
109 | # define LANG_SYRIAC 0x5a |
110 | #endif |
111 | |
112 | static inline bool useRTL_Extensions() |
113 | { |
114 | // Since the IsValidLanguageGroup/IsValidLocale functions always return true on |
115 | // Vista, check the Keyboard Layouts for enabling RTL. |
116 | if (const int nLayouts = GetKeyboardLayoutList(0, nullptr)) { |
117 | QScopedArrayPointer<HKL> lpList(new HKL[nLayouts]); |
118 | GetKeyboardLayoutList(nLayouts, lpList.data()); |
119 | for (int i = 0; i < nLayouts; ++i) { |
120 | switch (PRIMARYLANGID((quintptr)lpList[i])) { |
121 | case LANG_ARABIC: |
122 | case LANG_HEBREW: |
123 | case LANG_FARSI: |
124 | case LANG_SYRIAC: |
125 | return true; |
126 | default: |
127 | break; |
128 | } |
129 | } |
130 | } |
131 | return false; |
132 | } |
133 | |
134 | #if QT_CONFIG(sessionmanager) |
135 | static inline QWindowsSessionManager *platformSessionManager() |
136 | { |
137 | auto *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp)); |
138 | auto *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager)); |
139 | return static_cast<QWindowsSessionManager *>(managerPrivate->platformSessionManager); |
140 | } |
141 | |
142 | static inline bool sessionManagerInteractionBlocked() |
143 | { |
144 | return platformSessionManager()->isInteractionBlocked(); |
145 | } |
146 | #else // QT_CONFIG(sessionmanager) |
147 | static inline bool sessionManagerInteractionBlocked() { return false; } |
148 | #endif |
149 | |
150 | static inline int windowDpiAwareness(HWND hwnd) |
151 | { |
152 | return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext |
153 | ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) |
154 | : -1; |
155 | } |
156 | |
157 | // Note: This only works within WM_NCCREATE |
158 | static bool enableNonClientDpiScaling(HWND hwnd) |
159 | { |
160 | bool result = false; |
161 | if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) { |
162 | result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE; |
163 | if (!result) { |
164 | const DWORD errorCode = GetLastError(); |
165 | qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", |
166 | hwnd, errorCode); |
167 | } |
168 | } |
169 | return result; |
170 | } |
171 | |
172 | /*! |
173 | \class QWindowsUser32DLL |
174 | \brief Struct that contains dynamically resolved symbols of User32.dll. |
175 | |
176 | The stub libraries shipped with the MinGW compiler miss some of the |
177 | functions. They need to be retrieved dynamically. |
178 | |
179 | In addition, touch-related functions are available only from Windows onwards. |
180 | These need to resolved dynamically for Q_CC_MSVC as well. |
181 | |
182 | \sa QWindowsShell32DLL |
183 | |
184 | \internal |
185 | \ingroup qt-lighthouse-win |
186 | */ |
187 | |
188 | void QWindowsUser32DLL::init() |
189 | { |
190 | QSystemLibrary library(QStringLiteral("user32")); |
191 | setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware"); |
192 | |
193 | addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener"); |
194 | removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener"); |
195 | |
196 | getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences"); |
197 | setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences"); |
198 | |
199 | if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8) { |
200 | enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer"); |
201 | getPointerType = (GetPointerType)library.resolve("GetPointerType"); |
202 | getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo"); |
203 | getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects"); |
204 | getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo"); |
205 | getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo"); |
206 | getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory"); |
207 | getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo"); |
208 | getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory"); |
209 | skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages"); |
210 | } |
211 | |
212 | if (QOperatingSystemVersion::current() |
213 | >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) { |
214 | adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi"); |
215 | enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling"); |
216 | getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext"); |
217 | getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext"); |
218 | systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi"); |
219 | } |
220 | } |
221 | |
222 | bool QWindowsUser32DLL::supportsPointerApi() |
223 | { |
224 | return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects |
225 | && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory |
226 | && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages; |
227 | } |
228 | |
229 | void QWindowsShcoreDLL::init() |
230 | { |
231 | if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) |
232 | return; |
233 | QSystemLibrary library(QStringLiteral("SHCore")); |
234 | getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness"); |
235 | setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness"); |
236 | getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor"); |
237 | } |
238 | |
239 | QWindowsUser32DLL QWindowsContext::user32dll; |
240 | QWindowsShcoreDLL QWindowsContext::shcoredll; |
241 | |
242 | QWindowsContext *QWindowsContext::m_instance = nullptr; |
243 | |
244 | /*! |
245 | \class QWindowsContext |
246 | \brief Singleton container for all relevant information. |
247 | |
248 | Holds state information formerly stored in \c qapplication_win.cpp. |
249 | |
250 | \internal |
251 | \ingroup qt-lighthouse-win |
252 | */ |
253 | |
254 | typedef QHash<HWND, QWindowsWindow *> HandleBaseWindowHash; |
255 | |
256 | struct QWindowsContextPrivate { |
257 | QWindowsContextPrivate(); |
258 | |
259 | unsigned m_systemInfo = 0; |
260 | QSet<QString> m_registeredWindowClassNames; |
261 | HandleBaseWindowHash m_windows; |
262 | HDC m_displayContext = nullptr; |
263 | int m_defaultDPI = 96; |
264 | QWindowsKeyMapper m_keyMapper; |
265 | QWindowsMouseHandler m_mouseHandler; |
266 | QWindowsPointerHandler m_pointerHandler; |
267 | QWindowsMimeConverter m_mimeConverter; |
268 | QWindowsScreenManager m_screenManager; |
269 | QSharedPointer<QWindowCreationContext> m_creationContext; |
270 | #if QT_CONFIG(tabletevent) |
271 | QScopedPointer<QWindowsTabletSupport> m_tabletSupport; |
272 | #endif |
273 | const HRESULT m_oleInitializeResult; |
274 | QWindow *m_lastActiveWindow = nullptr; |
275 | bool m_asyncExpose = false; |
276 | }; |
277 | |
278 | QWindowsContextPrivate::QWindowsContextPrivate() |
279 | : m_oleInitializeResult(OleInitialize(nullptr)) |
280 | { |
281 | QWindowsContext::user32dll.init(); |
282 | QWindowsContext::shcoredll.init(); |
283 | |
284 | if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice()) |
285 | m_systemInfo |= QWindowsContext::SI_SupportsTouch; |
286 | m_displayContext = GetDC(nullptr); |
287 | m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY); |
288 | if (useRTL_Extensions()) { |
289 | m_systemInfo |= QWindowsContext::SI_RTL_Extensions; |
290 | m_keyMapper.setUseRTLExtensions(true); |
291 | } |
292 | if (FAILED(m_oleInitializeResult)) { |
293 | qWarning() << "QWindowsContext: OleInitialize() failed: " |
294 | << QWindowsContext::comErrorString(m_oleInitializeResult); |
295 | } |
296 | } |
297 | |
298 | QWindowsContext::QWindowsContext() : |
299 | d(new QWindowsContextPrivate) |
300 | { |
301 | #ifdef Q_CC_MSVC |
302 | # pragma warning( disable : 4996 ) |
303 | #endif |
304 | m_instance = this; |
305 | // ### FIXME: Remove this once the logging system has other options of configurations. |
306 | const QByteArray bv = qgetenv("QT_QPA_VERBOSE"); |
307 | if (!bv.isEmpty()) |
308 | QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv)); |
309 | } |
310 | |
311 | QWindowsContext::~QWindowsContext() |
312 | { |
313 | #if QT_CONFIG(tabletevent) |
314 | d->m_tabletSupport.reset(); // Destroy internal window before unregistering classes. |
315 | #endif |
316 | unregisterWindowClasses(); |
317 | if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) |
318 | OleUninitialize(); |
319 | |
320 | d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows. |
321 | if (d->m_displayContext) |
322 | ReleaseDC(nullptr, d->m_displayContext); |
323 | m_instance = nullptr; |
324 | } |
325 | |
326 | bool QWindowsContext::initTouch() |
327 | { |
328 | return initTouch(QWindowsIntegration::instance()->options()); |
329 | } |
330 | |
331 | bool QWindowsContext::initTouch(unsigned integrationOptions) |
332 | { |
333 | if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) |
334 | return true; |
335 | |
336 | QTouchDevice *touchDevice = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? |
337 | d->m_pointerHandler.ensureTouchDevice() : d->m_mouseHandler.ensureTouchDevice(); |
338 | if (!touchDevice) |
339 | return false; |
340 | |
341 | if (!(integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch)) |
342 | touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation); |
343 | |
344 | QWindowSystemInterface::registerTouchDevice(touchDevice); |
345 | |
346 | d->m_systemInfo |= QWindowsContext::SI_SupportsTouch; |
347 | |
348 | // A touch device was plugged while the app is running. Register all windows for touch. |
349 | if (QGuiApplicationPrivate::is_app_running) { |
350 | for (QWindowsWindow *w : qAsConst(d->m_windows)) |
351 | w->registerTouchWindow(); |
352 | } |
353 | |
354 | return true; |
355 | } |
356 | |
357 | bool QWindowsContext::initTablet(unsigned integrationOptions) |
358 | { |
359 | Q_UNUSED(integrationOptions); |
360 | #if QT_CONFIG(tabletevent) |
361 | d->m_tabletSupport.reset(QWindowsTabletSupport::create()); |
362 | return true; |
363 | #else |
364 | return false; |
365 | #endif |
366 | } |
367 | |
368 | bool QWindowsContext::initPointer(unsigned integrationOptions) |
369 | { |
370 | if (integrationOptions & QWindowsIntegration::DontUseWMPointer) |
371 | return false; |
372 | |
373 | if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) |
374 | return false; |
375 | |
376 | if (!QWindowsContext::user32dll.supportsPointerApi()) |
377 | return false; |
378 | |
379 | d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; |
380 | return true; |
381 | } |
382 | |
383 | void QWindowsContext::setTabletAbsoluteRange(int a) |
384 | { |
385 | #if QT_CONFIG(tabletevent) |
386 | if (!d->m_tabletSupport.isNull()) |
387 | d->m_tabletSupport->setAbsoluteRange(a); |
388 | #else |
389 | Q_UNUSED(a) |
390 | #endif |
391 | } |
392 | |
393 | void QWindowsContext::setDetectAltGrModifier(bool a) |
394 | { |
395 | d->m_keyMapper.setDetectAltGrModifier(a); |
396 | } |
397 | |
398 | int QWindowsContext::processDpiAwareness() |
399 | { |
400 | int result; |
401 | if (QWindowsContext::shcoredll.getProcessDpiAwareness |
402 | && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) { |
403 | return result; |
404 | } |
405 | return -1; |
406 | } |
407 | |
408 | void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness) |
409 | { |
410 | qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness; |
411 | if (QWindowsContext::shcoredll.isValid()) { |
412 | const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(dpiAwareness); |
413 | // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin). |
414 | // Silence warning in that case unless debug is enabled. |
415 | if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) { |
416 | qWarning().noquote().nospace() << "SetProcessDpiAwareness(" |
417 | << dpiAwareness << ") failed: "<< QWindowsContext::comErrorString(hr) |
418 | << ", using "<< QWindowsContext::processDpiAwareness(); |
419 | } |
420 | } else { |
421 | if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) { |
422 | if (!QWindowsContext::user32dll.setProcessDPIAware()) |
423 | qErrnoWarning("SetProcessDPIAware() failed"); |
424 | } |
425 | } |
426 | } |
427 | |
428 | QWindowsContext *QWindowsContext::instance() |
429 | { |
430 | return m_instance; |
431 | } |
432 | |
433 | unsigned QWindowsContext::systemInfo() const |
434 | { |
435 | return d->m_systemInfo; |
436 | } |
437 | |
438 | bool QWindowsContext::useRTLExtensions() const |
439 | { |
440 | return d->m_keyMapper.useRTLExtensions(); |
441 | } |
442 | |
443 | QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const |
444 | { |
445 | return d->m_keyMapper.possibleKeys(e); |
446 | } |
447 | |
448 | QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx) |
449 | { |
450 | const QSharedPointer<QWindowCreationContext> old = d->m_creationContext; |
451 | d->m_creationContext = ctx; |
452 | return old; |
453 | } |
454 | |
455 | QSharedPointer<QWindowCreationContext> QWindowsContext::windowCreationContext() const |
456 | { |
457 | return d->m_creationContext; |
458 | } |
459 | |
460 | int QWindowsContext::defaultDPI() const |
461 | { |
462 | return d->m_defaultDPI; |
463 | } |
464 | |
465 | HDC QWindowsContext::displayContext() const |
466 | { |
467 | return d->m_displayContext; |
468 | } |
469 | |
470 | QWindow *QWindowsContext::keyGrabber() const |
471 | { |
472 | return d->m_keyMapper.keyGrabber(); |
473 | } |
474 | |
475 | void QWindowsContext::setKeyGrabber(QWindow *w) |
476 | { |
477 | d->m_keyMapper.setKeyGrabber(w); |
478 | } |
479 | |
480 | // Window class registering code (from qapplication_win.cpp) |
481 | |
482 | QString QWindowsContext::registerWindowClass(const QWindow *w) |
483 | { |
484 | Q_ASSERT(w); |
485 | const Qt::WindowFlags flags = w->flags(); |
486 | const Qt::WindowFlags type = flags & Qt::WindowType_Mask; |
487 | // Determine style and icon. |
488 | uint style = CS_DBLCLKS; |
489 | bool icon = true; |
490 | // The following will not set CS_OWNDC for any widget window, even if it contains a |
491 | // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage. |
492 | if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC)) |
493 | style |= CS_OWNDC; |
494 | if (!(flags & Qt::NoDropShadowWindowHint) |
495 | && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) { |
496 | style |= CS_DROPSHADOW; |
497 | } |
498 | switch (type) { |
499 | case Qt::Tool: |
500 | case Qt::ToolTip: |
501 | case Qt::Popup: |
502 | style |= CS_SAVEBITS; // Save/restore background |
503 | icon = false; |
504 | break; |
505 | case Qt::Dialog: |
506 | if (!(flags & Qt::WindowSystemMenuHint)) |
507 | icon = false; // QTBUG-2027, dialogs without system menu. |
508 | break; |
509 | } |
510 | // Create a unique name for the flag combination |
511 | QString cname; |
512 | cname += QLatin1String("Qt5QWindow"); |
513 | switch (type) { |
514 | case Qt::Tool: |
515 | cname += QLatin1String("Tool"); |
516 | break; |
517 | case Qt::ToolTip: |
518 | cname += QLatin1String("ToolTip"); |
519 | break; |
520 | case Qt::Popup: |
521 | cname += QLatin1String("Popup"); |
522 | break; |
523 | default: |
524 | break; |
525 | } |
526 | if (style & CS_DROPSHADOW) |
527 | cname += QLatin1String("DropShadow"); |
528 | if (style & CS_SAVEBITS) |
529 | cname += QLatin1String("SaveBits"); |
530 | if (style & CS_OWNDC) |
531 | cname += QLatin1String("OwnDC"); |
532 | if (icon) |
533 | cname += QLatin1String("Icon"); |
534 | |
535 | return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon); |
536 | } |
537 | |
538 | QString QWindowsContext::registerWindowClass(QString cname, |
539 | WNDPROC proc, |
540 | unsigned style, |
541 | HBRUSH brush, |
542 | bool icon) |
543 | { |
544 | // since multiple Qt versions can be used in one process |
545 | // each one has to have window class names with a unique name |
546 | // The first instance gets the unmodified name; if the class |
547 | // has already been registered by another instance of Qt then |
548 | // add a UUID. |
549 | static int classExists = -1; |
550 | |
551 | const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); |
552 | if (classExists == -1) { |
553 | WNDCLASS wcinfo; |
554 | classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo); |
555 | classExists = classExists && wcinfo.lpfnWndProc != proc; |
556 | } |
557 | |
558 | if (classExists) |
559 | cname += QUuid::createUuid().toString(); |
560 | |
561 | if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list |
562 | return cname; |
563 | |
564 | WNDCLASSEX wc; |
565 | wc.cbSize = sizeof(WNDCLASSEX); |
566 | wc.style = style; |
567 | wc.lpfnWndProc = proc; |
568 | wc.cbClsExtra = 0; |
569 | wc.cbWndExtra = 0; |
570 | wc.hInstance = appInstance; |
571 | wc.hCursor = nullptr; |
572 | wc.hbrBackground = brush; |
573 | if (icon) { |
574 | wc.hIcon = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); |
575 | if (wc.hIcon) { |
576 | int sw = GetSystemMetrics(SM_CXSMICON); |
577 | int sh = GetSystemMetrics(SM_CYSMICON); |
578 | wc.hIconSm = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0)); |
579 | } else { |
580 | wc.hIcon = static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); |
581 | wc.hIconSm = nullptr; |
582 | } |
583 | } else { |
584 | wc.hIcon = nullptr; |
585 | wc.hIconSm = nullptr; |
586 | } |
587 | |
588 | wc.lpszMenuName = nullptr; |
589 | wc.lpszClassName = reinterpret_cast<LPCWSTR>(cname.utf16()); |
590 | ATOM atom = RegisterClassEx(&wc); |
591 | if (!atom) |
592 | qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.", |
593 | qPrintable(cname)); |
594 | |
595 | d->m_registeredWindowClassNames.insert(cname); |
596 | qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname |
597 | << " style=0x"<< Qt::hex << style << Qt::dec |
598 | << " brush="<< brush << " icon="<< icon << " atom="<< atom; |
599 | return cname; |
600 | } |
601 | |
602 | void QWindowsContext::unregisterWindowClasses() |
603 | { |
604 | const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); |
605 | |
606 | for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) { |
607 | if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose) |
608 | qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name)); |
609 | } |
610 | d->m_registeredWindowClassNames.clear(); |
611 | } |
612 | |
613 | int QWindowsContext::screenDepth() const |
614 | { |
615 | return GetDeviceCaps(d->m_displayContext, BITSPIXEL); |
616 | } |
617 | |
618 | QString QWindowsContext::windowsErrorMessage(unsigned long errorCode) |
619 | { |
620 | QString rc = QString::fromLatin1("#%1: ").arg(errorCode); |
621 | ushort *lpMsgBuf; |
622 | |
623 | const DWORD len = FormatMessage( |
624 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
625 | nullptr, errorCode, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr); |
626 | if (len) { |
627 | rc = QString::fromUtf16(lpMsgBuf, int(len)); |
628 | LocalFree(lpMsgBuf); |
629 | } else { |
630 | rc += QString::fromLatin1("<unknown error>"); |
631 | } |
632 | return rc; |
633 | } |
634 | |
635 | void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w) |
636 | { |
637 | d->m_windows.insert(hwnd, w); |
638 | } |
639 | |
640 | void QWindowsContext::removeWindow(HWND hwnd) |
641 | { |
642 | const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd); |
643 | if (it != d->m_windows.end()) { |
644 | if (d->m_keyMapper.keyGrabber() == it.value()->window()) |
645 | d->m_keyMapper.setKeyGrabber(nullptr); |
646 | d->m_windows.erase(it); |
647 | } |
648 | } |
649 | |
650 | QWindowsWindow *QWindowsContext::findPlatformWindow(const QWindowsMenuBar *mb) const |
651 | { |
652 | for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) { |
653 | if ((*it)->menuBar() == mb) |
654 | return *it; |
655 | } |
656 | return nullptr; |
657 | } |
658 | |
659 | QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const |
660 | { |
661 | return d->m_windows.value(hwnd); |
662 | } |
663 | |
664 | QWindowsWindow *QWindowsContext::findClosestPlatformWindow(HWND hwnd) const |
665 | { |
666 | QWindowsWindow *window = d->m_windows.value(hwnd); |
667 | |
668 | // Requested hwnd may also be a child of a platform window in case of embedded native windows. |
669 | // Find the closest parent that has a platform window. |
670 | if (!window) { |
671 | for (HWND w = hwnd; w; w = GetParent(w)) { |
672 | window = d->m_windows.value(w); |
673 | if (window) |
674 | break; |
675 | } |
676 | } |
677 | |
678 | return window; |
679 | } |
680 | |
681 | QWindow *QWindowsContext::findWindow(HWND hwnd) const |
682 | { |
683 | if (const QWindowsWindow *bw = findPlatformWindow(hwnd)) |
684 | return bw->window(); |
685 | return nullptr; |
686 | } |
687 | |
688 | QWindow *QWindowsContext::windowUnderMouse() const |
689 | { |
690 | return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? |
691 | d->m_pointerHandler.windowUnderMouse() : d->m_mouseHandler.windowUnderMouse(); |
692 | } |
693 | |
694 | void QWindowsContext::clearWindowUnderMouse() |
695 | { |
696 | if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) |
697 | d->m_pointerHandler.clearWindowUnderMouse(); |
698 | else |
699 | d->m_mouseHandler.clearWindowUnderMouse(); |
700 | } |
701 | |
702 | /*! |
703 | \brief Find a child window at a screen point. |
704 | |
705 | Deep search for a QWindow at global point, skipping non-owned |
706 | windows (accessibility?). Implemented using ChildWindowFromPointEx() |
707 | instead of (historically used) WindowFromPoint() to get a well-defined |
708 | behaviour for hidden/transparent windows. |
709 | |
710 | \a cwex_flags are flags of ChildWindowFromPointEx(). |
711 | \a parent is the parent window, pass GetDesktopWindow() for top levels. |
712 | */ |
713 | |
714 | static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned cwexFlags, |
715 | const QWindowsContext *context, |
716 | HWND *hwnd, QWindowsWindow **result) |
717 | { |
718 | POINT point = screenPoint; |
719 | screenToClient(*hwnd, &point); |
720 | // Returns parent if inside & none matched. |
721 | const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags); |
722 | if (!child || child == *hwnd) |
723 | return false; |
724 | if (QWindowsWindow *window = context->findPlatformWindow(child)) { |
725 | *result = window; |
726 | *hwnd = child; |
727 | return true; |
728 | } |
729 | // QTBUG-40555: despite CWP_SKIPINVISIBLE, it is possible to hit on invisible |
730 | // full screen windows of other applications that have WS_EX_TRANSPARENT set |
731 | // (for example created by screen sharing applications). In that case, try to |
732 | // find a Qt window by searching again with CWP_SKIPTRANSPARENT. |
733 | // Note that Qt 5 uses WS_EX_TRANSPARENT for Qt::WindowTransparentForInput |
734 | // as well. |
735 | if (!(cwexFlags & CWP_SKIPTRANSPARENT) |
736 | && (GetWindowLongPtr(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) { |
737 | const HWND nonTransparentChild = ChildWindowFromPointEx(*hwnd, point, cwexFlags | CWP_SKIPTRANSPARENT); |
738 | if (QWindowsWindow *nonTransparentWindow = context->findPlatformWindow(nonTransparentChild)) { |
739 | *result = nonTransparentWindow; |
740 | *hwnd = nonTransparentChild; |
741 | return true; |
742 | } |
743 | } |
744 | *hwnd = child; |
745 | return true; |
746 | } |
747 | |
748 | QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent, |
749 | const QPoint &screenPointIn, |
750 | unsigned cwex_flags) const |
751 | { |
752 | QWindowsWindow *result = nullptr; |
753 | const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() }; |
754 | while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {} |
755 | // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from |
756 | // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint(). |
757 | if (result == nullptr) { |
758 | if (const HWND window = WindowFromPoint(screenPoint)) |
759 | result = findPlatformWindow(window); |
760 | } |
761 | return result; |
762 | } |
763 | |
764 | bool QWindowsContext::isSessionLocked() |
765 | { |
766 | bool result = false; |
767 | const DWORD sessionId = WTSGetActiveConsoleSessionId(); |
768 | if (sessionId != 0xFFFFFFFF) { |
769 | LPTSTR buffer = nullptr; |
770 | DWORD size = 0; |
771 | #if !defined(Q_CC_MINGW) |
772 | if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, |
773 | WTSSessionInfoEx, &buffer, &size) == TRUE |
774 | && size > 0) { |
775 | const WTSINFOEXW *info = reinterpret_cast<WTSINFOEXW *>(buffer); |
776 | result = info->Level == 1 && info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK; |
777 | WTSFreeMemory(buffer); |
778 | } |
779 | #else // MinGW as of 7.3 does not have WTSINFOEXW in wtsapi32.h |
780 | // Retrieve the flags which are at offset 16 due to padding for 32/64bit alike. |
781 | if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, |
782 | WTS_INFO_CLASS(25), &buffer, &size) == TRUE |
783 | && size >= 20) { |
784 | const DWORD *p = reinterpret_cast<DWORD *>(buffer); |
785 | const DWORD level = *p; |
786 | const DWORD sessionFlags = *(p + 4); |
787 | result = level == 1 && sessionFlags == 1; |
788 | WTSFreeMemory(buffer); |
789 | } |
790 | #endif // Q_CC_MINGW |
791 | } |
792 | return result; |
793 | } |
794 | |
795 | QWindowsMimeConverter &QWindowsContext::mimeConverter() const |
796 | { |
797 | return d->m_mimeConverter; |
798 | } |
799 | |
800 | QWindowsScreenManager &QWindowsContext::screenManager() |
801 | { |
802 | return d->m_screenManager; |
803 | } |
804 | |
805 | QWindowsTabletSupport *QWindowsContext::tabletSupport() const |
806 | { |
807 | #if QT_CONFIG(tabletevent) |
808 | return d->m_tabletSupport.data(); |
809 | #else |
810 | return 0; |
811 | #endif |
812 | } |
813 | |
814 | /*! |
815 | \brief Convenience to create a non-visible, message-only dummy |
816 | window for example used as clipboard watcher or for GL. |
817 | */ |
818 | |
819 | HWND QWindowsContext::createDummyWindow(const QString &classNameIn, |
820 | const wchar_t *windowName, |
821 | WNDPROC wndProc, DWORD style) |
822 | { |
823 | if (!wndProc) |
824 | wndProc = DefWindowProc; |
825 | QString className = registerWindowClass(classNameIn, wndProc); |
826 | return CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()), |
827 | windowName, style, |
828 | CW_USEDEFAULT, CW_USEDEFAULT, |
829 | CW_USEDEFAULT, CW_USEDEFAULT, |
830 | HWND_MESSAGE, nullptr, static_cast<HINSTANCE>(GetModuleHandle(nullptr)), nullptr); |
831 | } |
832 | |
833 | // Re-engineered from the inline function _com_error::ErrorMessage(). |
834 | // We cannot use it directly since it uses swprintf_s(), which is not |
835 | // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). |
836 | static inline QString errorMessageFromComError(const _com_error &comError) |
837 | { |
838 | TCHAR *message = nullptr; |
839 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
840 | nullptr, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), |
841 | message, 0, nullptr); |
842 | if (message) { |
843 | const QString result = QString::fromWCharArray(message).trimmed(); |
844 | LocalFree(static_cast<HLOCAL>(message)); |
845 | return result; |
846 | } |
847 | if (const WORD wCode = comError.WCode()) |
848 | return QString::asprintf("IDispatch error #%u", uint(wCode)); |
849 | return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); |
850 | } |
851 | |
852 | /*! |
853 | \brief Common COM error strings. |
854 | */ |
855 | |
856 | QByteArray QWindowsContext::comErrorString(HRESULT hr) |
857 | { |
858 | QByteArray result = QByteArrayLiteral("COM error 0x") |
859 | + QByteArray::number(quintptr(hr), 16) + ' '; |
860 | switch (hr) { |
861 | case S_OK: |
862 | result += QByteArrayLiteral("S_OK"); |
863 | break; |
864 | case S_FALSE: |
865 | result += QByteArrayLiteral("S_FALSE"); |
866 | break; |
867 | case E_UNEXPECTED: |
868 | result += QByteArrayLiteral("E_UNEXPECTED"); |
869 | break; |
870 | case E_ACCESSDENIED: |
871 | result += QByteArrayLiteral("E_ACCESSDENIED"); |
872 | break; |
873 | case CO_E_ALREADYINITIALIZED: |
874 | result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED"); |
875 | break; |
876 | case CO_E_NOTINITIALIZED: |
877 | result += QByteArrayLiteral("CO_E_NOTINITIALIZED"); |
878 | break; |
879 | case RPC_E_CHANGED_MODE: |
880 | result += QByteArrayLiteral("RPC_E_CHANGED_MODE"); |
881 | break; |
882 | case OLE_E_WRONGCOMPOBJ: |
883 | result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ"); |
884 | break; |
885 | case CO_E_NOT_SUPPORTED: |
886 | result += QByteArrayLiteral("CO_E_NOT_SUPPORTED"); |
887 | break; |
888 | case E_NOTIMPL: |
889 | result += QByteArrayLiteral("E_NOTIMPL"); |
890 | break; |
891 | case E_INVALIDARG: |
892 | result += QByteArrayLiteral("E_INVALIDARG"); |
893 | break; |
894 | case E_NOINTERFACE: |
895 | result += QByteArrayLiteral("E_NOINTERFACE"); |
896 | break; |
897 | case E_POINTER: |
898 | result += QByteArrayLiteral("E_POINTER"); |
899 | break; |
900 | case E_HANDLE: |
901 | result += QByteArrayLiteral("E_HANDLE"); |
902 | break; |
903 | case E_ABORT: |
904 | result += QByteArrayLiteral("E_ABORT"); |
905 | break; |
906 | case E_FAIL: |
907 | result += QByteArrayLiteral("E_FAIL"); |
908 | break; |
909 | case RPC_E_WRONG_THREAD: |
910 | result += QByteArrayLiteral("RPC_E_WRONG_THREAD"); |
911 | break; |
912 | case RPC_E_THREAD_NOT_INIT: |
913 | result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT"); |
914 | break; |
915 | default: |
916 | break; |
917 | } |
918 | _com_error error(hr); |
919 | result += QByteArrayLiteral(" ("); |
920 | result += errorMessageFromComError(error); |
921 | result += ')'; |
922 | return result; |
923 | } |
924 | |
925 | bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, |
926 | unsigned dpi) |
927 | { |
928 | const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0 |
929 | ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi) |
930 | : SystemParametersInfo(action, param, out, 0); |
931 | return result == TRUE; |
932 | } |
933 | |
934 | bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out, |
935 | const QPlatformScreen *screen) |
936 | { |
937 | return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u); |
938 | } |
939 | |
940 | bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out, |
941 | const QPlatformWindow *win) |
942 | { |
943 | return systemParametersInfoForScreen(action, param, out, win ? win->screen() : nullptr); |
944 | } |
945 | |
946 | bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi) |
947 | { |
948 | memset(ncm, 0, sizeof(NONCLIENTMETRICS)); |
949 | ncm->cbSize = sizeof(NONCLIENTMETRICS); |
950 | return systemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm->cbSize, ncm, dpi); |
951 | } |
952 | |
953 | bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm, |
954 | const QPlatformScreen *screen) |
955 | { |
956 | const int dpi = screen ? qRound(screen->logicalDpi().first) : 0; |
957 | return nonClientMetrics(ncm, unsigned(dpi)); |
958 | } |
959 | |
960 | bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win) |
961 | { |
962 | return nonClientMetricsForScreen(ncm, win ? win->screen() : nullptr); |
963 | } |
964 | |
965 | static inline QWindowsInputContext *windowsInputContext() |
966 | { |
967 | return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext()); |
968 | } |
969 | |
970 | |
971 | // Child windows, fixed-size windows or pop-ups and similar should not be resized |
972 | static inline bool resizeOnDpiChanged(const QWindow *w) |
973 | { |
974 | bool result = false; |
975 | if (w->isTopLevel()) { |
976 | switch (w->type()) { |
977 | case Qt::Window: |
978 | case Qt::Dialog: |
979 | case Qt::Sheet: |
980 | case Qt::Drawer: |
981 | case Qt::Tool: |
982 | result = !w->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint); |
983 | break; |
984 | default: |
985 | break; |
986 | } |
987 | } |
988 | return result; |
989 | } |
990 | |
991 | bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) |
992 | { |
993 | return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 |
994 | && window->isTopLevel() |
995 | && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid() |
996 | #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL |
997 | && (window->surfaceType() != QSurface::OpenGLSurface |
998 | || QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL) |
999 | #endif |
1000 | ; |
1001 | } |
1002 | |
1003 | static inline bool isInputMessage(UINT m) |
1004 | { |
1005 | switch (m) { |
1006 | case WM_IME_STARTCOMPOSITION: |
1007 | case WM_IME_ENDCOMPOSITION: |
1008 | case WM_IME_COMPOSITION: |
1009 | case WM_INPUT: |
1010 | case WM_TOUCH: |
1011 | case WM_MOUSEHOVER: |
1012 | case WM_MOUSELEAVE: |
1013 | case WM_NCMOUSEHOVER: |
1014 | case WM_NCMOUSELEAVE: |
1015 | case WM_SIZING: |
1016 | case WM_MOVING: |
1017 | case WM_SYSCOMMAND: |
1018 | case WM_COMMAND: |
1019 | case WM_DWMNCRENDERINGCHANGED: |
1020 | case WM_PAINT: |
1021 | return true; |
1022 | default: |
1023 | break; |
1024 | } |
1025 | return (m >= WM_MOUSEFIRST && m <= WM_MOUSELAST) |
1026 | || (m >= WM_NCMOUSEMOVE && m <= WM_NCXBUTTONDBLCLK) |
1027 | || (m >= WM_KEYFIRST && m <= WM_KEYLAST); |
1028 | } |
1029 | |
1030 | /*! |
1031 | \brief Main windows procedure registered for windows. |
1032 | |
1033 | \sa QWindowsGuiEventDispatcher |
1034 | */ |
1035 | |
1036 | bool QWindowsContext::windowsProc(HWND hwnd, UINT message, |
1037 | QtWindows::WindowsEventType et, |
1038 | WPARAM wParam, LPARAM lParam, |
1039 | LRESULT *result, |
1040 | QWindowsWindow **platformWindowPtr) |
1041 | { |
1042 | *result = 0; |
1043 | |
1044 | MSG msg; |
1045 | msg.hwnd = hwnd; // re-create MSG structure |
1046 | msg.message = message; // time and pt fields ignored |
1047 | msg.wParam = wParam; |
1048 | msg.lParam = lParam; |
1049 | msg.pt.x = msg.pt.y = 0; |
1050 | if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) { |
1051 | msg.pt.x = GET_X_LPARAM(lParam); |
1052 | msg.pt.y = GET_Y_LPARAM(lParam); |
1053 | // For non-client-area messages, these are screen coordinates (as expected |
1054 | // in the MSG structure), otherwise they are client coordinates. |
1055 | if (!(et & QtWindows::NonClientEventFlag)) { |
1056 | clientToScreen(msg.hwnd, &msg.pt); |
1057 | } |
1058 | } else { |
1059 | GetCursorPos(&msg.pt); |
1060 | } |
1061 | |
1062 | QWindowsWindow *platformWindow = findPlatformWindow(hwnd); |
1063 | *platformWindowPtr = platformWindow; |
1064 | |
1065 | // Run the native event filters. QTBUG-67095: Exclude input messages which are sent |
1066 | // by QEventDispatcherWin32::processEvents() |
1067 | if (!isInputMessage(msg.message) && filterNativeEvent(&msg, result)) |
1068 | return true; |
1069 | |
1070 | if (platformWindow && filterNativeEvent(platformWindow->window(), &msg, result)) |
1071 | return true; |
1072 | |
1073 | if (et & QtWindows::InputMethodEventFlag) { |
1074 | QWindowsInputContext *windowsInputContext = ::windowsInputContext(); |
1075 | // Disable IME assuming this is a special implementation hooking into keyboard input. |
1076 | // "Real" IME implementations should use a native event filter intercepting IME events. |
1077 | if (!windowsInputContext) { |
1078 | QWindowsInputContext::setWindowsImeEnabled(platformWindow, false); |
1079 | return false; |
1080 | } |
1081 | switch (et) { |
1082 | case QtWindows::InputMethodStartCompositionEvent: |
1083 | return windowsInputContext->startComposition(hwnd); |
1084 | case QtWindows::InputMethodCompositionEvent: |
1085 | return windowsInputContext->composition(hwnd, lParam); |
1086 | case QtWindows::InputMethodEndCompositionEvent: |
1087 | return windowsInputContext->endComposition(hwnd); |
1088 | case QtWindows::InputMethodRequest: |
1089 | return windowsInputContext->handleIME_Request(wParam, lParam, result); |
1090 | default: |
1091 | break; |
1092 | } |
1093 | } // InputMethodEventFlag |
1094 | |
1095 | switch (et) { |
1096 | case QtWindows::GestureEvent: |
1097 | if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) |
1098 | return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result); |
1099 | break; |
1100 | case QtWindows::InputMethodOpenCandidateWindowEvent: |
1101 | case QtWindows::InputMethodCloseCandidateWindowEvent: |
1102 | // TODO: Release/regrab mouse if a popup has mouse grab. |
1103 | return false; |
1104 | case QtWindows::DestroyEvent: |
1105 | if (platformWindow && !platformWindow->testFlag(QWindowsWindow::WithinDestroy)) { |
1106 | qWarning() << "External WM_DESTROY received for "<< platformWindow->window() |
1107 | << ", parent: "<< platformWindow->window()->parent() |
1108 | << ", transient parent: "<< platformWindow->window()->transientParent(); |
1109 | } |
1110 | return false; |
1111 | case QtWindows::ClipboardEvent: |
1112 | return false; |
1113 | case QtWindows::CursorEvent: // Sent to windows that do not have capture (see QTBUG-58590). |
1114 | if (QWindowsCursor::hasOverrideCursor()) { |
1115 | QWindowsCursor::enforceOverrideCursor(); |
1116 | return true; |
1117 | } |
1118 | break; |
1119 | case QtWindows::UnknownEvent: |
1120 | return false; |
1121 | case QtWindows::AccessibleObjectFromWindowRequest: |
1122 | #if QT_CONFIG(accessibility) |
1123 | return QWindowsUiaAccessibility::handleWmGetObject(hwnd, wParam, lParam, result); |
1124 | #else |
1125 | return false; |
1126 | #endif |
1127 | case QtWindows::DisplayChangedEvent: |
1128 | if (QWindowsTheme *t = QWindowsTheme::instance()) |
1129 | t->displayChanged(); |
1130 | QWindowsWindow::displayChanged(); |
1131 | return d->m_screenManager.handleDisplayChange(wParam, lParam); |
1132 | case QtWindows::SettingChangedEvent: |
1133 | QWindowsWindow::settingsChanged(); |
1134 | return d->m_screenManager.handleScreenChanges(); |
1135 | default: |
1136 | break; |
1137 | } |
1138 | |
1139 | // Before CreateWindowEx() returns, some events are sent, |
1140 | // for example WM_GETMINMAXINFO asking for size constraints for top levels. |
1141 | // Pass on to current creation context |
1142 | if (!platformWindow && !d->m_creationContext.isNull()) { |
1143 | switch (et) { |
1144 | case QtWindows::QuerySizeHints: |
1145 | d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam)); |
1146 | return true; |
1147 | case QtWindows::ResizeEvent: |
1148 | d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); |
1149 | return true; |
1150 | case QtWindows::MoveEvent: |
1151 | d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); |
1152 | return true; |
1153 | case QtWindows::NonClientCreate: |
1154 | if (shouldHaveNonClientDpiScaling(d->m_creationContext->window)) |
1155 | enableNonClientDpiScaling(msg.hwnd); |
1156 | return false; |
1157 | case QtWindows::CalculateSize: |
1158 | return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); |
1159 | case QtWindows::GeometryChangingEvent: |
1160 | return QWindowsWindow::handleGeometryChangingMessage(&msg, d->m_creationContext->window, |
1161 | d->m_creationContext->margins + d->m_creationContext->customMargins); |
1162 | default: |
1163 | break; |
1164 | } |
1165 | } |
1166 | if (platformWindow) { |
1167 | // Suppress events sent during DestroyWindow() for native children. |
1168 | if (platformWindow->testFlag(QWindowsWindow::WithinDestroy)) |
1169 | return false; |
1170 | if (QWindowsContext::verbose > 1) |
1171 | qCDebug(lcQpaEvents) << "Event window: "<< platformWindow->window(); |
1172 | } else { |
1173 | qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.", |
1174 | __FUNCTION__, message, |
1175 | QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd); |
1176 | return false; |
1177 | } |
1178 | |
1179 | switch (et) { |
1180 | case QtWindows::DeviceChangeEvent: |
1181 | if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) |
1182 | break; |
1183 | // See if there are any touch devices added |
1184 | if (wParam == DBT_DEVNODES_CHANGED) |
1185 | initTouch(); |
1186 | break; |
1187 | case QtWindows::KeyboardLayoutChangeEvent: |
1188 | if (QWindowsInputContext *wic = windowsInputContext()) |
1189 | wic->handleInputLanguageChanged(wParam, lParam); |
1190 | Q_FALLTHROUGH(); |
1191 | case QtWindows::KeyDownEvent: |
1192 | case QtWindows::KeyEvent: |
1193 | case QtWindows::InputMethodKeyEvent: |
1194 | case QtWindows::InputMethodKeyDownEvent: |
1195 | case QtWindows::AppCommandEvent: |
1196 | return sessionManagerInteractionBlocked() || d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); |
1197 | case QtWindows::MenuAboutToShowEvent: |
1198 | if (sessionManagerInteractionBlocked()) |
1199 | return true; |
1200 | if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(wParam))) |
1201 | return true; |
1202 | if (platformWindow == nullptr || platformWindow->menuBar() == nullptr) |
1203 | return false; |
1204 | return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast<HMENU>(wParam)); |
1205 | case QtWindows::MenuCommandEvent: |
1206 | if (sessionManagerInteractionBlocked()) |
1207 | return true; |
1208 | if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam))) |
1209 | return true; |
1210 | if (platformWindow == nullptr || platformWindow->menuBar() == nullptr) |
1211 | return false; |
1212 | return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam)); |
1213 | case QtWindows::MoveEvent: |
1214 | platformWindow->handleMoved(); |
1215 | return true; |
1216 | case QtWindows::ResizeEvent: |
1217 | platformWindow->handleResized(static_cast<int>(wParam)); |
1218 | return true; |
1219 | case QtWindows::QuerySizeHints: |
1220 | platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam)); |
1221 | return true;// maybe available on some SDKs revisit WM_NCCALCSIZE |
1222 | case QtWindows::CalculateSize: |
1223 | return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); |
1224 | case QtWindows::NonClientHitTest: |
1225 | return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result); |
1226 | case QtWindows::GeometryChangingEvent: |
1227 | return platformWindow->QWindowsWindow::handleGeometryChanging(&msg); |
1228 | case QtWindows::ExposeEvent: |
1229 | return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); |
1230 | case QtWindows::NonClientMouseEvent: |
1231 | if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) |
1232 | return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); |
1233 | else |
1234 | return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); |
1235 | case QtWindows::NonClientPointerEvent: |
1236 | if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) |
1237 | return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); |
1238 | break; |
1239 | case QtWindows::EnterSizeMoveEvent: |
1240 | platformWindow->setFlag(QWindowsWindow::ResizeMoveActive); |
1241 | return true; |
1242 | case QtWindows::ExitSizeMoveEvent: |
1243 | platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive); |
1244 | platformWindow->checkForScreenChanged(); |
1245 | handleExitSizeMove(platformWindow->window()); |
1246 | return true; |
1247 | case QtWindows::ScrollEvent: |
1248 | if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) |
1249 | return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result); |
1250 | break; |
1251 | case QtWindows::MouseWheelEvent: |
1252 | case QtWindows::MouseEvent: |
1253 | case QtWindows::LeaveEvent: |
1254 | { |
1255 | QWindow *window = platformWindow->window(); |
1256 | while (window && (window->flags() & Qt::WindowTransparentForInput)) |
1257 | window = window->parent(); |
1258 | if (!window) |
1259 | return false; |
1260 | if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) |
1261 | return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result); |
1262 | else |
1263 | return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result); |
1264 | } |
1265 | break; |
1266 | case QtWindows::TouchEvent: |
1267 | if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) |
1268 | return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); |
1269 | break; |
1270 | case QtWindows::PointerEvent: |
1271 | if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) |
1272 | return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); |
1273 | break; |
1274 | case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow(). |
1275 | case QtWindows::FocusOutEvent: |
1276 | handleFocusEvent(et, platformWindow); |
1277 | return true; |
1278 | case QtWindows::ShowEventOnParentRestoring: // QTBUG-40696, prevent Windows from re-showing hidden transient children (dialogs). |
1279 | if (!platformWindow->window()->isVisible()) { |
1280 | *result = 0; |
1281 | return true; |
1282 | } |
1283 | break; |
1284 | case QtWindows::HideEvent: |
1285 | platformWindow->handleHidden(); |
1286 | return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING) |
1287 | case QtWindows::CloseEvent: |
1288 | QWindowSystemInterface::handleCloseEvent(platformWindow->window()); |
1289 | return true; |
1290 | case QtWindows::ThemeChanged: { |
1291 | // Switch from Aero to Classic changes margins. |
1292 | if (QWindowsTheme *theme = QWindowsTheme::instance()) |
1293 | theme->windowsThemeChanged(platformWindow->window()); |
1294 | return true; |
1295 | } |
1296 | case QtWindows::CompositionSettingsChanged: |
1297 | platformWindow->handleCompositionSettingsChanged(); |
1298 | return true; |
1299 | case QtWindows::ActivateWindowEvent: |
1300 | if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) { |
1301 | *result = LRESULT(MA_NOACTIVATE); |
1302 | return true; |
1303 | } |
1304 | #if QT_CONFIG(tabletevent) |
1305 | if (!d->m_tabletSupport.isNull()) |
1306 | d->m_tabletSupport->notifyActivate(); |
1307 | #endif // QT_CONFIG(tabletevent) |
1308 | if (platformWindow->testFlag(QWindowsWindow::BlockedByModal)) |
1309 | if (const QWindow *modalWindow = QGuiApplication::modalWindow()) { |
1310 | QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow); |
1311 | Q_ASSERT(platformWindow); |
1312 | platformWindow->alertWindow(); |
1313 | } |
1314 | break; |
1315 | case QtWindows::MouseActivateWindowEvent: |
1316 | case QtWindows::PointerActivateWindowEvent: |
1317 | if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) { |
1318 | *result = LRESULT(MA_NOACTIVATE); |
1319 | return true; |
1320 | } |
1321 | break; |
1322 | #ifndef QT_NO_CONTEXTMENU |
1323 | case QtWindows::ContextMenu: |
1324 | return handleContextMenuEvent(platformWindow->window(), msg); |
1325 | #endif |
1326 | case QtWindows::WhatsThisEvent: { |
1327 | #ifndef QT_NO_WHATSTHIS |
1328 | QWindowSystemInterface::handleEnterWhatsThisEvent(); |
1329 | return true; |
1330 | #endif |
1331 | } break; |
1332 | case QtWindows::DpiChangedEvent: { |
1333 | // Try to apply the suggested size first and then notify ScreenChanged |
1334 | // so that the resize event sent from QGuiApplication incorporates it |
1335 | // WM_DPICHANGED is sent with a size that avoids resize loops (by |
1336 | // snapping back to the previous screen, see QTBUG-65580). |
1337 | const bool doResize = resizeOnDpiChanged(platformWindow->window()); |
1338 | if (doResize) { |
1339 | platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); |
1340 | platformWindow->updateFullFrameMargins(); |
1341 | const auto prcNewWindow = reinterpret_cast<RECT *>(lParam); |
1342 | qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" |
1343 | << platformWindow->window() << *prcNewWindow; |
1344 | SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, |
1345 | prcNewWindow->right - prcNewWindow->left, |
1346 | prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); |
1347 | platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); |
1348 | } |
1349 | platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); |
1350 | return doResize; |
1351 | } |
1352 | #if QT_CONFIG(sessionmanager) |
1353 | case QtWindows::QueryEndSessionApplicationEvent: { |
1354 | QWindowsSessionManager *sessionManager = platformSessionManager(); |
1355 | if (sessionManager->isActive()) { // bogus message from windows |
1356 | *result = sessionManager->wasCanceled() ? 0 : 1; |
1357 | return true; |
1358 | } |
1359 | |
1360 | sessionManager->setActive(true); |
1361 | sessionManager->blocksInteraction(); |
1362 | sessionManager->clearCancellation(); |
1363 | |
1364 | auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp)); |
1365 | qGuiAppPriv->commitData(); |
1366 | |
1367 | if (lParam & ENDSESSION_LOGOFF) |
1368 | fflush(nullptr); |
1369 | |
1370 | *result = sessionManager->wasCanceled() ? 0 : 1; |
1371 | return true; |
1372 | } |
1373 | case QtWindows::EndSessionApplicationEvent: { |
1374 | QWindowsSessionManager *sessionManager = platformSessionManager(); |
1375 | |
1376 | sessionManager->setActive(false); |
1377 | sessionManager->allowsInteraction(); |
1378 | const bool endsession = wParam != 0; |
1379 | |
1380 | // we receive the message for each toplevel window included internal hidden ones, |
1381 | // but the aboutToQuit signal should be emitted only once. |
1382 | auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp)); |
1383 | if (endsession && !qGuiAppPriv->aboutToQuitEmitted) { |
1384 | qGuiAppPriv->aboutToQuitEmitted = true; |
1385 | int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()"); |
1386 | qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr); |
1387 | // since the process will be killed immediately quit() has no real effect |
1388 | QGuiApplication::quit(); |
1389 | } |
1390 | return true; |
1391 | } |
1392 | #endif // !defined(QT_NO_SESSIONMANAGER) |
1393 | default: |
1394 | break; |
1395 | } |
1396 | return false; |
1397 | } |
1398 | |
1399 | /* Compress activation events. If the next focus window is already known |
1400 | * at the time the current one receives focus-out, pass that to |
1401 | * QWindowSystemInterface instead of sending 0 and ignore its consecutive |
1402 | * focus-in event. |
1403 | * This helps applications that do handling in focus-out events. */ |
1404 | void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et, |
1405 | QWindowsWindow *platformWindow) |
1406 | { |
1407 | QWindow *nextActiveWindow = nullptr; |
1408 | if (et == QtWindows::FocusInEvent) { |
1409 | QWindow *topWindow = QWindowsWindow::topLevelOf(platformWindow->window()); |
1410 | QWindow *modalWindow = nullptr; |
1411 | if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) { |
1412 | modalWindow->requestActivate(); |
1413 | return; |
1414 | } |
1415 | // QTBUG-32867: Invoking WinAPI SetParent() can cause focus-in for the |
1416 | // window which is not desired for native child widgets. |
1417 | if (platformWindow->testFlag(QWindowsWindow::WithinSetParent)) { |
1418 | QWindow *currentFocusWindow = QGuiApplication::focusWindow(); |
1419 | if (currentFocusWindow && currentFocusWindow != platformWindow->window()) { |
1420 | currentFocusWindow->requestActivate(); |
1421 | return; |
1422 | } |
1423 | } |
1424 | nextActiveWindow = platformWindow->window(); |
1425 | } else { |
1426 | // Focus out: Is the next window known and different |
1427 | // from the receiving the focus out. |
1428 | if (const HWND nextActiveHwnd = GetFocus()) |
1429 | if (QWindowsWindow *nextActivePlatformWindow = findClosestPlatformWindow(nextActiveHwnd)) |
1430 | if (nextActivePlatformWindow != platformWindow) |
1431 | nextActiveWindow = nextActivePlatformWindow->window(); |
1432 | } |
1433 | if (nextActiveWindow != d->m_lastActiveWindow) { |
1434 | d->m_lastActiveWindow = nextActiveWindow; |
1435 | QWindowSystemInterface::handleWindowActivated(nextActiveWindow); |
1436 | } |
1437 | } |
1438 | |
1439 | #ifndef QT_NO_CONTEXTMENU |
1440 | bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) |
1441 | { |
1442 | bool mouseTriggered = false; |
1443 | QPoint globalPos; |
1444 | QPoint pos; |
1445 | if (msg.lParam != int(0xffffffff)) { |
1446 | mouseTriggered = true; |
1447 | globalPos.setX(msg.pt.x); |
1448 | globalPos.setY(msg.pt.y); |
1449 | pos = QWindowsGeometryHint::mapFromGlobal(msg.hwnd, globalPos); |
1450 | |
1451 | RECT clientRect; |
1452 | if (GetClientRect(msg.hwnd, &clientRect)) { |
1453 | if (pos.x() < clientRect.left || pos.x() >= clientRect.right || |
1454 | pos.y() < clientRect.top || pos.y() >= clientRect.bottom) |
1455 | { |
1456 | // This is the case that user has right clicked in the window's caption, |
1457 | // We should call DefWindowProc() to display a default shortcut menu |
1458 | // instead of sending a Qt window system event. |
1459 | return false; |
1460 | } |
1461 | } |
1462 | } |
1463 | |
1464 | QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, |
1465 | QWindowsKeyMapper::queryKeyboardModifiers()); |
1466 | return true; |
1467 | } |
1468 | #endif |
1469 | |
1470 | void QWindowsContext::handleExitSizeMove(QWindow *window) |
1471 | { |
1472 | // Windows can be moved/resized by: |
1473 | // 1) User moving a window by dragging the title bar: Causes a sequence |
1474 | // of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP, |
1475 | // leaving the left mouse button 'pressed' |
1476 | // 2) User choosing Resize/Move from System menu and using mouse/cursor keys: |
1477 | // No mouse events are received |
1478 | // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move(): |
1479 | // Mouse is left in pressed state after press on size grip (inside window), |
1480 | // no further mouse events are received |
1481 | // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons. |
1482 | const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons(); |
1483 | const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons(); |
1484 | if (currentButtons == appButtons) |
1485 | return; |
1486 | const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); |
1487 | const QPoint globalPos = QWindowsCursor::mousePosition(); |
1488 | const QPlatformWindow *platWin = window->handle(); |
1489 | const QPoint localPos = platWin->mapFromGlobal(globalPos); |
1490 | const QEvent::Type type = platWin->geometry().contains(globalPos) |
1491 | ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease; |
1492 | for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) { |
1493 | if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) { |
1494 | QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, |
1495 | currentButtons, button, type, |
1496 | keyboardModifiers); |
1497 | } |
1498 | } |
1499 | if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) |
1500 | d->m_pointerHandler.clearEvents(); |
1501 | else |
1502 | d->m_mouseHandler.clearEvents(); |
1503 | } |
1504 | |
1505 | bool QWindowsContext::asyncExpose() const |
1506 | { |
1507 | return d->m_asyncExpose; |
1508 | } |
1509 | |
1510 | void QWindowsContext::setAsyncExpose(bool value) |
1511 | { |
1512 | d->m_asyncExpose = value; |
1513 | } |
1514 | |
1515 | QTouchDevice *QWindowsContext::touchDevice() const |
1516 | { |
1517 | return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? |
1518 | d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice(); |
1519 | } |
1520 | |
1521 | static DWORD readDwordRegistrySetting(const wchar_t *regKey, const wchar_t *subKey, DWORD defaultValue) |
1522 | { |
1523 | DWORD result = defaultValue; |
1524 | HKEY handle; |
1525 | if (RegOpenKeyEx(HKEY_CURRENT_USER, regKey, 0, KEY_READ, &handle) == ERROR_SUCCESS) { |
1526 | DWORD type; |
1527 | if (RegQueryValueEx(handle, subKey, nullptr, &type, nullptr, nullptr) == ERROR_SUCCESS |
1528 | && type == REG_DWORD) { |
1529 | DWORD value; |
1530 | DWORD size = sizeof(result); |
1531 | if (RegQueryValueEx(handle, subKey, nullptr, nullptr, reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS) |
1532 | result = value; |
1533 | } |
1534 | RegCloseKey(handle); |
1535 | } |
1536 | return result; |
1537 | } |
1538 | |
1539 | DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue) |
1540 | { |
1541 | return readDwordRegistrySetting(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced", |
1542 | subKey, defaultValue); |
1543 | } |
1544 | |
1545 | static inline bool isEmptyRect(const RECT &rect) |
1546 | { |
1547 | return rect.right - rect.left == 0 && rect.bottom - rect.top == 0; |
1548 | } |
1549 | |
1550 | static inline QMargins marginsFromRects(const RECT &frame, const RECT &client) |
1551 | { |
1552 | return QMargins(client.left - frame.left, client.top - frame.top, |
1553 | frame.right - client.right, frame.bottom - client.bottom); |
1554 | } |
1555 | |
1556 | static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n) |
1557 | { |
1558 | RECT result = {0, 0, 0, 0}; |
1559 | if (message == WM_NCCALCSIZE && wParam) |
1560 | result = reinterpret_cast<const NCCALCSIZE_PARAMS *>(lParam)->rgrc[n]; |
1561 | return result; |
1562 | } |
1563 | |
1564 | static inline bool isMinimized(HWND hwnd) |
1565 | { |
1566 | WINDOWPLACEMENT windowPlacement; |
1567 | windowPlacement.length = sizeof(WINDOWPLACEMENT); |
1568 | return GetWindowPlacement(hwnd, &windowPlacement) && windowPlacement.showCmd == SW_SHOWMINIMIZED; |
1569 | } |
1570 | |
1571 | static inline bool isTopLevel(HWND hwnd) |
1572 | { |
1573 | return (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) == 0; |
1574 | } |
1575 | |
1576 | /*! |
1577 | \brief Windows functions for actual windows. |
1578 | |
1579 | There is another one for timers, sockets, etc in |
1580 | QEventDispatcherWin32. |
1581 | |
1582 | \ingroup qt-lighthouse-win |
1583 | */ |
1584 | |
1585 | extern "C"LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) |
1586 | { |
1587 | LRESULT result; |
1588 | const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam); |
1589 | QWindowsWindow *platformWindow = nullptr; |
1590 | const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0); |
1591 | const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow); |
1592 | if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) { |
1593 | if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) { |
1594 | qCDebug(lcQpaEvents).nospace() << "EVENT: hwd="<< hwnd << ' ' << eventName |
1595 | << " msg=0x"<< Qt::hex << message << " et=0x"<< et << Qt::dec << " wp=" |
1596 | << int(wParam) << " at "<< GET_X_LPARAM(lParam) << ',' |
1597 | << GET_Y_LPARAM(lParam) << " handled="<< handled; |
1598 | } |
1599 | } |
1600 | if (!handled) |
1601 | result = DefWindowProc(hwnd, message, wParam, lParam); |
1602 | |
1603 | // Capture WM_NCCALCSIZE on top level windows and obtain the window margins by |
1604 | // subtracting the rectangles before and after processing. This will correctly |
1605 | // capture client code overriding the message and allow for per-monitor margins |
1606 | // for High DPI (QTBUG-53255, QTBUG-40578). |
1607 | if (message == WM_NCCALCSIZE && !isEmptyRect(ncCalcSizeFrame) && isTopLevel(hwnd) && !isMinimized(hwnd)) { |
1608 | const QMargins margins = |
1609 | marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); |
1610 | if (margins.left() >= 0) { |
1611 | if (platformWindow) { |
1612 | qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for"<< hwnd << margins; |
1613 | platformWindow->setFullFrameMargins(margins); |
1614 | } else { |
1615 | const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext(); |
1616 | if (!ctx.isNull()) |
1617 | ctx->margins = margins; |
1618 | } |
1619 | } |
1620 | } |
1621 | return result; |
1622 | } |
1623 | |
1624 | |
1625 | static inline QByteArray nativeEventType() { return QByteArrayLiteral("windows_generic_MSG"); } |
1626 | |
1627 | // Send to QAbstractEventDispatcher |
1628 | bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result) |
1629 | { |
1630 | QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(); |
1631 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
1632 | qintptr filterResult = 0; |
1633 | #else |
1634 | long filterResult = 0; |
1635 | #endif |
1636 | if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) { |
1637 | *result = LRESULT(filterResult); |
1638 | return true; |
1639 | } |
1640 | return false; |
1641 | } |
1642 | |
1643 | // Send to QWindowSystemInterface |
1644 | bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result) |
1645 | { |
1646 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
1647 | qintptr filterResult = 0; |
1648 | #else |
1649 | long filterResult = 0; |
1650 | #endif |
1651 | if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg, &filterResult)) { |
1652 | *result = LRESULT(filterResult); |
1653 | return true; |
1654 | } |
1655 | return false; |
1656 | } |
1657 | |
1658 | QT_END_NAMESPACE |
1659 |
Warning: That file was not part of the compilation database. It may have many parsing errors.