gpt4 book ai didi

objective-c - 将非拥有的窗口设置为始终在顶部 - 就像应用程序 "Afloat"

转载 作者:行者123 更新时间:2023-12-03 16:03:50 32 4
gpt4 key购买 nike

我已经设置了一个全局热键 RegisterEventHotkey .当用户按下它时,它会使用 CGWindowListCopyWindowInfo 获取当前聚焦的窗口,然后我需要将它始终设置在顶部。

如果当前窗口在我的进程中(我正在执行代码),我可以简单地转换 windowNumber来自 CGWindowListCopyWindowInfoNSWindow并做 setLevel :

nswin = [NSApp windowWithWindowNumber:windowNumber]
[nswin setLevel: Int(CGWindowLevelForKey(kCGFloatingWindowLevelKey))]

我的问题 如果当前聚焦的窗口不在我的进程中,我将无法执行此操作。你能告诉我怎么做吗?

我试过的东西 :
  • 这个名为“Afloat”的应用程序使用“SIMBL”来实现这一点。在任何窗口中,您都可以按 Cmd + A,它将始终设置在顶部。但是,我正在尝试从我的普通桌面应用程序中使用 C/ObjC,而不使用像 SIMBL 这样的帮助程序。
  • 来源:Force keeping app window on top - Mac OS X
  • SIMBL:http://www.culater.net/software/SIMBL/SIMBL.php
  • 漂浮:https://www.macupdate.com/app/mac/22237/afloat
  • 我遇到了CGSSetWindowLevelCGPrivate.h - 未记录的东西 - https://gist.github.com/Noitidart/3664c5c2059c9aa6779f#file-cgsprivate-h-L63 - 但是我记得我过去尝试过类似的事情,但是当我尝试连接到不在调用过程中的窗口时会出错。
  • 它在这里说 - https://github.com/lipidity/CLIMac/blob/114dfee39d24809f62ccb000ea22dfda15c11ce8/src/CGS/CGSInternal/.svn/text-base/CGSConnection.h.svn-base#L82

    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如果 widtrue ,否则最前面的进程 ID(或 pid_t)是结果。注意返回类型 unsigned long可以安全地转换到 CGWindowIDpid_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/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com