Skip to content

Commit 9ac0a1b

Browse files
committed
Fix Linux window size not persisting across logout/login
On Linux with HiDPI/fractional scaling, window size was not properly restored after logout and login. The issue was caused by: 1. Window bounds saved as logical pixels (scaled by display scale factor) 2. After logout/login, display scale might differ 3. Saved logical pixels resulted in different physical window sizes Changes: - Add scaleFactor field to lastWindowState config - Save display scale factor when storing window state - Adjust window size on Linux based on scale factor changes - Add SIGTERM/SIGHUP handlers to save state during shutdown - Validate window dimensions before saving This ensures the window maintains the same physical size regardless of display scaling changes between sessions.
1 parent fffc54b commit 9ac0a1b

2 files changed

Lines changed: 74 additions & 10 deletions

File tree

source/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type StoreType = {
1414
width: number;
1515
height: number;
1616
isMaximized: boolean;
17+
scaleFactor: number;
1718
};
1819
menuBarMode: boolean;
1920
showDockIcon: boolean;
@@ -89,13 +90,17 @@ const schema: Store.Schema<StoreType> = {
8990
isMaximized: {
9091
type: 'boolean',
9192
},
93+
scaleFactor: {
94+
type: 'number',
95+
},
9296
},
9397
default: {
9498
x: undefined,
9599
y: undefined,
96100
width: 800,
97101
height: 600,
98102
isMaximized: false,
103+
scaleFactor: 1,
99104
},
100105
},
101106
menuBarMode: {

source/index.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,34 @@ let previousMessageCount = 0;
8484
let dockMenu: Menu;
8585
let isDNDEnabled = false;
8686

87+
function saveWindowState(): void {
88+
if (!mainWindow) {
89+
return;
90+
}
91+
92+
const bounds = mainWindow.getNormalBounds();
93+
const {isMaximized} = config.get('lastWindowState');
94+
95+
// Validate window dimensions - ensure they're at least minimum size
96+
// This prevents saving corrupted/invalid window states on Linux
97+
const validWidth = Math.max(bounds.width, 400);
98+
const validHeight = Math.max(bounds.height, 200);
99+
100+
// Get the scale factor of the display where the window is located
101+
// This is needed to handle HiDPI/fractional scaling on Linux
102+
const display = electronScreen.getDisplayMatching(bounds);
103+
const {scaleFactor} = display;
104+
105+
config.set('lastWindowState', {
106+
x: bounds.x,
107+
y: bounds.y,
108+
width: validWidth,
109+
height: validHeight,
110+
isMaximized,
111+
scaleFactor,
112+
});
113+
}
114+
87115
if (!app.requestSingleInstanceLock()) {
88116
app.quit();
89117
}
@@ -289,13 +317,36 @@ function createMainWindow(): BrowserWindow {
289317
const shouldUseDarkColors = theme === 'dark' || (theme === 'system' && nativeTheme.shouldUseDarkColors);
290318
const backgroundColor = shouldUseDarkColors ? '#1e1e1e' : undefined;
291319

320+
// Handle HiDPI/fractional scaling on Linux
321+
// getNormalBounds() returns logical pixels (scaled by display scale factor)
322+
// We need to convert saved logical pixels to physical pixels, then to current display's logical pixels
323+
let windowWidth = lastWindowState.width;
324+
let windowHeight = lastWindowState.height;
325+
326+
if (is.linux && lastWindowState.scaleFactor) {
327+
// Get the display where the window will be created
328+
const display = electronScreen.getDisplayNearestPoint({
329+
x: lastWindowState.x ?? 0,
330+
y: lastWindowState.y ?? 0,
331+
});
332+
const currentScaleFactor = display.scaleFactor;
333+
const savedScaleFactor = lastWindowState.scaleFactor;
334+
335+
if (savedScaleFactor !== currentScaleFactor) {
336+
// Convert: saved_logical * saved_scale / current_scale = current_logical
337+
// This maintains the same physical pixel size
338+
windowWidth = Math.round((windowWidth * savedScaleFactor) / currentScaleFactor);
339+
windowHeight = Math.round((windowHeight * savedScaleFactor) / currentScaleFactor);
340+
}
341+
}
342+
292343
const win = new BrowserWindow({
293344
title: app.name,
294345
show: false,
295346
x: lastWindowState.x,
296347
y: lastWindowState.y,
297-
width: lastWindowState.width,
298-
height: lastWindowState.height,
348+
width: windowWidth,
349+
height: windowHeight,
299350
icon: is.linux ? caprineIconPath : undefined,
300351
minWidth: 400,
301352
minHeight: 200,
@@ -374,8 +425,7 @@ function createMainWindow(): BrowserWindow {
374425
});
375426

376427
win.on('resize', () => {
377-
const {isMaximized} = config.get('lastWindowState');
378-
config.set('lastWindowState', {...win.getNormalBounds(), isMaximized});
428+
saveWindowState();
379429
});
380430

381431
win.on('maximize', () => {
@@ -692,18 +742,27 @@ app.on('activate', () => {
692742
app.on('before-quit', () => {
693743
isQuitting = true;
694744

695-
// Checking whether the window exists to work around an Electron race issue:
696-
// https://github.com/sindresorhus/caprine/issues/809
697-
if (mainWindow) {
698-
const {isMaximized} = config.get('lastWindowState');
699-
config.set('lastWindowState', {...mainWindow.getNormalBounds(), isMaximized});
700-
}
745+
// Save window state before quitting
746+
saveWindowState();
701747

702748
if (is.windows) {
703749
app.setJumpList([]);
704750
}
705751
});
706752

753+
// Handle Linux shutdown signals - SIGTERM is sent during logout/shutdown
754+
if (is.linux) {
755+
process.on('SIGTERM', () => {
756+
saveWindowState();
757+
app.quit();
758+
});
759+
760+
process.on('SIGHUP', () => {
761+
saveWindowState();
762+
app.quit();
763+
});
764+
}
765+
707766
const notifications = new Map();
708767

709768
ipc.answerRenderer(

0 commit comments

Comments
 (0)