@@ -26,11 +26,16 @@ static WindowId GetWindowIdFromHwnd(HWND hwnd) {
2626 return IdAllocator::kInvalidId ;
2727 }
2828
29- // First, try to get window ID from HWND's custom property
29+ // First, try to get window ID from HWND's custom property. A window can carry
30+ // an ID without being registered (created by Window(), or wrapped by a
31+ // binding), so make sure WindowManager::Get() can find it either way.
3032 HANDLE prop_handle = GetPropW (hwnd, kWindowIdProperty );
3133 if (prop_handle) {
3234 WindowId window_id = static_cast <WindowId>(reinterpret_cast <uintptr_t >(prop_handle));
3335 if (window_id != IdAllocator::kInvalidId && window_id != 0 ) {
36+ if (!WindowRegistry::GetInstance ().Get (window_id)) {
37+ WindowRegistry::GetInstance ().Add (window_id, std::make_shared<Window>(hwnd));
38+ }
3439 return window_id;
3540 }
3641 }
@@ -459,18 +464,43 @@ WindowManager::~WindowManager() {
459464 StopEventListening ();
460465}
461466
467+ namespace {
468+
469+ struct WindowIdSearch {
470+ WindowId id;
471+ HWND found;
472+ };
473+
474+ // Finds this process's top-level window carrying an ID, visible or not.
475+ BOOL CALLBACK FindWindowWithId (HWND hwnd, LPARAM data) {
476+ auto * search = reinterpret_cast <WindowIdSearch*>(data);
477+ if (!IsOwnProcessWindow (hwnd)) {
478+ return TRUE ;
479+ }
480+ HANDLE prop_handle = GetPropW (hwnd, kWindowIdProperty );
481+ if (prop_handle && static_cast <WindowId>(reinterpret_cast <uintptr_t >(prop_handle)) == search->id ) {
482+ search->found = hwnd;
483+ return FALSE ;
484+ }
485+ return TRUE ;
486+ }
487+
488+ } // namespace
489+
462490std::shared_ptr<Window> WindowManager::Get (WindowId id) {
463491 // First try to get from registry
464492 auto window = WindowRegistry::GetInstance ().Get (id);
465493 if (window) {
466494 return window;
467495 }
468496
469- // If not in registry, enumerate all windows to find it
470- // This will create and register the window if it exists
471- GetAll ();
472-
473- // Try again after enumeration
497+ // Not registered yet: the window may still exist, hidden or untitled (which
498+ // GetAll() skips), so look for the ID on this process's windows directly.
499+ WindowIdSearch search = {id, nullptr };
500+ EnumWindows (FindWindowWithId, reinterpret_cast <LPARAM >(&search));
501+ if (search.found ) {
502+ GetWindowIdFromHwnd (search.found );
503+ }
474504 return WindowRegistry::GetInstance ().Get (id);
475505}
476506
@@ -573,41 +603,38 @@ POINT LogicalToPhysicalScreenPoint(Point point) {
573603 static_cast <LONG >(std::lround (point.y * scale))};
574604}
575605
576- struct HitTestSearch {
577- POINT point;
578- HWND excluded;
579- HWND found;
580- };
581-
582- // EnumWindows visits top-level windows in z-order, topmost first, so the first
583- // window that is actually painted at the point is the one the user sees there.
584- BOOL CALLBACK HitTestTopLevelWindow (HWND hwnd, LPARAM data) {
585- auto * search = reinterpret_cast <HitTestSearch*>(data);
586- if (hwnd == search->excluded || !IsWindowVisible (hwnd) || IsIconic (hwnd)) {
587- return TRUE ;
606+ // Whether `hwnd`, a top-level window, visibly covers `point`. Used below the
607+ // window excluded from a hit test, where the system's own hit test cannot look.
608+ bool CoversPoint (HWND hwnd, POINT point) {
609+ if (!IsWindowVisible (hwnd) || IsIconic (hwnd)) {
610+ return false ;
588611 }
589612 LONG ex_style = GetWindowLongW (hwnd, GWL_EXSTYLE );
590- if ((ex_style & WS_EX_LAYERED ) && (ex_style & WS_EX_TRANSPARENT )) {
591- return TRUE ; // Click-through overlay.
613+ if (ex_style & WS_EX_LAYERED ) {
614+ if (ex_style & WS_EX_TRANSPARENT ) {
615+ return false ; // Click-through overlay.
616+ }
617+ BYTE alpha = 255 ;
618+ DWORD flags = 0 ;
619+ if (GetLayeredWindowAttributes (hwnd, nullptr , &alpha, &flags) && (flags & LWA_ALPHA ) &&
620+ alpha == 0 ) {
621+ return false ; // Fully transparent.
622+ }
592623 }
593624 BOOL cloaked = FALSE ;
594625 if (SUCCEEDED (DwmGetWindowAttribute (hwnd, DWMWA_CLOAKED , &cloaked, sizeof (cloaked))) &&
595626 cloaked) {
596- return TRUE ; // On another virtual desktop, or a suspended UWP frame.
627+ return false ; // On another virtual desktop, or a suspended UWP frame.
597628 }
598629 // The extended frame excludes the invisible resize borders that
599630 // GetWindowRect() includes on Windows 10 and later.
600631 RECT rect;
601632 if (FAILED (DwmGetWindowAttribute (hwnd, DWMWA_EXTENDED_FRAME_BOUNDS , &rect, sizeof (rect)))) {
602633 if (!GetWindowRect (hwnd, &rect)) {
603- return TRUE ;
634+ return false ;
604635 }
605636 }
606- if (!PtInRect (&rect, search->point )) {
607- return TRUE ;
608- }
609- search->found = hwnd;
610- return FALSE ;
637+ return PtInRect (&rect, point) != FALSE ;
611638}
612639
613640} // namespace
@@ -619,13 +646,32 @@ std::shared_ptr<Window> WindowManager::GetWindowAtPoint(Point point, WindowId ex
619646 excluded = static_cast <HWND >(window->GetNativeObject ());
620647 }
621648 }
622-
623- HitTestSearch search = {LogicalToPhysicalScreenPoint (point), excluded, nullptr };
624- EnumWindows (HitTestTopLevelWindow, reinterpret_cast <LPARAM >(&search));
625- if (!search.found || !IsOwnProcessWindow (search.found )) {
649+ const POINT physical = LogicalToPhysicalScreenPoint (point);
650+
651+ // Ask the system first: WindowFromPoint() is the window a click would reach,
652+ // so it looks through click-through overlays (layered windows with
653+ // transparent pixels, like game or capture overlays) that cover the screen.
654+ HWND found = WindowFromPoint (physical);
655+ if (found) {
656+ found = GetAncestor (found, GA_ROOT );
657+ }
658+ if (found && found == excluded) {
659+ // The excluded window, typically the one being dragged, is right under the
660+ // point. Walk down the stack beneath it instead.
661+ found = nullptr ;
662+ for (HWND below = GetWindow (excluded, GW_HWNDNEXT ); below;
663+ below = GetWindow (below, GW_HWNDNEXT )) {
664+ if (CoversPoint (below, physical)) {
665+ found = below;
666+ break ;
667+ }
668+ }
669+ }
670+ // Another application's window on top means the point is covered.
671+ if (!found || !IsOwnProcessWindow (found)) {
626672 return nullptr ;
627673 }
628- WindowId window_id = GetWindowIdFromHwnd (search. found );
674+ WindowId window_id = GetWindowIdFromHwnd (found);
629675 if (window_id == IdAllocator::kInvalidId ) {
630676 return nullptr ;
631677 }
0 commit comments