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.)
Related
I've writing a macOS application with Qt. This application is a launch agent, meaning that it's launched by launchd and always running in the background. Normally the application only has a menu bar icon, and it doesn't have any open windows or a Dock icon. (i.e. the shared NSApplication instance's activationPolicy property is set to a value of NSApplicationActivationPolicyAccessory.)
However, there are a few menu items available in its menu bar item that open some windows, and when those windows are open the app switches to not being background-only any longer, so it will have a Dock icon and menu bar. (i.e. activationPolicy is changed to NSApplicationActivationPolicyRegular.) With there being a Dock icon, that means it's possible for the user to right-click it and open its menu, and that menu has its default menu item for quitting the application.
Since the app is a launch agent, though, and is intended to always be running, quitting it just causes launchd to relaunch the app. I'd like to disable or remove this menu item if possible, or otherwise prevent the user from being able to quit the app in this manner.
Is there any way to do that? If there's a way to accomplish this purely using Qt's functions that would be great, but if not it's fine for me to use macOS specific functions too.
I should add that because this is a Qt application, I can't use the same method as outlined here because I don't have access to the application's delegate. I would need to use another approach. (For example, it may be possible to swizzle methods on Qt's application delegate, though if there's a cleaner way to accomplish this than I'd much rather do that.)
After doing some more research, I've found that it's not possible to remove the "Quit" menu item from an application's Dock menu, or any of the other standard menu items there as they are created and handled by the Dock itself.
It is possible to stop an application from quitting when the user quits the application via the Dock. In a Qt application the method is to subclass QApplication and override its bool event(QEvent *) method. The overridden method should check for events of type QEvent::Close, call the ignore() method on the event, and then return true. Note though that this will stop the application from quitting via all other conventional methods as well.
edit: It is also possible to tell when the app is being quit via the Dock, at least when using Apple's native API. See: macOS: Is there any way to know when the user has tried to quit an application via its Dock icon?
By using Objective-C method swizzling it's possible to override the applicationShouldTerminate: method of Qt's application delegate and prevent a Qt app from being quit by the dock.
I'm working on a cross-platform command-line application (in C++ on
Win/Linux and ObjC++ on OSX) which sometimes creates an OpenGL
context. The OpenGL context and window creation code is obviously
different for the different platforms, but on OSX it's done through
NSOpenGLView and NSWindow. There's no nib, and it's not built with
Xcode (it uses a cross-platform build script).
On OSX, the window is created and works fine, but the OpenGL window
doesn't show up in the Application Switcher (Cmd-Tab). This means
that it's tricky to find the window if you 'lose it' behind other
windows, and can often only be found by going to Mission
Control/Expose.
My question is: is there a programmatic way (i.e. a message to send to
the NSWindow object or NSApplication) to ensure that a (unix-style)
command line application will show up in the Cmd-Tab list once the
window is created?
You need to transform the process from an accessory to a regular app. Call [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular].
Once you do that, though, the app will also have a menu bar when it's active. This is good because it's what users expect. However, you probably need to add appropriate items to the menus in that menu bar to get a decent user experience.
Your app will appear in the Dock as well as the Command-Tab application switcher. By default, an unbundled executable will get an icon that looks like a CRT display showing the word "exec". You can use [NSApp setApplicationIconImage:someImage] to set a better icon, although the Dock will revert to showing the generic executable icon briefly as the process exits.
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.
I have a foreground application that shows a NSStatusItem along with a menu (via NSStatusItem setMenu:(NSMenu *)menu). However, this menu does not display when I am looking at another app in fullscreen mode (say Safari) in Lion.
I know that I can make it work by setting NSBGOnly to true in the Info.plist file (or NSUIElement), but both methods will make my app icon disappear from the task switcher as well as hide the main menu once I manage to focus my app.
Finally, I have tried setting NSUIElement to true and do the following in my app upon startup (see also How to hide the Dock icon):
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
This made the menubar appear again as well as the dock icon but the original problem (status item menu does not show up in another fullscreen app) is visible again. Whatever I try, I can't win.
Any suggestions would be highly appreciated!
Unfortunately I think this is expected behaviour. Your app is considered a foreground app, so all its UI is disabled while another app is in full screen. You should file a bug if you feel that status items in foreground apps should still be available to other apps in full screen mode.
Probably the best solution would be to split your app into two parts, an agent app which has LSUIElement set to true, which creates and manages the status item and its menu, and your main foreground app which does most of the work and which launches and manages the agent app.
There are a variety of inter-process communication methods that you can use to get the two apps talking to each other, such as Distributed Objects or Apple Events.
I've submitted a helper application (using LSUIElement)to the Mac App Store. I was under the false impression that the App Store install process would put a dock icon for helper apps.
How can I create a dock icon that the user could remove, while the status bar app runs independently (like the popular app Caffeine)? Do I need to create a non-LSUIElement app that loads the LSUIElement app, or is there a better way?
Instead of using LSUIElement, use NSApplication's setActivationPolicy: method. By default, the application will have a dock icon, but by changing the activation policy to NSApplicationActivationPolicyAccessory, you get the same effect as LSUIElement while being able to change it programatically (the documentation for NSApplicationActivationPolicyAccessory says it is equivalent to LSUIElement=1).
- (void)applicationDidFinishLaunching:(NSApplication *)app {
if([[NSUserDefaults standardUserDefaults] boolForKey:#"HideDockIcon"])
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
}
Apparently I was misinformed by my app reviewer (two of them actually). The dock icon is created for you by the install process. Pressing the issue, I was able to get the app through the review process.