From 6948ffca53a857762c9bc077fe7793b3ca84a50c Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Sun, 6 Aug 2023 17:16:56 +0200 Subject: [PATCH] [MouseHighlighter]Bringing to front to be on top of other topmost windows (#27062) * [MouseHighlighter] modifying window to be 'topmost' to be visible when clicking on 'pinned to top' windows. * Fixing window hidden when clicking on a pinned to top window. * Moving the code which starts the bring-to-front-timer to the mouse click events. Also adding modifications to await 5 times 10 ms after the event to have surely the desired effect as in some cases the window repositioning takes longer than 10 ms. * Minor changes * Add safety code to ensure timer is never started paralely. Also minor format changes. * Add comments * spell checker corrections --- .../MouseHighlighter/MouseHighlighter.cpp | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp index 49f04a75ef..65bbb5ce5d 100644 --- a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp @@ -45,6 +45,7 @@ private: void StartDrawingPointFading(MouseButton button); void ClearDrawingPoint(MouseButton button); void ClearDrawing(); + void BringToFront(); HHOOK m_mouseHook = NULL; static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept; @@ -72,6 +73,7 @@ private: bool m_leftButtonPressed = false; bool m_rightButtonPressed = false; + UINT_PTR m_timer_id = 0; bool m_visible = false; @@ -85,7 +87,7 @@ private: winrt::Windows::UI::Color m_rightClickColor = MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR; winrt::Windows::UI::Color m_alwaysColor = MOUSE_HIGHLIGHTER_DEFAULT_ALWAYS_COLOR; }; - +static const uint32_t BRING_TO_FRONT_TIMER_ID = 123; Highlighter* Highlighter::instance = nullptr; bool Highlighter::CreateHighlighter() @@ -126,11 +128,10 @@ bool Highlighter::CreateHighlighter() } } - void Highlighter::AddDrawingPoint(MouseButton button) { POINT pt; - + // Applies DPIs. GetCursorPos(&pt); @@ -257,6 +258,13 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa } instance->AddDrawingPoint(MouseButton::Left); instance->m_leftButtonPressed = true; + // start a timer for the scenario, when the user clicks a pinned window which has no focus. + // after we drow the highlighting circle the pinned window will jump in front of us, + // we have to bring our window back to topmost position + if (instance->m_timer_id == 0) + { + instance->m_timer_id = SetTimer(instance->m_hwnd, BRING_TO_FRONT_TIMER_ID, 10, nullptr); + } } break; case WM_RBUTTONDOWN: @@ -269,6 +277,11 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa } instance->AddDrawingPoint(MouseButton::Right); instance->m_rightButtonPressed = true; + // same as for the left button, start a timer for reposition ourselves to topmost position + if (instance->m_timer_id == 0) + { + instance->m_timer_id = SetTimer(instance->m_hwnd, BRING_TO_FRONT_TIMER_ID, 10, nullptr); + } } break; case WM_MOUSEMOVE: @@ -316,7 +329,6 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa return CallNextHookEx(0, nCode, wParam, lParam); } - void Highlighter::StartDrawing() { Logger::info("Starting draw mode."); @@ -363,6 +375,11 @@ void Highlighter::ApplySettings(MouseHighlighterSettings settings) { m_alwaysPointerEnabled = settings.alwaysColor.A != 0; } +void Highlighter::BringToFront() { + // HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen. + SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0); +} + void Highlighter::DestroyHighlighter() { StopDrawing(); @@ -393,6 +410,28 @@ LRESULT CALLBACK Highlighter::WndProc(HWND hWnd, UINT message, WPARAM wParam, LP case WM_DESTROY: instance->DestroyHighlighter(); break; + case WM_TIMER: + { + switch (wParam) + { + // when the bring-to-front-timer expires (every 10 ms), we are repositioning our window to topmost Z order position + // As we experience that it takes 0-30 ms that the pinned window hides our window, + // we await 5 timer ticks (50 ms together) and then we stop the timer. + // If we would use a timer with a 50 ms period, there would be a flickering on the UI, as in most of the cases + // the pinned window hides our window in a few milliseconds. + case BRING_TO_FRONT_TIMER_ID: + static int fireCount = 0; + if (fireCount++ >= 4) + { + KillTimer(instance->m_hwnd, instance->m_timer_id); + instance->m_timer_id = 0; + fireCount = 0; + } + instance->BringToFront(); + break; + } + break; + } default: return DefWindowProc(hWnd, message, wParam, lParam); }