Skip to content

fix(banner): prevent focus stealing in fullscreen games with multi-layer defense#2241

Merged
Belphemur merged 5 commits into
devfrom
copilot/fix-banner-focus-in-fullscreen-game
Jun 21, 2026
Merged

fix(banner): prevent focus stealing in fullscreen games with multi-layer defense#2241
Belphemur merged 5 commits into
devfrom
copilot/fix-banner-focus-in-fullscreen-game

Conversation

Copilot AI commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Banner notifications still minimize exclusive fullscreen games (e.g. CS2) despite PR #2156's SWP_NOACTIVATE fix. Multiple internal WinForms code paths bypass that guard and trigger window activation.

Changes

  • WM_WINDOWPOSCHANGING handler — Intercepts every SetWindowPos call (including WinForms-internal and system-initiated) and injects SWP_NOACTIVATE into the WINDOWPOS struct before it takes effect
  • WS_EX_LAYERED in CreateParams — Window is created layered from the start, preventing WinForms from recreating the handle when Opacity is set (handle recreation is a known activation trigger)
  • WM_ACTIVATEWA_INACTIVE — Rejects any activation attempt at the message level
  • WM_MOUSEACTIVATEMA_NOACTIVATE — Prevents click-activation while still delivering the click event (changed from MA_NOACTIVATEANDEAT which swallowed clicks)
  • Removed redundant TopMost = true from constructor — already set via WS_EX_TOPMOST in CreateParams; the WinForms property setter triggered its own unguarded SetWindowPos during handle creation

Core of the fix in WndProc:

case WM_WINDOWPOSCHANGING:
    var windowPos = Marshal.PtrToStructure<WINDOWPOS>(m.LParam);
    windowPos.flags |= SWP_NOACTIVATE;
    Marshal.StructureToPtr(windowPos, m.LParam, false);
    base.WndProc(ref m);
    return;

Docs

  • Updated website/src/configuration/notifications.md to document that banners never steal focus from fullscreen apps
  • Updated docs/banner-manager.md design table with focus safety details

Copilot AI and others added 2 commits June 21, 2026 23:09
…yer defense

- Remove redundant TopMost=true from constructor (already set via CreateParams)
- Add WS_EX_LAYERED to CreateParams to prevent handle recreation when
  Opacity is changed, which would trigger a focus-stealing cycle
- Handle WM_WINDOWPOSCHANGING to inject SWP_NOACTIVATE into ALL window
  position/z-order changes, including internal WinForms and system calls
- Handle WM_ACTIVATE to always force WA_INACTIVE state
- Handle WM_MOUSEACTIVATE with MA_NOACTIVATE to prevent mouse-click
  activation while still delivering click events
- Clean up stale comment from previous PR

Co-authored-by: Belphemur <197810+Belphemur@users.noreply.github.com>
Co-authored-by: Belphemur <197810+Belphemur@users.noreply.github.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 21, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
soundswitch a02055c Jun 21 2026, 11:22 PM

Copilot AI changed the title [WIP] Fix banner focus issue in full-screen CS2 game fix(banner): prevent focus stealing in fullscreen games with multi-layer defense Jun 21, 2026
Copilot AI requested a review from Belphemur June 21, 2026 23:14
@Belphemur Belphemur marked this pull request as ready for review June 21, 2026 23:14
Copilot AI review requested due to automatic review settings June 21, 2026 23:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the non-activating behavior of BannerForm to prevent banner notifications from stealing focus (and minimizing exclusive fullscreen games), and updates documentation to reflect the intended focus-safety guarantees.

Changes:

  • Adds a WM_WINDOWPOSCHANGING hook to inject SWP_NOACTIVATE into all position changes.
  • Creates the banner window as layered from the start (WS_EX_LAYERED) to avoid handle recreation when setting Opacity.
  • Adds message-level activation defenses (WM_ACTIVATE, WM_MOUSEACTIVATE) and updates docs accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
website/src/configuration/notifications.md Documents banner focus-safety behavior (needs wording tightened to avoid overpromising visibility in exclusive fullscreen).
SoundSwitch/Framework/Banner/BannerForm.cs Implements multi-layer non-activation defenses in CreateParams and WndProc.
docs/banner-manager.md Updates banner manager design table with focus-safety details (needs wording tightened to avoid overpromising visibility).

Comment on lines +209 to +212
case WM_ACTIVATE:
// Always force inactive state to prevent the banner from stealing focus
m.Result = (IntPtr)WA_INACTIVE;
return;
Comment on lines +195 to +196
const int WM_WINDOWPOSCHANGING = 0x0046;
const uint SWP_NOACTIVATE = 0x0010;
Comment thread website/src/configuration/notifications.md Outdated
Comment thread docs/banner-manager.md Outdated
Belphemur and others added 2 commits June 21, 2026 19:20
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@Belphemur Belphemur merged commit a81f169 into dev Jun 21, 2026
13 checks passed
@Belphemur Belphemur deleted the copilot/fix-banner-focus-in-fullscreen-game branch June 21, 2026 23:36
github-actions Bot pushed a commit that referenced this pull request Jun 21, 2026
## [7.1.1-beta.2](v7.1.1-beta.1...v7.1.1-beta.2) (2026-06-21)

### Enhancements

* **tray:** reduce click delay to 250ms, add Disabled double-click option ([aa2c03b](aa2c03b))

### Bug Fixes

* **app-rules:** fallback to glob when regex pattern is invalid ([4672c6b](4672c6b))
* **banner:** prevent focus stealing and fix drag-position/NRE bugs in BannerForm ([feefcd6](feefcd6))
* **banner:** prevent focus stealing in fullscreen games with multi-layer defense ([#2241](#2241)) ([a81f169](a81f169)), closes [#2240](#2240)
* build error and review feedback ([6f4ef44](6f4ef44))
* **build:** avoid CS8417 compiler bug with using var and await using in PlaySoundJob.cs ([9b7e9a9](9b7e9a9))
* clean up doc comments and add dispose idempotency ([366ecb4](366ecb4))
* **installer:** update required .NET version to 10.0.9 in installer scripts ([385819b](385819b))
* **notification:** implement mic mute sound notification and harden PlaySoundJob ([d030236](d030236)), closes [#2187](#2187)
* review feedback improvements ([ddc896f](ddc896f))
* **tray-icon:** add monochrome systray icon option ([05e08e5](05e08e5)), closes [#2029](#2029)
* **website:** finding-soundswitch FAQ — reopening opens settings ([#2221](#2221)) ([590b247](590b247))

### Languages

* **Chinese (Simplified Han script):** Translated Settings using Weblate ([99cb9be](99cb9be))
* **Chinese (Simplified Han script):** Translated Tray Icon using Weblate ([5cb5e90](5cb5e90))
* **French:** Translated Settings using Weblate ([d823469](d823469))
* **Hebrew:** Translated Tray Icon using Weblate ([385e059](385e059))
* **Italian:** Translated Settings using Weblate ([e8dfb91](e8dfb91))
* **Italian:** Translated Settings using Weblate ([0541491](0541491))
* **Italian:** Translated Tray Icon using Weblate ([c83eb2b](c83eb2b))
* **Japanese:** Translated Settings using Weblate ([2bfdf54](2bfdf54))
* **Japanese:** Translated Tray Icon using Weblate ([ba89f4a](ba89f4a))
* **Portuguese (Brazil):** Translated Tray Icon using Weblate ([f18b4dd](f18b4dd))
* **Spanish:** Translated Settings using Weblate ([44bbb0e](44bbb0e))
* **Spanish:** Translated Tray Icon using Weblate ([a38c28c](a38c28c))
* **Spanish:** Translated Update Download using Weblate ([906b4ac](906b4ac))
* **Swedish:** Translated Settings using Weblate ([0696a33](0696a33))
* **Swedish:** Translated Tray Icon using Weblate ([c038b30](c038b30))
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.

3 participants