Skip to content

fix: surface notification when globalShortcut.register() fails#517

Open
lacymorrow wants to merge 2 commits into
mainfrom
fix/shortcut-registration-failure
Open

fix: surface notification when globalShortcut.register() fails#517
lacymorrow wants to merge 2 commits into
mainfrom
fix/shortcut-registration-failure

Conversation

@lacymorrow

Copy link
Copy Markdown
Owner

Problem

CrossOver uses Electron's globalShortcut.register() to bind keyboard shortcuts. This function returns false when registration fails silently — which happens on Windows when the OS, a game, or an overlay (Discord, GeForce Experience, Xbox Game Bar) has already claimed the key combo. CrossOver ignored the return value, leaving the shortcut inactive with no feedback to the user.

This explains the pattern in #516: shortcut works after rebinding (because unregisterAll() + re-register runs while no conflict exists), then breaks on next launch (registration fails at startup when the conflict is already present).

Changes

src/main/keyboard.js

  • registerShortcut() now returns the boolean from globalShortcut.register() and logs a console.warn when it returns false

src/main/crossover.js

  • registerKeyboardShortcuts() collects all failed accelerators and fires a user-facing notification listing the conflicting combos
  • Notification is wrapped in try/catch so early startup calls (before the renderer is ready) degrade gracefully to log-only

Test plan

  • Launch CrossOver with no conflicts — shortcuts work normally, no notification shown
  • Simulate a conflict: register a matching combo in another app, then launch CrossOver — notification appears naming the failed shortcut
  • Rebind the shortcut to an unused combo — notification clears on next sync
  • Verify early startup path does not throw (notification silently suppressed before renderer loads)

Closes #516

When Electron's globalShortcut.register() returns false (silent failure
due to OS-level or another app's conflict), CrossOver previously logged
nothing and left the shortcut silently inactive.

- registerShortcut() in keyboard.js now returns the boolean result and
  logs a console warning on failure
- registerKeyboardShortcuts() in crossover.js collects failed accelerators
  and fires a user-facing notification with the conflicting combos
- Notification is wrapped in try/catch so early startup calls (before
  renderer is ready) degrade gracefully to log-only

Fixes the 'shortcut stops working after relaunch on Windows 11' pattern
reported in #516: registration can fail silently at startup when a game
or overlay has already claimed the combo.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces tracking and user notifications for failed keyboard shortcut registrations, updating the shortcut registration process to return its success status. The review feedback suggests improving the notification logic by explicitly checking if the window is ready instead of relying on a try/catch block for control flow. Additionally, it is recommended to use the application's custom logging module instead of console.warn to ensure consistent logging behavior.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/main/crossover.js Outdated
Comment on lines 235 to 247
// Notify the user — renderer must be ready; guard against early startup calls
try {

const notification = require( './notification' )
notification( {
title: 'Keyboard Shortcut Conflict',
body: `Could not register: ${failed.join( ', ' )}. Another app may be using this combo. Try rebinding in Settings.`,
} )

} catch ( _err ) {

// Window not yet ready — failure already logged above
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Relying on a try/catch block to handle expected control flow (such as checking if the window is ready before sending a notification) is an anti-pattern. If windows.win is null or doesn't have webContents loaded yet, notification will throw a TypeError which is caught here. It is much cleaner and safer to explicitly check if windows.win?.webContents is available before attempting to send the notification.

		// Notify the user — renderer must be ready; guard against early startup calls
		if ( windows.win?.webContents ) {

			try {

				const notification = require( './notification' )
				notification( {
					title: 'Keyboard Shortcut Conflict',
					body: 'Could not register: ' + failed.join( ', ' ) + '. Another app may be using this combo. Try rebinding in Settings.',
				} )

			} catch ( err ) {

				log.error( 'Failed to send notification: ' + err.message )

			}

		}

Comment thread src/main/keyboard.js
Comment on lines +28 to +33
if ( !registered ) {

// eslint-disable-next-line no-console
console.warn( `[CrossOver] globalShortcut.register failed for: ${accelerator} (another app may have claimed this combo)` )

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using console.warn bypasses the application's configured logging system (which might write to files, format logs, or handle log levels differently). It is better to import and use the custom log module, which also removes the need for the eslint-disable-next-line comment.

Suggested change
if ( !registered ) {
// eslint-disable-next-line no-console
console.warn( `[CrossOver] globalShortcut.register failed for: ${accelerator} (another app may have claimed this combo)` )
}
if ( !registered ) {
const log = require( './log' )
log.warn( '[CrossOver] globalShortcut.register failed for: ' + accelerator + ' (another app may have claimed this combo)' )
}

- Replace try/catch anti-pattern with explicit windows.win && !isDestroyed() check
- Import log module in keyboard.js; replace console.warn with log.warn

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@lacymorrow

Copy link
Copy Markdown
Owner Author

Addressed both review points:

  • Replaced try/catch with explicit windows.win && !windows.win.isDestroyed() guard (crossover.js)
  • Imported log module in keyboard.js; replaced console.warn + eslint-disable with log.warn

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.

[BUG] Keyboard Shortcut to Hide Background stop working.

1 participant