Posts tagged “mac”
4/28 MacBook Pro updates imminent (speculation) (1)
I purchased a new MacBook Pro last week. My 3 1/2 year old PowerBook has served admirably but it simply can’t keep up with me anymore. The faint and occasional clicking from the hard disk was the final push over the Intel edge for me and I couldn’t be happier. I decided to go with the 2.4 GHz model because $750 is a lot to pay for 200 MHz. I haven’t upgraded the memory to all 4 GB yet but that’ll come as soon as I spend some serious time with Aperture and Photoshop (CS3, finally).
I present this post as a warning to anyone holding off to see what Apple does next. Because I’ve made this purchase, I’m relatively certain that the WWDC will bring glorious new MacBook Pros, probably even radically redone AirBook-style. Maybe I’ll pawn this one off on my parents when the new ones come out (almost, but not quite not kidding).
A parting thought: holy crap the LED-backlit displays are bright.
7/17 Cross platform XPCOM (a howto) (9)
The most important part of Mozilla’s XPCOM technology is its promise of a cross-platform interface between Javascript and native code. Yet few examples exist showing how to build a codebase for both Windows and Mac OS X. Building on Mark Finkle’s Windows how-to, here is a completion showing the parallel track for Mac OS X and how to setup your development environment for a code-once, compile-twice, use anywhere XPCOM build.
Our entire app is going to reside within the app_repo directory. Really you can place it wherever you like, but remember to change paths appropriately. You know the drill. Confusingly, the root of our app will be the app subdirectory. So far we have:
- app_repo
- app
XULRunner
Since the goal here is an XPCOM object for use in XULRunner, we’ll start there. Installing XULRunner is quite painless. We have a version for each platform, which are kept quite separate.
XULRunner for Windows is simply a ZIP file, which you should extract into the root of your app directory. Copy the xulrunner-stub.exe program from app/xulrunner down into app and rename it however you like. This stub just looks for a XULRunner app in the current directory and runs it. Handy.
XULRunner for Mac is slightly more complicated because of strict requirements for GUI apps running in OS X. First, go download XULRunner and install the package. It will create itself deep within /Library/Frameworks (quite separate from the Windows version). In app_repo, create a new directory called MacApp.app or something else ending in .app. Within this directory create one called Contents (capitalization is important), and within Contents create Frameworks and MacOS. Now create three symbolic links to complete the Mac directory structure:
ln -s /Library/Frameworks/XUL.framework MacApp.app/Contents/Frameworks/XUL.framework ln -s ../../app MacApp.app/Contents/Resources ln -s /Library/Frameworks/XUL.framework/Versions/Current/xulrunner MacApp.app/Contents/MacOS/xulrunner
Now create MacApp.app/Contents/Info.plist and dump this in, making sure to change things in ALL CAPS. I am almost certain this is not optimal as it repeats itself a lot. But it is functional. Can someone who knows demystify this?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>xulrunner</string>
<key>CFBundleGetInfoString</key>
<string>3.0</string>
<key>CFBundleIdentifier</key>
<string>YOUR_APP_ID</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>YOUR_APP_VERSION</string>
<key>CFBundleName</key>
<string>YOUR_APP_NAME</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>YOUR_APP_VERSION</string>
<key>CFBundleSignature</key>
<string>YOUR_APP_ID</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>YOUR_APP_NAME</string>
<key>CFBundleURLSchemes</key>
<array>
<string>chrome</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>YOUR_APP_VERSION</string>
</dict>
</plist>
As a review, here’s how our tree looks now:
- app_repo
- app
- xulrunner
- MacApp.app
- Contents
- Frameworks
- XUL.framework -> /Library/Frameworks/XUL.framework
- Info.plist
- MacOS
- xulrunner -> /Library/Frameworks/XUL.framework/Versions/Current/xulrunner
Resources -> ../../app
- xulrunner -> /Library/Frameworks/XUL.framework/Versions/Current/xulrunner
- Frameworks
- Contents
- app
The rest of your XULRunner app
Building the XUL and Javascript part of a XULRunner app is a bit outside of the scope of this already very long post. There are plenty of good references on this though, starting with XUL Planet. Go forth and learn.
The Gecko SDK
You’ll need to grab the Gecko SDK to build XPCOM objects. I took these and placed them in app at gecko-sdk.win and gecko-sdk.mac. I haven’t started building PPC Mac versions, but the SDK for those will be under gecko-sdk.ppc.
- Windows and PPC Mac: http://developer.mozilla.org/en/docs/Gecko_SDK
- Intel Mac: http://www.oxymoronical.com/view/1114
At last, some C++
Now that your development environment is setup, I’ll let you write some C++ code. I created another directory within app called xpcom, in which I placed my project, called foo. Here are some stub files that will get you off the ground. Be sure to create your own UUID.
foo.idl
#include "nsISupports.idl"
[scriptable, uuid(0e0d0b74-2c06-11dc-8314-0800200c9a66)]
interface IFoo : nsISupports {
attribute AString name;
long add(in long a, in long b);
};
foo.cpp (just to make Windows happy)
#include "windows.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return TRUE;
}
foo_impl.h
#ifndef FOO_IMPL_H
#define FOO_IMPL_H
#include "foo.h"
#include "nsStringAPI.h"
#define FOO_CONTRACTID "@richarddcrowley.org/foo;1"
#define FOO_CLASSNAME "Foo"
#define FOO_CID { 0x0e0d0b74, 0x2c06, 0x11dc, { 0x83, 0x14, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
class CFoo : public IFoo {
public:
NS_DECL_ISUPPORTS
NS_DECL_IFOO
CFoo();
private:
~CFoo();
protected:
nsString mName;
};
#endif
foo_impl.cpp
#include "foo_impl.h"
NS_IMPL_ISUPPORTS1(CFoo, IFoo)
CFoo::CFoo() {
}
CFoo::~CFoo() {
}
NS_IMETHODIMP CFoo::GetName(nsAString & aName) {
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP CFoo::SetName(const nsAString & aName) {
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP CFoo::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) {
*_retval = a + b;
return NS_OK;
}
foo_module.cpp
#include "nsIGenericFactory.h"
#include "foo_impl.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(CFoo)
static nsModuleComponentInfo components[] = {
{
FOO_CLASSNAME,
FOO_CID,
FOO_CONTRACTID,
CFooConstructor,
}
};
NS_IMPL_NSGETMODULE(”FooModule”, components)
Building for Windows
Because the length here is getting out of hand, I will defer to Mark Finkle for the ins-and-outs of the Visual Studio build. The short version is you setup a few includes and libraries, set some preprocessor directives, and go. Mark Finkle’s Weblog » Mozilla Platform - XPCOM in C++
I used Visual Studio 2005 on Windows XP and did exactly as Mark prescribed. It worked on the first try. I wanted to gloat a little bit, but I seriously hope you have as much luck.
Building for Mac
Building on the Mac is easy since a Mac is Unix-y. Use this Makefile:
GECKO_SDK := ../../gecko-sdk.mac XULRUNNER := /Library/Frameworks/XUL.framework/Versions/Current DEFINE := -DXP_UNIX -DXP_MACOSX all: xpt dylib xpt: $(GECKO_SDK)/bin/xpidl -m header -I$(GECKO_SDK)/idl foo.idl $(GECKO_SDK)/bin/xpidl -m typelib -I$(GECKO_SDK)/idl foo.idl impl: g++ -w -c -o foo_impl.o -I $(GECKO_SDK)/include $(DEFINE) foo_impl.cpp module: g++ -w -c -o foo_module.o -I $(GECKO_SDK)/include $(DEFINE) foo_module.cpp dylib: impl module g++ -dynamiclib -o foo.dylib foo_impl.o foo_module.o -L$(GECKO_SDK)/lib -L$(XULRUNNER) -Wl,-executable_path,$(XULRUNNER) -lxpcomglue_s -lxpcom -lnspr4 clean: rm *.o rm foo.h rm foo.xpt rm foo.dylib
Having the XPT file (which is the same for all platforms) as well as a DLL and a DYLIB, we can dump them into app/components, increment BuildID in application.ini and run our application.
The interface between C++ and Javascript
Now that the application has access to the native code, let’s write some Javascript to call it. The code here will create the object, alerting us of any error, and then use the add function we defined in foo_impl.cpp.
try {
var xpcom = Components.classes['@richarddcrowley.org/foo;1'].createInstance();
foo = xpcom.QueryInterface(Components.interfaces.IFoo);
} catch (err) alert(err);
alert(foo.add(3, 4));
That wasn’t so bad, was it?
This has been a bit of a whirlwind tour, I know. But that’s how it goes when you’re playing with relatively new technologies. It will be sweet, though, the first time you get 7 from 3 + 4.