Noi*_*art 12 macos cocoa macos-carbon objective-c core-foundation
我已经建立了一个全球热键RegisterEventHotkey.当用户按下它时,它会获得当前聚焦的窗口CGWindowListCopyWindowInfo,然后我需要将它始终置于顶部.
如果当前窗口在我的进程中(我从中执行代码)我可以简单地将windowNumberfrom 转换CGWindowListCopyWindowInfo为a NSWindow并执行setLevel:
nswin = [NSApp windowWithWindowNumber:windowNumber]
[nswin setLevel: Int(CGWindowLevelForKey(kCGFloatingWindowLevelKey))]
Run Code Online (Sandbox Code Playgroud)
我的问题如果当前关注的窗口不在我的进程中,我无法做到这一点.你能告诉我怎么样吗?
我试过的东西:
我碰到过CGSSetWindowLevel的CGPrivate.h-无证的东西- https://gist.github.com/Noitidart/3664c5c2059c9aa6779f#file-cgsprivate-h-L63 -但是我记得我试过在过去这样的事情,但我想会得到一个错误连接到不在调用进程中的窗口.
只有窗口的所有者才能操纵它.因此,Apple拥有一个拥有所有窗口的通用所有者的概念,并且可以操纵它们.一次只能有一个通用所有者(Dock).
也许,无论如何,假装我的呼叫过程暂时成为码头?也许CGSGetConnectionIDForPSN对于码头然后使用该连接?
我的用法:我正在尝试复制我的开源,免费,浏览器插件的功能 - https://addons.mozilla.org/en-US/firefox/addon/topick/ - 所以我的调用过程如果Firefox.它现在适用于Windows和Linux,只需要弄清楚如何在Mac中为非Firefox窗口做这件事.
小智 1
看来您想让外部进程的窗口保持在所有其他应用程序之上,而我在这里提供的代码并不能完全满足您的需求,它至少有些相似,并且可能足以满足您的需求,取决于您的用例。在此示例中,我演示了如何将 a 保持CGWindowID在特定的NSWindow *. 注意 -NSWindow *是父窗口,它需要由您的应用程序拥有,但CGWindowID用于子窗口的 可以属于任何应用程序)。如果您希望NSWindow *成为子窗口,请将NSWindowBelow选项更改为NSWindowAbove。
这个解决方案有一个小问题,那就是当父窗口试图获得焦点但随后立即失去焦点时,这里和那里会出现一些轻微的闪烁 - 闪烁发生得非常快且间歇性,如果你超级强大,也许可以忽略它绝望的。
无论如何,代码是...
可可.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];
}
Run Code Online (Sandbox Code Playgroud)
子类.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
Run Code Online (Sandbox Code Playgroud)
子类.h
#import <Cocoa/Cocoa.h>
bool cocoa_wid_exists(CGWindowID wid);
@interface NSWindow(subclass)
- (void)setChildWindowWithNumber:(CGWindowID)wid;
- (void)windowDidBecomeKey:(NSNotification *)notification;
@end
Run Code Online (Sandbox Code Playgroud)
我付出了额外的努力,添加了一些函数来帮助您CGWindowID根据最前面的检索适当的内容CGWindowID,如果您事先通过 AppleScript 知道正确的内容CGWindowID,或者您愿意,您可以使用 将其带到前面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从给定的返回 a NSWindow *,前提是该NSWindow *属于当前应用程序,否则我相信您会遇到段错误。这就是我的测试中发生的情况,当您知道 an 的值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_tCGWindowID
unsigned long cocoa_get_wid_or_pid(bool wid);CGWindowID如果wid是则返回最前面true,否则最前面的进程ID(或pid_t)是结果。请注意,返回类型可以根据需要unsigned long安全地与CGWindowIDor进行转换。pid_t
void cocoa_wid_to_top(CGWindowID wid);pid_t尝试将属于与给定关联的进程 ID(或 )的所有窗口设置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);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
750 次 |
| 最近记录: |