mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
539 lines
18 KiB
TypeScript
539 lines
18 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {
|
|
type DesktopTroubleshootingSettings,
|
|
type DesktopWindowBehaviorSettings,
|
|
getDesktopTroubleshootingSettings,
|
|
getDesktopWindowBehaviorSettings,
|
|
setDesktopWindowBehaviorSettings,
|
|
} from '@electron/common/DesktopConfig';
|
|
import type {
|
|
ClipboardWriteFileResult,
|
|
DownloadFileResult,
|
|
MediaAccessType,
|
|
TrayPresenceStatus,
|
|
} from '@electron/common/Types';
|
|
import {hasEnabledBlinkFeature, MIDDLE_CLICK_AUTOSCROLL_BLINK_FEATURE} from '@electron/main/ChromiumRuntime';
|
|
import {
|
|
applyDesktopWindowBehaviorSettings,
|
|
desktopTrayChangePendingRestart,
|
|
hasActiveDesktopTray,
|
|
updateTrayRuntimeState,
|
|
} from '@electron/main/DesktopTray';
|
|
import {downloadFile} from '@electron/main/FileDownloads';
|
|
import {
|
|
type LinuxAppearanceSnapshot,
|
|
type LinuxAppearanceSubscription,
|
|
readLinuxAppearance,
|
|
subscribeLinuxAppearance,
|
|
} from '@electron/main/LinuxAppearance';
|
|
import {getTccStatus, registerMacTccIpcHandlers} from '@electron/main/MacTcc';
|
|
import {setNativeStrings} from '@electron/main/MainI18n';
|
|
import {copyRemoteFileToClipboard, parseClipboardWriteFileOptions} from '@electron/main/MediaClipboard';
|
|
import {registerNotificationIpcHandlers} from '@electron/main/NotificationsIpc';
|
|
import {openExternalDeduped} from '@electron/main/OpenExternal';
|
|
import {getStatus as getOpenH264Status, setEnabled as setOpenH264Enabled} from '@electron/main/OpenH264Manager';
|
|
import {registerPasskeyHandlers} from '@electron/main/Passkeys';
|
|
import {getAppMetricsSnapshot, getDesktopInfo, getGpuInfo} from '@electron/main/PlatformInfo';
|
|
import {getStreamerModeCaptureAppStatus} from '@electron/main/StreamerModeProcessDetection';
|
|
import {
|
|
acquireStreamingPriority,
|
|
getStreamingPriorityDiagnostics,
|
|
releaseStreamingPriority,
|
|
resetStreamingPriority,
|
|
} from '@electron/main/StreamingPriority';
|
|
import {setTaskbarProgress, type TaskbarProgressMode} from '@electron/main/TaskbarProgress';
|
|
import {registerThemeLocalFileHandlers} from '@electron/main/ThemeLocalFiles';
|
|
import {
|
|
popupHelpMenu,
|
|
relaunchAndExit,
|
|
reloadMainWindow,
|
|
resetAppDataAndRestart,
|
|
setHardwareAccelerationDisabled,
|
|
setHardwareAccelerationDisabledAndRestart,
|
|
} from '@electron/main/Troubleshooting';
|
|
import {registerVoiceBackgroundMediaCacheHandlers} from '@electron/main/VoiceBackgroundMediaCache';
|
|
import {
|
|
focusVoiceDebugEventSinkPopout,
|
|
registerVoiceDebugEventSinkPopoutIpcHandlers,
|
|
setVoiceDebugEventSinkAlwaysOnTop,
|
|
VOICE_DEBUG_EVENT_SINK_POPOUT_KEY,
|
|
} from '@electron/main/VoiceDebugEventSinkPopout';
|
|
import {
|
|
clearSavedWindowBounds,
|
|
closeThemeStudioPopoutWindow,
|
|
desktopFirstClickPassThroughPendingRestart,
|
|
desktopTransparencyPendingRestart,
|
|
desktopUseNativeTitleBarPendingRestart,
|
|
focusThemeStudioPopoutWindow,
|
|
focusVoicePopoutWindow,
|
|
getActiveAllowTransparency,
|
|
getActiveUseNativeTitleBar,
|
|
getMainWindow,
|
|
setThemeStudioPopoutAlwaysOnTop,
|
|
setVoicePopoutAlwaysOnTop,
|
|
showWindow,
|
|
THEME_STUDIO_POPOUT_KEY,
|
|
toggleWindowDevTools,
|
|
} from '@electron/main/Window';
|
|
import {flashWindowForAttention, stopFlashingWindow} from '@electron/main/WindowFlash';
|
|
import {setWindowsBadgeOverlay} from '@electron/main/WindowsBadge';
|
|
import {registerWindowsToastIpcHandlers} from '@electron/main/WindowsToast';
|
|
import {app, BrowserWindow, clipboard, dialog, ipcMain, powerMonitor, shell, systemPreferences} from 'electron';
|
|
|
|
interface TrayRuntimeStateUpdate {
|
|
voiceConnected?: boolean;
|
|
voiceChannelLabel?: string | null;
|
|
selfMute?: boolean;
|
|
selfDeaf?: boolean;
|
|
presenceStatus?: TrayPresenceStatus | null;
|
|
buildInfo?: string | null;
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return value !== null && typeof value === 'object';
|
|
}
|
|
|
|
function normalizeDesktopWindowBehaviorUpdate(value: unknown): Partial<DesktopWindowBehaviorSettings> {
|
|
if (!isRecord(value)) {
|
|
return {};
|
|
}
|
|
const update: Partial<DesktopWindowBehaviorSettings> = {};
|
|
if (typeof value.showTrayIcon === 'boolean') {
|
|
update.showTrayIcon = value.showTrayIcon;
|
|
}
|
|
if (typeof value.minimizeToTray === 'boolean') {
|
|
update.minimizeToTray = value.minimizeToTray;
|
|
}
|
|
if (typeof value.closeToTray === 'boolean') {
|
|
update.closeToTray = value.closeToTray;
|
|
}
|
|
if (typeof value.useNativeTitleBar === 'boolean') {
|
|
update.useNativeTitleBar = value.useNativeTitleBar;
|
|
}
|
|
if (typeof value.rememberWindowState === 'boolean') {
|
|
update.rememberWindowState = value.rememberWindowState;
|
|
}
|
|
if (typeof value.allowTransparency === 'boolean') {
|
|
update.allowTransparency = value.allowTransparency;
|
|
}
|
|
if (typeof value.smoothScrolling === 'boolean') {
|
|
update.smoothScrolling = value.smoothScrolling;
|
|
}
|
|
if (typeof value.middleClickAutoscroll === 'boolean') {
|
|
update.middleClickAutoscroll = value.middleClickAutoscroll;
|
|
}
|
|
if (typeof value.firstClickPassThroughWhenUnfocused === 'boolean') {
|
|
update.firstClickPassThroughWhenUnfocused = value.firstClickPassThroughWhenUnfocused;
|
|
}
|
|
return update;
|
|
}
|
|
|
|
function getActiveSmoothScrolling(): boolean {
|
|
return !app.commandLine.hasSwitch('disable-smooth-scrolling');
|
|
}
|
|
|
|
function getActiveMiddleClickAutoscroll(): boolean {
|
|
return process.platform === 'linux' && hasEnabledBlinkFeature(MIDDLE_CLICK_AUTOSCROLL_BLINK_FEATURE);
|
|
}
|
|
|
|
export function registerIpcHandlers(): void {
|
|
registerVoiceDebugEventSinkPopoutIpcHandlers();
|
|
registerVoiceBackgroundMediaCacheHandlers();
|
|
ipcMain.handle('get-desktop-info', () => getDesktopInfo());
|
|
ipcMain.handle('get-gpu-info', () => getGpuInfo());
|
|
ipcMain.handle('get-app-metrics', () => getAppMetricsSnapshot());
|
|
ipcMain.handle('get-openh264-status', () => getOpenH264Status());
|
|
ipcMain.handle('set-openh264-enabled', (_event, enabled: unknown) => setOpenH264Enabled(Boolean(enabled)));
|
|
ipcMain.handle('streamer-mode:get-capture-app-status', () => getStreamerModeCaptureAppStatus());
|
|
ipcMain.handle('system-idle-time-ms', (): number => {
|
|
return Math.max(0, powerMonitor.getSystemIdleTime() * 1000);
|
|
});
|
|
ipcMain.handle('desktop-window-behavior-get', (): DesktopWindowBehaviorSettings => {
|
|
return {
|
|
...getDesktopWindowBehaviorSettings(),
|
|
activeUseNativeTitleBar: getActiveUseNativeTitleBar(),
|
|
activeAllowTransparency: getActiveAllowTransparency(),
|
|
activeSmoothScrolling: getActiveSmoothScrolling(),
|
|
activeMiddleClickAutoscroll: getActiveMiddleClickAutoscroll(),
|
|
};
|
|
});
|
|
ipcMain.handle('desktop-troubleshooting-get', (): DesktopTroubleshootingSettings => {
|
|
if (process.platform === 'darwin') {
|
|
return {...getDesktopTroubleshootingSettings(), disableHardwareAcceleration: false};
|
|
}
|
|
return getDesktopTroubleshootingSettings();
|
|
});
|
|
ipcMain.handle(
|
|
'desktop-troubleshooting-set-disable-hardware-acceleration',
|
|
(
|
|
_event,
|
|
payload: {
|
|
disable: boolean;
|
|
restart?: boolean;
|
|
},
|
|
): DesktopTroubleshootingSettings => {
|
|
const disable = Boolean(payload?.disable);
|
|
if (payload?.restart) {
|
|
setHardwareAccelerationDisabledAndRestart(disable);
|
|
} else {
|
|
setHardwareAccelerationDisabled(disable);
|
|
}
|
|
if (process.platform === 'darwin') {
|
|
return {...getDesktopTroubleshootingSettings(), disableHardwareAcceleration: false};
|
|
}
|
|
return getDesktopTroubleshootingSettings();
|
|
},
|
|
);
|
|
ipcMain.handle('desktop-troubleshooting-reload', (): void => {
|
|
reloadMainWindow();
|
|
});
|
|
ipcMain.handle(
|
|
'desktop-troubleshooting-reset-app-data',
|
|
async (
|
|
_event,
|
|
options?: {
|
|
confirm?: boolean;
|
|
},
|
|
): Promise<void> => {
|
|
await resetAppDataAndRestart(options);
|
|
},
|
|
);
|
|
ipcMain.handle('desktop-troubleshooting-popup-help-menu', (event): void => {
|
|
popupHelpMenu(BrowserWindow.fromWebContents(event.sender));
|
|
});
|
|
ipcMain.on('streaming-priority-acquire', (event) => {
|
|
acquireStreamingPriority(event.sender);
|
|
});
|
|
ipcMain.on('streaming-priority-release', () => {
|
|
releaseStreamingPriority();
|
|
});
|
|
ipcMain.on('streaming-priority-reset', () => {
|
|
resetStreamingPriority();
|
|
});
|
|
ipcMain.handle('streaming-priority-get-diagnostics', () => getStreamingPriorityDiagnostics());
|
|
ipcMain.on('tray-runtime-state-update', (event, state: unknown) => {
|
|
if (!state || typeof state !== 'object') return;
|
|
const update = state as TrayRuntimeStateUpdate;
|
|
updateTrayRuntimeState(update, event.sender.id);
|
|
});
|
|
ipcMain.on('native-locale-set', (_event, payload: unknown) => {
|
|
if (!payload || typeof payload !== 'object') return;
|
|
const {locale, strings} = payload as {
|
|
locale?: unknown;
|
|
strings?: unknown;
|
|
};
|
|
if (typeof locale !== 'string' || !strings || typeof strings !== 'object') return;
|
|
const sanitized: Record<string, string> = {};
|
|
for (const [key, value] of Object.entries(strings)) {
|
|
if (typeof key === 'string' && typeof value === 'string') {
|
|
sanitized[key] = value;
|
|
}
|
|
}
|
|
setNativeStrings(locale, sanitized);
|
|
});
|
|
ipcMain.on('window-flash', (_event, persistent: unknown) => {
|
|
flashWindowForAttention(persistent === true);
|
|
});
|
|
ipcMain.on('window-stop-flash', () => {
|
|
stopFlashingWindow();
|
|
});
|
|
ipcMain.on('taskbar-progress-set', (_event, payload: unknown) => {
|
|
if (!payload || typeof payload !== 'object') return;
|
|
const {fraction, mode} = payload as {
|
|
fraction?: unknown;
|
|
mode?: unknown;
|
|
};
|
|
const numericFraction = typeof fraction === 'number' ? fraction : -1;
|
|
const validModes: ReadonlyArray<TaskbarProgressMode> = ['normal', 'indeterminate', 'error', 'paused', 'none'];
|
|
const resolvedMode: TaskbarProgressMode =
|
|
typeof mode === 'string' && (validModes as ReadonlyArray<string>).includes(mode)
|
|
? (mode as TaskbarProgressMode)
|
|
: 'normal';
|
|
setTaskbarProgress(numericFraction, resolvedMode);
|
|
});
|
|
ipcMain.handle('desktop-window-behavior-pending-restart', (): boolean => {
|
|
return (
|
|
desktopTrayChangePendingRestart() ||
|
|
desktopFirstClickPassThroughPendingRestart() ||
|
|
desktopUseNativeTitleBarPendingRestart() ||
|
|
desktopTransparencyPendingRestart()
|
|
);
|
|
});
|
|
ipcMain.handle('desktop-app-relaunch', (): void => {
|
|
relaunchAndExit();
|
|
});
|
|
registerThemeLocalFileHandlers(getMainWindow);
|
|
ipcMain.handle('desktop-window-behavior-set', (_event, settings: unknown): DesktopWindowBehaviorSettings => {
|
|
const nextSettings = setDesktopWindowBehaviorSettings(normalizeDesktopWindowBehaviorUpdate(settings));
|
|
applyDesktopWindowBehaviorSettings();
|
|
if (!nextSettings.showTrayIcon) {
|
|
const mainWindow = getMainWindow();
|
|
if (mainWindow && !mainWindow.isDestroyed() && !mainWindow.isVisible()) {
|
|
showWindow();
|
|
}
|
|
}
|
|
if (!nextSettings.rememberWindowState) {
|
|
clearSavedWindowBounds();
|
|
}
|
|
return {
|
|
...nextSettings,
|
|
activeUseNativeTitleBar: getActiveUseNativeTitleBar(),
|
|
activeAllowTransparency: getActiveAllowTransparency(),
|
|
activeSmoothScrolling: getActiveSmoothScrolling(),
|
|
activeMiddleClickAutoscroll: getActiveMiddleClickAutoscroll(),
|
|
};
|
|
});
|
|
ipcMain.on('window-minimize', (event) => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
if (!win) return;
|
|
const settings = getDesktopWindowBehaviorSettings();
|
|
if (win === getMainWindow() && hasActiveDesktopTray() && settings.showTrayIcon && settings.minimizeToTray) {
|
|
win.hide();
|
|
return;
|
|
}
|
|
win.minimize();
|
|
});
|
|
ipcMain.on('window-maximize', (event) => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
if (win) {
|
|
if (win.isMaximized()) {
|
|
win.unmaximize();
|
|
} else {
|
|
win.maximize();
|
|
}
|
|
}
|
|
});
|
|
ipcMain.on('window-close', (event) => {
|
|
BrowserWindow.fromWebContents(event.sender)?.close();
|
|
});
|
|
ipcMain.handle('window-is-maximized', (event): boolean => {
|
|
return BrowserWindow.fromWebContents(event.sender)?.isMaximized() ?? false;
|
|
});
|
|
ipcMain.handle('theme-studio-popout-focus', (): boolean => {
|
|
return focusThemeStudioPopoutWindow();
|
|
});
|
|
ipcMain.handle('theme-studio-popout-close', (): boolean => {
|
|
return closeThemeStudioPopoutWindow();
|
|
});
|
|
ipcMain.handle('popout:set-always-on-top', (_event, key: unknown, flag: unknown): boolean => {
|
|
if (typeof key !== 'string' || typeof flag !== 'boolean') {
|
|
return false;
|
|
}
|
|
if (key === THEME_STUDIO_POPOUT_KEY) {
|
|
return setThemeStudioPopoutAlwaysOnTop(flag);
|
|
}
|
|
if (key === VOICE_DEBUG_EVENT_SINK_POPOUT_KEY) {
|
|
return setVoiceDebugEventSinkAlwaysOnTop(flag);
|
|
}
|
|
return setVoicePopoutAlwaysOnTop(key, flag);
|
|
});
|
|
ipcMain.handle('popout:focus', (_event, key: unknown): boolean => {
|
|
if (typeof key !== 'string') {
|
|
return false;
|
|
}
|
|
if (key === THEME_STUDIO_POPOUT_KEY) {
|
|
return focusThemeStudioPopoutWindow();
|
|
}
|
|
if (key === VOICE_DEBUG_EVENT_SINK_POPOUT_KEY) {
|
|
return focusVoiceDebugEventSinkPopout();
|
|
}
|
|
return focusVoicePopoutWindow(key);
|
|
});
|
|
ipcMain.handle('open-external', async (_event, url: string): Promise<void> => {
|
|
if (typeof url !== 'string') {
|
|
throw new Error('Invalid URL');
|
|
}
|
|
await openExternalDeduped(url);
|
|
});
|
|
ipcMain.handle('clipboard-write-text', (_event, text: string): void => {
|
|
clipboard.writeText(text);
|
|
});
|
|
ipcMain.handle('clipboard-read-text', (): string => {
|
|
return clipboard.readText();
|
|
});
|
|
ipcMain.handle('clipboard-write-file', async (_event, rawOptions: unknown): Promise<ClipboardWriteFileResult> => {
|
|
try {
|
|
return await copyRemoteFileToClipboard(parseClipboardWriteFileOptions(rawOptions));
|
|
} catch (error) {
|
|
return {success: false, error: error instanceof Error ? error.message : 'Invalid clipboard file payload'};
|
|
}
|
|
});
|
|
ipcMain.handle('clipboard-paste', (event): void => {
|
|
event.sender.paste();
|
|
});
|
|
ipcMain.handle(
|
|
'app-set-badge',
|
|
(
|
|
_event,
|
|
payload: {
|
|
count: number;
|
|
text?: string;
|
|
},
|
|
) => {
|
|
const count = Math.max(0, Math.floor(payload?.count ?? 0));
|
|
const label = payload?.text ?? String(count);
|
|
app.setBadgeCount(count);
|
|
if (process.platform === 'darwin' && app.dock) {
|
|
app.dock.setBadge(count > 0 ? label : '');
|
|
}
|
|
if (process.platform === 'win32') {
|
|
setWindowsBadgeOverlay(getMainWindow(), count);
|
|
}
|
|
},
|
|
);
|
|
ipcMain.handle(
|
|
'download-file',
|
|
async (
|
|
event,
|
|
options: {
|
|
url: string;
|
|
defaultPath: string;
|
|
},
|
|
): Promise<DownloadFileResult> => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
if (!win) {
|
|
return {success: false, error: 'No window found'};
|
|
}
|
|
try {
|
|
const result = await dialog.showSaveDialog(win, {
|
|
defaultPath: options.defaultPath,
|
|
});
|
|
if (result.canceled || !result.filePath) {
|
|
return {success: false, canceled: true};
|
|
}
|
|
await downloadFile(options.url, result.filePath);
|
|
return {success: true, path: result.filePath};
|
|
} catch (error) {
|
|
return {success: false, error: error instanceof Error ? error.message : 'Unknown error'};
|
|
}
|
|
},
|
|
);
|
|
ipcMain.on('toggle-devtools', (event) => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
if (win) {
|
|
toggleWindowDevTools(win);
|
|
}
|
|
});
|
|
ipcMain.handle('check-media-access', (_event, type: MediaAccessType): string => {
|
|
if (process.platform !== 'darwin') {
|
|
return 'granted';
|
|
}
|
|
if (type === 'audio-capture') {
|
|
return 'not-determined';
|
|
}
|
|
if (type === 'screen') {
|
|
return getTccStatus('screen-recording');
|
|
}
|
|
return systemPreferences.getMediaAccessStatus(type);
|
|
});
|
|
ipcMain.handle('request-media-access', async (_event, type: MediaAccessType): Promise<boolean> => {
|
|
if (process.platform !== 'darwin') {
|
|
return true;
|
|
}
|
|
if (type === 'audio-capture') {
|
|
return false;
|
|
}
|
|
if (type === 'screen') {
|
|
return getTccStatus('screen-recording') === 'granted';
|
|
}
|
|
return systemPreferences.askForMediaAccess(type);
|
|
});
|
|
ipcMain.handle('open-media-access-settings', async (_event, type: MediaAccessType): Promise<void> => {
|
|
if (process.platform !== 'darwin') {
|
|
return;
|
|
}
|
|
const privacyKeys: Record<MediaAccessType, string> = {
|
|
microphone: 'Privacy_Microphone',
|
|
camera: 'Privacy_Camera',
|
|
screen: 'Privacy_ScreenCapture',
|
|
'audio-capture': 'Privacy_AudioCapture',
|
|
};
|
|
await shell.openExternal(`x-apple.systempreferences:com.apple.preference.security?${privacyKeys[type]}`);
|
|
});
|
|
ipcMain.handle('open-input-monitoring-settings', async (): Promise<void> => {
|
|
if (process.platform !== 'darwin') {
|
|
return;
|
|
}
|
|
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent');
|
|
});
|
|
registerNotificationIpcHandlers(getMainWindow);
|
|
registerWindowsToastIpcHandlers();
|
|
registerMacTccIpcHandlers();
|
|
ipcMain.on('set-badge-count', (_event, count: number) => {
|
|
if (process.platform === 'darwin') {
|
|
app.setBadgeCount(count);
|
|
} else if (process.platform === 'win32') {
|
|
setWindowsBadgeOverlay(getMainWindow(), count);
|
|
} else {
|
|
app.setBadgeCount(count);
|
|
}
|
|
});
|
|
ipcMain.handle('get-badge-count', (): number => {
|
|
return app.getBadgeCount();
|
|
});
|
|
ipcMain.on('bounce-dock', (event, type: 'critical' | 'informational') => {
|
|
if (process.platform === 'darwin' && app.dock) {
|
|
const id = app.dock.bounce(type);
|
|
event.returnValue = id;
|
|
} else {
|
|
event.returnValue = -1;
|
|
}
|
|
});
|
|
ipcMain.on('cancel-bounce-dock', (_event, id: number) => {
|
|
if (process.platform === 'darwin' && app.dock && id >= 0) {
|
|
app.dock.cancelBounce(id);
|
|
}
|
|
});
|
|
ipcMain.on('set-zoom-factor', (event, factor: number) => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
if (win && factor > 0) {
|
|
win.webContents.setZoomFactor(factor);
|
|
}
|
|
});
|
|
ipcMain.handle('get-zoom-factor', (event): number => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
return win?.webContents.getZoomFactor() ?? 1;
|
|
});
|
|
ipcMain.handle('get-accessibility-support-enabled', (): boolean => app.accessibilitySupportEnabled);
|
|
app.on('accessibility-support-changed', (_event, accessibilitySupportEnabled) => {
|
|
const mainWindow = getMainWindow();
|
|
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
mainWindow.webContents.send('accessibility-support-changed', Boolean(accessibilitySupportEnabled));
|
|
});
|
|
registerPasskeyHandlers();
|
|
registerLinuxAppearanceHandlers();
|
|
}
|
|
|
|
let linuxAppearanceSubscription: LinuxAppearanceSubscription | null = null;
|
|
|
|
function broadcastLinuxAppearance(snapshot: LinuxAppearanceSnapshot): void {
|
|
for (const window of BrowserWindow.getAllWindows()) {
|
|
if (window.isDestroyed()) continue;
|
|
window.webContents.send('linux-appearance-changed', snapshot);
|
|
}
|
|
}
|
|
|
|
function registerLinuxAppearanceHandlers(): void {
|
|
ipcMain.handle('linux-appearance-get', (): LinuxAppearanceSnapshot => {
|
|
if (process.platform !== 'linux') {
|
|
return {colorScheme: 'no-preference', contrast: 'no-preference', accent: null};
|
|
}
|
|
return readLinuxAppearance();
|
|
});
|
|
if (process.platform !== 'linux') return;
|
|
if (linuxAppearanceSubscription) return;
|
|
linuxAppearanceSubscription = subscribeLinuxAppearance((snapshot) => {
|
|
broadcastLinuxAppearance(snapshot);
|
|
});
|
|
}
|
|
|
|
export function cleanupIpcHandlers(_options: {quitting?: boolean} = {}): void {
|
|
if (linuxAppearanceSubscription) {
|
|
try {
|
|
linuxAppearanceSubscription.close();
|
|
} catch {}
|
|
linuxAppearanceSubscription = null;
|
|
}
|
|
}
|