Skip to content

Instantly share code, notes, and snippets.

@andsve
Created January 17, 2019 13:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andsve/2a154a82faa806b3b1d6d71f18a2ad24 to your computer and use it in GitHub Desktop.
Save andsve/2a154a82faa806b3b1d6d71f18a2ad24 to your computer and use it in GitHub Desktop.
A simple implementation of a NSApplicationDelegate and two methods (openFile:, openFiles:) in pure C.
/*
app_delegate.c
1. Compile and link with:
$ clang -g -o AppInC app_delegate.c -lobjc -framework Foundation -framework AppKit -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
2. Create a Info.plist with:
[[
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>AppInC</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.foo.AppInC</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>AppInC</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key>
<string></string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleTypeName</key>
<string>Lua</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>lua</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>LUA</string>
</dict>
</array>
</dict>
</plist>
]]
3. Make a "real" MacOS app of it:
$ mkdir -p AppInC.app/Contents/MacOS
$ cp AppInC AppInC.app/Contents/MacOS/
$ cp Info.plist AppInC.app/Contents/
4. Try to open a Lua file with the AppInC.app, it should display an alertbox with the path of the Lua file.
References:
- https://stackoverflow.com/questions/11319170/c-as-principal-class-or-a-cocoa-app-without-objc
*/
#include <stdio.h>
#include <stdlib.h>
#include <objc/runtime.h>
#include <objc/message.h>
#define ALLOC_INIT_OBJC_CLASS(class_name) objc_msgSend(objc_msgSend((id)objc_getClass(class_name), sel_getUid("alloc")), sel_getUid("init"));
extern id NSApp;
struct AppDel
{
Class isa;
};
// This is a strong reference to the class of the AppDelegate
// (same as [AppDelegate class])
Class AppDelClass;
BOOL AppDel_openFile(struct AppDel *self, SEL _cmd, id sender, id filename)
{
id t_alertbox = ALLOC_INIT_OBJC_CLASS("NSAlert");
objc_msgSend(t_alertbox, sel_getUid("setMessageText:"), filename);
objc_msgSend(t_alertbox, sel_getUid("runModal"));
return YES;
}
BOOL AppDel_openFiles(struct AppDel *self, SEL _cmd, id sender, id filenames)
{
// Pick first filename in the array
id first_nsstr = objc_msgSend(filenames, sel_getUid("firstObject"));
// Do a little dance here to get the C string, just to see that it's possible.
const char* c_str = (const char*)objc_msgSend(first_nsstr, sel_getUid("UTF8String"));
id msg_text = objc_msgSend((id)objc_getClass("NSString"), sel_getUid("alloc"));
msg_text = objc_msgSend(msg_text, sel_getUid("initWithUTF8String:"), c_str);
id t_alertbox = ALLOC_INIT_OBJC_CLASS("NSAlert");
objc_msgSend(t_alertbox, sel_getUid("setMessageText:"), msg_text);
objc_msgSend(t_alertbox, sel_getUid("runModal"));
return YES;
}
// Set up the delegate class that has the two openFile and openFiles methods.
static void init_delegate_class()
{
AppDelClass = objc_allocateClassPair((Class) objc_getClass("NSObject"), "AppDelegate", 0);
class_addMethod(AppDelClass, sel_getUid("application:openFile:"), (IMP) AppDel_openFile, "i@:@:@");
class_addMethod(AppDelClass, sel_getUid("application:openFiles:"), (IMP) AppDel_openFiles, "i@:@:@");
objc_registerClassPair(AppDelClass);
}
void init_app(void)
{
objc_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
if (NSApp == NULL)
{
fprintf(stderr,"Failed to initialized NSApplication... terminating...\n");
return;
}
id appDelObj = ALLOC_INIT_OBJC_CLASS("AppDelegate");
objc_msgSend(NSApp, sel_getUid("setDelegate:"), appDelObj);
objc_msgSend(NSApp, sel_getUid("run"));
}
int main(int argc, char** argv)
{
init_delegate_class();
init_app();
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment