ask for permissions instead of exiting when the hook fails

This commit is contained in:
bea
2026-09-29 12:10:42 +02:00
parent 005f22c581
commit 93a64ca1f3
2 changed files with 56 additions and 6 deletions
@@ -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;
}
}
@@ -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.