clean up on cmd-q and forced exits

This commit is contained in:
bea
2026-10-01 23:10:41 +02:00
parent b6e6080435
commit 77497fd9f7
2 changed files with 34 additions and 2 deletions
@@ -21,6 +21,10 @@ public class Main {
public static void main(String[] args) {
LOGGER.info("Hello world!");
// ⌘Q on macOS, the IDE stop button and signals all bypass
// windowClosing: make cleanup run on every exit path instead
Runtime.getRuntime().addShutdownHook(new Thread(mainWindow::cleanup, "autosqueal-shutdown"));
useSystemLookAndFeel();
boolean hookRegistered = registerJNativeHook();
@@ -66,6 +70,10 @@ public class Main {
}
public static void unregisterJNativeHook() {
if (!hookRegistered) {
return;
}
try {
GlobalScreen.unregisterNativeHook();
LOGGER.info("Successfully unregistered jnativehook library!");
@@ -12,6 +12,7 @@ import wtf.beatrice.autosqueal.util.SystemUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.desktop.QuitStrategy;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
@@ -46,6 +47,9 @@ public class MainWindow
private TrayIcon trayIcon;
private MenuItem trayToggleItem;
/** Whether cleanup already ran, so it can safely be called twice. */
private boolean cleanedUp = false;
/**
* Builds and shows the main window, and starts the automation.
* Must be called on the EDT.
@@ -63,6 +67,17 @@ public class MainWindow
}
});
// ⌘Q on macOS defaults to calling System.exit(0) directly, which
// bypasses windowClosing and skips cleanup: route quits through
// the window instead, so the hook is always released
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
} catch (UnsupportedOperationException ex) {
LOGGER.debug("Quit strategy not supported here", ex);
}
}
toggleButton = new JButton();
toggleButton.setBounds(new Rectangle((WINDOW_WIDTH / 2) - 60, WINDOW_HEIGHT - 60, 120, 30));
toggleButton.addActionListener(e -> toggleRunning());
@@ -167,8 +182,17 @@ public class MainWindow
return image;
}
/** Stops the automation and releases everything the app is holding. */
private void cleanup() {
/**
* Stops the automation and releases everything the app is holding.
* Safe to call more than once, from any thread: window closing and
* JVM shutdown hooks can race each other on the way out.
*/
public synchronized void cleanup() {
if (cleanedUp) {
return;
}
cleanedUp = true;
LOGGER.info("Shutting down...");
stopAutomation();