/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "nativewindowdump.h" #include "qwindowdump.h" #include #include #include #include #include #include #ifndef WS_EX_NOREDIRECTIONBITMAP # define WS_EX_NOREDIRECTIONBITMAP 0x00200000L #endif namespace QtDiag { struct DumpContext { DumpContext() : indentation(0), parent(0) {} int indentation; HWND parent; QSharedPointer stream; }; #define debugWinStyle(str, style, styleConstant) \ if (style & styleConstant) \ str << ' ' << #styleConstant; static QTextStream &operator<<(QTextStream &str, const QPoint &p) { str << p.x() << ", " << p.y(); return str; } static QTextStream &operator<<(QTextStream &str, const QSize &s) { str << s.width() << 'x' << s.height(); return str; } static QTextStream &operator<<(QTextStream &str, const QRect &rect) { str << rect.size() << forcesign << rect.x() << rect.y() << noforcesign; return str; } static inline QSize qsizeFromRECT(const RECT &rect) { return QSize(rect.right -rect.left, rect.bottom - rect.top); } static inline QRect qrectFromRECT(const RECT &rect) { return QRect(QPoint(rect.left, rect.top), qsizeFromRECT(rect)); } static QRect getFrameGeometry(HWND hwnd) { RECT rect; return GetWindowRect(hwnd, &rect) ? qrectFromRECT(rect) : QRect(); } static QPoint getClientAreaScreenPos(HWND hwnd) { POINT clientPos{0, 0}; return ClientToScreen(hwnd, &clientPos) ? QPoint(clientPos.x, clientPos.y) : QPoint(); } static QRect getClientAreaGeometry(HWND hwnd) { RECT clientRect; return GetClientRect(hwnd, &clientRect) ? QRect(getClientAreaScreenPos(hwnd), qsizeFromRECT(clientRect)) : QRect(); } static bool isTopLevel(HWND hwnd) { auto parent = GetParent(hwnd); return !parent || parent == GetDesktopWindow(); } static void formatNativeWindow(HWND hwnd, QTextStream &str) { str << hex << showbase << quintptr(hwnd) << noshowbase << dec; const bool topLevel = isTopLevel(hwnd); if (topLevel) str << " [top]"; const auto frameGeometry = getFrameGeometry(hwnd); const auto clientGeometry = getClientAreaGeometry(hwnd); str << ' ' << frameGeometry; if (!topLevel) str << " local: " << (clientGeometry.topLeft() - getClientAreaScreenPos(GetParent(hwnd))); if (clientGeometry != frameGeometry) { str << " client: " << clientGeometry << " frame: " << (clientGeometry.topLeft() - frameGeometry.topLeft()); } if (IsWindowVisible(hwnd)) str << " [visible]"; wchar_t buf[512]; if (GetWindowText(hwnd, buf, sizeof(buf)/sizeof(buf[0])) && buf[0]) str << " title=\"" << QString::fromWCharArray(buf) << "\"/"; else str << ' '; if (GetClassName(hwnd, buf, sizeof(buf)/sizeof(buf[0]))) str << '"' << QString::fromWCharArray(buf) << '"'; str << hex << showbase; if (const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE)) { str << " style=" << style; debugWinStyle(str, style, WS_OVERLAPPED) debugWinStyle(str, style, WS_POPUP) debugWinStyle(str, style, WS_MINIMIZE) debugWinStyle(str, style, WS_CHILD) debugWinStyle(str, style, WS_VISIBLE) debugWinStyle(str, style, WS_DISABLED) debugWinStyle(str, style, WS_CLIPSIBLINGS) debugWinStyle(str, style, WS_CLIPCHILDREN) debugWinStyle(str, style, WS_MAXIMIZE) debugWinStyle(str, style, WS_CAPTION) debugWinStyle(str, style, WS_BORDER) debugWinStyle(str, style, WS_DLGFRAME) debugWinStyle(str, style, WS_VSCROLL) debugWinStyle(str, style, WS_HSCROLL) debugWinStyle(str, style, WS_SYSMENU) debugWinStyle(str, style, WS_THICKFRAME) debugWinStyle(str, style, WS_GROUP) debugWinStyle(str, style, WS_TABSTOP) debugWinStyle(str, style, WS_MINIMIZEBOX) debugWinStyle(str, style, WS_MAXIMIZEBOX) } if (const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE)) { str << " exStyle=" << exStyle; debugWinStyle(str, exStyle, WS_EX_DLGMODALFRAME) debugWinStyle(str, exStyle, WS_EX_NOPARENTNOTIFY) debugWinStyle(str, exStyle, WS_EX_TOPMOST) debugWinStyle(str, exStyle, WS_EX_ACCEPTFILES) debugWinStyle(str, exStyle, WS_EX_TRANSPARENT) debugWinStyle(str, exStyle, WS_EX_MDICHILD) debugWinStyle(str, exStyle, WS_EX_TOOLWINDOW) debugWinStyle(str, exStyle, WS_EX_WINDOWEDGE) debugWinStyle(str, exStyle, WS_EX_CLIENTEDGE) debugWinStyle(str, exStyle, WS_EX_CONTEXTHELP) debugWinStyle(str, exStyle, WS_EX_RIGHT) debugWinStyle(str, exStyle, WS_EX_LEFT) debugWinStyle(str, exStyle, WS_EX_RTLREADING) debugWinStyle(str, exStyle, WS_EX_LTRREADING) debugWinStyle(str, exStyle, WS_EX_LEFTSCROLLBAR) debugWinStyle(str, exStyle, WS_EX_RIGHTSCROLLBAR) debugWinStyle(str, exStyle, WS_EX_CONTROLPARENT) debugWinStyle(str, exStyle, WS_EX_STATICEDGE) debugWinStyle(str, exStyle, WS_EX_APPWINDOW) debugWinStyle(str, exStyle, WS_EX_LAYERED) debugWinStyle(str, exStyle, WS_EX_NOINHERITLAYOUT) debugWinStyle(str, exStyle, WS_EX_NOREDIRECTIONBITMAP) debugWinStyle(str, exStyle, WS_EX_LAYOUTRTL) debugWinStyle(str, exStyle, WS_EX_COMPOSITED) debugWinStyle(str, exStyle, WS_EX_NOACTIVATE) } if (const ULONG_PTR classStyle = GetClassLongPtr(hwnd, GCL_STYLE)) { str << " classStyle=" << classStyle; debugWinStyle(str, classStyle, CS_BYTEALIGNCLIENT) debugWinStyle(str, classStyle, CS_BYTEALIGNWINDOW) debugWinStyle(str, classStyle, CS_CLASSDC) debugWinStyle(str, classStyle, CS_DBLCLKS) debugWinStyle(str, classStyle, CS_DROPSHADOW) debugWinStyle(str, classStyle, CS_GLOBALCLASS) debugWinStyle(str, classStyle, CS_HREDRAW) debugWinStyle(str, classStyle, CS_NOCLOSE) debugWinStyle(str, classStyle, CS_OWNDC) debugWinStyle(str, classStyle, CS_PARENTDC) debugWinStyle(str, classStyle, CS_SAVEBITS) debugWinStyle(str, classStyle, CS_VREDRAW) } if (const ULONG_PTR wndProc = GetClassLongPtr(hwnd, GCLP_WNDPROC)) str << " wndProc=" << wndProc; str << noshowbase << dec; if (GetWindowModuleFileName(hwnd, buf, sizeof(buf)/sizeof(buf[0]))) str << " module=\"" << QString::fromWCharArray(buf) << '"'; str << '\n'; } static void dumpNativeWindowRecursion(HWND hwnd, DumpContext *dc); BOOL CALLBACK dumpWindowEnumChildProc(HWND hwnd, LPARAM lParam) { DumpContext *dumpContext = reinterpret_cast(lParam); // EnumChildWindows enumerates grand children as well, skip these to // get the hierarchical formatting right. if (GetAncestor(hwnd, GA_PARENT) == dumpContext->parent) dumpNativeWindowRecursion(hwnd, dumpContext); return TRUE; } static void dumpNativeWindowRecursion(HWND hwnd, DumpContext *dc) { indentStream(*dc->stream, dc->indentation); formatNativeWindow(hwnd, *dc->stream); DumpContext nextLevel = *dc; nextLevel.indentation += 2; nextLevel.parent = hwnd; EnumChildWindows(hwnd, dumpWindowEnumChildProc, reinterpret_cast(&nextLevel)); } /* recurse by Z order, same result */ static void dumpNativeWindowRecursionByZ(HWND hwnd, DumpContext *dc) { indentStream(*dc->stream, dc->indentation); formatNativeWindow(hwnd, *dc->stream); if (const HWND topChild = GetTopWindow(hwnd)) { DumpContext nextLevel = *dc; nextLevel.indentation += 2; for (HWND child = topChild; child ; child = GetNextWindow(child, GW_HWNDNEXT)) dumpNativeWindowRecursionByZ(child, &nextLevel); } } typedef QVector WIdVector; static void dumpNativeWindows(const WIdVector& wins) { DumpContext dc; QString s; dc.stream = QSharedPointer(new QTextStream(&s)); foreach (WId win, wins) dumpNativeWindowRecursion(reinterpret_cast(win), &dc); #if QT_VERSION >= 0x050400 qDebug().noquote() << s; #else qDebug("%s", qPrintable(s)); #endif } void dumpNativeWindows(WId rootIn) { const WId root = rootIn ? rootIn : reinterpret_cast(GetDesktopWindow()); dumpNativeWindows(WIdVector(1, root)); } BOOL CALLBACK findQtTopLevelEnumChildProc(HWND hwnd, LPARAM lParam) { WIdVector *v = reinterpret_cast(lParam); wchar_t buf[512]; if (GetClassName(hwnd, buf, sizeof(buf)/sizeof(buf[0]))) { if (wcsstr(buf, L"Qt")) v->append(reinterpret_cast(hwnd)); } return TRUE; } static WIdVector findQtTopLevels() { WIdVector result; EnumChildWindows(GetDesktopWindow(), findQtTopLevelEnumChildProc, reinterpret_cast(&result)); return result; } void dumpNativeQtTopLevels() { dumpNativeWindows(findQtTopLevels()); } } // namespace QtDiag