diff --git a/AutoRaise.mm b/AutoRaise.mm index 29fc9ef..99f6c11 100644 --- a/AutoRaise.mm +++ b/AutoRaise.mm @@ -28,7 +28,7 @@ #include #include -#define AUTORAISE_VERSION "3.5" +#define AUTORAISE_VERSION "3.6" #define STACK_THRESHOLD 20 #define __MAC_11_06_0 110600 @@ -57,10 +57,6 @@ static CGPoint oldCorrectedPoint = {0, 0}; #endif -// Lowering the polling interval increases responsiveness, but steals more cpu -// cycles. A workable, yet responsible value seems to be about 50 microseconds. -#define POLLING_MS 50 - // An activate delay of about 10 microseconds is just high enough to ensure we always // find the latest focused (main)window. This value should be kept as low as possible. #define ACTIVATE_DELAY_MS 10 @@ -98,6 +94,7 @@ #endif CFMachPortRef eventTap = NULL; +static NSScreen * previousScreen = NULL; static char pathBuffer[PROC_PIDPATHINFO_MAXSIZE]; static bool activated_by_task_switcher = false; static AXUIElementRef _accessibility_object = AXUIElementCreateSystemWide(); @@ -113,9 +110,11 @@ static const NSString * NoTitle = @""; static CGPoint desktopOrigin = {0, 0}; static CGPoint oldPoint = {0, 0}; +static bool ignoreSpaceChanged = false; static bool spaceHasChanged = false; static bool appWasActivated = false; static bool altTaskSwitcher = false; +static bool onScreenChangedOnly = true; static bool warpMouse = false; static bool verbose = false; static float warpX = 0.5; @@ -127,6 +126,7 @@ static int raiseTimes = 0; static int delayTicks = 0; static int delayCount = 0; +static int pollMillis = 0; #ifdef FOCUS_FIRST static int raiseDelayCount = 0; #endif @@ -526,7 +526,6 @@ inline bool main_window(AXUIElementRef _window) { } #endif -#if MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_12_00_0 inline NSScreen * findScreen(CGPoint point) { NSScreen * main_screen = NSScreen.screens[0]; point.y = NSMaxY(main_screen.frame) - point.y; @@ -537,7 +536,19 @@ inline bool main_window(AXUIElementRef _window) { } return NULL; } -#endif + +inline bool screenChanged(CGPoint point) { + bool changed = false; + NSScreen * screen = findScreen(point); + if (screen) { + changed = previousScreen != screen; + previousScreen = screen; + } + + if (verbose && changed) { NSLog(@"screen changed"); } + return changed; +} + //-----------------------------------------------notifications---------------------------------------------- void spaceChanged(); @@ -613,10 +624,10 @@ - (void)onTick:(NSNumber *)timerInterval { #ifdef FOCUS_FIRST - (void)windowFocused:(AXUIElementRef)_window { - if (verbose) { NSLog(@"Window focused, waiting %0.3fs", raiseDelayCount*POLLING_MS/1000.0); } + if (verbose) { NSLog(@"Window focused, waiting %0.3fs", raiseDelayCount*pollMillis/1000.0); } [self performSelector: @selector(onWindowFocused:) withObject: [NSNumber numberWithUnsignedLong: (uint64_t) _window] - afterDelay: raiseDelayCount*POLLING_MS/1000.0]; + afterDelay: raiseDelayCount*pollMillis/1000.0]; } - (void)onWindowFocused:(NSNumber *)_window { @@ -635,13 +646,15 @@ - (void)onWindowFocused:(NSNumber *)_window { const NSString *kScale = @"scale"; const NSString *kVerbose = @"verbose"; const NSString *kAltTaskSwitcher = @"altTaskSwitcher"; +const NSString *kIgnoreSpaceChanged = @"ignoreSpaceChanged"; const NSString *kIgnoreApps = @"ignoreApps"; const NSString *kMouseDelta = @"mouseDelta"; +const NSString *kPollMillis = @"pollMillis"; #ifdef FOCUS_FIRST const NSString *kFocusDelay = @"focusDelay"; -NSArray *parametersDictionary = @[kDelay, kWarpX, kWarpY, kScale, kVerbose, kAltTaskSwitcher, kFocusDelay, kIgnoreApps, kMouseDelta]; +NSArray *parametersDictionary = @[kDelay, kWarpX, kWarpY, kScale, kVerbose, kAltTaskSwitcher, kFocusDelay, kIgnoreSpaceChanged, kIgnoreApps, kMouseDelta, kPollMillis]; #else -NSArray *parametersDictionary = @[kDelay, kWarpX, kWarpY, kScale, kVerbose, kAltTaskSwitcher, kIgnoreApps, kMouseDelta]; +NSArray *parametersDictionary = @[kDelay, kWarpX, kWarpY, kScale, kVerbose, kAltTaskSwitcher, kIgnoreSpaceChanged, kIgnoreApps, kMouseDelta, kPollMillis]; #endif NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; @@ -744,6 +757,7 @@ - (void) validateParameters { #endif parameters[kDelay] = @"1"; } + if ([parameters[kPollMillis] intValue] < 20) { parameters[kPollMillis] = @"50"; } if ([parameters[kMouseDelta] floatValue] < 0) { parameters[kMouseDelta] = @"0"; } if ([parameters[kScale] floatValue] < 1) { parameters[kScale] = @"2.0"; } warpMouse = @@ -911,8 +925,10 @@ void onTick() { // spaceHasChanged has priority // over waiting for the delay if (mouseMoved) { return; } - raiseTimes = 3; - delayTicks = 0; + else if (!ignoreSpaceChanged) { + raiseTimes = 3; + delayTicks = 0; + } spaceHasChanged = false; } else if (delayTicks && mouseMoved) { delayTicks = 0; @@ -947,6 +963,13 @@ void onTick() { AXUIElementRef _mouseWindow = get_mousewindow(mousePoint); if (_mouseWindow) { + if (onScreenChangedOnly && (raiseTimes || delayCount == 1 || delayTicks == 1) && + !desktop_window(_mouseWindow) && !screenChanged(mousePoint)) { + CFRelease(_mouseWindow); + raiseTimes = 0; + delayTicks = 0; + return; + } pid_t mouseWindow_pid; if (AXUIElementGetPid(_mouseWindow, &mouseWindow_pid) == kAXErrorSuccess) { bool needs_raise = true; @@ -1089,29 +1112,33 @@ CGEventRef eventTapHandler(CGEventTapProxy proxy, CGEventType type, CGEventRef e int main(int argc, const char * argv[]) { @autoreleasepool { + ConfigClass * config = [[ConfigClass alloc] init]; + [config readConfig: argc]; + [config validateParameters]; + + delayCount = [parameters[kDelay] intValue]; + warpX = [parameters[kWarpX] floatValue]; + warpY = [parameters[kWarpY] floatValue]; + cursorScale = [parameters[kScale] floatValue]; + verbose = [parameters[kVerbose] boolValue]; + altTaskSwitcher = [parameters[kAltTaskSwitcher] boolValue]; + mouseDelta = [parameters[kMouseDelta] floatValue]; + pollMillis = [parameters[kPollMillis] intValue]; + ignoreSpaceChanged = [parameters[kIgnoreSpaceChanged] boolValue]; + printf("\nv%s by sbmpost(c) 2022, usage:\n\nAutoRaise\n", AUTORAISE_VERSION); - printf(" -delay <0=no-raise, 1=no-delay, 2=%dms, 3=%dms, ...>\n", POLLING_MS, POLLING_MS*2); + printf(" -pollMillis <20, 30, 40, 50, ...>\n"); + printf(" -delay <0=no-raise, 1=no-delay, 2=%dms, 3=%dms, ...>\n", pollMillis, pollMillis*2); #ifdef FOCUS_FIRST - printf(" -focusDelay <0=no-focus, 1=no-delay, 2=%dms, 3=%dms, ...>\n", POLLING_MS, POLLING_MS*2); + printf(" -focusDelay <0=no-focus, 1=no-delay, 2=%dms, 3=%dms, ...>\n", pollMillis, pollMillis*2); #endif printf(" -warpX <0.5> -warpY <0.5> -scale <2.0>\n"); printf(" -altTaskSwitcher \n"); + printf(" -ignoreSpaceChanged \n"); printf(" -ignoreApps \"\"\n"); printf(" -mouseDelta <0.1>\n"); printf(" -verbose \n\n"); - ConfigClass * config = [[ConfigClass alloc] init]; - [config readConfig: argc]; - [config validateParameters]; - - delayCount = [parameters[kDelay] intValue]; - warpX = [parameters[kWarpX] floatValue]; - warpY = [parameters[kWarpY] floatValue]; - cursorScale = [parameters[kScale] floatValue]; - verbose = [parameters[kVerbose] boolValue]; - altTaskSwitcher = [parameters[kAltTaskSwitcher] boolValue]; - mouseDelta = [parameters[kMouseDelta] floatValue]; - NSMutableArray * ignore; if (parameters[kIgnoreApps]) { ignore = [[NSMutableArray alloc] initWithArray: @@ -1119,21 +1146,28 @@ int main(int argc, const char * argv[]) { } else { ignore = [[NSMutableArray alloc] init]; } printf("Started with:\n"); + printf(" * pollMillis: %dms\n", pollMillis); if (delayCount) { - printf(" * delay: %dms\n", (delayCount-1)*POLLING_MS); + printf(" * delay: %dms\n", (delayCount-1)*pollMillis); + } else { + printf(" * delay: disabled\n"); } #ifdef FOCUS_FIRST if ([parameters[kFocusDelay] intValue]) { raiseDelayCount = delayCount; delayCount = [parameters[kFocusDelay] intValue]; - printf(" * focusDelay: %dms\n", (delayCount-1)*POLLING_MS); - } else { raiseDelayCount = 1; } + printf(" * focusDelay: %dms\n", (delayCount-1)*pollMillis); + } else { + raiseDelayCount = 1; + printf(" * focusDelay: disabled\n"); + } #endif if (warpMouse) { printf(" * warpX: %.1f, warpY: %.1f, scale: %.1f\n", warpX, warpY, cursorScale); printf(" * altTaskSwitcher: %s\n", altTaskSwitcher ? "true" : "false"); } + printf(" * ignoreSpaceChanged: %s\n", ignoreSpaceChanged ? "true" : "false"); for (id ignoreApp in ignore) { printf(" * ignoreApp: %s\n", [ignoreApp UTF8String]); } @@ -1185,7 +1219,7 @@ int main(int argc, const char * argv[]) { #else if (altTaskSwitcher || delayCount) { #endif - [workspaceWatcher onTick: [NSNumber numberWithFloat: POLLING_MS/1000.0]]; + [workspaceWatcher onTick: [NSNumber numberWithFloat: pollMillis/1000.0]]; } _dock_app = findDockApplication(); diff --git a/Info.plist b/Info.plist index 953ec6e..b3e0ac1 100644 --- a/Info.plist +++ b/Info.plist @@ -9,7 +9,7 @@ CFBundleGetInfoString Copyright © 2022 sbmpost CFBundleShortVersionString - 3.5 + 3.6 CFBundleIconFile AutoRaise CFBundleName diff --git a/README.md b/README.md index 5aa1ae3..ad673cd 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,15 @@ can only be stopped via "Activity Monitor" or the AppleScript provided near the **Command line usage:** - ./AutoRaise -delay 1 -focusDelay 0 -warpX 0.5 -warpY 0.1 -scale 2.5 -altTaskSwitcher false -ignoreApps "App1,App2" -mouseDelta 0.1 + ./AutoRaise -pollMillis 50 -delay 1 -focusDelay 0 -warpX 0.5 -warpY 0.1 -scale 2.5 -altTaskSwitcher false -ignoreSpaceChanged false -ignoreApps "App1,App2" -mouseDelta 0.1 *Note*: focusDelay is only supported when compiled with the "EXPERIMENTAL_FOCUS_FIRST" flag. - - delay: Raise delay, specified in units of 50ms. Disabled if 0. A delay > 1 requires the mouse to stop for a moment before raising. + - pollMillis: How often to poll the mouse position and consider a raise/focus. Lower values increase responsiveness but also CPU load. Default = 50 - - focusDelay: Focus delay, specified in units of 50ms. Disabled if 0. A delay > 1 requires the mouse to stop for a moment before focusing. + - delay: Raise delay, specified in units of pollMillis. Disabled if 0. A delay > 1 requires the mouse to stop for a moment before raising. + + - focusDelay: Focus delay, specified in units of pollMillis. Disabled if 0. A delay > 1 requires the mouse to stop for a moment before focusing. - warpX: A Factor between 0 and 1. Makes the mouse jump horizontally to the activated window. By default disabled. @@ -71,6 +73,8 @@ can only be stopped via "Activity Monitor" or the AppleScript provided near the - altTaskSwitcher: Set to true if you use 3rd party tools to switch between applications (other than standard command-tab). + - ignoreSpaceChanged: Do not immediately raise/focus after a space change. The default is false. + - ignoreApps: Comma separated list of apps for which you would like to disable focus/raise. - mouseDelta: Requires the mouse to move a certain distance. 0.0 = most sensitive whereas higher values decrease sensitivity. @@ -79,12 +83,14 @@ AutoRaise can read these parameters from a configuration file. To make this happ **~/.config/AutoRaise/config** file. The format is as follows: #AutoRaise config file + pollMillis=50 delay=1 focusDelay=0 warpX=0.5 warpY=0.1 scale=2.5 altTaskSwitcher=false + ignoreSpaceChanged=false ignoreApps="App1,App2" mouseDelta=0.1 @@ -133,21 +139,26 @@ like so: The output should look something like this: - v3.5 by sbmpost(c) 2022, usage: + v3.6 by sbmpost(c) 2022, usage: AutoRaise + -pollMillis <20, 30, 40, 50, ...> -delay <0=no-raise, 1=no-delay, 2=50ms, 3=100ms, ...> -focusDelay <0=no-focus, 1=no-delay, 2=50ms, 3=100ms, ...> -warpX <0.5> -warpY <0.5> -scale <2.0> -altTaskSwitcher + -ignoreSpaceChanged -ignoreApps "" -mouseDelta <0.1> -verbose Started with: + * pollMillis: 50ms * delay: 0ms + * focusDelay: disabled * warpX: 0.5, warpY: 0.1, scale: 2.5 * altTaskSwitcher: false + * ignoreSpaceChanged: false * ignoreApp: App1 * ignoreApp: App2 * mouseDelta: 0.1 @@ -157,10 +168,10 @@ The output should look something like this: * OLD_ACTIVATION_METHOD * EXPERIMENTAL_FOCUS_FIRST - 2022-08-06 00:37:22.187 AutoRaise[64697:2574991] AXIsProcessTrusted: YES - 2022-08-06 00:37:22.209 AutoRaise[64697:2574991] System cursor scale: 1.000000 - 2022-08-06 00:37:22.225 AutoRaise[64697:2574991] Got run loop source: YES - 2022-08-06 00:37:22.226 AutoRaise[64697:2574991] Registered app activated selector + 2022-09-02 22:40:50.498 AutoRaise[60894:1255026] AXIsProcessTrusted: YES + 2022-09-02 22:40:50.518 AutoRaise[60894:1255026] System cursor scale: 1.000000 + 2022-09-02 22:40:50.533 AutoRaise[60894:1255026] Got run loop source: YES + 2022-09-02 22:40:50.533 AutoRaise[60894:1255026] Registered app activated selector 2022-08-06 00:37:22.273 AutoRaise[64697:2574991] Desktop origin (-1280.000000, 0.000000) ... ...