diff --git a/espanso-info/src/cocoa/ffi.rs b/espanso-info/src/cocoa/ffi.rs index 4a94f7b..473de73 100644 --- a/espanso-info/src/cocoa/ffi.rs +++ b/espanso-info/src/cocoa/ffi.rs @@ -22,6 +22,7 @@ use std::os::raw::c_char; #[link(name = "espansoinfo", kind = "static")] extern "C" { pub fn info_get_title(buffer: *mut c_char, buffer_size: i32) -> i32; + pub fn info_get_title_fallback(buffer: *mut c_char, buffer_size: i32) -> i32; pub fn info_get_exec(buffer: *mut c_char, buffer_size: i32) -> i32; pub fn info_get_class(buffer: *mut c_char, buffer_size: i32) -> i32; } diff --git a/espanso-info/src/cocoa/mod.rs b/espanso-info/src/cocoa/mod.rs index 4f45fcd..1cd1cf5 100644 --- a/espanso-info/src/cocoa/mod.rs +++ b/espanso-info/src/cocoa/mod.rs @@ -21,7 +21,7 @@ use std::{ffi::CStr, os::raw::c_char}; use crate::{AppInfo, AppInfoProvider}; -use self::ffi::{info_get_class, info_get_exec, info_get_title}; +use self::ffi::{info_get_class, info_get_exec, info_get_title, info_get_title_fallback}; mod ffi; @@ -36,7 +36,7 @@ impl CocoaAppInfoProvider { impl AppInfoProvider for CocoaAppInfoProvider { fn get_info(&self) -> AppInfo { AppInfo { - title: self.get_title(), + title: self.get_title().or_else(|| self.get_title_fallback()), class: self.get_class(), exec: self.get_exec(), } @@ -88,4 +88,20 @@ impl CocoaAppInfoProvider { None } } + + // Fallback using Accessibility API instead of Carbon + fn get_title_fallback(&self) -> Option { + let mut buffer: [c_char; 2048] = [0; 2048]; + if unsafe { info_get_title_fallback(buffer.as_mut_ptr(), (buffer.len() - 1) as i32) } > 0 { + let string = unsafe { CStr::from_ptr(buffer.as_ptr()) }; + let string = string.to_string_lossy(); + if !string.is_empty() { + Some(string.to_string()) + } else { + None + } + } else { + None + } + } } \ No newline at end of file diff --git a/espanso-info/src/cocoa/native.h b/espanso-info/src/cocoa/native.h index 027299a..77b8edc 100644 --- a/espanso-info/src/cocoa/native.h +++ b/espanso-info/src/cocoa/native.h @@ -23,6 +23,7 @@ #include extern "C" int32_t info_get_title(char * buffer, int32_t buffer_size); +extern "C" int32_t info_get_title_fallback(char * buffer, int32_t buffer_size); extern "C" int32_t info_get_exec(char * buffer, int32_t buffer_size); extern "C" int32_t info_get_class(char * buffer, int32_t buffer_size); diff --git a/espanso-info/src/cocoa/native.mm b/espanso-info/src/cocoa/native.mm index 7bf3c77..86d9403 100644 --- a/espanso-info/src/cocoa/native.mm +++ b/espanso-info/src/cocoa/native.mm @@ -51,6 +51,51 @@ int32_t info_get_title(char *buffer, int32_t buffer_size) return 0; } +// Partially taken from: https://stackoverflow.com/questions/480866/get-the-title-of-the-current-active-window-document-in-mac-os-x/23451568#23451568 +int32_t info_get_title_fallback(char *buffer, int32_t buffer_size) +{ + @autoreleasepool { + // Get the process ID of the frontmost application. + NSRunningApplication* app = [[NSWorkspace sharedWorkspace] frontmostApplication]; + pid_t pid = [app processIdentifier]; + + AXUIElementRef appElem = AXUIElementCreateApplication(pid); + if (!appElem) { + return -1; + } + + // Get the accessibility element corresponding to the frontmost window + // of the frontmost application. + AXUIElementRef window = NULL; + if (AXUIElementCopyAttributeValue(appElem, + kAXFocusedWindowAttribute, (CFTypeRef*)&window) != kAXErrorSuccess) { + CFRelease(appElem); + return -2; + } + + // Finally, get the title of the frontmost window. + CFStringRef title = NULL; + AXError result = AXUIElementCopyAttributeValue(window, kAXTitleAttribute, + (CFTypeRef*)&title); + + // At this point, we don't need window and appElem anymore. + CFRelease(window); + CFRelease(appElem); + + if (result != kAXErrorSuccess) { + // Failed to get the window title. + return -3; + } + + if (CFStringGetCString(title, buffer, buffer_size, kCFStringEncodingUTF8)) { + CFRelease(title); + return 1; + } else { + return -4; + } + } +} + int32_t info_get_exec(char *buffer, int32_t buffer_size) { @autoreleasepool {