From 7704f153f4c12d5b9b6d29a5cafa5e5b2e1e10ab Mon Sep 17 00:00:00 2001 From: SeraphimaZ Date: Wed, 17 Nov 2021 11:46:23 +0300 Subject: [PATCH] order windows --- tools/AlwaysOnTop/AlwaysOnTop.cpp | 105 ++++++++++++++++++++++++++---- tools/AlwaysOnTop/AlwaysOnTop.h | 37 ++++++++++- 2 files changed, 127 insertions(+), 15 deletions(-) diff --git a/tools/AlwaysOnTop/AlwaysOnTop.cpp b/tools/AlwaysOnTop/AlwaysOnTop.cpp index 3f57639d3..1304278ca 100644 --- a/tools/AlwaysOnTop/AlwaysOnTop.cpp +++ b/tools/AlwaysOnTop/AlwaysOnTop.cpp @@ -42,12 +42,31 @@ void AlwaysOnTop::Init() wcex.lpszClassName = HOTKEY_WINDOW_CLASS_NAME; RegisterClassExW(&wcex); - hotKeyHandleWindow = CreateWindowExW(WS_EX_TOOLWINDOW, HOTKEY_WINDOW_CLASS_NAME, L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hinstance, this); - if (!hotKeyHandleWindow) { + m_hotKeyHandleWindow = CreateWindowExW(WS_EX_TOOLWINDOW, HOTKEY_WINDOW_CLASS_NAME, L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hinstance, this); + if (!m_hotKeyHandleWindow) { return; } - RegisterHotKey(hotKeyHandleWindow, 1, MOD_CONTROL | MOD_NOREPEAT, 0x54 /* T */); + RegisterHotKey(m_hotKeyHandleWindow, 1, MOD_CONTROL | MOD_NOREPEAT, 0x54 /* T */); + + // subscribe to windows events + std::array events_to_subscribe = { + EVENT_SYSTEM_MOVESIZEEND, + EVENT_SYSTEM_SWITCHEND, + EVENT_OBJECT_FOCUS + }; + for (const auto event : events_to_subscribe) + { + auto hook = SetWinEventHook(event, event, nullptr, WinHookProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); + if (hook) + { + m_staticWinEventHooks.emplace_back(hook); + } + else + { + MessageBoxW(NULL, L"Failed to set win event hook", L"AlwaysOnTop error", MB_OK | MB_ICONERROR); + } + } } LRESULT AlwaysOnTop::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept @@ -75,10 +94,10 @@ void AlwaysOnTop::ProcessCommand(HWND window) { if (ResetTopmostWindow(window)) { - auto iter = std::find(topmostWindows.begin(), topmostWindows.end(), window); - if (iter != topmostWindows.end()) + auto iter = std::find(m_topmostWindows.begin(), m_topmostWindows.end(), window); + if (iter != m_topmostWindows.end()) { - topmostWindows.erase(iter); + m_topmostWindows.erase(iter); } } } @@ -86,7 +105,8 @@ void AlwaysOnTop::ProcessCommand(HWND window) { if (SetTopmostWindow(window)) { - topmostWindows.push_back(window); + m_topmostWindows.insert(m_topmostWindows.begin(), window); + OrderWindows(); } } @@ -124,14 +144,16 @@ void AlwaysOnTop::StartTrackingTopmostWindows() { if (IsTopmost(window)) { - topmostWindows.push_back(window); + m_topmostWindows.push_back(window); } } + + OrderWindows(); } void AlwaysOnTop::ResetAll() { - for (HWND topWindow : topmostWindows) + for (HWND topWindow : m_topmostWindows) { if (!ResetTopmostWindow(topWindow)) { @@ -139,20 +161,45 @@ void AlwaysOnTop::ResetAll() } } - topmostWindows.clear(); + m_topmostWindows.clear(); } void AlwaysOnTop::CleanUp() { ResetAll(); - if (hotKeyHandleWindow) + if (m_hotKeyHandleWindow) { - DestroyWindow(hotKeyHandleWindow); - hotKeyHandleWindow = nullptr; + DestroyWindow(m_hotKeyHandleWindow); + m_hotKeyHandleWindow = nullptr; } UnregisterClass(HOTKEY_WINDOW_CLASS_NAME, reinterpret_cast(&__ImageBase)); } +bool AlwaysOnTop::OrderWindows() const noexcept +{ + if (m_topmostWindows.empty()) + { + return true; + } + + BOOL res = true; + for (int i = static_cast(m_topmostWindows.size()) - 1; i >= 0; i--) + { + auto cTxtLen = GetWindowTextLength(m_topmostWindows[i]); + auto pszMem = (LPWSTR)VirtualAlloc((LPVOID)NULL, (DWORD)(cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE); + GetWindowText(m_topmostWindows[i], pszMem, cTxtLen + 1); + + res &= SetWindowPos(m_topmostWindows[i], nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_DRAWFRAME); + + if (!res) + { + MessageBoxW(NULL, L"Failed to order windows", L"AlwaysOnTop error", MB_OK | MB_ICONERROR); + } + } + + return res; +} + bool AlwaysOnTop::IsTopmost(HWND window) const noexcept { int exStyle = GetWindowLong(window, GWL_EXSTYLE); @@ -168,3 +215,35 @@ bool AlwaysOnTop::ResetTopmostWindow(HWND window) const noexcept { return SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } + +bool AlwaysOnTop::IsTracked(HWND window) const noexcept +{ + for (HWND topmostWindow : m_topmostWindows) + { + if (window == topmostWindow) + { + return true; + } + } + + return false; +} + +void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept +{ + switch (data->event) + { + case EVENT_SYSTEM_MOVESIZEEND: // moved or resized + case EVENT_SYSTEM_SWITCHEND: // alt-tab + case EVENT_OBJECT_FOCUS: // focused + { + if (IsTracked(data->hwnd)) + { + OrderWindows(); + } + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/tools/AlwaysOnTop/AlwaysOnTop.h b/tools/AlwaysOnTop/AlwaysOnTop.h index dc1c76476..e9714e84f 100644 --- a/tools/AlwaysOnTop/AlwaysOnTop.h +++ b/tools/AlwaysOnTop/AlwaysOnTop.h @@ -1,5 +1,16 @@ #pragma once +// common +struct WinHookEvent +{ + DWORD event; + HWND hwnd; + LONG idObject; + LONG idChild; + DWORD idEventThread; + DWORD dwmsEventTime; +}; + class AlwaysOnTop { public: @@ -26,9 +37,11 @@ protected: private: static inline AlwaysOnTop* s_instance = nullptr; + std::vector m_staticWinEventHooks; + + HWND m_hotKeyHandleWindow{ nullptr }; + std::vector m_topmostWindows; - HWND hotKeyHandleWindow{ nullptr }; - std::vector topmostWindows; bool m_activateInGameMode = false; LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; @@ -37,9 +50,29 @@ private: void StartTrackingTopmostWindows(); void ResetAll(); void CleanUp(); + bool OrderWindows() const noexcept; bool IsTopmost(HWND window) const noexcept; bool SetTopmostWindow(HWND window) const noexcept; bool ResetTopmostWindow(HWND window) const noexcept; + + bool IsTracked(HWND window) const noexcept; + + void HandleWinHookEvent(WinHookEvent* data) noexcept; + + static void CALLBACK WinHookProc(HWINEVENTHOOK winEventHook, + DWORD event, + HWND window, + LONG object, + LONG child, + DWORD eventThread, + DWORD eventTime) + { + WinHookEvent data{ event, window, object, child, eventThread, eventTime }; + if (s_instance) + { + s_instance->HandleWinHookEvent(&data); + } + } };