I have the following problem. There is a Cocoa app I have with UI, nib, etc. I need to be able to start the application in background or in foreground. In other words, I need NSApplicationActivationPolicyProhibited in the former and NSApplicationActivationPolicyRegular in the latter. Now...the decision of how to start depends on an argument pass to the startup of the app. So the code looks more or less like:
shouldBeBackground = // read this from argument - argv
if (shouldBeBackground) {
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
} else {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES];
// later on I do the makeKeyAndOrderFront:
}
Now the problem is with the Info.plist. If I put LSBackgroundOnly to 1 everything works as expected. But if I put it to 0 (or do not put the key), when the app starts it shows up the icon of the app in the dock for a couple of miliseconds and a little flash. I guess this is because the app is initialized with the default policy (starting to show the icon) and I switch it to background too late. This is also explained in: Cocoa - go to foreground/background programmatically
I know places WHERE to read the argument and do the previous IF even before I start cocoa or the app (for example, I can hook in MyOwnPrincipalClass +initialize), but even with that (even if I set the policy) it looks like the startup of Cocoa will set the default one. So it doesn't work.
The only solution I found is to put my IF at the beginning (before initialize cocoa) and depending on the argument, I write/modify the Info.plist (LSBackgroundOnly flag). This could work but it is a real hack. I would like to find a cleaner solution.
BTW, if I do the other way around: start up always as background and then make it foreground has a worst result since when going foreground the menubar of the app is not shown up automatically: you need to switch to another app and come back to make it appear....I searched in the internet and indeed it looks like a known bug/limitation.
BTW2: having 2 different binaries is not an option either.
Thanks for any help you can give me,
Just always startup as a background app at launch. Then if at startup you want to be a foreground app you can change the plist value programmatically, and then restart your application programmatically (you send an NSTask to launch your app before you terminate it). At app quit ensure that you are in background mode. Seems simple enough.
Related
I have an LSUIElement application that needs to keep it's status as an LSUIElement (it cannot have a dock icon) this application is launched and handled by a plugin.
When I change the app so it is a "Regular" app (without LSUIElement), [NSApp activateIgnoringOtherApps:YES] works perfectly. However when I make it an LSUIElement it shows the window, but the window is stuck behind safari (where the plugin is running) but on top of everything else. I am calling -[NSWindow makeKeyAndOrderFront:self]; but that doesn't make any difference.
To be clear the "plugin" side is irrelevant as the plugin and application are two separate processes. The plugin sends an event (over a mach port) to the application which calls some code to open the window and bring it to focus. The problem is the code to bring it to focus only works when it's not an LSUIElement application.
I've exhaustively searched for an existing call to make this work, I'm open to suggestions on how to fake a mouse click, or even run some applescript to make this work, maybe there's an undocumented method of doing this.
The Apple docs for LSUIElement state "The application does not appear in the Dock and does not have a menu bar, but it may be activated programmatically" but I cannot find the magic that enables this.
You need to activate your app first:
[NSApp activateIgnoringOtherApps:YES];
and then make your window become key window:
[NSWindow makeKeyAndOrderFront:nil];
This works in my project.
The following approach works for me in an app with "LSUIElement" set to "1", even if the entire application is using Cocoa:
ProcessSerialNumber psn;
if (noErr == GetCurrentProcess(&psn))
{
(OSStatus)SetFrontProcess(&psn);
}
(This is part of the Carbon API.)
Recently i've tried ColorSnapper app, and I've noticed that it works keeping other applications active.
(Check the screen below, as you can see safari window is still active and ColorSnapper keeps being active too).
How can is possible reproduce this effect ?
I believe these types of apps use LSBackgroundOnly or LSUIElement as Launch Services keys in their plist files (reference).
Here's a bit more about it.
I just try it and LSBackgroundOnly is the solution, but you must set the level of the window.
Example :
[mySpecialNSWindow setLevel:NSMainMenuWindowLevel];
This will display the special window above the windows of other applications.
I think the right approach is a mixture of (1) making the app LSBackgroundOnly, (2) using a custom transparent window as described here and set its level to NSFloatingWindowLevel, (3) using something like this in your app delegate to monitor mouse movements though your app is not active and, for example, to let your window follow the mouse position:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent *event) {
[window setFrameOrigin:[NSEvent mouseLocation]];
}];
You can then have views as you like in the (transparent) window, move them around and modify its contents according to the mouse position.
In a Cocoa app, is there a way to tell if another application currently is in full screen mode?
My application is configured to show up on all Spaces and listens for mouseEntered events to order itself to the front.
Problem is that when another app is in full screen mode and the user happens to move the mouse across the black area where my app's window is located, it is brought to the front (happens with multiple monitors).
I've only seen the above behavior with [self setCollectionBehavior: NSWindowCollectionBehaviorCanJoinAllSpaces]; enabled.
Here the other relevant code for my app.
- (void) mouseEntered:(NSEvent *)theEvent
{
// Don't do this when another app is in full screen mode:
[[self window] orderFront:self];
}
The above mentioned methods of registering for
"NSWindowWillEnterFullScreenNotification"
does not work, they can be used to notify your own app, using them we cannot detect whether any other application is in full screen mode or not.
However After trying out so many options found out FullScreen detector app at github this usefull link ..:):)
https://github.com/shinypb/FullScreenDetector.git
After a great deal of frustration, this worked for me to get a window that floats on all spaces except full-screen ones. I saw the fullScreenNone constant name, and since it described what I wanted, I tried it and found that it worked.
window.level = .floating
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenNone]
window.canHide = false
Hmm, have you ruled out using applescript/scriptingbridge? You can get the size of windows from applescript and compare them to the size of the screen. (or do you not know which screen a given app is on?)
Certain apps which are accessible will have an 'AXFullScreen' attribute on their windows. For example this works for some apps:
tell application "System Events"
tell process "Pages"
repeat with myWin in windows
get value of attribute "AXFullScreen" of myWin
end repeat
end tell
end tell
The real action seems to be down in carbon... MacWindows.h and CarbonEvents.h have references to "FullScreen" in them.
You will need to research this though.
Use notifications. For example:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(willEnterFull:)
name:NSWindowWillEnterFullScreenNotification
object:nil];
Actually, you'll probably want to use NSDistributedNotificationCenter instead, since it goes across processes.
You're adding your object as an observer, so that when something else posts a notification that it will enter full screen, your object will receive that notification.
The selector is the message/method you want called by the notification process.
The name parameter is the actual name of the notification. These are standard, unless you were to create a custom notification for something you would be using.
The object parameter is for specifying which object you want to receive notifications from. Since you want to know when ANY app is going full screen, you'd want to leave this nil.
Remember to remove your object as an observer before it's deallocated!
I have a simple single window app, with a drop target on it for files. This works fine. However, I also want that when it is started with a command line, it just processes those files instead, rather than showing a window.
Is there a delegate method in NSApplicationDelegate that I can prevent the window from showing, process the files and quit the application in?
You can set the window not to show when loaded from the nib file. There's a setting in the inspector in the Interface builder.
Then you can show the window if necessary, using methods described in this documentation.
But this will still show the icon in the Dock while your app is processing the file.
You can prevent showing the main window like Yuji said. There's also a notification called applicationDidFinishLaunching:
There you can process your files and quit the application with [[NSApplication sharedApplication] terminate:self];
The most maintainable and simple approach I've seen to this is to put your commandline version in a separate executable as a simple Foundation program, and then make your Cocoa program just call it, handling the UI. Doing it this way gets rid of many of the things you'd need to work around. You can then install links to your commandline version where you like.
If you dislike using NSTask for this, then put your logic into a shared framework, and layer GUI and commandline apps on top of it.
I want to write an app for Mac OS X. The app/utility would act according to preset schedule. I will have different time intervals at which I want this app to show a certain image in full screen regarding if there are other apps running at the time.
The real question is how to check this time interval in the background and bring this app in-front and enter full screen. I know how to go full screen, but I am stuck at bringing this app in-front of all other apps.
To schedule a method to be called after an interval, just use NSTimer and one of its +scheduledTimer... methods.
To force your application to be active, call [NSApp activateIgnoringOtherApps:YES].
If you want your window to appear above absolutely everything, including the screensaver, set its level to NSScreenSaverWindowLevel + 1.
The only solution that comes immediately to mind is use AppleScript, e.g. if you execute the following AppleScript from within your app:
tell application "MyBackgroundApp"
activate
end tell
There is an Apple Tech Note with sample code for sending AppleScript from a Cocoa app using NSAppleScript.
How to force Mac window to foreground?