Skip to content

fix(wayland): harden global hotkey lifecycle and defensive handling#79

Open
bwendell wants to merge 2 commits intomainfrom
fix/wayland-hotkey-defensive-hardening
Open

fix(wayland): harden global hotkey lifecycle and defensive handling#79
bwendell wants to merge 2 commits intomainfrom
fix/wayland-hotkey-defensive-hardening

Conversation

@bwendell
Copy link
Owner

Summary

  • Harden Wayland global hotkey lifecycle by queuing in-flight re-registrations, invalidating stale async registration completions after teardown, and cleaning up D-Bus sessions during unregister/no-hotkey states.
  • Add defensive portal signal payload parsing for Activated and Deactivated events so malformed messages are ignored safely instead of leaking invalid state.
  • Expand unit and coordinated coverage for race conditions, teardown behavior, and malformed Wayland D-Bus payloads to prevent regressions.

Verification

  • npm run test:electron -- tests/unit/main/hotkeyManager.test.ts tests/unit/main/utils/dbusFallback.test.ts
  • npm run test:coordinated -- tests/coordinated/wayland-hotkey-coordination.coordinated.test.ts
  • npm run build

Copilot AI review requested due to automatic review settings February 14, 2026 20:48
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link

Copilot AI left a comment

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 hardens the Wayland global hotkey lifecycle by introducing an epoch-based mechanism to invalidate stale async registration completions, implementing a queue for in-flight re-registrations, adding D-Bus session cleanup during unregister and no-hotkey states, and adding defensive parsing for portal signal payloads to prevent malformed messages from causing issues.

Changes:

  • Introduced epoch mechanism and pending re-registration queue to handle race conditions during async D-Bus registration
  • Added D-Bus session cleanup in unregisterAll() and when all hotkeys are disabled
  • Added defensive payload parsing (parseShortcutSignalPayload) for Activated and Deactivated D-Bus signals to gracefully handle malformed messages
  • Expanded test coverage for malformed payloads, race conditions, and cleanup behavior

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/main/managers/hotkeyManager.ts Implemented epoch mechanism for stale registration invalidation, pending re-registration queue, and D-Bus session cleanup in unregisterAll and no-hotkey scenarios
src/main/utils/dbusFallback.ts Added parseShortcutSignalPayload function to defensively parse Activated/Deactivated signal payloads and reject malformed messages
tests/unit/main/utils/dbusFallback.test.ts Added comprehensive tests for malformed signal payloads (null body, non-array body, empty shortcutId, non-string shortcutId)
tests/unit/main/hotkeyManager.test.ts Updated test expectations for queued re-registration behavior and added test for D-Bus session cleanup on unregisterAll
tests/coordinated/wayland-hotkey-coordination.coordinated.test.ts Updated test expectations to reflect queued re-registration behavior and improved cleanup verification

Comment on lines 589 to 593
if (this._hasPendingWaylandReregistration) {
const latestStatus = getPlatformAdapter().getWaylandStatus();
this._hasPendingWaylandReregistration = false;
this._requestWaylandRegistration(latestStatus);
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

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

Missing epoch check in the early return path when no hotkeys are enabled. If unregisterAll() is called during the await destroySession() call on line 582, the epoch will be incremented, but the pending re-registration check on lines 589-593 doesn't verify the epoch before potentially triggering a new registration. This could cause a stale registration to proceed after teardown. Add an epoch check similar to the one in the finally block at lines 632-633.

Copilot uses AI. Check for mistakes.
});
expect(hotkeyManager.getPlatformHotkeyStatus().waylandStatus.portalMethod).toBe('none');
expect(mockDbusFallback.registerViaDBus).not.toHaveBeenCalled();
expect(hotkeyManager.getPlatformHotkeyStatus().globalHotkeysEnabled).toBe(false);
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

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

This test disables all hotkeys and calls registerShortcuts(), which should trigger the destroySession call on line 582 of hotkeyManager.ts. Consider adding an assertion to verify that mockDbusFallback.destroySession is called to ensure the cleanup behavior is properly tested.

Suggested change
expect(hotkeyManager.getPlatformHotkeyStatus().globalHotkeysEnabled).toBe(false);
expect(hotkeyManager.getPlatformHotkeyStatus().globalHotkeysEnabled).toBe(false);
expect(mockDbusFallback.destroySession).toHaveBeenCalled();

Copilot uses AI. Check for mistakes.
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