I'm writing a "UIElement" app that shows a status window on the side of the screen, similar to the Dock.
Now, when a program takes over the entire screen, I need to hide my status window, just like the Dock does.
What are my options to detect this and the inverse event?
I like to avoid polling via a timed event and also cannot use undocumented tricks (such as suggested here)
Registering a Carbon Event Handler for the kEventAppSystemUIModeChanged
event isn't sufficient - it works to detect VLC's full screen mode, but not for modern Cocoa apps that use the new fullscreen widget at the top right corner of their windows.
Similarly, following Apple's instructions about the NSApplication presentationOptions API by observing changes to the currentSystemPresentationOptions property does not help, either - again, it only informs about VLC's fullscreen mode, but not about apps using the window' top right fullscreen widget.
Monitoring changes to the screen configuration using CGDisplayRegisterReconfigurationCallback
is not working because there aren't any callbacks for these fullscreen modes.
The method to exit full screen Mac is also simple. If you're asking yourself “how do i get out of fullscreen mode?” here's are your options – and they're mostly the same as entering full screen: In an app's window, click the green button on the top left. While using an app, press Command + Control + F.
2] Use Ctrl+Shift+Esc and then Alt+O To force quit a Full-Screen Always-On-Top Program in Windows 10: Press Ctrl+Shift+Esc to launch the Task Manager.
On the iPhone (the only phone I can test with), there is a simple way to detect for full screen mode: If you are using the web page in standard Safari mode, this value will be False. If you are using the web page in app mode (full screen mode), this value will be True.
When the cursor icon changes to a diagonal bi-directional arrow, press and hold the Option key and double click. This would maximize the window to occupy the entire screen without it entering into full-screen mode.
Move the cursor to any one of the four corners of the Window. When the cursor icon changes to a diagonal bi-directional arrow, press and hold the Option key and double click. This would maximize the window to occupy the entire screen without it entering into full-screen mode.
As you can see above, the "full screen suggestion" note is hidden by default. The note only gets shown if the user is both on a device that supports full screen mode and is not currently taking advantage of it. Furthermore, the touch-event handlers only get bound if the suggestion note is going to be shown.
Based on @Chuck's suggestion, I've come up with a solution that works somewhat, but may not be foolproof.
The solution is based on the assumption that 10.7's new fullscreen mode for windows moves these windows to a new Screen Space. Therefore, we subscribe to notifications for changes to the active space. In that notification handler, we check the window list to detect whether the menubar is included. If it is not, it probably means that we're in a fullscreen space.
Checking for the presence of the "Menubar" window is the best test I could come up with based on Chuck's idea. I don't like it too much, though, because it makes assumptions on the naming and presence of internally managed windows.
Here's the test code that goes inside AppDelegate.m, which also includes the test for the other app-wide fullscreen mode:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSApplication *app = [NSApplication sharedApplication];
// Observe full screen mode from apps setting SystemUIMode
// or invoking 'setPresentationOptions'
[app addObserver:self
forKeyPath:@"currentSystemPresentationOptions"
options:NSKeyValueObservingOptionNew
context:NULL];
// Observe full screen mode from apps using a separate space
// (i.e. those providing the fullscreen widget at the right
// of their window title bar).
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification
object:NULL queue:NULL
usingBlock:^(NSNotification *note)
{
// The active space changed.
// Now we need to detect if this is a fullscreen space.
// Let's look at the windows...
NSArray *windows = CFBridgingRelease(CGWindowListCopyWindowInfo
(kCGWindowListOptionOnScreenOnly, kCGNullWindowID));
//NSLog(@"active space change: %@", windows);
// We detect full screen spaces by checking if there's a menubar
// in the window list.
// If not, we assume it's in fullscreen mode.
BOOL hasMenubar = NO;
for (NSDictionary *d in windows) {
if ([d[@"kCGWindowOwnerName"] isEqualToString:@"Window Server"]
&& [d[@"kCGWindowName"] isEqualToString:@"Menubar"]) {
hasMenubar = YES;
break;
}
}
NSLog(@"fullscreen: %@", hasMenubar ? @"No" : @"Yes");
}
];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqual:@"currentSystemPresentationOptions"]) {
NSLog(@"currentSystemPresentationOptions: %@", [change objectForKey:NSKeyValueChangeNewKey]); // a value of 4 indicates fullscreen mode
}
}
Since my earlier answer doesn't work for detecting full screen mode between apps, I did some experimentation. Starting with the solution that Thomas Tempelmann came up with of checking the presence of menu bar, I found a variation that I think could be more reliable.
The problem with checking for the menu bar is that in full screen mode you can move the mouse cursor to the top of the screen to make the menu bar appear, but you're still in full screen mode. I did some crawling through the CGWindow
info, and discovered that when I enter full screen, there is window named "Fullscreen Backdrop"
owned by the "Dock"
, and it's not there when not in full screen mode.
This is on Catalina (10.15.6) in an Xcode playground, so it should be tested in a real app, and on Big Sur (or whatever the current OS is, when you're reading this).
Here's the code (in Swift... easier to quickly test something)
func isFullScreen() -> Bool
{
guard let windows = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) else {
return false
}
for window in windows as NSArray
{
guard let winInfo = window as? NSDictionary else { continue }
if winInfo["kCGWindowOwnerName"] as? String == "Dock",
winInfo["kCGWindowName"] as? String == "Fullscreen Backdrop"
{
return true
}
}
return false
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With