From 93a64ca1f341a866e855c59bba8ee4e1e8f6d28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beatrice=20Dellac=C3=A0?= Date: Tue, 29 Sep 2026 12:10:42 +0200 Subject: [PATCH] ask for permissions instead of exiting when the hook fails --- .../java/wtf/beatrice/autosqueal/Main.java | 21 +++++++--- .../beatrice/autosqueal/ui/MainWindow.java | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/main/java/wtf/beatrice/autosqueal/Main.java b/src/main/java/wtf/beatrice/autosqueal/Main.java index b5087e0..bd06191 100644 --- a/src/main/java/wtf/beatrice/autosqueal/Main.java +++ b/src/main/java/wtf/beatrice/autosqueal/Main.java @@ -19,10 +19,15 @@ public class Main { LOGGER.info("Hello world!"); useSystemLookAndFeel(); - registerJNativeHook(); + boolean hookRegistered = registerJNativeHook(); // all Swing components must be created and updated on the EDT - SwingUtilities.invokeLater(mainWindow::init); + SwingUtilities.invokeLater(() -> { + mainWindow.init(); + if (!hookRegistered) { + mainWindow.showPermissionsHelp(); + } + }); } private static void useSystemLookAndFeel() { @@ -33,18 +38,22 @@ public class Main { } } - private static void registerJNativeHook() { + private static boolean registerJNativeHook() { LOGGER.info("Registering jnativehook library..."); try { GlobalScreen.registerNativeHook(); GlobalScreen.addNativeKeyListener(new KeyPressListener()); LOGGER.info("Successfully registered jnativehook library!"); + return true; } catch (NativeHookException ex) { - LOGGER.error("There was a problem registering the native hook."); - LOGGER.error(ex.getMessage()); + LOGGER.error("There was a problem registering the native hook.", ex); - System.exit(1); + // without the accessibility permission the hotkey cannot be + // registered and the mouse cannot be moved either: the app would + // look alive but do nothing. don't exit: keep the window and the + // tray icon usable, and tell the user what is missing. + return false; } } diff --git a/src/main/java/wtf/beatrice/autosqueal/ui/MainWindow.java b/src/main/java/wtf/beatrice/autosqueal/ui/MainWindow.java index 6d3f512..bcca3e1 100644 --- a/src/main/java/wtf/beatrice/autosqueal/ui/MainWindow.java +++ b/src/main/java/wtf/beatrice/autosqueal/ui/MainWindow.java @@ -15,6 +15,7 @@ import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; +import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -174,6 +175,46 @@ public class MainWindow Main.unregisterJNativeHook(); } + /** + * Explains that the app cannot work without its permissions, and offers + * a shortcut to the right system settings pane. Must be called on the + * EDT, after the window is visible. + */ + public void showPermissionsHelp() { + Object[] options = {"Open System Settings", "Close"}; + + int choice = JOptionPane.showOptionDialog(frame, + "autosqueal needs the accessibility permission to move the mouse and to\n" + + "listen for the ctrl+alt hotkey, and the screen recording one to\n" + + "show the screen preview. they cannot be granted while it is running.\n\n" + + "grant them in privacy & security, then restart autosqueal.", + "permissions needed", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, + options, + options[0]); + + if (choice == 0) { + openPrivacySettings(); + } + } + + /** Opens the privacy & security pane at the accessibility section. */ + private void openPrivacySettings() { + if (SystemUtil.getHostSystem() != SystemUtil.OperatingSystem.MAC_OS) { + return; + } + + try { + new ProcessBuilder("open", + "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") + .start(); + } catch (IOException ex) { + LOGGER.error("Could not open the system settings", ex); + } + } + /** * Toggles the automation on or off. Safe to call from any thread: the actual * work is always marshalled to the EDT, since it touches Swing components.