The Developer’s Cry

a blog about computer programming

Porting old Objective-C codes from OSX to Linux

In the end of 2009 I got a shiny 27-inch iMac, and spent the following years writing small games and 3D codes using Cocoa OpenGL. The language of Mac OS X, as the operating system was called back then, was Objective-C—a quirky dialect of the C programming language. It featured a lightweight kind of OOP, which was actually quite nice and suitable for making games.

Fast-forward to 2025; I no longer own an iMac, Mac OS is called “macOS”, the Apple architecture has changed from 32 bit to x86_64 to aarch64, OpenGL is deprecated (in favor of Apple Metal), and while Objective-C technically still works (it was succeeded by Swift), code no longer compiles, moreover I’m on Linux, and there is no Cocoa there.

The only way to resurrect this old code from the dead is to do some serious porting effort.

First off, what do we have? A lot of times I used ObjC just as the glue language to talk to the system; create a window, setup OpenGL, and the rest of the code would be plain C. Digging in the archives, we find code that looks like this:

@implementation GameView

- (void)prepareOpenGL {
/*
    float screen_w = [self frame].size.width;
    float screen_h = [self frame].size.height;

    NSLog(@"TD screen_w %.1f  screen_h %.1f", screen_w, screen_h);
*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    // setup coordinate system
    // note that this will be reset again by -reshape:  (see below)
    glOrtho(0, SCREEN_W, 0, SCREEN_H, -Z_FAR, Z_FAR);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // already done by NSOpenGLView
//  glViewport(0, 0, [self frame].size.width, [self frame].size.height);

    // this sets swap interval for double buffering
    GLint swapInt = 1;
    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
}

The GameView is/was derived from NSOpenGLView, which is a Cocoa window that supports OpenGL. When the window opened it invoked the prepareOpenGL method for initialization. There are also methods for event handling such as resizing the window, keyboard and mouse input.

We can port all of that to SDL using pure C. The core of it is creating an OpenGL capable window, and writing the SDL main event loop.

window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, 0, w, h, SDL_WINDOW_OPENGL);
glcontext = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, glcontext);

// enable vsync
SDL_GL_SetSwapInterval(1);

... render scene

SDL_GL_SwapWindow(window);

Besides rewriting old code in C, I also have a bunch of game code that uses Objective-C more extensively. As said, it was a nice language for making games because of its lightweight object orientedness. For example, you could have different kinds of monsters inherit from Monster, with an inheritance tree like MotherSpider : Spider : Monster : WorldObject : NSObject.

A snippet for illustration:

@interface Monster : WorldObject {
    Vector2D direction;
    float speed, steeringSpeed;
    Vertex goal;
}

@property Vector2D direction;
@property float speed;
@property Vertex goal;

-(float)sight;
-(void)chooseGoal;
-(void)copyGoal:(Monster *)m;
-(BOOL)arrivedAtGoal;
-(void)turnTowardsGoal;
-(void)steerTowardsVector:(Vertex2D *)delta;

Rewriting all that old code would be madness, and we’re not going to. Instead, we can simply compile Objective-C code on Linux. Here comes GNUstep to the rescue:

$ sudo apt install gobjc gobjc++ gnustep gnustep-devel libgnustep-base-dev

Add to the Makefile:

INCLUDE_OBJC = $(shell gnustep-config --objc-flags)
LIB_OBJC = -lgnustep-base -lobjc

...

# gcc/clang will compile .m files without much complaint
$(OBJDIR)/%.o: %.m
    $(CC) $(CFLAGS) -c $< -o $@

Let’s enter make … Lo and behold, it builds!

Seeing old codes come back to life again on a different platform is nothing short of amazing.

This particular code (screenshot above) originated from Mac, but ironically, the new port does not work on a modern Mac. Attempts of fixing it got me past the dreaded blank screen, but it still doesn’t render properly. Apparently we’re hitting a known issue with OpenGL on Apple M-series, but since OpenGL on Mac is deprecated, I don’t have any high hopes. Good for us it does run on Linux, saving this code from bitter extinction.