a0749388f9b3d18b2f43c54ca9f76bec90743094
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowscontext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qwindowscontext.h"
43 #include "qwindowswindow.h"
44 #include "qwindowskeymapper.h"
45 #include "qwindowsguieventdispatcher.h"
46 #include "qwindowsmousehandler.h"
47 #include "qtwindowsglobal.h"
48 #include "qwindowsmime.h"
49 #include "qwindowsinputcontext.h"
50 #ifndef QT_NO_ACCESSIBILITY
51 #include "accessible/qwindowsaccessibility.h"
52 #endif
53 #include "qwindowsscreen.h"
54 #include "qwindowstheme.h"
55
56 #include <QtGui/QWindow>
57 #include <qpa/qwindowsysteminterface.h>
58 #include <qpa/qplatformnativeinterface.h>
59 #include <QtGui/QGuiApplication>
60
61 #include <QtCore/QSet>
62 #include <QtCore/QHash>
63 #include <QtCore/QStringList>
64 #include <QtCore/QDebug>
65 #include <QtCore/QSysInfo>
66 #include <QtCore/QScopedArrayPointer>
67 #include <QtCore/private/qsystemlibrary_p.h>
68
69 #include <stdlib.h>
70 #include <stdio.h>
71 #include <windowsx.h>
72
73 QT_BEGIN_NAMESPACE
74
75 // Verbosity of components
76 int QWindowsContext::verboseIntegration = 0;
77 int QWindowsContext::verboseWindows = 0;
78 int QWindowsContext::verboseEvents = 0;
79 int QWindowsContext::verboseBackingStore = 0;
80 int QWindowsContext::verboseFonts = 0;
81 int QWindowsContext::verboseGL = 0;
82 int QWindowsContext::verboseOLE = 0;
83 int QWindowsContext::verboseInputMethods = 0;
84 int QWindowsContext::verboseDialogs = 0;
85 int QWindowsContext::verboseTheming = 0;
86
87 // Get verbosity of components from "foo:2,bar:3"
88 static inline int componentVerbose(const char *v, const char *keyWord)
89 {
90     if (const char *k = strstr(v, keyWord)) {
91         k += qstrlen(keyWord);
92         if (*k == ':') {
93             ++k;
94             if (isdigit(*k))
95                 return *k - '0';
96         }
97     }
98     return 0;
99 }
100
101 static inline bool hasTouchSupport(QSysInfo::WinVersion wv)
102 {
103     enum { QT_SM_DIGITIZER = 94, QT_NID_INTEGRATED_TOUCH = 0x1,
104            QT_NID_EXTERNAL_TOUCH = 0x02, QT_NID_MULTI_INPUT = 0x40 };
105
106     return wv < QSysInfo::WV_WINDOWS7 ? false :
107            (GetSystemMetrics(QT_SM_DIGITIZER) & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT)) != 0;
108 }
109
110 #if !defined(LANG_SYRIAC)
111 #    define LANG_SYRIAC 0x5a
112 #endif
113
114 static inline bool useRTL_Extensions(QSysInfo::WinVersion ver)
115 {
116     // This is SDK dependent on CE so out of scope for now
117     if (QSysInfo::windowsVersion() & QSysInfo::WV_CE_based)
118         return false;
119     if ((ver & QSysInfo::WV_NT_based) && (ver >= QSysInfo::WV_VISTA)) {
120         // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
121         // Vista, check the Keyboard Layouts for enabling RTL.
122         if (const UINT nLayouts = GetKeyboardLayoutList(0, 0)) {
123             QScopedArrayPointer<HKL> lpList(new HKL[nLayouts]);
124             GetKeyboardLayoutList(nLayouts, lpList.data());
125             for (UINT i = 0; i < nLayouts; ++i) {
126                 switch (PRIMARYLANGID((quintptr)lpList[i])) {
127                 case LANG_ARABIC:
128                 case LANG_HEBREW:
129                 case LANG_FARSI:
130                 case LANG_SYRIAC:
131                     return true;
132                 default:
133                     break;
134                 }
135             }
136         }
137         return false;
138     } // NT/Vista
139 #ifndef Q_OS_WINCE
140     // Pre-NT: figure out whether a RTL language is installed
141     return IsValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED)
142                             || IsValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED)
143                             || IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
144                             || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
145                             || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
146                             || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED);
147 #else
148     return false;
149 #endif
150 }
151
152 /*!
153     \class QWindowsUser32DLL
154     \brief Struct that contains dynamically resolved symbols of User32.dll.
155
156     The stub libraries shipped with the MinGW compiler miss some of the
157     functions. They need to be retrieved dynamically.
158
159     In addition, touch-related functions are available only from Windows onwards.
160     These need to resolved dynamically for Q_CC_MSVC as well.
161
162     \sa QWindowsShell32DLL
163
164     \internal
165     \ingroup qt-lighthouse-win
166 */
167
168 #ifndef Q_OS_WINCE
169
170 QWindowsUser32DLL::QWindowsUser32DLL() :
171     setLayeredWindowAttributes(0), updateLayeredWindow(0),
172     updateLayeredWindowIndirect(0),
173     isHungAppWindow(0),
174     registerTouchWindow(0), getTouchInputInfo(0), closeTouchInputHandle(0)
175 {
176 }
177
178 void QWindowsUser32DLL::init()
179 {
180     QSystemLibrary library(QStringLiteral("user32"));
181     // MinGW (g++ 3.4.5) accepts only C casts.
182     setLayeredWindowAttributes = (SetLayeredWindowAttributes)(library.resolve("SetLayeredWindowAttributes"));
183     updateLayeredWindow = (UpdateLayeredWindow)(library.resolve("UpdateLayeredWindow"));
184     if (!setLayeredWindowAttributes || !updateLayeredWindow)
185         qFatal("This version of Windows is not supported (User32.dll is missing the symbols 'SetLayeredWindowAttributes', 'UpdateLayeredWindow').");
186
187     updateLayeredWindowIndirect = (UpdateLayeredWindowIndirect)(library.resolve("UpdateLayeredWindowIndirect"));
188     isHungAppWindow = (IsHungAppWindow)library.resolve("IsHungAppWindow");
189 }
190
191 bool QWindowsUser32DLL::initTouch()
192 {
193     QSystemLibrary library(QStringLiteral("user32"));
194     registerTouchWindow = (RegisterTouchWindow)(library.resolve("RegisterTouchWindow"));
195     getTouchInputInfo = (GetTouchInputInfo)(library.resolve("GetTouchInputInfo"));
196     closeTouchInputHandle = (CloseTouchInputHandle)(library.resolve("CloseTouchInputHandle"));
197     return registerTouchWindow && getTouchInputInfo && getTouchInputInfo;
198 }
199
200 /*!
201     \class QWindowsShell32DLL
202     \brief Struct that contains dynamically resolved symbols of Shell32.dll.
203
204     The stub libraries shipped with the MinGW compiler miss some of the
205     functions. They need to be retrieved dynamically.
206
207     \sa QWindowsUser32DLL
208
209     \internal
210     \ingroup qt-lighthouse-win
211 */
212
213 QWindowsShell32DLL::QWindowsShell32DLL()
214     : sHCreateItemFromParsingName(0)
215     , sHGetStockIconInfo(0)
216 {
217 }
218
219 void QWindowsShell32DLL::init()
220 {
221     QSystemLibrary library(QStringLiteral("shell32"));
222     sHCreateItemFromParsingName = (SHCreateItemFromParsingName)(library.resolve("SHCreateItemFromParsingName"));
223     sHGetStockIconInfo = (SHGetStockIconInfo)library.resolve("SHGetStockIconInfo");
224 }
225
226 QWindowsUser32DLL QWindowsContext::user32dll;
227 QWindowsShell32DLL QWindowsContext::shell32dll;
228
229 #endif // !Q_OS_WINCE
230
231 QWindowsContext *QWindowsContext::m_instance = 0;
232
233 /*!
234     \class QWindowsContext
235     \brief Singleton container for all relevant information.
236
237     Holds state information formerly stored in \c qapplication_win.cpp.
238
239     \internal
240     \ingroup qt-lighthouse-win
241 */
242
243 typedef QHash<HWND, QWindowsWindow *> HandleBaseWindowHash;
244
245 struct QWindowsContextPrivate {
246
247     QWindowsContextPrivate();
248
249     unsigned m_systemInfo;
250     QSet<QString> m_registeredWindowClassNames;
251     HandleBaseWindowHash m_windows;
252     HDC m_displayContext;
253     const int m_defaultDPI;
254     QWindowsKeyMapper m_keyMapper;
255     QWindowsMouseHandler m_mouseHandler;
256     QWindowsMimeConverter m_mimeConverter;
257     QWindowsScreenManager m_screenManager;
258     QSharedPointer<QWindowCreationContext> m_creationContext;
259     const HRESULT m_oleInitializeResult;
260     const QByteArray m_eventType;
261     QWindow *m_lastActiveWindow;
262     bool m_asyncExpose;
263 };
264
265 QWindowsContextPrivate::QWindowsContextPrivate() :
266     m_systemInfo(0),
267     m_displayContext(GetDC(0)),
268     m_defaultDPI(GetDeviceCaps(m_displayContext,LOGPIXELSY)),
269     m_oleInitializeResult(OleInitialize(NULL)),
270     m_eventType(QByteArrayLiteral("windows_generic_MSG")),
271     m_lastActiveWindow(0), m_asyncExpose(0)
272 {
273 #ifndef Q_OS_WINCE
274     QWindowsContext::user32dll.init();
275     QWindowsContext::shell32dll.init();
276 #endif
277
278     const QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
279 #ifndef Q_OS_WINCE
280     if (hasTouchSupport(ver) && QWindowsContext::user32dll.initTouch())
281         m_systemInfo |= QWindowsContext::SI_SupportsTouch;
282 #endif
283
284     if (useRTL_Extensions(ver)) {
285         m_systemInfo |= QWindowsContext::SI_RTL_Extensions;
286         m_keyMapper.setUseRTLExtensions(true);
287     }
288 }
289
290 QWindowsContext::QWindowsContext() :
291     d(new QWindowsContextPrivate)
292 {
293 #ifdef Q_CC_MSVC
294 #    pragma warning( disable : 4996 )
295 #endif
296     m_instance = this;
297     const QByteArray bv = qgetenv("QT_QPA_VERBOSE");
298     if (!bv.isEmpty()) {
299         const char *v = bv.data();
300         QWindowsContext::verboseIntegration = componentVerbose(v, "integration");
301         QWindowsContext::verboseWindows = componentVerbose(v, "windows");
302         QWindowsContext::verboseEvents = componentVerbose(v, "events");
303         QWindowsContext::verboseBackingStore = componentVerbose(v, "backingstore");
304         QWindowsContext::verboseFonts = componentVerbose(v, "fonts");
305         QWindowsContext::verboseGL = componentVerbose(v, "gl");
306         QWindowsContext::verboseOLE = componentVerbose(v, "ole");
307         QWindowsContext::verboseInputMethods = componentVerbose(v, "im");
308         QWindowsContext::verboseDialogs = componentVerbose(v, "dialogs");
309         QWindowsContext::verboseTheming = componentVerbose(v, "theming");
310     }
311 }
312
313 QWindowsContext::~QWindowsContext()
314 {
315     unregisterWindowClasses();
316     if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE)
317         OleUninitialize();
318
319     d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
320     m_instance = 0;
321 }
322
323 QWindowsContext *QWindowsContext::instance()
324 {
325     return m_instance;
326 }
327
328 unsigned QWindowsContext::systemInfo() const
329 {
330     return d->m_systemInfo;
331 }
332
333 bool QWindowsContext::useRTLExtensions() const
334 {
335     return d->m_keyMapper.useRTLExtensions();
336 }
337
338 QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const
339 {
340     return d->m_keyMapper.possibleKeys(e);
341 }
342
343 void QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
344 {
345     d->m_creationContext = ctx;
346 }
347
348 int QWindowsContext::defaultDPI() const
349 {
350     return d->m_defaultDPI;
351 }
352
353 HDC QWindowsContext::displayContext() const
354 {
355     return d->m_displayContext;
356 }
357
358 QWindow *QWindowsContext::keyGrabber() const
359 {
360     return d->m_keyMapper.keyGrabber();
361 }
362
363 void QWindowsContext::setKeyGrabber(QWindow *w)
364 {
365     d->m_keyMapper.setKeyGrabber(w);
366 }
367
368 // Window class registering code (from qapplication_win.cpp)
369 // If 0 is passed as the widget pointer, register a window class
370 // for QWidget as default. This is used in QGLTemporaryContext
371 // during GL initialization, where we don't want to use temporary
372 // QWidgets or QGLWidgets, neither do we want to have separate code
373 // to register window classes.
374
375 QString QWindowsContext::registerWindowClass(const QWindow *w, bool isGL)
376 {
377     const Qt::WindowFlags flags = w ? w->windowFlags() : (Qt::WindowFlags)0;
378     const Qt::WindowFlags type = flags & Qt::WindowType_Mask;
379
380     uint style = 0;
381     bool icon = false;
382     QString cname = QStringLiteral("Qt5");
383     if (w && isGL) {
384         cname += QStringLiteral("QGLWindow");
385         style = CS_DBLCLKS|CS_OWNDC;
386         icon  = true;
387     } else if (w && (flags & Qt::MSWindowsOwnDC)) {
388         cname += QStringLiteral("QWindowOwnDC");
389         style = CS_DBLCLKS|CS_OWNDC;
390         icon  = true;
391     } else if (w && (type == Qt::Tool || type == Qt::ToolTip)) {
392         style = CS_DBLCLKS;
393         if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) {
394             if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
395                 && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))) {
396                 style |= CS_DROPSHADOW;
397             }
398             cname += QStringLiteral("QToolTip");
399         } else {
400             cname += QStringLiteral("QTool");
401         }
402         style |= CS_SAVEBITS;
403         icon = false;
404     } else if (w && (type == Qt::Popup)) {
405         cname += QStringLiteral("QPopup");
406         style = CS_DBLCLKS|CS_SAVEBITS;
407         if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
408             && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)))
409             style |= CS_DROPSHADOW;
410         icon = false;
411     } else {
412         cname += QStringLiteral("QWindow");
413         style = CS_DBLCLKS;
414         icon  = true;
415     }
416
417     HBRUSH brush = 0;
418     if (w && !isGL)
419         brush = GetSysColorBrush(COLOR_WINDOW);
420     return registerWindowClass(cname, qWindowsWndProc, style, brush, icon);
421 }
422
423 QString QWindowsContext::registerWindowClass(QString cname,
424                                              WNDPROC proc,
425                                              unsigned style,
426                                              HBRUSH brush,
427                                              bool icon)
428 {
429     // since multiple Qt versions can be used in one process
430     // each one has to have window class names with a unique name
431     // The first instance gets the unmodified name; if the class
432     // has already been registered by another instance of Qt then
433     // add an instance-specific ID, the address of the window proc.
434     static int classExists = -1;
435
436     const HINSTANCE appInstance = (HINSTANCE)GetModuleHandle(0);
437     if (classExists == -1) {
438         WNDCLASS wcinfo;
439         classExists = GetClassInfo(appInstance, (wchar_t*)cname.utf16(), &wcinfo);
440         classExists = classExists && wcinfo.lpfnWndProc != proc;
441     }
442
443     if (classExists)
444         cname += QString::number((quintptr)proc);
445
446     if (d->m_registeredWindowClassNames.contains(cname))        // already registered in our list
447         return cname;
448
449 #ifndef Q_OS_WINCE
450     WNDCLASSEX wc;
451     wc.cbSize       = sizeof(WNDCLASSEX);
452 #else
453     WNDCLASS wc;
454 #endif
455     wc.style        = style;
456     wc.lpfnWndProc  = proc;
457     wc.cbClsExtra   = 0;
458     wc.cbWndExtra   = 0;
459     wc.hInstance    = appInstance;
460     wc.hCursor      = 0;
461 #ifndef Q_OS_WINCE
462     wc.hbrBackground = brush;
463     if (icon) {
464         wc.hIcon = (HICON)LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
465         if (wc.hIcon) {
466             int sw = GetSystemMetrics(SM_CXSMICON);
467             int sh = GetSystemMetrics(SM_CYSMICON);
468             wc.hIconSm = (HICON)LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0);
469         } else {
470             wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
471             wc.hIconSm = 0;
472         }
473     } else {
474         wc.hIcon    = 0;
475         wc.hIconSm  = 0;
476     }
477 #else
478     if (icon) {
479         wc.hIcon = (HICON)LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
480     } else {
481         wc.hIcon    = 0;
482     }
483 #endif
484
485     wc.lpszMenuName  = 0;
486     wc.lpszClassName = (wchar_t*)cname.utf16();
487 #ifndef Q_OS_WINCE
488     ATOM atom = RegisterClassEx(&wc);
489 #else
490     ATOM atom = RegisterClass(&wc);
491 #endif
492
493     if (!atom)
494         qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.",
495                       qPrintable(cname));
496
497     d->m_registeredWindowClassNames.insert(cname);
498     if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows)
499         qDebug().nospace() << __FUNCTION__ << ' ' << cname
500                  << " style=0x" << QString::number(style, 16)
501                  << " brush=" << brush << " icon=" << icon << " atom=" << atom;
502     return cname;
503 }
504
505 void QWindowsContext::unregisterWindowClasses()
506 {
507     const HINSTANCE appInstance = (HINSTANCE)GetModuleHandle(0);
508
509     foreach (const QString &name,  d->m_registeredWindowClassNames) {
510         if (QWindowsContext::verboseIntegration)
511             qDebug() << __FUNCTION__ << name;
512         UnregisterClass((wchar_t*)name.utf16(), appInstance);
513     }
514     d->m_registeredWindowClassNames.clear();
515 }
516
517 int QWindowsContext::screenDepth() const
518 {
519     return GetDeviceCaps(d->m_displayContext, BITSPIXEL);
520 }
521
522 QString QWindowsContext::windowsErrorMessage(unsigned long errorCode)
523 {
524     QString rc = QString::fromLatin1("#%1: ").arg(errorCode);
525     ushort *lpMsgBuf;
526
527     const int len = FormatMessage(
528             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
529             NULL, errorCode, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
530     if (len) {
531         rc = QString::fromUtf16(lpMsgBuf, len);
532         LocalFree(lpMsgBuf);
533     } else {
534         rc += QString::fromLatin1("<unknown error>");
535     }
536     return rc;
537 }
538
539 void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w)
540 {
541     d->m_windows.insert(hwnd, w);
542 }
543
544 void QWindowsContext::removeWindow(HWND hwnd)
545 {
546     const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd);
547     if (it != d->m_windows.end()) {
548         if (d->m_keyMapper.keyGrabber() == it.value()->window())
549             d->m_keyMapper.setKeyGrabber(0);
550         d->m_windows.erase(it);
551     }
552 }
553
554 QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const
555 {
556     return d->m_windows.value(hwnd);
557 }
558
559 QWindowsWindow *QWindowsContext::findClosestPlatformWindow(HWND hwnd) const
560 {
561     QWindowsWindow *window = d->m_windows.value(hwnd);
562
563     // Requested hwnd may also be a child of a platform window in case of embedded native windows.
564     // Find the closest parent that has a platform window.
565     if (!window) {
566         for (HWND w = hwnd; w; w = GetParent(w)) {
567             window = d->m_windows.value(w);
568             if (window)
569                 break;
570         }
571     }
572
573     return window;
574 }
575
576 QWindow *QWindowsContext::findWindow(HWND hwnd) const
577 {
578     if (const QWindowsWindow *bw = findPlatformWindow(hwnd))
579             return bw->window();
580     return 0;
581 }
582
583 QWindow *QWindowsContext::windowUnderMouse() const
584 {
585     return d->m_mouseHandler.windowUnderMouse();
586 }
587
588 /*!
589     \brief Find a child window at a screen point.
590
591     Deep search for a QWindow at global point, skipping non-owned
592     windows (accessibility?). Implemented using ChildWindowFromPointEx()
593     instead of (historically used) WindowFromPoint() to get a well-defined
594     behaviour for hidden/transparent windows.
595
596     \a cwex_flags are flags of ChildWindowFromPointEx().
597     \a parent is the parent window, pass GetDesktopWindow() for top levels.
598 */
599
600 QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent,
601                                                           const QPoint &screenPointIn,
602                                                           unsigned cwex_flags) const
603 {
604     QWindowsWindow *result = 0;
605     const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
606     while (true) {
607         POINT point = screenPoint;
608         ScreenToClient(parent, &point);
609         // Returns parent if inside & none matched.
610         const HWND child = ChildWindowFromPointEx(parent, point, cwex_flags);
611         if (child && child != parent) {
612             if (QWindowsWindow *window = findPlatformWindow(child))
613                 result = window;
614             parent = child;
615         } else {
616             break;
617         }
618     }
619     return result;
620 }
621
622 QWindowsMimeConverter &QWindowsContext::mimeConverter() const
623 {
624     return d->m_mimeConverter;
625 }
626
627 QWindowsScreenManager &QWindowsContext::screenManager()
628 {
629     return d->m_screenManager;
630 }
631
632 /*!
633     \brief Convenience to create a non-visible, message-only dummy
634     window for example used as clipboard watcher or for GL.
635 */
636
637 HWND QWindowsContext::createDummyWindow(const QString &classNameIn,
638                                         const wchar_t *windowName,
639                                         WNDPROC wndProc, DWORD style)
640 {
641     if (!wndProc)
642         wndProc = DefWindowProc;
643     QString className = registerWindowClass(classNameIn, wndProc);
644     return CreateWindowEx(0, (wchar_t*)className.utf16(),
645                           windowName, style,
646                           CW_USEDEFAULT, CW_USEDEFAULT,
647                           CW_USEDEFAULT, CW_USEDEFAULT,
648                           HWND_MESSAGE, NULL, (HINSTANCE)GetModuleHandle(0), NULL);
649 }
650
651 /*!
652     \brief Common COM error strings.
653 */
654
655 QByteArray QWindowsContext::comErrorString(HRESULT hr)
656 {
657     switch (hr) {
658     case S_OK:
659         return QByteArrayLiteral("S_OK");
660     case S_FALSE:
661         return QByteArrayLiteral("S_FALSE");
662     case E_UNEXPECTED:
663         return QByteArrayLiteral("E_UNEXPECTED");
664     case CO_E_ALREADYINITIALIZED:
665         return QByteArrayLiteral("CO_E_ALREADYINITIALIZED");
666     case CO_E_NOTINITIALIZED:
667         return QByteArrayLiteral("CO_E_NOTINITIALIZED");
668     case RPC_E_CHANGED_MODE:
669         return QByteArrayLiteral("RPC_E_CHANGED_MODE");
670     case OLE_E_WRONGCOMPOBJ:
671         return QByteArrayLiteral("OLE_E_WRONGCOMPOBJ");
672     case CO_E_NOT_SUPPORTED:
673         return QByteArrayLiteral("CO_E_NOT_SUPPORTED");
674     case E_NOTIMPL:
675         return QByteArrayLiteral("E_NOTIMPL");
676     case E_INVALIDARG:
677         return QByteArrayLiteral("E_INVALIDARG");
678     case E_NOINTERFACE:
679         return QByteArrayLiteral("E_NOINTERFACE");
680     case E_POINTER:
681         return QByteArrayLiteral("E_POINTER");
682     case E_HANDLE:
683         return QByteArrayLiteral("E_HANDLE");
684     case E_ABORT:
685         return QByteArrayLiteral("E_ABORT");
686     case E_FAIL:
687         return QByteArrayLiteral("E_FAIL");
688     case RPC_E_WRONG_THREAD:
689         return QByteArrayLiteral("RPC_E_WRONG_THREAD");
690     case RPC_E_THREAD_NOT_INIT:
691         return QByteArrayLiteral("RPC_E_THREAD_NOT_INIT");
692     default:
693         break;
694     }
695     return "Unknown error 0x" + QByteArray::number(quint64(hr), 16);
696 }
697
698 /*!
699      \brief Main windows procedure registered for windows.
700
701      \sa QWindowsGuiEventDispatcher
702 */
703
704 bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
705                                   QtWindows::WindowsEventType et,
706                                   WPARAM wParam, LPARAM lParam, LRESULT *result)
707 {
708     *result = 0;
709
710     MSG msg;
711     msg.hwnd = hwnd;         // re-create MSG structure
712     msg.message = message;   // time and pt fields ignored
713     msg.wParam = wParam;
714     msg.lParam = lParam;
715     msg.pt.x = GET_X_LPARAM(lParam);
716     msg.pt.y = GET_Y_LPARAM(lParam);
717
718     // Run the native event filters.
719     long filterResult = 0;
720     QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
721     if (dispatcher && dispatcher->filterNativeEvent(d->m_eventType, &msg, &filterResult)) {
722         *result = LRESULT(filterResult);
723         return true;
724     }
725
726     QWindowsWindow *platformWindow = findPlatformWindow(hwnd);
727     if (platformWindow) {
728         filterResult = 0;
729         if (QWindowSystemInterface::handleNativeEvent(platformWindow->window(), d->m_eventType, &msg, &filterResult)) {
730             *result = LRESULT(filterResult);
731             return true;
732         }
733     }
734
735     switch (et) {
736     case QtWindows::InputMethodStartCompositionEvent:
737         return QWindowsInputContext::instance()->startComposition(hwnd);
738     case QtWindows::InputMethodCompositionEvent:
739         return QWindowsInputContext::instance()->composition(hwnd, lParam);
740     case QtWindows::InputMethodEndCompositionEvent:
741         return QWindowsInputContext::instance()->endComposition(hwnd);
742     case QtWindows::InputMethodRequest:
743         return QWindowsInputContext::instance()->handleIME_Request(wParam, lParam, result);
744     case QtWindows::InputMethodOpenCandidateWindowEvent:
745     case QtWindows::InputMethodCloseCandidateWindowEvent:
746         // TODO: Release/regrab mouse if a popup has mouse grab.
747         return false;
748     case QtWindows::ClipboardEvent:
749     case QtWindows::DestroyEvent:
750
751     case QtWindows::UnknownEvent:
752         return false;
753     case QtWindows::AccessibleObjectFromWindowRequest:
754 #ifndef QT_NO_ACCESSIBILITY
755         return QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(hwnd, wParam, lParam, result);
756 #else
757         return false;
758 #endif
759     case QtWindows::DisplayChangedEvent:
760         return d->m_screenManager.handleDisplayChange(wParam, lParam);
761     case QtWindows::SettingChangedEvent:
762         return d->m_screenManager.handleScreenChanges();
763     default:
764         break;
765     }
766
767     // Before CreateWindowEx() returns, some events are sent,
768     // for example WM_GETMINMAXINFO asking for size constraints for top levels.
769     // Pass on to current creation context
770     if (!platformWindow && !d->m_creationContext.isNull()) {
771         switch (et) {
772 #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO
773         case QtWindows::QuerySizeHints:
774             d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
775             return true;
776 #endif
777         case QtWindows::ResizeEvent:
778             d->m_creationContext->obtainedGeometry.setSize(QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
779             return true;
780         case QtWindows::MoveEvent:
781             d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
782             return true;
783         case QtWindows::CalculateSize:
784             return false;
785         default:
786             break;
787         }
788     }
789     if (platformWindow) {
790         // Suppress events sent during DestroyWindow() for native children.
791         if (platformWindow->testFlag(QWindowsWindow::WithinDestroy))
792             return false;
793         if (QWindowsContext::verboseEvents > 1)
794             qDebug().nospace() << "Event window: " << platformWindow->window();
795     } else {
796         qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.",
797                  __FUNCTION__, message,
798                  QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd);
799         return false;
800     }
801
802     switch (et) {
803     case QtWindows::KeyDownEvent:
804     case QtWindows::KeyEvent:
805     case QtWindows::InputMethodKeyEvent:
806     case QtWindows::InputMethodKeyDownEvent:
807         return d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
808     case QtWindows::MoveEvent:
809         platformWindow->handleMoved();
810         return true;
811     case QtWindows::ResizeEvent:
812         platformWindow->handleResized((int)wParam);
813         return true;
814 #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO
815     case QtWindows::QuerySizeHints:
816         platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam));
817         return true;// maybe available on some SDKs revisit WM_NCCALCSIZE
818     case QtWindows::CalculateSize:
819         // NCCALCSIZE_PARAMS structure if wParam==TRUE
820         if (wParam && QWindowsContext::verboseWindows) {
821             const NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam);
822             qDebug() << platformWindow->window() << *ncp;
823         }
824         break;
825 #endif
826     case QtWindows::ExposeEvent:
827         return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
828     case QtWindows::NonClientMouseEvent:
829         if (platformWindow->frameStrutEventsEnabled())
830             return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
831         break;
832 /* the mouse tracking on windows already handles the reset of the cursor
833  * and does not like somebody else handling it.
834  * on WINCE its necessary to handle this event to get the correct cursor
835  */
836 #ifdef Q_OS_WINCE
837     case QtWindows::CursorEvent:
838         {
839             QWindowsWindow::baseWindowOf(platformWindow->window())->applyCursor();
840             return true;
841         }
842 #endif
843     case QtWindows::MouseWheelEvent:
844     case QtWindows::MouseEvent:
845     case QtWindows::LeaveEvent:
846         return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
847     case QtWindows::TouchEvent:
848         return d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
849     case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
850     case QtWindows::FocusOutEvent:
851         handleFocusEvent(et, platformWindow);
852         return true;
853     case QtWindows::ShowEvent:
854         platformWindow->handleShown();
855         return true;
856     case QtWindows::HideEvent:
857         platformWindow->handleHidden();
858         return true;
859     case QtWindows::CloseEvent:
860         QWindowSystemInterface::handleCloseEvent(platformWindow->window());
861         return true;
862     case QtWindows::ThemeChanged: // ### fixme: Compress these events?
863         if (QWindowsTheme *theme = QWindowsTheme::instance())
864             theme->windowsThemeChanged(platformWindow->window());
865         return true;
866 #ifndef Q_OS_WINCE
867     case QtWindows::ActivateWindowEvent:
868         if (platformWindow->testFlag(QWindowsWindow::BlockedByModal))
869             if (const QWindow *modalWindow = QGuiApplication::modalWindow())
870                 QWindowsWindow::baseWindowOf(modalWindow)->alertWindow();
871         break;
872 #endif
873 #ifndef QT_NO_CONTEXTMENU
874     case QtWindows::ContextMenu:
875         handleContextMenuEvent(platformWindow->window(), msg);
876         return true;
877 #endif
878     default:
879         break;
880     }
881     return false;
882 }
883
884 /* Compress activation events. If the next focus window is already known
885  * at the time the current one receives focus-out, pass that to
886  * QWindowSystemInterface instead of sending 0 and ignore its consecutive
887  * focus-in event.
888  * This helps applications that do handling in focus-out events. */
889 void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
890                                        QWindowsWindow *platformWindow)
891 {
892     QWindow *nextActiveWindow = 0;
893     if (et == QtWindows::FocusInEvent) {
894         nextActiveWindow = platformWindow->window();
895     } else {
896         // Focus out: Is the next window known and different
897         // from the receiving the focus out.
898         if (const HWND nextActiveHwnd = GetFocus())
899             if (QWindowsWindow *nextActivePlatformWindow = findClosestPlatformWindow(nextActiveHwnd))
900                 if (nextActivePlatformWindow != platformWindow)
901                     nextActiveWindow = nextActivePlatformWindow->window();
902     }
903     if (nextActiveWindow != d->m_lastActiveWindow) {
904          d->m_lastActiveWindow = nextActiveWindow;
905          QWindowSystemInterface::handleWindowActivated(nextActiveWindow);
906     }
907 }
908
909 #ifndef QT_NO_CONTEXTMENU
910 void QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg)
911 {
912     bool mouseTriggered = false;
913     QPoint globalPos;
914     QPoint pos;
915     if (msg.lParam != (int)0xffffffff) {
916         mouseTriggered = true;
917         globalPos.setX(msg.pt.x);
918         globalPos.setY(msg.pt.y);
919         pos = QWindowsGeometryHint::mapFromGlobal(msg.hwnd, globalPos);
920     }
921
922     QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos,
923                                                    QWindowsKeyMapper::queryKeyboardModifiers());
924 }
925 #endif
926
927 bool QWindowsContext::asyncExpose() const
928 {
929     return d->m_asyncExpose;
930 }
931
932 void QWindowsContext::setAsyncExpose(bool value)
933 {
934     d->m_asyncExpose = value;
935 }
936
937 /*!
938     \brief Windows functions for actual windows.
939
940     There is another one for timers, sockets, etc in
941     QEventDispatcherWin32.
942
943     \ingroup qt-lighthouse-win
944 */
945
946 extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
947 {
948     LRESULT result;
949     const QtWindows::WindowsEventType et = windowsEventType(message, wParam);
950     const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result);
951     if (QWindowsContext::verboseEvents > 1)
952         if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message))
953             qDebug("EVENT: hwd=%p %s msg=0x%x et=0x%x wp=%d at %d,%d handled=%d",
954                    hwnd, eventName, message, et, int(wParam),
955                    GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), handled);
956     if (!handled)
957         result = DefWindowProc(hwnd, message, wParam, lParam);
958     return result;
959 }
960
961 QT_END_NAMESPACE