我已经设置了一个全局热键 RegisterEventHotkey
.当用户按下它时,它会使用 CGWindowListCopyWindowInfo
获取当前聚焦的窗口,然后我需要将它始终设置在顶部。
如果当前窗口在我的进程中(我正在执行代码),我可以简单地转换 windowNumber
来自 CGWindowListCopyWindowInfo
到 NSWindow
并做 setLevel
:
nswin = [NSApp windowWithWindowNumber:windowNumber]
[nswin setLevel: Int(CGWindowLevelForKey(kCGFloatingWindowLevelKey))]
我的问题 如果当前聚焦的窗口不在我的进程中,我将无法执行此操作。你能告诉我怎么做吗?
我试过的东西 :
CGSSetWindowLevel
在 CGPrivate.h
- 未记录的东西 - https://gist.github.com/Noitidart/3664c5c2059c9aa6779f#file-cgsprivate-h-L63 - 但是我记得我过去尝试过类似的事情,但是当我尝试连接到不在调用过程中的窗口时会出错。Only the owner of a window can manipulate it. So, Apple has the concept of a universal owner that owns all windows and can manipulate them all. There can only be one universal owner at a time (the Dock).
CGSGetConnectionIDForPSN
用于扩展坞然后使用该连接? 我的用途:我试图复制我的开源、免费、浏览器插件的功能 - https://addons.mozilla.org/en-US/firefox/addon/topick/ - 所以我的调用过程如果是 Firefox。它现在适用于 Windows 和 Linux,只需要弄清楚如何在 mac 中为非 Firefox 窗口执行此操作。
最佳答案
似乎您想让外部进程的窗口位于所有其他应用程序之上,而我在此处提供的代码并不能完全满足您的要求,它至少有些相似,并且可能足以满足您的需求,取决于您的用例。在这个例子中,我演示了如何保留 CGWindowID
在特定的 NSWindow *
之上.注意 - NSWindow *
是父窗口,它需要由您的应用程序拥有,但 CGWindowID
用于子窗口可以属于任何应用程序)。如果您想要 NSWindow *
要成为子窗口,更改 NSWindowBelow
选项 NSWindowAbove
.
这个解决方案有一个小问题,那就是这里和那里的一些小闪烁,当父窗口试图获得焦点但随后立即失去焦点时 - 闪烁发生得非常快且间歇性,如果您是 super 用户,也许可以忽略它绝望的。
无论如何,代码是...
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)
把它放在前面。 ,(如果用户允许),但是这对于同时拥有多个可见窗口的进程来说效果不佳,因为它带来了与给定 CGWindowID
关联的进程 ID 所拥有的所有窗口。到顶部,所以您可能没有 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)
的辅助函数返回与给定 pid_t
关联的进程 ID(或 CGWindowID
) .unsigned long cocoa_get_wid_or_pid(bool wid);
返回最前面 CGWindowID
如果 wid
是 true
,否则最前面的进程 ID(或 pid_t
)是结果。注意返回类型 unsigned long
可以安全地转换到 CGWindowID
或 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
来自继承关系。如果 parent 或 child 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);
关于objective-c - 将非拥有的窗口设置为始终在顶部 - 就像应用程序 "Afloat",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36075213/