Create a GTK+ application on Linux with Objective-C

As sort of follow-up-in-spirit to my older post I decided to share a really straight forward way to use Objective-C to build GTK+ applications.

Objective-what?

Objective-C is an improvement to the iconic C programming language that remains backwards compatible while adding many new and interesting features. Chief among these additions is syntax for real objects (and thus object-oriented programming). Popularized by NeXT and eventually Apple, Objective-C is most commonly seen in development for Apple OSX and iOS based platforms. It ships with or without a large standard library (sometimes referred to as the Foundation Kit library) that makes it very easy for developers to quickly create fast and efficient programs. The result is a language that compiles down to binary, requires no virtual machines (just a runtime library), and achieves performance comparable to C and C++.

Marrying Objective-C with GTK+

Normally when writing a GTK+ application the language (or a library) will supply you with bindings that let you create GUIs in a way native to that language. So for instance in C++ you would create GTK+ objects, whereas in C you would create structures or ask functions for pointers back to the objects. Unfortunately while there used to exist a couple of different Objective-C bindings for GTK+, all of them are quite out of date. So instead we are going to rely on the fact that Objective-C is backwards compatible with C to get our program to work.

What you need to start

I’m going to assume that Ubuntu will be our operating system for development. To ensure that we have what we need to compile the programs, just install the following packages:

  1. gnustep-core-devel
  2. libgtk2.0-dev

As you can see from the list above we will be using GNUstep as our Objective-C library of choice.

Setting it all up

In order to make this work we will be creating two Objective-C classes, one that will house our GTK+ window and another that will actually start our program. I’m going to call my GTK+ object MainWindow and create the two necessary files: MainWindow.h and MainWindow.m. Finally I will create a main.m that will start the program and clean it up after it is done.

Let me apologize here for the poor code formatting; apparently WordPress likes to destroy whatever I try and do to make it better. If you want properly indented code please see the download link below.

MainWindow.h

In the MainWindow.h file put the following code:

#import <gtk/gtk.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

//A pointer to this object (set on init) so C functions can call
//Objective-C functions
id myMainWindow;

/*
* This class is responsible for initializing the GTK render loop
* as well as setting up the GUI for the user. It also handles all GTK
* callbacks for the winMain GtkWindow.
*/
@interface MainWindow : NSObject
{
//The main GtkWindow
GtkWidget *winMain;
GtkWidget *button;
}

/*
* Constructs the object and initializes GTK and the GUI for the
* application.
*
* *********************************************************************
* Input
* *********************************************************************
* argc (int *): A pointer to the arg count variable that was passed
* in at the application start. It will be returned
* with the count of the modified argv array.
* argv (char *[]): A pointer to the argument array that was passed in
* at the application start. It will be returned with
* the GTK arguments removed.
*
* *********************************************************************
* Returns
* *********************************************************************
* MainWindow (id): The constructed object or nil
* arc (int *): The modified input int as described above
* argv (char *[]): The modified input array modified as described above
*/
-(id)initWithArgCount:(int *)argc andArgVals:(char *[])argv;

/*
* Frees the Gtk widgets that we have control over
*/
-(void)destroyWidget;

/*
* Starts and hands off execution to the GTK main loop
*/
-(void)startGtkMainLoop;

/*
* Example Objective-C function that prints some output
*/
-(void)printSomething;

/*
********************************************************
* C callback functions
********************************************************
*/

/*
* Called when the user closes the window
*/
void on_MainWindow_destroy(GtkObject *object, gpointer user_data);

/*
* Called when the user presses the button
*/
void on_btnPushMe_clicked(GtkObject *object, gpointer user_data);

@end

MainWindow.m

For the class’ actual code file fill it in as show below. This class will create a GTK+ window with a single button and will react to both the user pressing the button, and closing the window.

#import “MainWindow.h”

/*
* For documentation see MainWindow.h
*/

@implementation MainWindow

-(id)initWithArgCount:(int *)argc andArgVals:(char *[])argv
{
//call parent class’ init
if (self = [super init]) {

//setup the window
winMain = gtk_window_new (GTK_WINDOW_TOPLEVEL);

gtk_window_set_title (GTK_WINDOW (winMain), “Hello World”);
gtk_window_set_default_size(GTK_WINDOW(winMain), 230, 150);

//setup the button
button = gtk_button_new_with_label (“Push me!”);

gtk_container_add (GTK_CONTAINER (winMain), button);

//connect the signals
g_signal_connect (winMain, “destroy”, G_CALLBACK (on_MainWindow_destroy), NULL);
g_signal_connect (button, “clicked”, G_CALLBACK (on_btnPushMe_clicked), NULL);

//force show all
gtk_widget_show_all(winMain);
}

//assign C-compatible pointer
myMainWindow = self;

//return pointer to this object
return self;
}

-(void)startGtkMainLoop
{
//start gtk loop
gtk_main();
}

-(void)printSomething{
NSLog(@”Printed from Objective-C’s NSLog function.”);
printf(“Also printed from standard printf function.\n”);
}

-(void)destroyWidget{

myMainWindow = NULL;

if(GTK_IS_WIDGET (button))
{
//clean up the button
gtk_widget_destroy(button);
}

if(GTK_IS_WIDGET (winMain))
{
//clean up the main window
gtk_widget_destroy(winMain);
}
}

-(void)dealloc{
[self destroyWidget];

[super dealloc];
}

void on_MainWindow_destroy(GtkObject *object, gpointer user_data)
{
//exit the main loop
gtk_main_quit();
}

void on_btnPushMe_clicked(GtkObject *object, gpointer user_data)
{
printf(“Button was clicked\n”);

//call Objective-C function from C function using global object pointer
[myMainWindow printSomething];
}

@end

main.m

To finish I will write a main file and function that creates the MainWindow object and eventually cleans it up. Objective-C (1.0) does not support automatic garbage collection so it is important that we don’t forget to clean up after ourselves.

#import “MainWindow.h”
#import <Foundation/NSAutoreleasePool.h>

int main(int argc, char *argv[]) {

//create an AutoreleasePool
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

//init gtk engine
gtk_init(&argc, &argv);

//set up GUI
MainWindow *mainWindow = [[MainWindow alloc] initWithArgCount:&argc andArgVals:argv];

//begin the GTK loop
[mainWindow startGtkMainLoop];

//free the GUI
[mainWindow release];

//drain the pool
[pool release];

//exit application
return 0;
}

Compiling it all together

Use the following command to compile the program. This will automatically include all .m files in the current directory so be careful when and where you run this.

gcc `pkg-config –cflags –libs gtk+-2.0` -lgnustep-base -fconstant-string-class=NSConstantString -o “./myprogram” $(find . -name ‘*.m’) -I /usr/include/GNUstep/ -L /usr/lib/GNUstep/ -std=c99 -O3

Once complete you will notice a new executable in the directory called myprogram. Start this program and you will see our GTK+ window in action.

If you run it from the command line you can see the output that we coded when the button is pushed.

Wrapping it up

There you have it. We now have a program that is written in Objective-C, using C’s native GTK+ ‘bindings’ for the GUI, that can call both regular C and Objective-C functions and code. In addition, thanks to the porting of both GTK+ and GNUstep to Windows, this same code will also produce a cross-platform application that works on both Mac OSX and Windows.

Source Code Downloads

Source Only Package
File name: objective_c_gtk_source.zip
File hashes: Download Here
File size: 2.4KB
File download: Download Here

Originally posted on my personal website here.



8 Comments

  1. You have combined two things I wouldn’t touch with a 10-metre pole.

    ObjC: the GCC compiler for it is basically dead, so you’d better make sure to have Clang installed, and that’s not production-ready…
    GTK: Dropped at birth, and battered about by its own community ever since. Generally used to make ugly applications with ever-decreasing feature-sets.

    Hell, the D compiler for GCC is under active development, and Clutter is far more interesting than GTK.
    D and OpenGL play well with each other.

  2. How about you indent the code, and make comments less obstructive? Just because of this, I didn’t bother looking at anything you wrote.

  3. Kyle :

    How about you indent the code, and make comments less obstructive? Just because of this, I didn’t bother looking at anything you wrote.

    Sorry that’s WordPress not me. If you bothered to scroll just a little lower you would have seen a download link for the correctly formatted code.

  4. Well, I appreciate that you have put up a tutorial on combining these 2 technologies for those of us that are curious. Thank you very much.

  5. Doesn’t work, when I run the command, I end up with this:
    -cflags: unknown option
    ./main.m:1:13: error: #import expects “FILENAME” or
    ./main.m: In function ‘main’:
    ./main.m:10:5: warning: implicit declaration of function ‘gtk_init’
    ./main.m:13:5: error: ‘MainWindow’ undeclared (first use in this function)
    ./main.m:13:5: note: each undeclared identifier is reported only once for each function it appears in
    ./main.m:13:17: error: ‘mainWindow’ undeclared (first use in this function)
    ./MainWindow.m:1:13: error: #import expects “FILENAME” or
    ./MainWindow.m:9:5: warning: cannot find interface declaration for ‘MainWindow’
    ./MainWindow.m: In function ‘-[MainWindow initWithArgCount:andArgVals:]’:
    ./MainWindow.m:12:5: error: no super class declared in @interface for ‘MainWindow’
    ./MainWindow.m:15:5: error: ‘winMain’ undeclared (first use in this function)
    ./MainWindow.m:15:5: note: each undeclared identifier is reported only once for each function it appears in
    ./MainWindow.m:15:5: warning: implicit declaration of function ‘gtk_window_new’
    ./MainWindow.m:15:31: error: ‘GTK_WINDOW_TOPLEVEL’ undeclared (first use in this function)
    ./MainWindow.m:17:5: warning: implicit declaration of function ‘gtk_window_set_title’
    ./MainWindow.m:17:5: warning: implicit declaration of function ‘GTK_WINDOW’
    ./MainWindow.m:17:5: error: stray ‘\342’ in program
    ./MainWindow.m:17:5: error: stray ‘\200’ in program
    ./MainWindow.m:17:5: error: stray ‘\234’ in program
    ./MainWindow.m:17:52: error: ‘Hello’ undeclared (first use in this function)
    ./MainWindow.m:17:58: error: expected ‘)’ before ‘World’
    ./MainWindow.m:17:58: error: stray ‘\342’ in program
    ./MainWindow.m:17:58: error: stray ‘\200’ in program
    ./MainWindow.m:17:58: error: stray ‘\235’ in program
    ./MainWindow.m:18:5: warning: implicit declaration of function ‘gtk_window_set_default_size’
    ./MainWindow.m:21:5: error: ‘button’ undeclared (first use in this function)
    ./MainWindow.m:21:5: warning: implicit declaration of function ‘gtk_button_new_with_label’
    ./MainWindow.m:21:5: error: stray ‘\342’ in program
    ./MainWindow.m:21:5: error: stray ‘\200’ in program
    ./MainWindow.m:21:5: error: stray ‘\234’ in program
    ./MainWindow.m:21:44: error: ‘Push’ undeclared (first use in this function)
    ./MainWindow.m:21:49: error: expected ‘)’ before ‘me’
    ./MainWindow.m:21:49: error: stray ‘\342’ in program
    ./MainWindow.m:21:49: error: stray ‘\200’ in program
    ./MainWindow.m:21:49: error: stray ‘\235’ in program
    ./MainWindow.m:23:5: warning: implicit declaration of function ‘gtk_container_add’
    ./MainWindow.m:23:5: warning: implicit declaration of function ‘GTK_CONTAINER’
    ./MainWindow.m:26:5: warning: implicit declaration of function ‘g_signal_connect’
    ./MainWindow.m:26:5: error: stray ‘\342’ in program
    ./MainWindow.m:26:5: error: stray ‘\200’ in program
    ./MainWindow.m:26:5: error: stray ‘\234’ in program
    ./MainWindow.m:26:5: error: stray ‘\342’ in program
    ./MainWindow.m:26:5: error: stray ‘\200’ in program
    ./MainWindow.m:26:5: error: stray ‘\235’ in program
    ./MainWindow.m:26:35: error: ‘destroy’ undeclared (first use in this function)
    ./MainWindow.m:26:5: warning: implicit declaration of function ‘G_CALLBACK’
    ./MainWindow.m:26:59: error: ‘on_MainWindow_destroy’ undeclared (first use in this function)
    ./MainWindow.m:26:83: error: ‘NULL’ undeclared (first use in this function)
    ./MainWindow.m:27:5: error: stray ‘\342’ in program
    ./MainWindow.m:27:5: error: stray ‘\200’ in program
    ./MainWindow.m:27:5: error: stray ‘\234’ in program
    ./MainWindow.m:27:5: error: stray ‘\342’ in program
    ./MainWindow.m:27:5: error: stray ‘\200’ in program
    ./MainWindow.m:27:5: error: stray ‘\235’ in program
    ./MainWindow.m:27:34: error: ‘clicked’ undeclared (first use in this function)
    ./MainWindow.m:27:58: error: ‘on_btnPushMe_clicked’ undeclared (first use in this function)
    ./MainWindow.m:30:5: warning: implicit declaration of function ‘gtk_widget_show_all’
    ./MainWindow.m:34:5: error: ‘myMainWindow’ undeclared (first use in this function)
    ./MainWindow.m: In function ‘-[MainWindow startGtkMainLoop]’:
    ./MainWindow.m:43:5: warning: implicit declaration of function ‘gtk_main’
    ./MainWindow.m: In function ‘-[MainWindow printSomething]’:
    ./MainWindow.m:47:5: warning: implicit declaration of function ‘NSLog’
    ./MainWindow.m:47:11: error: stray ‘@’ in program
    ./MainWindow.m:47:5: error: stray ‘\342’ in program
    ./MainWindow.m:47:5: error: stray ‘\200’ in program
    ./MainWindow.m:47:5: error: stray ‘\235’ in program
    ./MainWindow.m:47:15: error: ‘Printed’ undeclared (first use in this function)
    ./MainWindow.m:47:23: error: expected ‘)’ before ‘from’
    ./MainWindow.m:47:23: error: stray ‘\342’ in program
    ./MainWindow.m:47:23: error: stray ‘\200’ in program
    ./MainWindow.m:47:23: error: stray ‘\231’ in program
    ./MainWindow.m:47:23: error: stray ‘\342’ in program
    ./MainWindow.m:47:23: error: stray ‘\200’ in program
    ./MainWindow.m:47:23: error: stray ‘\235’ in program
    ./MainWindow.m:48:5: warning: implicit declaration of function ‘printf’
    ./MainWindow.m:48:5: warning: incompatible implicit declaration of built-in function ‘printf’
    ./MainWindow.m:48:5: error: stray ‘\342’ in program
    ./MainWindow.m:48:5: error: stray ‘\200’ in program
    ./MainWindow.m:48:5: error: stray ‘\234’ in program
    ./MainWindow.m:48:15: error: ‘Also’ undeclared (first use in this function)
    ./MainWindow.m:48:20: error: expected ‘)’ before ‘printed’
    ./MainWindow.m:48:20: error: stray ‘\’ in program
    ./MainWindow.m:48:20: error: stray ‘\342’ in program
    ./MainWindow.m:48:20: error: stray ‘\200’ in program
    ./MainWindow.m:48:20: error: stray ‘\235’ in program
    ./MainWindow.m: In function ‘-[MainWindow destroyWidget]’:
    ./MainWindow.m:53:5: error: ‘myMainWindow’ undeclared (first use in this function)
    ./MainWindow.m:53:20: error: ‘NULL’ undeclared (first use in this function)
    ./MainWindow.m:55:5: warning: implicit declaration of function ‘GTK_IS_WIDGET’
    ./MainWindow.m:55:23: error: ‘button’ undeclared (first use in this function)
    ./MainWindow.m:58:5: warning: implicit declaration of function ‘gtk_widget_destroy’
    ./MainWindow.m:61:23: error: ‘winMain’ undeclared (first use in this function)
    ./MainWindow.m: In function ‘-[MainWindow dealloc]’:
    ./MainWindow.m:71:5: error: no super class declared in @interface for ‘MainWindow’
    ./MainWindow.m: At top level:
    ./MainWindow.m:74:42: error: expected ‘)’ before ‘*’ token
    ./MainWindow.m:80:41: error: expected ‘)’ before ‘*’ token
    ./MainWindow.m:82:5: error: stray ‘\342’ in program
    ./MainWindow.m:82:5: error: stray ‘\200’ in program
    ./MainWindow.m:82:5: error: stray ‘\234’ in program
    ./MainWindow.m:82:5: error: stray ‘\’ in program
    ./MainWindow.m:82:5: error: stray ‘\342’ in program
    ./MainWindow.m:82:5: error: stray ‘\200’ in program
    ./MainWindow.m:82:5: error: stray ‘\235’ in program

  6. This time I got this:

    hpmini@ubuntu:~$ cd myprogram
    hpmini@ubuntu:~/myprogram$ gcc ‘pkg-config -cflags -libs gtk+-2.0’ -lgnustep-base -fconstant-string-class=NSConstantString -o “./myprogram” $(find . -name ‘*.m’) -I /usr/include/GNUstep/ -L /usr/lib/GNUstep/ -std=c99 -O3
    gcc: pkg-config -cflags -libs gtk+-2.0: No such file or directory
    In file included from ./main.m:7:0:
    ./MainWindow.h:7:20: fatal error: gtk/gtk.h: No such file or directory
    compilation terminated.
    In file included from ./MainWindow.m:7:0:
    ./MainWindow.h:7:20: fatal error: gtk/gtk.h: No such file or directory
    compilation terminated.
    hpmini@ubuntu:~/myprogram$

  7. Brandon I had the same until I did this:
    gcc `pkg-config –cflags –libs gtk+-2.0` -lgnustep-base -fconstant-string-class=NSConstantString -o testprog MainWindow.m main.m -I /usr/include/GNUstep/ -L /usr/lib/GNUstep/ -std=c99 -O3
    Note “–” not “-” which I had originally from the cut and paste; and for me I had then to change the $(find….) to explicitly list files. hth

1 Trackback / Pingback

  1. The Linux Experiment » Blast from the Past: Create a GTK+ application on Linux with Objective-C

Leave a Reply

Your email address will not be published.


*