diff --git a/apps/finicky/src/main.go b/apps/finicky/src/main.go index 459af0d..4ee25d3 100644 --- a/apps/finicky/src/main.go +++ b/apps/finicky/src/main.go @@ -33,9 +33,10 @@ import ( var embeddedFiles embed.FS type ProcessInfo struct { - Name string `json:"name"` - BundleID string `json:"bundleId"` - Path string `json:"path"` + Name string `json:"name"` + BundleID string `json:"bundleId"` + Path string `json:"path"` + WindowTitle string `json:"windowTitle,omitempty"` } type UpdateInfo struct { @@ -254,7 +255,7 @@ func getConfigOption(optionName string, defaultValue bool) bool { } //export HandleURL -func HandleURL(url *C.char, name *C.char, bundleId *C.char, path *C.char, openInBackground C.bool) { +func HandleURL(url *C.char, name *C.char, bundleId *C.char, path *C.char, windowTitle *C.char, openInBackground C.bool) { var opener ProcessInfo if name != nil && bundleId != nil && path != nil { @@ -263,6 +264,9 @@ func HandleURL(url *C.char, name *C.char, bundleId *C.char, path *C.char, openIn BundleID: C.GoString(bundleId), Path: C.GoString(path), } + if windowTitle != nil { + opener.WindowTitle = C.GoString(windowTitle) + } } urlString := C.GoString(url) @@ -341,12 +345,16 @@ func evaluateURL(vm *goja.Runtime, url string, opener *ProcessInfo) (*browser.Br vm.Set("url", resolvedURL) if opener != nil { - vm.Set("opener", map[string]interface{}{ + openerMap := map[string]interface{}{ "name": opener.Name, "bundleId": opener.BundleID, "path": opener.Path, - }) - slog.Debug("Setting opener", "name", opener.Name, "bundleId", opener.BundleID, "path", opener.Path) + } + if opener.WindowTitle != "" { + openerMap["windowTitle"] = opener.WindowTitle + } + vm.Set("opener", openerMap) + slog.Debug("Setting opener", "name", opener.Name, "bundleId", opener.BundleID, "path", opener.Path, "windowTitle", opener.WindowTitle) } else { vm.Set("opener", nil) slog.Debug("No opener detected") diff --git a/apps/finicky/src/main.h b/apps/finicky/src/main.h index fe1e983..e8029c4 100644 --- a/apps/finicky/src/main.h +++ b/apps/finicky/src/main.h @@ -9,7 +9,7 @@ #include #include -extern void HandleURL(char *url, char *name, char *bundleId, char *path, bool openInBackground); +extern void HandleURL(char *url, char *name, char *bundleId, char *path, char *windowTitle, bool openInBackground); extern void QueueWindowDisplay(int launchedByUser); extern void ShowConfigWindow(); extern char* GetCurrentConfigPath(); diff --git a/apps/finicky/src/main.m b/apps/finicky/src/main.m index af11695..261191c 100644 --- a/apps/finicky/src/main.m +++ b/apps/finicky/src/main.m @@ -1,6 +1,7 @@ #include "main.h" #include "util/info.h" #import +#import #import #import @@ -114,7 +115,7 @@ - (bool)application:(NSApplication *)sender openFile:(NSString *)filename { NSString *urlString = [fileURL absoluteString]; // Handle the file URL the same way we handle other URLs - HandleURL((char*)[urlString UTF8String], NULL, NULL, NULL, false); + HandleURL((char*)[urlString UTF8String], NULL, NULL, NULL, NULL, false); return true; } @@ -138,6 +139,8 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event // to detect if Finicky was launched in the background bool finickyIsInFront = !self.keepRunning || [frontApp isEqual:[NSRunningApplication currentApplication]]; + char *windowTitle = NULL; + if (application) { NSString *appName = [application localizedName]; NSString *appBundleID = [application bundleIdentifier]; @@ -146,12 +149,31 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event name = [appName UTF8String]; bundleId = [appBundleID UTF8String]; path = [appPath UTF8String]; + + // Try to get the focused window title via Accessibility API + AXUIElementRef appElement = AXUIElementCreateApplication(pid); + if (appElement) { + AXUIElementRef focusedWindow = NULL; + AXError err = AXUIElementCopyAttributeValue(appElement, kAXFocusedWindowAttribute, (CFTypeRef *)&focusedWindow); + if (err == kAXErrorSuccess && focusedWindow) { + CFTypeRef titleValue = NULL; + AXError titleErr = AXUIElementCopyAttributeValue(focusedWindow, kAXTitleAttribute, &titleValue); + if (titleErr == kAXErrorSuccess && titleValue && CFGetTypeID(titleValue) == CFStringGetTypeID()) { + // strdup to keep a copy alive after CFRelease (no ARC in this project) + windowTitle = strdup([(NSString *)titleValue UTF8String]); + } + if (titleValue) CFRelease(titleValue); + CFRelease(focusedWindow); + } + CFRelease(appElement); + } } else { NSLog(@"No running application found with PID: %d", pid); } // If Finicky isn't frontmost, we take that to mean that the browser should, by default, be opened in the background - HandleURL((char*)url, (char*)name, (char*)bundleId, (char*)path, !finickyIsInFront); + HandleURL((char*)url, (char*)name, (char*)bundleId, (char*)path, windowTitle, !finickyIsInFront); + free(windowTitle); } - (bool)application:(NSApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType { @@ -168,7 +190,7 @@ - (bool)application:(NSApplication *)application continueUserActivity:(NSUserAct return false; } - HandleURL((char*)[[url absoluteString] UTF8String], NULL, NULL, NULL, false); + HandleURL((char*)[[url absoluteString] UTF8String], NULL, NULL, NULL, NULL, false); return true; } diff --git a/packages/config-api/src/configSchema.ts b/packages/config-api/src/configSchema.ts index b117067..cea27ae 100644 --- a/packages/config-api/src/configSchema.ts +++ b/packages/config-api/src/configSchema.ts @@ -31,6 +31,7 @@ const ProcessInfoSchema = z name: z.string(), bundleId: z.string(), path: z.string(), + windowTitle: z.string().optional(), }) .identifier("ProcessInfo");