• entries
73
131
• views
54769

# Hacking

313 views

Haha, boy I love hacking. It excites a strange feeling of rebellion.

I am working on making my development environment as comfortable as possible. My thoughts an IDE's and workspaces are this: do whatever makes you happy. It's tempting to evangelize and build up an attitude of "being right" (see the Emacs/VI holy war). But whatever; you're the one doing the work, and you should choose how to do it.

With that said, I can't stand IDE's. I think they enforce an extremely specific workflow and style of development, and also make it freaking annoying to tweak many parts of the build process. I'm an Emacs guy. I thought about writing up a formal review of current IDE's and why they don't work for me, but I don't really feel like doing it anymore. Let's just say I have used both Visual Studio and Xcode, but I've never been happier with Emacs. Maybe I can show you why in the next couple of posts.

I'll explain my opening statement at some point, I promise. I suddenly feel like I should attempt to describe Emacs, however, since much of what I will work on involves customizing it. Emacs is a text editor written mostly in Emacs Lisp. Obviously, ELisp was built for Emacs, and the whole language is exposed to you. You are allowed to redefine any part of the text editor itself, and as you use Emacs, you begin tailoring it to your specific needs. The result is two-fold: there are several packages out there you can install which turn Emacs into some amazing things, and you get to transform your text editor into something perfect for you.

My current focus is making my development environment in Emacs as fun and intuitive as possible. Why don't I start with a simple program, and see how I want to develop it from there?

I want my first program to:

• Use Cocoa's application framework and open up a window

• Embed a Gambit-C Scheme function which prints out some text on load

• Be invokable on the command-line, such as ./main' (easy management of stdout/stderr, also can use Gambit-C's REPL)

There's not much code to do this, but it's certainly a start in figuring out issues with using Cocoa outside of Xcode, Gambit-C Scheme integration, and more. Here's the program and the output:

 --- main.c --------------------------------------#import #define ___VERSION 403002#include "gambit.h"#include "engine.h"#include "stdio.h"#define LINKER ____20_link_____BEGIN_C_LINKAGEextern ___mod_or_lnk LINKER (___global_state_struct*);___END_C_LINKAGE___setup_params_struct setup_params;int main(int argc, char* argv[]) {	___setup_params_reset (&setup_params);	setup_params.version = ___VERSION;	setup_params.linker = LINKER;	___setup(&setup_params);	print_something();	int ret = NSApplicationMain(argc, argv);	___cleanup();	return 0;} --- engine.h --------------------------------------void print_something(); --- engine.scm --------------------------------------(c-define (print-something) () void "print_something" ""  (display "Hello, World!")) --- Makefile --------------------------------------gsc -debug -link -o link_.c engine.scmgcc -o main main.c link_.c engine.c 		-D___LIBRARY 		-I/usr/local/Gambit-C/current/include 	        -lgambc 		-framework Cocoa -framework OpenGL 		-sectcreate __TEXT __info_plist Info.plist ---------------------------------------------------(there's also a supporting .nib file and Info.plist which Cocoareads to build the window from)

Sweet! There's one big problem which the screenshot doesn't show: Ctrl-C doesn't kill the program. After some research, it looks like Gambit-C takes control over the interrupts, and since I never call back into Gambit it doesn't have a chance to respond. This is perfectly reasonable, and should be solved once we're calling Gambit in the rendering code.

My second goal was to have some triangles on the screen, rendered from Scheme code. It's pretty much the same program above, but some more Cocoa integration to initialize OpenGL and such. The only interesting part is probably the Scheme code, so I'll post it:

(define medium #f)(c-define (init-engine) () void "init_engine" ""  (set! medium (obj-load "/Users/james/projects/scheme/graphics-gambit/resources/medium.obj")))(define x 0.0)(c-define (run-frame) () void "run_frame" ""  (set! x (+ x 1.5))  (glLoadIdentity)  (glRotatef 90.0 1.0 0.0 0.0)  (glRotatef x 0.0 0.0 1.0)  (glScalef 0.02 0.02 0.02)  (glClearColor 0.2 0.3 0.4 1.0)  (glClear GL_COLOR_BUFFER_BIT)  (glColor3f 1.0 0.9 0.8)  (glBegin GL_TRIANGLES)  (for-each   (lambda (triangle)     (glVertex3f (vector3d-x (triangle-v1 triangle))                 (vector3d-y (triangle-v1 triangle))                 (vector3d-z (triangle-v1 triangle)))     (glVertex3f (vector3d-x (triangle-v2 triangle))                 (vector3d-y (triangle-v2 triangle))                 (vector3d-z (triangle-v2 triangle)))     (glVertex3f (vector3d-x (triangle-v3 triangle))                 (vector3d-y (triangle-v3 triangle))                 (vector3d-z (triangle-v3 triangle))))   (obj-data-triangles medium))  (glEnd))

INIT-ENGINE is called at the beginning of the program, and RUN-FRAME is called every 1/60th of a second. I wrote a .obj file loader so that I can have some stuff to test with, so OBJ-LOAD just returns a list of triangles from a mesh inside a .obj. Here's the result now:

Even sweeter! There's no lighting, so it looks bad, but it's a rotating 3d mesh spelling "medium".

Interrupting the program works now, although I'm getting a strange crash when the program exits. I'm shutting down the Gambit system wrong somehow. Need to fix.

My third goal was to learn how to track mouse and keyboard events with Cocoa. As I got into this, however, I realized that I can't do this. The windowing system can't possibly dispatch any user input events on the window. The process is still seen as a command-line process, a child of my current shell. The shell still receives all the mouse movements and key presses. It looks like I'll have to invoke the program as a windowed application.

On OSX, this is achieved by creating a bundled application and using open'. So instead of ./main', I will do open Main.app'. The reasons why I initially tried to avoid this is that it is no longer a child process, and now I have to track down where stdout/stderr are going and there's no chance of using Gambit's REPL.

In reality, I'll be invoking the app in several different ways, compiled with several different options, depending on how I intend to use it. Debugging scene rendering problems, cocoa, gambit code, etc. are all different which will require different tools. It'll be good to build in all the options of compiling & invoking the app.

Anyway, this is a lot more than I intended. I didn't even get to talk about configuring Emacs. But essentially, anything that happens on the command line can be hooked into Emacs, so my most common actions are only a keyboard shortcut away. I hope to show some example of this later.

ALSO, in reference to my opening statement, I was playing around with the idea of dynamically loading in Cocoa and keeping Gambit in control. It's a huge hack, since Cocoa is meant to handle the entry point and be statically compiled down to an executable. But essentially, I compiled a gambit program which wraps around Cocoa's NSApplicationMain procedure into a dynamic library. I then proceeded to load it and try to invoke it, which would hopefully create a window, but let me stay in Gambit land. I got a nice little error from Cocoa though; it couldn't find the preferences file which was compiled in as the __TEXT section. I'm guessing there's something I'd have to do to locate it in a dynamic library (it would be ok in a flat executable).

[19:07] james:~/projects/scheme/graphics-gambitjames% gsc -cc-options 'cocoa-entry.m glview.m glwindow.m -framework Cocoa -framework OpenGL -sectcreate __TEXT __info_plist Info.plist' entry.scmcocoa-entry.m: In function 'cocoa_entry':cocoa-entry.m:28: warning: passing argument 2 of 'NSApplicationMain' from incompatible pointer type[19:07] james:~/projects/scheme/graphics-gambitjames% gsi gsi Gambit v4.3.2> (load "entry")2008-12-18 19:07:14.945 gsi[37765:10b] No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting

Oh boy, I think I just dumped a bunch of stuff here at the end. Forgive me. I'm still getting the hang of decent writing (I probably should have split this up).

There are no comments to display.