简体   繁体   English

在 Mac OS X 中获取当前活动窗口/文档的标题

[英]Get the title of the current active Window/Document in Mac OS X

Refering to a previously asked question , I would like to know how to get the title of the current active document.参考以前问过的问题,我想知道如何获取当前活动文档的标题。

I tried the script mention in the answers to the question above.我尝试了上面问题的答案中提到的脚本。 This works, but only gives me the name of the application.这有效,但只给了我应用程序的名称。 For example, I am writing this question: When I fire up the script it gives me the name of the application, ie "Firefox".例如,我正在写这个问题:当我启动脚本时,它会给我应用程序的名称,即“Firefox”。 This is pretty neat, but does not really help.这非常简洁,但并没有真正帮助。 I would rather like to capture the title of my current active document.我宁愿捕获我当前活动文档的标题。 See the image.看图片。

Firefox title http://img.skitch.com/20090126-nq2egknhjr928d1s74i9xixckf.jpg Firefox 标题 http://img.skitch.com/20090126-nq2egknhjr928d1s74i9xixckf.jpg

I am using Leopard, so no backward compatibility needed.我正在使用 Leopard,因此不需要向后兼容。 Also I am using Python's Appkit to gain access to the NSWorkspace class, but if you tell me the Objective-C code, I could figure out the translation to Python.此外,我正在使用 Python 的 Appkit 来访问 NSWorkspace class,但如果你告诉我 Objective-C 代码,我可以找出 Python 的翻译。


Ok, I've got a solution which is not very satisfing, thats why I don't mark Koen Bok's answer. 好的,我有一个不太令人满意的解决方案,这就是为什么我不标记 Koen Bok 的答案。 At least not yet. 至少现在还没有。

 tell application "System Events" set frontApp to name of first application process whose frontmost is true end tell tell application frontApp if the (count of windows) is not 0 then set window_name to name of front window end if end tell

Save as script and invoke it with osascript from the shell.另存为脚本并使用 shell 中的 osascript 调用它。

As far as I know your best bet is wrapping an AppleScript. 据我所知,你最好的选择是包装一个AppleScript。 But AppleScript is magic to me so I leave it as an exercise for the questioner :-) 但AppleScript对我来说很神奇,所以我把它作为提问者的练习:-)

This might help a little: A script to resize frontmost two windows to fill screen - Mac OS X Hints 这可能有点帮助: 一个脚本调整最前面的两个窗口以填充屏幕 - Mac OS X提示

In Objective-C, the short answer, using a little Cocoa and mostly the Carbon Accessibility API is: 在Objective-C中,使用一点Cocoa和主要是Carbon Accessibility API的简短答案是:

// Get the process ID of the frontmost application.
NSRunningApplication* app = [[NSWorkspace sharedWorkspace]
                              frontmostApplication];
pid_t pid = [app processIdentifier];

// See if we have accessibility permissions, and if not, prompt the user to
// visit System Preferences.
NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES};
Boolean appHasPermission = AXIsProcessTrustedWithOptions(
                             (__bridge CFDictionaryRef)options);
if (!appHasPermission) {
   return; // we don't have accessibility permissions

// Get the accessibility element corresponding to the frontmost application.
AXUIElementRef appElem = AXUIElementCreateApplication(pid);
if (!appElem) {
  return;
}

// 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;
}

// 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;
}

// Success! Now, do something with the title, e.g. copy it somewhere.

// Once we're done with the title, release it.
CFRelease(title);

Alternatively, it may be simpler to use the CGWindow API, as alluded to in this StackOverflow answer . 或者,使用CGWindow API可能更简单,正如StackOverflow答案中所提到的那样。

refered to https://stackoverflow.com/a/23451568/11185460参考 https://stackoverflow.com/a/23451568/11185460

package main

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Cocoa
#import <Cocoa/Cocoa.h>

int
GetFrontMostAppPid(void){
    NSRunningApplication* app = [[NSWorkspace sharedWorkspace]
                                  frontmostApplication];
    pid_t pid = [app processIdentifier];
    return pid;
}

CFStringRef
GetAppTitle(pid_t pid) {
    CFStringRef title = NULL;
    // Get the process ID of the frontmost application.
    //  NSRunningApplication* app = [[NSWorkspace sharedWorkspace]
    //                                frontmostApplication];
    //  pid_t pid = [app processIdentifier];

    // See if we have accessibility permissions, and if not, prompt the user to
    // visit System Preferences.
    NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES};
    Boolean appHasPermission = AXIsProcessTrustedWithOptions(
                                 (__bridge CFDictionaryRef)options);
    if (!appHasPermission) {
       return title; // we don't have accessibility permissions
    }
    // Get the accessibility element corresponding to the frontmost application.
    AXUIElementRef appElem = AXUIElementCreateApplication(pid);
    if (!appElem) {
      return title;
    }

    // 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 title;
    }

    // Finally, get the title of the frontmost window.
    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 title;
    }

    // Success! Now, do something with the title, e.g. copy it somewhere.

    // Once we're done with the title, release it.
    CFRelease(title);

    return title;
}
static inline CFIndex cfstring_utf8_length(CFStringRef str, CFIndex *need) {
  CFIndex n, usedBufLen;
  CFRange rng = CFRangeMake(0, CFStringGetLength(str));
  return CFStringGetBytes(str, rng, kCFStringEncodingUTF8, 0, 0, NULL, 0, need);
}
*/
import "C"
import (
    "github.com/shirou/gopsutil/v3/process"
    "reflect"
    "unsafe"
)

//import "github.com/shirou/gopsutil/v3/process"
func cfstringGo(cfs C.CFStringRef) string {
    var usedBufLen C.CFIndex
    n := C.cfstring_utf8_length(cfs, &usedBufLen)
    if n <= 0 {
        return ""
    }
    rng := C.CFRange{location: C.CFIndex(0), length: n}
    buf := make([]byte, int(usedBufLen))

    bufp := unsafe.Pointer(&buf[0])
    C.CFStringGetBytes(cfs, rng, C.kCFStringEncodingUTF8, 0, 0, (*C.UInt8)(bufp), C.CFIndex(len(buf)), &usedBufLen)

    sh := &reflect.StringHeader{
        Data: uintptr(bufp),
        Len:  int(usedBufLen),
    }
    return *(*string)(unsafe.Pointer(sh))
}

func main() {
    pid := C.GetFrontMostAppPid()

    ps, _ := process.NewProcess(int32(pid))
    title_ref := C.CFStringRef(C.GetAppTitle(pid))

    println(pid) // pid
    println(ps.Name()) // process name
    println(cfstringGo(title_ref)) // active window title
}


I then found this property wont change after it is called.然后我发现这个属性在调用后不会改变。 By this , only after we implement NSWorkspaceDidActivateApplicationNotification , we can monitor the change of activity window. But I didn't find any solution which can implement NSWorkspaceDidActivateApplicationNotification in golang. 这样,只有在我们实现NSWorkspaceDidActivateApplicationNotification之后,我们才能监听到activity window的变化。但是我没有找到任何可以在golang中实现NSWorkspaceDidActivateApplicationNotification的解决方案。

A workaround method is compile one go program and call it by another go program.一种解决方法是编译一个 go 程序并由另一个 go 程序调用它。 I then try full Objective-C code in here然后我在这里尝试完整的 Objective-C 代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM