Skip to content

Commit b108c50

Browse files
crutkasCopilot
andcommitted
fix(fancyzones): Prevent stuck drag state and key swallowing
- Subscribe to EVENT_OBJECT_DESTROY to clean up when dragged window dies mid-drag (root cause of microsoft#410) - Only suppress bare Shift key during drag, not all Shift+key combos - Require Win+Ctrl+Alt for quickLayoutSwitch during drag - Tag SwallowKey SendInput with dwExtraInfo Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f8cadbf commit b108c50

6 files changed

Lines changed: 38 additions & 5 deletions

File tree

src/modules/fancyzones/FancyZones/FancyZonesApp.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,14 @@ void FancyZonesApp::InitHooks()
9393
}
9494
}
9595

96-
std::array<DWORD, 6> events_to_subscribe = {
96+
std::array<DWORD, 7> events_to_subscribe = {
9797
EVENT_SYSTEM_MOVESIZESTART,
9898
EVENT_SYSTEM_MOVESIZEEND,
9999
EVENT_OBJECT_NAMECHANGE,
100100
EVENT_OBJECT_UNCLOAKED,
101101
EVENT_OBJECT_SHOW,
102-
EVENT_OBJECT_CREATE
102+
EVENT_OBJECT_CREATE,
103+
EVENT_OBJECT_DESTROY
103104
};
104105
for (const auto event : events_to_subscribe)
105106
{

src/modules/fancyzones/FancyZonesLib/FancyZones.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
129129
PostMessageW(m_window, WM_PRIV_WINDOWCREATED, wparam, lparam);
130130
}
131131
break;
132+
133+
case EVENT_OBJECT_DESTROY:
134+
if (data->idObject == OBJID_WINDOW)
135+
{
136+
PostMessageW(m_window, WM_PRIV_WINDOWDESTROYED, wparam, lparam);
137+
}
138+
break;
132139
}
133140
}
134141

@@ -342,9 +349,12 @@ void FancyZones::MoveSizeEnd()
342349
if (m_windowMouseSnapper)
343350
{
344351
m_windowMouseSnapper->MoveSizeEnd();
345-
m_draggingState.Disable();
346352
m_windowMouseSnapper = nullptr;
347353
}
354+
355+
// Always disable dragging state, even if m_windowMouseSnapper was already null.
356+
// This prevents stuck drag state when a window is destroyed mid-drag.
357+
m_draggingState.Disable();
348358
}
349359

350360
bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor, GUID currentVirtualDesktop) noexcept
@@ -505,7 +515,9 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
505515

506516
bool dragging = m_draggingState.IsDragging();
507517
bool changeLayoutWhileNotDragging = !dragging && !shift && win && ctrl && alt && digitPressed != -1;
508-
bool changeLayoutWhileDragging = dragging && digitPressed != -1;
518+
// Require Win+Ctrl+Alt even while dragging to prevent accidental layout switches
519+
// when drag state is stuck (root cause of #410 "steals number keys")
520+
bool changeLayoutWhileDragging = dragging && win && ctrl && alt && digitPressed != -1;
509521

510522
if (changeLayoutWhileNotDragging || changeLayoutWhileDragging)
511523
{
@@ -519,7 +531,10 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
519531
}
520532
}
521533

522-
if (m_draggingState.IsDragging() && shift)
534+
// Only suppress the bare Shift key itself during drag (used for drag-toggle).
535+
// Do NOT swallow Shift+<other key> combos - that steals keystrokes from apps.
536+
if (m_draggingState.IsDragging() && shift &&
537+
(info->vkCode == VK_LSHIFT || info->vkCode == VK_RSHIFT))
523538
{
524539
return true;
525540
}
@@ -699,6 +714,16 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
699714
auto hwnd = reinterpret_cast<HWND>(wparam);
700715
WindowCreated(hwnd);
701716
}
717+
else if (message == WM_PRIV_WINDOWDESTROYED)
718+
{
719+
auto hwnd = reinterpret_cast<HWND>(wparam);
720+
// If the destroyed window was being dragged, force-end the drag
721+
if (m_windowMouseSnapper && m_windowMouseSnapper->GetDraggedWindow() == hwnd)
722+
{
723+
Logger::info(L"Window destroyed during drag - forcing MoveSizeEnd");
724+
MoveSizeEnd();
725+
}
726+
}
702727
else if (message == WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE)
703728
{
704729
LayoutHotkeys::instance().LoadData();

src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ UINT WM_PRIV_MOVESIZEEND;
77
UINT WM_PRIV_LOCATIONCHANGE;
88
UINT WM_PRIV_NAMECHANGE;
99
UINT WM_PRIV_WINDOWCREATED;
10+
UINT WM_PRIV_WINDOWDESTROYED;
1011
UINT WM_PRIV_INIT;
1112
UINT WM_PRIV_VD_SWITCH;
1213
UINT WM_PRIV_EDITOR;
@@ -30,6 +31,7 @@ void InitializeWinhookEventIds()
3031
WM_PRIV_LOCATIONCHANGE = RegisterWindowMessage(L"{d56c5ee7-58e5-481c-8c4f-8844cf4d0347}");
3132
WM_PRIV_NAMECHANGE = RegisterWindowMessage(L"{b7b30c61-bfa0-4d95-bcde-fc4f2cbf6d76}");
3233
WM_PRIV_WINDOWCREATED = RegisterWindowMessage(L"{bdb10669-75da-480a-9ec4-eeebf09a02d7}");
34+
WM_PRIV_WINDOWDESTROYED = RegisterWindowMessage(L"{a1b2c3d4-e5f6-7890-abcd-fancyzones01}");
3335
WM_PRIV_INIT = RegisterWindowMessage(L"{469818a8-00fa-4069-b867-a1da484fcd9a}");
3436
WM_PRIV_VD_SWITCH = RegisterWindowMessage(L"{128c2cb0-6bdf-493e-abbe-f8705e04aa95}");
3537
WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}");

src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ extern UINT WM_PRIV_MOVESIZEEND;
55
extern UINT WM_PRIV_LOCATIONCHANGE;
66
extern UINT WM_PRIV_NAMECHANGE;
77
extern UINT WM_PRIV_WINDOWCREATED;
8+
extern UINT WM_PRIV_WINDOWDESTROYED;
89
extern UINT WM_PRIV_INIT; // Scheduled when FancyZones is initialized
910
extern UINT WM_PRIV_VD_SWITCH; // Scheduled when virtual desktop switch occurs
1011
extern UINT WM_PRIV_EDITOR; // Scheduled when the editor exits

src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class WindowMouseSnap
1313
static std::unique_ptr<WindowMouseSnap> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, notifications::NotificationUtil* notificationUtil);
1414
~WindowMouseSnap();
1515

16+
HWND GetDraggedWindow() const noexcept { return m_window; }
17+
1618
bool MoveSizeStart(HMONITOR monitor, bool isSnapping);
1719
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState);
1820
void MoveSizeEnd();

src/modules/fancyzones/FancyZonesLib/util.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <common/utils/process_path.h>
77
#include <common/utils/window.h>
88
#include <common/utils/excluded_apps.h>
9+
#include <modules/interface/powertoy_module_interface.h>
910

1011
#include <array>
1112
#include <complex>
@@ -263,6 +264,7 @@ namespace FancyZonesUtils
263264
inputKey[0].type = INPUT_KEYBOARD;
264265
inputKey[0].ki.wVk = key;
265266
inputKey[0].ki.dwFlags = KEYEVENTF_KEYUP;
267+
inputKey[0].ki.dwExtraInfo = PowertoyModuleIface::CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
266268
SendInput(1, inputKey, sizeof(INPUT));
267269
}
268270
}

0 commit comments

Comments
 (0)