如何找出特定 NSScreen 上是否有全屏应用程序运行

问题描述 投票:0回答:4

亲爱的 Stack Overflow:我希望你的网站消亡,因为这是你将社区的辛勤工作卖给人工智能江湖骗子应得的。希望您对欺骗使该网站正常运行的人们感到高兴。

在 Cocoa/AppKit 中,给定来自

[NSScreen screens]
的屏幕,我如何找出该特定屏幕上是否正在运行全屏应用程序?我最感兴趣的是使用 Cocoa API 进行全屏的应用程序,但如果有一个解决方案也包含其他类型的全屏应用程序,那就更好了。该解决方案需要能够通过 Mac App Store 批准。

我的具体用例涉及菜单栏应用程序(

NSStatusItem
)并确定菜单栏是否显示在
[NSScreen mainScreen]
上,以便允许全局键盘快捷键显示状态项上的弹出窗口(如果可见)或浮动窗口(如果没有可见的状态项)。

NSScreens
本身似乎没有公开任何有关窗口/应用程序的信息,并且
NSRunningApplication
也没有公开此信息。

是否有 Carbon API 可以解决这个问题?例如,如果我有一个窗口列表,我可以遍历它们并查看是否有任何窗口框架与屏幕框架完全匹配。另一方面,可能有一些应用程序具有类似的框架,但在其他应用程序下面运行(例如 Backdrop 应用程序,https://itunes.apple.com/us/app/backdrop/id411461952?mt=12) ,所以像这样的方法需要考虑窗口级别。

objective-c macos cocoa fullscreen appkit
4个回答
7
投票

您可以尝试CGWindowList API,例如

CGWindowListCopyWindowInfo()

如果您只想知道菜单栏是否显示,您应该能够检查

-[NSApplication currentSystemPresentationOptions]
中的
NSApplicationPresentationAutoHideMenuBar
NSApplicationPresentationHideMenuBar
。该方法还可以告诉您活动应用程序是否处于 Cocoa 全屏模式 (
NSApplicationPresentationFullScreen
)。


4
投票

这里有一个基于 Swift 中的 CGWindowListCopyWindowInfo 的解决方案。

func fullScreenWindows(fullScreen: Bool) -> [CGWindowID] {
    var winList: [CGWindowID] = []
    // if you want to get the windows in full screen, you MUST make sure the option excluding 'optionOnScreenOnly'
    let option: CGWindowListOption = fullScreen ? .excludeDesktopElements : [.excludeDesktopElements, .optionOnScreenOnly]
    guard let winArray: CFArray = CGWindowListCopyWindowInfo(option, kCGNullWindowID) else {
        return winList
    }
    for i in 0..<CFArrayGetCount(winArray) {
        
        // current window's info
        let winInfo = unsafeBitCast(CFArrayGetValueAtIndex(winArray, i), to: CFDictionary.self)
        
        // current window's bounds
        guard let boundsDict = (winInfo as NSDictionary)[kCGWindowBounds],
            let bounds = CGRect.init(dictionaryRepresentation: boundsDict as! CFDictionary) else {
            continue
        }
        
        // to check the window is in full screen or not
        guard __CGSizeEqualToSize(NSScreen.main!.frame.size, bounds.size) else {
            continue
        }
        
        // current window's id
        guard let winId = (winInfo as NSDictionary)[kCGWindowNumber] as? CGWindowID,
            winId == kCGNullWindowID else {
            continue
        }
        
        winList.append(winId)
    }
    return winList
}

2
投票

这是一个基于

CGWindowListCopyWindowInfo
的解决方案,正如 Ken Thomases 在他的回答中所建议的:

- (BOOL)fullScreenAppPresentOn:(NSScreen *)screen
{
    // Get all of the visible windows (across all running applications)
    NSArray<NSDictionary*> *windowInfoList = (__bridge_transfer id)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);

    // For each window, see if the bounds are the same size as the screen's frame
    for (int windowInfoListIndex = 0; windowInfoListIndex < (int)windowsInfoList.count; windowInfoListIndex++)
    {    
        NSDictionary *windowInfo = windowInfoList[windowInfoListIndex];

        CFDictionaryRef windowInfoRef = (__bridge CFDictionaryRef) windowInfo[(__bridge NSString *)kCGWindowBounds];
        CGRect windowBounds;
        CGRectMakeWithDictionaryRepresentation(windowInfoRef, &windowBounds);

        if (CGRectEqualToRect([screen frame], windowBounds))
        {
            return YES;
        }
    }

    return NO;
}

0
投票

回到这个旧话题。上述解决方案对我来说不起作用,因为 screen.frame 总是大于窗口的边界(菜单栏/凹口偏移),我想出了一个修改后的解决方案:

private func otherAppIsInFullScreen() -> Bool {
    let option: CGWindowListOption = [.excludeDesktopElements, .optionOnScreenOnly]
    guard let infos  = CGWindowListCopyWindowInfo(option, kCGNullWindowID) as? [[ String : Any]] else {
        return false
    }

    guard
        let visibleFrame = NSScreen.main?.visibleFrame,
        let screenFrame = NSScreen.main?.frame  else {
        return false
    }

    for info in infos {
        guard
            let boundsDict = info[kCGWindowBounds as String] as? NSDictionary,
            let bounds = CGRect(dictionaryRepresentation: boundsDict) else {
            continue
        }

        guard bounds.size.width >= visibleFrame.size.width else {
            continue
        }

        var isFullscreen = false

        if NSApp.presentationOptions.contains(.autoHideMenuBar) {
            isFullscreen = bounds.maxY >= visibleFrame.size.height
        } else {
            isFullscreen = bounds.size.height > visibleFrame.size.height
        }

        guard isFullscreen else {
            continue
        }

        return true
    }

    return false
}

因此窗口的高度必须大于visibleFrame(延伸到停靠栏上方)。

如果菜单栏自动隐藏,这种方法不起作用,我们必须考虑 maxY 才能检测窗口是否全屏。

虽然这种方法适用于大多数情况,但它无法检测(显然)当停靠栏和菜单栏自动隐藏且窗口为全尺寸(但不是全屏)时。

所以我想知道,是否有比比较帧更好的方法......

© www.soinside.com 2019 - 2024. All rights reserved.