What is the best way to make another app active in Cocoa? - applescript

I have an app that generates key commands in another app. I used to do it in Applescript studio but I have rewritten in Obj-C. Now I can't figure out how to make the other active in the foreground so I can start generating key commands.
Currently I am just running an Applescript call:
key = [[NSAppleScript alloc] initWithSource:#"tell application \"Pro Tools\" to activate"];
[key executeAndReturnError:nil] ;
The PID for Protools is 14925. I was thinking I could use NSApplications, but I don't think that is the right call.
Thoughts?

Try this...
[[NSRunningApplication runningApplicationWithProcessIdentifier:PID] activateWithOptions:NSApplicationActivateIgnoringOtherApps];

Related

macOS - Get path to currently running application

Question
Is it possible to determine the location a macOS app was launched from at runtime?
Context
I develop a Safari Extension, and in order for the extension to be enabled the application needs to be present /Applications/. Several users have tried to run the application directly from the DMG file and then complained that the extension doesn't appear in Safari's extension settings. I would like to detect this and alert them that they need to move the file.
You can use NSBundle, specifically the bundlePath property. The documentation says:
The full pathname of the receiver’s bundle directory.
And you can use it something like this:
NSString* bundlePath;
bundlePath = [[NSBundle mainBundle] bundlePath]
NSLog(#"%#", bundlePath);
There's also bundleURL if you want a NSURL to work with instead of a string.

Cocoa applications from the command line

I've been attempting to create a Cocoa (desktop) application without using NIBs or XIBs, following the instructions here. All is well, and from Terminal.app I can successfully run the application. However it's spawned without any of the usual decorations, such as a menu bar or a dock icon, behind the terminal window and seemingly attached very much to the terminal process.
I suspected that this might be a consequence of running the binary itself directly from the command line, but playing around a bit I found that at least Calculator.app is more than happy to spawn a "real" application when called using:
$ /Applications/Calculator.app/Contents/MacOS/Calculator
What am I missing, if anything, here? I have also tried loading a XIB file via
NSNib *mainNib = [[NSNib alloc] initWithContentsOfURL:[[NSURL URLWithString:#"../MainMenu.xib"] absoluteURL]];
[NSNib instantiateNibWithOwner:application topLevelObjects:nil];
which seems to find the XIB, but makes no difference (i.e. the GUI isn't fully loaded).
After a bit of fiddling, I found the solution to the issue. Transforming the process type via TransformProcessType and then using the accurately-named [NSApp activateIgnoringOtherApps:YES] did the trick:
ProcessSerialNumber psn = { 0, kCurrentProcess };
OSStatus returnCode = TransformProcessType(& psn, kProcessTransformToForegroundApplication);
[NSApp activateIgnoringOtherApps:YES];
(I also added a custom menu to finish it off). Thanks to Mankarse for the pointers!
You need a NSApplication.. why are you even doing this?
If you create a new GUI application, look at main.m you see that it is spawninng a NSApplication there.
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
This requires you to have a bundle btw and if you want to launch a command line utility without the terminal you could either use launchd or create a bundle that you simply double click in the finder.

Building Mac OS X App instead of console

I have a port of my application, a game, running on Mac OS X. I build with make, and only added a few .mm files to access the necessities from NSApplication, NSWindow and NSOpenGLView.
How do I "convert it" into a proper App as opposed to the current console form? Do I bundle it with something, if so how? Or is it some type of linker setting (as in Windows)? Do I have to build using XCode?
Currently I just "tick" the system, i.e. poll it for events rather than utilizing OS X message pump properly. Could you point me to some basic sample or tutorial which shows me how to do it properly, while still being able to "tick" my own stuff at a fixed frame rate? I say "sample or tutorial", since I am blind when it comes to documentation.
Thanks!
For the creation of a proper Mac application, you'll have to create a Mac OS X bundle. You can find the great documentation on the Apple site, here. You can create them with standard tool (I did write a small python script to create a bundle from a simple .ini file that describe files to pack, and how to construct the Info.plist file).
Basically, an application bundle is just a regular directory with a .app extension, and a fixed structure. The following file are required:
Application.app/
+ Contents/
+ MacOS/
| + <Executable>
+ Resources/
| + <Icon>
+ Info.plist
The Info.plist file is a standard property list file (either in XML or in the old format), that indicate what is the name of the executable file (CFBundleExecutable), what is the name of the icon file (CFBundleIconFile), the bundle type (CFBundleType with a value of APPL), and some other informations (file type supported, version string, development language, ...). Those file is the strict minimum required to have a basic Mac OS X application.
For explanation of how the Mac OS X message pump work, I recommend the reading of this article by Matt Gallagher. He explains how the run message of the NSApplication class is implemented. You can then write this method runOnce that only iterate when there are pending messages. You'll then call this function periodically (it is really similar to the PeekMessage, TranslateMessage, and DispatchMessage sequence on Win32):
- (void)runOnce
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self finishLaunching];
for (;;)
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event =
[self
nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (sender == nil)
break;
[self sendEvent:event];
[self updateWindows];
}
[pool release];
}
Unless you really want to learn a bunch of Mac-specific APIs and XCode, you should just use SDL. It's a cross-platform library that abstracts things like event handling, threading, and multimedia. It includes XCode templates that allow you to build a proper app bundle with minimal changes to your code. Using SDL would mean throwing away most of the Mac-specific code you've written, but it would vastly simplify any future ports and would make it easier to maintain your app for multiple platforms.

get app icon from window id in cocoa

I am looking for a way to get the application icon from a window id in cocoa. Do you have any pointer for this?
First, use CGWindowListCreateDescriptionFromArray() to get the PID of the owning process (kCGWindowOwnerPID). If this is 10.6, you can then use +[NSRunningApplication runningApplicationWithProcessIdentifier:] to get the application object and then use -icon.
Before 10.6, you need to use GetProcessForPID() to switch to a PSN, use GetProcessBundleLocation() to get the location of the bundle, switch the FSRef into a path string, and then use -[NSWorkspace iconForFile:] to get the icon.
Rob Napier's answer is correct.
In the latest api and in swift language, it should be like this:
let runningApp = NSRunningApplication(processIdentifier: pid_t(the_process_id))
let icon = runningApp?.icon

Create simple cocoa application wrapper

I have an application I want to bundle (with some additional files) together inside a wrapper application.
Literally the only thing I want to do is have the wrapper application launch a resource file (i.e. as if I'd typed:
/path/to/Resources/Bundled.app/Contents/MacOS/executable
into the terminal.
Make sense? I thought this should be simple, but I caouldn't find a simple way to do this-- my cocoa programming is limited, to say the least.
Thanks in advance!
One way, if the wrapped “application” is just a shell script or something, is Platypus.
If it's an actual application (.app bundle), why does app A do nothing but launch app B? Why have app A at all?
Your outer program can use NSBundle to locate the inner program within the outer program's bundle.
To run the inner program: If it's an application, use Launch Services or NSWorkspace; if it's a command-line tool, use NSTask.
I have a blog post up on this: Turn any shell script into a double-clickable app. The entry mentions "start with an empty app bundle"... which you can get by using the Pashua tool mentioned, if I remember correctly...
Just for the sake of posterity (and if it helps anyone else, here is the full code I used (inside the AppDelegate.m file):
NSString *appName = #"";
NSString *bundledApp = [[NSBundle bundleWithPath:[[NSBundle
mainBundle] pathForResource:appName ofType:#"app"]]
bundlePath];
NSWorkspace *launchApp = [[NSWorkspace alloc] init];
NSLog(#"Launching %s", bundledApp);
[launchApp launchApplication:bundledApp];
[launchApp release];
// Make Launcher terminate (if it serves no other purpose)
[NSApp terminate:nil];

Resources