Skip to content

Commit 7e8a7ae

Browse files
committed
refactor(hotkey): 尝试让 globalHotkey.ts 更清晰
1 parent 0e19beb commit 7e8a7ae

1 file changed

Lines changed: 46 additions & 41 deletions

File tree

electron/main/services/globalHotkey.ts

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,19 @@ const writeConfig = (next: HotkeyConfig): HotkeyConfig => {
8585
return readConfig();
8686
};
8787

88-
/** 卸载所有 globalShortcut + 清空冲突 */
89-
const unregisterAll = (): void => {
88+
/** 卸载 Electron 快捷键并清空冲突 */
89+
const unregisterElectronShortcuts = (): void => {
9090
globalShortcut.unregisterAll();
9191
conflicts = [];
9292
};
9393

9494
/**
9595
* 注册全部 global accelerators(仅 Electron 模式)
9696
* 同 accelerator 重复 / OS 占用 / 解析异常 都计入 conflicts
97-
* globalEnabled = false 时只 unregisterAll 不注册新的
97+
* globalEnabled = false 时只解绑,不注册新的
9898
*/
99-
const applyAll = (): void => {
100-
unregisterAll();
99+
const registerElectronShortcuts = (): void => {
100+
unregisterElectronShortcuts();
101101
if (currentMode === "portal") return;
102102
const config = readConfig();
103103
if (!config.globalEnabled) {
@@ -157,6 +157,16 @@ const buildPortalShortcuts = (): PortalShortcut[] =>
157157
: undefined,
158158
}));
159159

160+
/** 绑定 Portal 快捷键,失败时记录原因 */
161+
const bindPortalShortcuts = async (): Promise<boolean> => {
162+
const res = await portalModule!.bindShortcuts(buildPortalShortcuts());
163+
if (!res.ok) {
164+
coreLog.error(`[portal] 绑定全局快捷键失败: ${res.error ?? "未知错误"}`);
165+
return false;
166+
}
167+
return true;
168+
};
169+
160170
/** 解绑当前 portal 会话并释放绑定(globalEnabled 关闭 / 退出 portal)入串行链 */
161171
const portalUnbindAll = (): Promise<void> =>
162172
enqueueTransition(async () => {
@@ -172,13 +182,33 @@ const portalUnbindAll = (): Promise<void> =>
172182
const portalBindAll = (): Promise<void> =>
173183
enqueueTransition(async () => {
174184
if (currentMode !== "portal" || !portalModule) return;
175-
// 原生层绑定前已关闭旧会话,失败后实际无任何快捷键;直接 await switchToElectron()
176-
// 会在当前节点内等自己排入链尾的节点而死锁,故作为链的下一节点排队回退
177-
const res = await portalModule.bindShortcuts(buildPortalShortcuts());
178-
if (!res.ok) {
179-
coreLog.error(`[portal] 绑定全局快捷键失败: ${res.error ?? "未知错误"}`);
185+
if (!readConfig().globalEnabled || (await bindPortalShortcuts())) return;
186+
// 回退操作必须排在当前 transition 之后,避免等待自身造成死锁。
187+
void switchToElectron();
188+
});
189+
190+
/** 切换到 Portal 模式并绑定当前配置 */
191+
const switchToPortal = (epoch: number): Promise<void> =>
192+
enqueueTransition(async () => {
193+
if (epoch !== portalEpoch) return;
194+
195+
currentMode = "portal";
196+
unregisterElectronShortcuts();
197+
198+
const config = readConfig();
199+
if (config.globalEnabled && !(await bindPortalShortcuts())) {
200+
if (epoch !== portalEpoch) {
201+
void portalModule!.unbindShortcuts();
202+
return;
203+
}
180204
void switchToElectron();
205+
return;
181206
}
207+
208+
portalBound = config.globalEnabled;
209+
conflicts = [];
210+
broadcast("hotkey:conflicts", conflicts);
211+
broadcastMode();
182212
});
183213

184214
/**
@@ -193,7 +223,7 @@ const switchToElectron = (): Promise<void> =>
193223
portalBound = false;
194224
// 等待解绑完成释放按键后再注册 Electron,避免注册被占用
195225
if (portalModule) await portalModule.unbindShortcuts();
196-
applyAll();
226+
registerElectronShortcuts();
197227
broadcastMode();
198228
});
199229

@@ -244,32 +274,7 @@ const ensurePortalReady = async (): Promise<void> => {
244274
await descriptionsReady;
245275
if (epoch !== portalEpoch) return;
246276

247-
// 绑定与解绑入同一串行链:确保前一个 switchToElectron 的解绑先完成,再切换并绑定 portal
248-
await enqueueTransition(async () => {
249-
if (epoch !== portalEpoch) return;
250-
currentMode = "portal";
251-
// 从 Electron 模式切回 portal 时,同键可能已在两个后端注册:先释放 globalShortcut,
252-
// 避免一次按键触发两次或与 portal preferredTrigger 冲突(绑定失败时 switchToElectron 会重注册)
253-
unregisterAll();
254-
// globalEnabled=false 时不注册任何 portal 动作(与 Electron 模式语义一致)
255-
const config = readConfig();
256-
if (config.globalEnabled) {
257-
const res = await portalModule!.bindShortcuts(buildPortalShortcuts());
258-
if (epoch !== portalEpoch) {
259-
void portalModule!.unbindShortcuts();
260-
return;
261-
}
262-
if (!res.ok) {
263-
coreLog.error(`[portal] 绑定全局快捷键失败: ${res.error ?? "未知错误"}`);
264-
await switchToElectron();
265-
return;
266-
}
267-
}
268-
portalBound = config.globalEnabled;
269-
conflicts = [];
270-
broadcast("hotkey:conflicts", conflicts);
271-
broadcastMode();
272-
});
277+
await switchToPortal(epoch);
273278
};
274279

275280
/** 触发一次 portal 初始化(幂等,进行中则复用) */
@@ -320,7 +325,7 @@ export const initGlobalHotkey = (): void => {
320325

321326
/** 退出清理 */
322327
export const cleanupGlobalHotkey = (): void => {
323-
unregisterAll();
328+
unregisterElectronShortcuts();
324329
if (portalModule) void portalModule.shutdown();
325330
};
326331

@@ -406,20 +411,20 @@ export const probeAccelerator = (accelerator: string): boolean => {
406411
ok = false;
407412
}
408413
if (wasRegistered) {
409-
applyAll();
414+
registerElectronShortcuts();
410415
}
411416
return ok;
412417
};
413418

414419
/**
415420
* 绑定变更后的重注册
416421
* - Electron 模式:整体重注册 globalShortcut
417-
* - portal 模式:globalEnabled 翻转时同步绑定/解绑,其余配置变更不重建会话
422+
* - Portal 模式:只在 globalEnabled 翻转时绑定或解绑会话
418423
* (避免覆盖用户在系统设置侧的自定义)
419424
*/
420425
const syncRegistration = (): void => {
421426
if (currentMode !== "portal") {
422-
applyAll();
427+
registerElectronShortcuts();
423428
return;
424429
}
425430
const config = readConfig();

0 commit comments

Comments
 (0)