亲爱的 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) ,所以像这样的方法需要考虑窗口级别。
您可以尝试CGWindowList API,例如
CGWindowListCopyWindowInfo()
。
如果您只想知道菜单栏是否显示,您应该能够检查
-[NSApplication currentSystemPresentationOptions]
中的 NSApplicationPresentationAutoHideMenuBar
或 NSApplicationPresentationHideMenuBar
。该方法还可以告诉您活动应用程序是否处于 Cocoa 全屏模式 (NSApplicationPresentationFullScreen
)。
这里有一个基于 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
}
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;
}
回到这个旧话题。上述解决方案对我来说不起作用,因为 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 才能检测窗口是否全屏。
虽然这种方法适用于大多数情况,但它无法检测(显然)当停靠栏和菜单栏自动隐藏且窗口为全尺寸(但不是全屏)时。
所以我想知道,是否有比比较帧更好的方法......