Calling Cocoa Commandline
The euphoria. All of Shoes’ Carbon code is gone, rewritten using entirely Cocoa native code. Not only did this shrink the native code to nearly half of its previous size, but things actually work now. The euphoria. And also: the elation.
The only painful part was figuring out how to get started with only gcc and get it all linked with the C code. To get away without an IDE. It’s been done in lots of other cross-platform libs, sure. Buried under autotools and lost in deep directories.
Here’s a summary of using Cocoa from the commandline.
A truly trivial Cocoa program reads thusly:
#import <Cocoa/Cocoa.h>
#define INIT NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#define RELEASE [pool release];
int
main(int argc, char *argv[])
{
NSApplication *app = [NSApplication sharedApplication];
INIT;
NSWindow *win = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 340, 140)
styleMask: (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)
backing: NSBackingStoreBuffered defer: NO];
NSTextField *text = [[[NSTextField alloc] initWithFrame: NSMakeRect(40, 90, 260, 18)]
autorelease];
[[win contentView] addSubview: text];
[text setBezeled: NO];
[text setStringValue: @"Drink Coke-O"];
[text setBackgroundColor: [NSColor windowBackgroundColor]];
[text setEditable: NO];
[text setSelectable: NO];
[win center];
[win orderFront: nil];
RELEASE;
[app run];
return 0;
}
Save this as coke-o.m
and compile it (you’ll need the Xcode tools installed):
$ gcc -framework Cocoa -o coke-o coke-o.m
Run it: ./coke-o
(although the window will appear behind your terminal for now.)
While we’re messing with gcc, let’s compile it as a Universal binary. Since Universal binaries aren’t that much bigger for little progs.
$ export MACOSX_DEPLOYMENT_TARGET=10.4
$ gcc -O -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc \
-framework Cocoa -o coke-o coke-o.m
Now, about the window coming up behind the Terminal. To me, this just seems to be one of those things that Apple is so pedantic about that it ends up not really making sense when you’re starting out with Cocoa.
From what I can gather, this is all tied into OS X’s demand that every app have a menu and an icon. And, in order to get a menu and an icon, you’ve got to get an Info.plist file and a .app directory structure and all of that. So, that’s one way of solving the problem.
Since our commandline app is being launched from the terminal, though, it’s just becoming another window in Terminal.app’s environment.
So, if you just want your app to work from the terminal, expand the win center
call to also move the window up to the floating level.
[win center];
[win setLevel: NSFloatingWindowLevel];
[win makeKeyAndOrderFront: nil];
In the case of the Shoes installer, I also needed to sniff out the CPU type. I was okay having the little installer stub be a universal binary, but I really wanted to have it download other libraries specific to either PowerPC or Intel architectures.
The simplest way of doing this is to use the __ppc__
pre-processor def.
NSString *url;
#ifdef __ppc__
url = @"http://hacketyhack.net/pkg/osx/shoes-ppc";
#else
url = @"http://hacketyhack.net/pkg/osx/shoes";
#endif
Since the universal binary will actually compile this code twice (once for -arch i386
and once for -arch ppc
,) each of the url
assignment lines will end up in the proper side of the binary for its architecture.
If you’d rather sniff out the CPU type at runtime (or if you need the CPU frequency as well,) I’d look into calling sysctl
with the CTL_HW
and HW_CPU_FREQ
flags. For some nice, complete examples, see the arch_info.c sample from the Darwin sources.
Lastly, I will mention briefly about linking to C. While you may be able to get away with just writing plain C functions in coke-o.m
that can be exposed to C, you might eventually need to pass around Cocoa objects in your C structs.
Since Objective-C is a superset of C, I found it easiest to just compile everything as Objective-C. All of Cocoa’s controls can be passed as C pointers.
I kept all my C sources with the .c
extension and added -x objective-c
to the gcc flags when compiling vanilla C sources. And then you can safely #include <Cocoa/Cocoa.h>
throughout your program.
Now begin the comments …
aemadrid
I’m totally lost. Does this all mean the next release of Shoes will support PPC back again? That would be a dream. I’m still a poor developer running an old G4 macbook.
_why
Yes, definitely, the PPC builds will be out with the next set. Sans video support, however.
Dr Nic
This is some sweet behind-the-scenes compiler info. I suck at this stuff. Hmm, I wonder if I can just pass ‘arm’ as the -arch flag for the rubycocoa.framework to get it into the iphone.
matthew
hey sweet. curious why you made those INIT and RELEASE macros, tho.
Dr Nic
FYI , in TextMate the ‘pool’ snippet generates:
(the cursor ends at $0; its not printed)
Tim Burks
Good digging! You might also enjoy having your Cocoa the Nu way:
Tim Burks
One more thing: put this before (app run) to make your program exit when the window is closed.
_why
Lovely, Tim. That delegate is edible.
Say, while you’re here. Did you ever figure out Cocotron? I saw that you were trying to get Nu going on that. Anyhow, I downloaded Cocotron and I couldn’t make head nor tail of it. Does it go?
Tim Burks
Not for me, but I didn’t spend much time with it. That’s mainly because its focus is Windows, which just isn’t on my radar. I’ve ported Nu to Linux using a smaller and simpler library called libFoundation. People have also expressed interest in GNUstep, but I haven’t seen a GNUstep-based Nu myself yet. I thought I would be doing that by now, but this iPhone SDK has been so much fun…
Socketwiz
Your “Shoes” link in the right column is pointing to a broken web site: http://code.whytheluckystiff.net/shoes/
till
“If you’d rather sniff out the CPU type at runtime …”
/usr/bin/arch + NSTask
aemadrid
Can’t wait for the next release!
Comments are closed for this entry.