CoreGTK

A while back I made it my goal to put together an open source project as my way of contributing back to the community. Well fast forward a couple of months and my hobby project is finally ready to be shown the light of day. I give you… CoreGTK

CoreGTK is an Objective-C binding for the GTK+ library which wraps all objects descending from GtkWidget (plus a few others here and there). Like other “core” Objective-C libraries it is designed to be a very thin wrapper, so that anyone familiar with the C version of GTK+ should be able to pick it up easily.

However the real goal of CoreGTK is not to replace the C implementation for every day use but instead to allow developers to more easily code GTK+ interfaces using Objective-C. This could be especially useful if a developer already has a program, say one they are developing for the Mac, and they want to port it to Linux or Windows. With a little bit of MVC a savvy developer would only need to re-write the GUI portion of their application in CoreGTK.

So what does a CoreGTK application look like? Pretty much like a normal Objective-C program:

/*
 * Objective-C imports
 */
#import <Foundation/Foundation.h>
#import "CGTK.h"
#import "CGTKButton.h"
#import "CGTKSignalConnector.h"
#import "CGTKWindow.h"

/*
 * C imports
 */
#import <gtk/gtk.h>

@interface HelloWorld : NSObject
/* This is a callback function. The data arguments are ignored
 * in this example. More callbacks below. */
+(void)hello;

/* Another callback */
+(void)destroy;
@end

@implementation HelloWorld
int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    /* We could use also CGTKWidget here instead */
    CGTKWindow *window;
    CGTKButton *button;

    /* This is called in all GTK applications. Arguments are parsed
    * from the command line and are returned to the application. */
    [CGTK autoInitWithArgc:argc andArgv:argv];

    /* Create a new window */
    window = [[CGTKWindow alloc] initWithGtkWindowType:GTK_WINDOW_TOPLEVEL];

    /* Here we connect the "destroy" event to a signal handler in the HelloWorld class */
    [CGTKSignalConnector connectGpointer:[window WIDGET] withSignal:@"destroy" 
        toTarget:[HelloWorld class] withSelector:@selector(destroy) andData:NULL];

    /* Sets the border width of the window */
    [window setBorderWidth: [NSNumber numberWithInt:10]];

    /* Creates a new button with the label "Hello World" */
    button = [[CGTKButton alloc] initWithLabel:@"Hello World"];

    /* When the button receives the "clicked" signal, it will call the
     * function hello() in the HelloWorld class (below) */
    [CGTKSignalConnector connectGpointer:[button WIDGET] withSignal:@"clicked" 
        toTarget:[HelloWorld class] withSelector:@selector(hello) andData:NULL];

    /* This packs the button into the window (a gtk container) */
    [window add:button];

    /* The final step is to display this newly created widget */
    [button show];

    /* and the window */
    [window show];

    /* All GTK applications must have a [CGTK main] call. Control ends here
     * and waits for an event to occur (like a key press or
     * mouse event). */
    [CGTK main];

    [pool release];

    return 0;
}

+(void)hello
{
    NSLog(@"Hello World");
}

+(void)destroy
{
    [CGTK mainQuit];
}
@end
Hello World in action

Hello World in action

And because Objective-C is completely compatible with regular old C code there is nothing stopping you from simply extracting the GTK+ objects and using them like normal.

// Use it as an Objective-C CoreGTK object!
CGTKWindow *cWindow = [[CGTKWindow alloc] initWithGtkWindowType:GTK_WINDOW_TOPLEVEL];

// Or as a C GTK+ window!
GtkWindow *gWindow = [cWindow WINDOW];

// Or even as a C GtkWidget!
GtkWidget *gWidget = [cWindow WIDGET];

// This...
[cWindow show];

// ...is the same as this:
gtk_widget_show([cWindow WIDGET]);

You can even use a UI builder like GLADE, import the XML and wire up the signals to Objective-C instance and class methods.

CGTKBuilder *builder = [[CGTKBuilder alloc] init];
if(![builder addFromFile:@"test.glade"])
{
    NSLog(@"Error loading GUI file");
    return 1;
}

[CGTKBuilder setDebug:YES];

NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:
                 [CGTKCallbackData withObject:[CGTK class] andSEL:@selector(mainQuit)], @"endMainLoop",
                 [CGTKCallbackData withObject:[HelloWorld class] andSEL:@selector(hello)], @"on_button2_clicked",
                 [CGTKCallbackData withObject:[HelloWorld class] andSEL:@selector(hello)], @"on_button1_activate",
                 nil];

[builder connectSignalsToObjects:dic];

CGTKWidget *w = [builder getWidgetWithName:@"window1"];
if(w != nil)
{
    [w showAll];
}

[builder release];

So there you have it that’s CoreGTK in a nutshell.

There are a variety of ways to help me out with this project if you are so inclined to do so. The first task is probably just to get familiar with it. Download CoreGTK from the GitHub project page and play around with it. If you find a bug (very likely) please create an issue for it.

Another easy way to get familiar with CoreGTK is to help write/fix documentation – a lot of which is written in the source code itself. Sadly most of the current documentation simply states which underlying GTK+ function is called and so it could be cleaned up quite a bit.

At the moment there really isn’t anything more formal than that in place but of course code contributions would also be welcome!

Update: added some pictures of the same program running on all three operating systems.

Hello World on Windows

Hello World on Windows

Hello World on Mac

Hello World on Mac

Hello World on Linux

Hello World on Linux

8 thoughts on “CoreGTK

  1. Erik

    As a iOS and OS X developer, I think this is great work. I always thought it would be great to be able to port my OS X apps to Linux. There is one thing I don’t get in your API though. Why do you use NSNumber in the API for tings like x, y and border width? NSNumber is just a necessary evil in the cases where you need an object, e.g. in an NSArray, or when using Key-Value coding. In all other respects NSNumber and similar box types are just awkward to work with compared to their native types.

    • Tyler Burton

      Initially I made a point to always prefer NSNumber (and in general all Foundation types) especially when dealing with all of the g-basic data types. I thought this approach would at least be more familiar to Objective-C developers (who are really the target). Your point is certainly very valid though and it has been something I’ve considered changing as well. I would gladly accept code changes for this if you’re interested :)

      • Erik

        Then I suggest you take a closer look at UIKit, AppKit or any other Apple framework. You will notice that Objective-C APIs almost never take NSNumber as arguments. E.g for UIBezierPath, lineWidth is given as CGFloat which is just a typedef for double or float. UIView alpha value is also given as CGFloat. Just look at any property taking a number and you will usually see a float or int. So this IS what Objective-C developers expect.

        I have not written all that much usefull stuff in Objective-C, so kudos for you for taking the effort of making this, but I have programmed Objective-C on and off for about 10 years, so I would consider myself a pretty typical Objective-C developer. Although to be fair, with the rapid rise of iOS I have seen a huge influx of Java and C++ developers learning Objective-C and taking their practices with them. I honestly think that if you have seen a lot of usage of Foundation types in Objective-C code than that is because you have read the code of a recent convert from Java or C++. My impression is that they tend to overuse Objective-C types and make lots of small classes. Objective-C has a whole different tradition in style where one unlike C++ developers use the C subset a lot more actively and one tends to create fewer but larger classes. This is facilitiated by the use of Categories.

        I’d love to contribute if I had the time. Maybe I will, but with two small kids, a job and too many other projects I never get time for that might take a while ;-)

  2. Chad Russell

    This is really great! I’m a huge fan on objective-c, and always thought it was underutilized on linux. One note: I was installing in Arch linux, and when I tried to compile using “make linux”, the compiler threw an error saying “error: `-fobjc-exceptions` is required to enable Objective-C exception syntax” from the file CGTKWidget.m, line 106. I went into the makefile and added “-fobjc-exceptions” to the OBJCFLAGS variable and it compiled perfectly. I’m sending you a comment on your website because I don’t think something so small warranted a pull request on github

  3. Chad Russell

    oops, just realized it would probably be better just to file an issue on github. ok, I’ll do it there too :)

    • Tyler Burton

      Haha thanks for the feedback I will make this change when I get a chance. If you’d like to create a pull request I will gladly take it. Either way I’ll make sure you get the credit :)

  4. Ago

    Hi! I recently view the documentation about the future of clang/LLVM version 3.5 and there is an information about ARC (Automatic Reference Counting).
    Is It possible to use clang with coreGtk?

    http://clang.llvm.org/docs/AutomaticReferenceCounting.html

    • Tyler Burton

      At this point I have not done anything to enable ARC support in CoreGTK. This was a decision made to have the broadest compatibility (some stable distributions have packages which don’t support ARC at the moment). Long-term this is certainly a feature I would like to implement.

      All that said it may be possible to get the existing code to work with ARC with little effort but it is not something I have personally tried.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>