看来您想让外部进程的窗口保持在所有其他应用程序之上,而我在这里提供的代码并不能完全满足您的需求,它至少有些相似,并且可能足以满足您的需求,取决于您的用例。在这个例子中,我演示了如何保持CGWindowID
在特定的之上NSWindow *
。注意——NSWindow *
是父窗口,它需要由您的应用程序拥有,但是CGWindowID
用于子窗口可以属于任何应用程序)。如果你想要NSWindow *
要成为子窗口,请更改NSWindowBelow
选项NSWindowAbove
.
这个解决方案有一个小问题,那就是当父窗口试图获得焦点但随后立即失去焦点时,这里和那里会出现一些轻微的闪烁 - 闪烁发生得非常快且间歇性,如果你超级强大,也许可以忽略它绝望的。
无论如何,代码是...
cocoa.mm
#import "subclass.h"
#import <Cocoa/Cocoa.h>
#import <sys/types.h>
NSWindow *cocoa_window_from_wid(CGWindowID wid) {
return [NSApp windowWithWindowNumber:wid];
}
CGWindowID cocoa_wid_from_window(NSWindow *window) {
return [window windowNumber];
}
bool cocoa_wid_exists(CGWindowID wid) {
bool result = false;
const CGWindowLevel kScreensaverWindowLevel = CGWindowLevelForKey(kCGScreenSaverWindowLevelKey);
CFArrayRef windowArray = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
CFIndex windowCount = 0;
if ((windowCount = CFArrayGetCount(windowArray))) {
for (CFIndex i = 0; i < windowCount; i++) {
NSDictionary *windowInfoDictionary =
(__bridge NSDictionary *)((CFDictionaryRef)CFArrayGetValueAtIndex(windowArray, i));
NSNumber *ownerPID = (NSNumber *)(windowInfoDictionary[(id)kCGWindowOwnerPID]);
NSNumber *level = (NSNumber *)(windowInfoDictionary[(id)kCGWindowLayer]);
if (level.integerValue < kScreensaverWindowLevel) {
NSNumber *windowID = windowInfoDictionary[(id)kCGWindowNumber];
if (wid == windowID.integerValue) {
result = true;
break;
}
}
}
}
CFRelease(windowArray);
return result;
}
pid_t cocoa_pid_from_wid(CGWindowID wid) {
pid_t pid;
const CGWindowLevel kScreensaverWindowLevel = CGWindowLevelForKey(kCGScreenSaverWindowLevelKey);
CFArrayRef windowArray = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
CFIndex windowCount = 0;
if ((windowCount = CFArrayGetCount(windowArray))) {
for (CFIndex i = 0; i < windowCount; i++) {
NSDictionary *windowInfoDictionary =
(__bridge NSDictionary *)((CFDictionaryRef)CFArrayGetValueAtIndex(windowArray, i));
NSNumber *ownerPID = (NSNumber *)(windowInfoDictionary[(id)kCGWindowOwnerPID]);
NSNumber *level = (NSNumber *)(windowInfoDictionary[(id)kCGWindowLayer]);
if (level.integerValue < kScreensaverWindowLevel) {
NSNumber *windowID = windowInfoDictionary[(id)kCGWindowNumber];
if (wid == windowID.integerValue) {
pid = ownerPID.integerValue;
break;
}
}
}
}
CFRelease(windowArray);
return pid;
}
unsigned long cocoa_get_wid_or_pid(bool wid) {
unsigned long result;
const CGWindowLevel kScreensaverWindowLevel = CGWindowLevelForKey(kCGScreenSaverWindowLevelKey);
CFArrayRef windowArray = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
CFIndex windowCount = 0;
if ((windowCount = CFArrayGetCount(windowArray))) {
for (CFIndex i = 0; i < windowCount; i++) {
NSDictionary *windowInfoDictionary =
(__bridge NSDictionary *)((CFDictionaryRef)CFArrayGetValueAtIndex(windowArray, i));
NSNumber *ownerPID = (NSNumber *)(windowInfoDictionary[(id)kCGWindowOwnerPID]);
NSNumber *level = (NSNumber *)(windowInfoDictionary[(id)kCGWindowLayer]);
if (level.integerValue == 0) {
NSNumber *windowID = windowInfoDictionary[(id)kCGWindowNumber];
result = wid ? windowID.integerValue : ownerPID.integerValue;
break;
}
}
}
CFRelease(windowArray);
return result;
}
void cocoa_wid_to_top(CGWindowID wid) {
CFIndex appCount = [[[NSWorkspace sharedWorkspace] runningApplications] count];
for (CFIndex i = 0; i < appCount; i++) {
NSWorkspace *sharedWS = [NSWorkspace sharedWorkspace];
NSArray *runningApps = [sharedWS runningApplications];
NSRunningApplication *currentApp = [runningApps objectAtIndex:i];
if (cocoa_pid_from_wid(wid) == [currentApp processIdentifier]) {
NSRunningApplication *appWithPID = currentApp;
NSUInteger options = NSApplicationActivateAllWindows;
options |= NSApplicationActivateIgnoringOtherApps;
[appWithPID activateWithOptions:options];
break;
}
}
}
void cocoa_wid_set_pwid(CGWindowID wid, CGWindowID pwid) {
[cocoa_window_from_wid(pwid) setChildWindowWithNumber:wid];
}
子类.mm
#import "subclass.h"
#import <Cocoa/Cocoa.h>
CGWindowID cocoa_wid = kCGNullWindowID;
CGWindowID cocoa_pwid = kCGNullWindowID;
@implementation NSWindow(subclass)
- (void)setChildWindowWithNumber:(CGWindowID)wid {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidBecomeKey:)
name:NSWindowDidUpdateNotification object:self];
cocoa_pwid = [self windowNumber]; cocoa_wid = wid;
[self orderWindow:NSWindowBelow relativeTo:wid];
}
- (void)windowDidBecomeKey:(NSNotification *)notification {
if (cocoa_wid_exists(cocoa_wid)) {
[self setCanHide:NO];
[self orderWindow:NSWindowBelow relativeTo:cocoa_wid];
} else {
cocoa_wid = kCGNullWindowID;
[self setCanHide:YES];
}
}
@end
子类.h
#import <Cocoa/Cocoa.h>
bool cocoa_wid_exists(CGWindowID wid);
@interface NSWindow(subclass)
- (void)setChildWindowWithNumber:(CGWindowID)wid;
- (void)windowDidBecomeKey:(NSNotification *)notification;
@end
我加倍努力并添加了一些功能来帮助您检索适当的CGWindowID
基于最前面的CGWindowID
,如果你知道正确的CGWindowID
事先,通过 AppleScript,或者您喜欢的任何方式,您可以使用cocoa_wid_to_top(wid)
,(如果用户允许),但是这对于同时拥有多个可见窗口的进程来说效果不佳,因为它会带来与给定关联的进程 ID 所拥有的所有窗口CGWindowID
到顶部,所以你可能没有CGWindowID
您必然希望位于窗口堆栈的绝对顶部。您可能希望将窗口置于堆栈顶部的原因是,在某些情况下,可能会打开一个窗口,您希望创建一个子窗口,但它出现在父窗口下方的屏幕上,从而强制您必须单击它才能有效地建立窗口的父/子关系。
文档如下...
NSWindow *cocoa_window_from_wid(CGWindowID wid);
返回一个NSWindow *
从给定的CGWindowID
,假设CGWindowID
属于当前应用程序,否则无效CGWindowID
返回,可以用常量表示kCGNullWindowID
.
CGWindowID cocoa_wid_from_window(NSWindow *window);
返回一个CGWindowID
从给定的NSWindow *
,假设NSWindow *
属于当前应用程序,否则我相信你会遇到段错误。这就是我的测试中当你知道某个值的值时会发生的情况NSWindow *
并尝试在不属于它的应用程序中使用它,所以不要尝试。
bool cocoa_wid_exists(CGWindowID wid);
退货true
如果一个窗口基于指定的CGWindowID
存在,不包括您的屏幕保护程序和桌面元素,false
如果没有。
pid_t cocoa_pid_from_wid(CGWindowID wid);
一个辅助函数cocoa_wid_to_top(wid)
它返回进程 ID,(或pid_t
),与给定的关联CGWindowID
.
unsigned long cocoa_get_wid_or_pid(bool wid);
返回最前面CGWindowID
if wid
is true
,否则是最前面的进程 ID,(或pid_t
),就是结果。注意返回类型unsigned long
可以安全地往返于CGWindowID
or pid_t
如所须。
void cocoa_wid_to_top(CGWindowID wid);
尝试将属于该进程 ID 的所有窗口(或pid_t
),与给定的关联CGWindowID
成为最顶级的应用程序。
现在最重要的功能...
void cocoa_wid_set_pwid(CGWindowID wid, CGWindowID pwid);
根据指定分配父窗口CGWindowID
到与正确关联的给定子窗口CGWindowID
。父窗口 ID,(或pwid
),必须由当前应用程序拥有,而子窗口 id,(或wid
),可能属于任何应用程序,不包括屏幕保护程序和桌面元素。如果父窗口或子窗口不复存在,它们就失去父子关系以避免被回收CGWindowID
来自继承关系。如果父母或孩子CGWindowID
不存在,它们将被设置为kCGNullWindowID
,这可靠地结束了关系。
请注意,此代码已在 Catalina 中进行了测试,并且确实按照撰写本文时所宣传的那样工作。
要使用我在 C 或 C++ 代码中提供的 cocoa 函数,您可以在标头中执行此操作:
typedef void NSWindow;
typedef unsigned long CGWindowID;
extern "C" NSWindow *cocoa_window_from_wid(CGWindowID wid);
extern "C" CGWindowID cocoa_wid_from_window(NSWindow *window);
extern "C" bool cocoa_wid_exists(CGWindowID wid);
extern "C" pid_t cocoa_pid_from_wid(CGWindowID wid);
extern "C" unsigned long cocoa_get_wid_or_pid(bool wid);
extern "C" void cocoa_wid_to_top(CGWindowID wid);
extern "C" void cocoa_wid_set_pwid(CGWindowID wid, CGWindowID pwid);