Skip to content

[FancyZones] Fix stuck drag state and swallowed keys when a window is destroyed mid-drag#48569

Open
crutkas wants to merge 3 commits into
microsoft:mainfrom
crutkas:user/crutkas/fix-fancyzones-stuck-drag-on-window-destroy
Open

[FancyZones] Fix stuck drag state and swallowed keys when a window is destroyed mid-drag#48569
crutkas wants to merge 3 commits into
microsoft:mainfrom
crutkas:user/crutkas/fix-fancyzones-stuck-drag-on-window-destroy

Conversation

@crutkas

@crutkas crutkas commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

Fixes a class of "stuck drag" bugs in FancyZones where closing or destroying a window while it is being dragged leaves FancyZones in a half-dragging state — zone overlays stay on screen and subsequent keystrokes (notably number keys) are swallowed or misrouted.

What this changes

  • Subscribe to and dispatch EVENT_OBJECT_DESTROY. FancyZonesApp never subscribed to the destroy event, and the consumer's WM_PRIV_WINDOWDESTROYED branch could therefore never fire. The event is now registered and routed through HandleWinHookEvent.
  • Abort the drag (without snapping) when the dragged window is destroyed. On WM_PRIV_WINDOWDESTROYED, if the destroyed HWND is the one being dragged, call the new WindowMouseSnap::Abort() (tears down overlays/highlights/transparency) instead of MoveSizeEnd(), which would try to snap the now-dead HWND and corrupt zone state. Dragging state is then disabled.
  • Always clear dragging state in MoveSizeEnd(), even when the snapper was already null, so the state can't get stranded.
  • Require Win+Ctrl+Alt to switch layouts while dragging. Previously any digit switched layouts while dragging was true; if drag state was stuck this "stole" number keys from the focused app. This is the root-cause fix for the number-key-stealing symptom.
  • Only swallow the bare Shift key during a drag, not Shift+<other> combos, so real keystrokes are no longer eaten by an in-progress drag.

Testing

  • Builds Release x64 (FancyZones) clean against current main.
  • Manually verified drag → close window mid-drag no longer leaves overlays up or steals number keys. (FancyZones has no unit-test harness for this path.)

This is one of a small set of related "stuck key / stuck state" hardening fixes; each stands alone.

crutkas and others added 3 commits June 12, 2026 17:29
- 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>
The EVENT_OBJECT_DESTROY win-event was subscribed and mapped to
WM_PRIV_WINDOWDESTROYED by the consumer, but FancyZonesApp's dispatch
switch had no case for it, so the event was dropped at default: break
and the stuck-drag fix never ran. Forward EVENT_OBJECT_DESTROY to the
callback alongside the other window-lifecycle events.

When the dragged window is destroyed mid-drag, the WM_PRIV_WINDOWDESTROYED
handler called MoveSizeEnd(), which snaps the now-dead HWND into a zone
and corrupts layout state. Abort the drag instead via a new
WindowMouseSnap::Abort() that tears down overlays/highlights/transparency
without snapping, then resets the snapper and disables dragging state.

Drop the redundant 'shift &&' term from the bare-Shift swallow: in a
WH_KEYBOARD_LL hook GetAsyncKeyState(VK_SHIFT) is not yet set on the
initial Shift-down, so the first press leaked through to the app. The
explicit VK_LSHIFT/VK_RSHIFT vkCode check already scopes this to the
bare Shift key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.

❌ Errors and Warnings Count
⚠️ binary-file 1
⚠️ duplicate-pattern 2
❌ forbidden-pattern 1
⚠️ large-file 1

See ❌ Event descriptions for more information.

Some files were automatically ignored 🙈

These sample patterns would exclude them:

^src/modules/ZoomIt/ZoomIt/rnnoise/rnnoise_data_little\.c$
^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$

You should consider adding them to:

.github/actions/spell-check/excludes.txt

File matching is via Perl regular expressions.

To check these files, more of their words need to be in the dictionary than not. You can use patterns.txt to exclude portions, add items to the dictionary (e.g. by adding them to allow.txt), or fix typos.

To update file exclusions, you could run the following commands

... in a clone of the git@github.com:crutkas/autoUpgradeAttempt.git repository
on the user/crutkas/fix-fancyzones-stuck-drag-on-window-destroy branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/27451213326/attempts/1' &&
git commit -m 'Update check-spelling metadata'

Forbidden patterns 🙅 (1)

In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves.

These forbidden patterns matched content:

Should be a
\san (?=(?:[b-dfgjklpqtvwz]|h(?!onou?r|our|s[lv]|tml|ttp|ref)|n(?!ginx|grok|pm)|r(?!c)|s(?!s[ho]|vg))[a-z]|x(?!\b|[-\d]|ml))
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant