November 2005


Beside the bindings to Objective-C runtime API adding Cocoa flavor to Scheme 48 means dealing with GUI events. This has been the hardest part of this project. Basically, the Scheme 48 VM executes byte code and, if it has to wait for events (file, network, or terminal I/O) it calls select. GUI events are further source of events select needs to take into account. However, only few GUI libraries offer notifcations on events via a file descriptor. X11 does this, Qt doesn’t. I’m not sure about GTK. However, this is a problem. In the case of Qt this has been solved by re-implementing the select mechanismn in terms of Qt classes and their Notification mechanismn. Well, that’s a different story.

Well, for Cocoa this is also a problem. Cocoa GUI application usually fire up a NSRunLoop which takes over the control. In a first attempt Gregor tried to re-implement the run loop to enable running it only for a defined period of time. This is possible, however, doesn’t work very well in practice. The run loop is completly mysterious. It seems that this run loop does some strange initializations we were not able to follow. With Gregor’s run loop some things happened that didn’t happen when using the original run loop. For example, in certain situations there was more than one window holding the focus or some black hole swallowed events unpredictablely. This behavior and the apprehension that Apple might change the mysterious initialization any time made us give up this attempt.

So, here is the real solution, which is more complex. If the run loop wants to run, let it run, but in an extra operating system thread. At startup the Cocoa edition of Scheme 48 spawns an operating system thread. This thread runs the VM. The main thread executes the run loop (for some esoteric reason it’s a good idea to run the run loop in main thread), like a normal Cocoa would do. To react to GUI events a Cocoa programmer attaches callbacks to a control/widget (i.e., using the setTarget or setAction methods). Thing become a bit complicated if the callback is written in Scheme. Also it’s possible to install a Scheme function as a callback, it’s dangerous: The callback may occur at any time and that is not the way the Scheme 48 VM excepts the world to be. The VM decides when to run code and when to wait. The FFI knows to handle calls from C to Scheme, however, that’s a different situation: Some piece of Scheme code calls some piece of C code and that code calls some Scheme function. This works fine, however, just calling Scheme code from C at an arbitrary point in time is not advisable.

So, the solution is to enqueue the callback events and notify the VM somehow instead of directly executing the callbacks. Scheme 48 Cocoa uses a proxy object (S48EventHandler) to callbacks. If the Scheme programmer registers for a callback, a method of S48EventHandler is set as the callback. This callback is executed in main thread which is also the thread running the run loop (it seems to be of some importance to Cocoa to handle events in thread they occur). The callback (of the proxy object) does two things. It enqueues a tuple consisting of an object id (which identifies the affected object) and a event type id into a global queue so the VM can later read from that queue. And the method also writes to a pipe that connects the main thread with Scheme 48 VM. Besides the file descriptors the VM also selects on this pipe. This way the VM notices GUI events when selecting. In this case the VM may check the queue (filled by the proxy object) and schedule a so-called GUI interrupt and then things become even more complicated. It seems that using a pipe for ending the select is the easiest method. After Gregor’s talk there was a discussion whether a using a signal would be a better solution. I think that this makes things even more complicated, because GUI events may also happen when select is not running, i.e. it’s not the time to execute the callback. It’s unclear to me what the signal handler should do in this case.

If the GUI interrupt handler catches the GUI interrupt things get a bit complicated, but that’s a different story. However, with the information from the queue it’s easy to find the callback to execute. A table maps object id, event type tuples to the Scheme function that serves as the callback. For the Scheme programmer this looks like this:

(define nsbutton
  (objc-make-class "NSButton"))

(define my-button
  (objc-alloc nsbutton))

(register-cocoa-callback
  my-button
  s48-action-notification
  (lambda ()
    (display "button pressed")
    (newline)))

(start-action-notifications my-button)

The function register-cocoa-callback installs the callback for the NSButton represented by my-button. The second argument for register-cocoa-callback specifies to event type, in this case it’s s48-action-notification. There are several different types of events like low level events like mouse events, and “action” is one of the high level events. Such an event is generated if the user clicks a NSButton. Register-cocoa-callback stores the information about the callback (object id, type id and callback) in the global callback table and start-action-notifications actually connects the callbacks of my-button to the proxy object.

Hm, I probably misused some of the Objective-C and Cocoa slang in this story—sorry. That’s simply because I haven’t done much Cocoa programming yet. But I hope to use Cocoa more often in the future—in Scheme programs of course.

Technorati Tags: , , ,

Today one of my students gave a talk on his term project: “Objective-C Runtime API Bindings and Cocoa Events For Scheme48″. Don’t take “term project” too literal, this project has been running for few years—it started the time I was finishing my diploma thesis, I think. So it’s more like an “multiterm project”. However, the result is impressing and I hope to find the time to polish the code so that I can commit it to the Scheme 48 repository soon. So, here is what the project is about:

OS X comes with an extremly rich set of class libraries called Cocoa. These class libraries include classes for writing applications on OS X, using the Aqua GUI, tons of widgets (called controls), and even a class library for writing web browsers (I’m looking forward to the first browser written in Scheme ;-). All these classes are written in (and for) Objective-C, a very dynamic object-oriented programming language based on C. Objective-C (at least the variant in OS X) comes with a powerful runtime API for C, that allows C programmers to use all of Objective-C’s language features from their C programs. For example, the C functionobjc_MsgSend() corresponds to a method call in Objective-C—given some representation for a Objective-C object, a C program can call a method of the object easily using objc_MsgSend(). Even better: Almost everything is dynamic, nothing needs to be fixed at compile time: the name of class or method serves as a reference.

This is ideal. Having bindings from Scheme to Runtime API means that one can call all methods from all classes without the need for any statically generated stub. Here is the simple API for the runtime bindings from Gregor’s talk:

(define nsstring
  (objc-make-class "NSString"))

(define method/init-with-c-string
  (objc-make-selector "initWithCString:"
		      (list (objc-extract-type string))
		      (objc-extract-type objc-object)))

(define method/c-string
  (objc-make-selector "cString"
		      '()
		      (objc-extract-type string)))

(define my-string
  (objc-msg-send (objc-alloc nsstring)
		 method/init-with-c-string
		 (list "Hello Cocoa!")))

> (objc-msg-send my-string method/c-string '())
"Hello Cocoa!"

The function objc-make-class returns a Scheme representation for the Objective-C class NSString which is basically some opaque record. objc-make-selector works the same way and returns a record that represents a selector. A selector identifies a method (a selector is a method name or some unique id). The second argument for objc-make-selector is a list of objc-extract-type values (a Scheme 48 finite type). These values tell objc-msg-send how to convert Scheme values given as arguments to a method into C (or Objective-C) values. objc-msg-send also needs some type hint to convert the return value of a method call into a Scheme value. This hint is given as the third argument to objc-make-selector. Basically these type hints choose which of Scheme 48’s FFI conversion functions (s48_enter_*() and s48_extract_*()) objc-msg-send uses. At the moment there is support for strings, integers, boolean, objects, void, double, and some special Cocoa objects used very frequently. The method initWithCString inits a NSString with a C string; cString returns a NSString as a C string.

To alloacte and initialize a fresh object, a Objective-C programmer uses something like this:

ClassA* object;
object = [[ClassA alloc] init];

This code sends the alloc message to an object of ClassA to allocate some memory and then sends theinit message to the allocated object which does the initialization (i.e. sets instance variables and stuff like that). The Objective-C bindings wraps this sequence in the Scheme function objc-alloc. objc-msg-send actually calls a method specified by a selector (method/init-with-c-string in this case), in this case with the Scheme string “Hello Cocoa!” as the only argument. The method/c-string method for Objective-C object represented by my-string returns that string, and, well… it works!

The API (and the Scheme 48 bindings) have some more advanced features, like defining new classes, methods, and instance variables at runtime in Scheme. However, there is still an issue with that. If someone writes a method in Scheme and adds the method to some class or object, an call from (Objective-) C into Scheme may occur at an unpredictable and possible awkward point in time. That’s dangerous because the VM simply doesn’t expect this to happen. The situation is pretty much the same as in the case for the callbacks of GUI widgets. I think the solution is the same as in GUI case. However, I still have to implement this. Well, and clean up the rest of the code (Yes, I’m aware of the rest list argument).

Technorati Tags: , , ,

Somehow I managed to miss the one feature of OS X 10.4 that is really useful to me:

modifier keys

When was this introduced? How come I didn’t notice? I pledged everlasting loyality to my 93 IBM Model-M keyboard, which has no Windows or Apple/Command key of course. Ever since I used the IBM keyboard this was a problem.

The problem is the strangeness and mystery surrounding the Command key on OS X. Some time ago, I wrote an kernel extension for OS X that tried to make CapsLock the Command key. This extension was based on iJect, SwapCommandOption and uControl and involved some low-level C++ hacking. With this extension it was possible to intercept CapsLock key events. However, turning them into Command key events was not easy. I don’t remember all the details, but I think that the Command key event was so special that it wasn’t possible to create a fake event that yielded the same effect as a the mysterious real event. Never mind, that’s nonrelevant by now! Hurray, long live Model M!

Update: I didn’t know that the Model M had so many fans. Try a Google search on “Model M”. Some examples: Here, there, and the Wikipedia entry.