[Openmcl-devel] Carbon FFI: How can I debug this?

Gary Byers gb at clozure.com
Fri Dec 24 22:48:02 UTC 2004

--On Friday, December 24, 2004 3:17 PM -0500 David Steuber 
<david at david-steuber.com> wrote:

> I'm trying to get an event handler working so that I can be finished with
> Chapter 6 of Learning Carbon.  The problem is, I am getting no feedback
> from the program I've written.  I do not know how far along I go before
> the code fails.  How do people debug their code when FORMAT T doesn't
> send output through a SLIME swank socket.  Is there a way I can get
> FORMAT to send output there when it is called from the main thread?

I believe that Emacs maintains a buffer that's connected to the same
IPC mechanism (socket/pty/pipe/whatever) that was established when
the lisp process was created.  (The buffer's name is something like
"*lisp-interaction*" or "*inferior-lisp*"; if you find that buffer,
you'll probably see that it contains a listener that's wondering
where the user went (and possibly any output sent to some thread's
*TERMINAL-IO* stream, or a synonym stream to *TERMINAL-IO*.)

> Can anyone see anything obviously wrong with this code?

There may be some subtle problems in translation (I'm not familiar
enough with Carbon to be able to spot such a thing quickly), but
there may also be a few other issues that a tutorial on using
Carbon in C wouldn't have reason to mention:

- OpenMCL isn't a GUI application (which basically means that the
  lisp kernel isn't linked against Carbon or Cocoa and doesn't
  run from those libraries during application initialization.

  Fortunately, there are ways (unfortunately, undocumented ways;
  fortunately, widely-used ways) for a command-line application
  to connect to the OSX window server after it's been initialized.
  The Cocoa example does this; I'm not on a Mac at the moment, but
  IIRC the magic function is called something like ENABLE-FOREGROUND.

- Only one thread can have a connection to the Window Server.  At
  least in Cocoa (and I think that the very low-level details of
  event processing are likely very similar, if not identical, between
  Carbon and Cocoa), that distinguished thread has to be the initial
  thread (the one created by the OS when the OS-level process was

  In a typical C program, the main() function runs in the initial
  thread.  In OpenMCL, the initial thread (by default) starts a
  thread to run the REPL, then waits around, sleeping most of the
  time and occasionally waking up to perform "housekeeping" tasks
  (flushing buffers to interactive streams, etc.)

  The Cocoa example jumps through a few extra hoops to try to free up
  the initial thread to run an event loop (and has another thread take
  over responsibility for periodic housekeeping tasks.)  It's also
  pretty careful to try to ensure that anything GUI-related - including
  the loading of the Cocoa libraries - happens in the initial thread.
  Failure to be sufficiently paranoid about this often leads to cases
  where the event thread waits forever, listening for an event that'll
  never come.

- The code below tries to load some resources (.nib files describing
  menus/windows) from "the application bundle.")  OSX GUI applications
  are typically distributed as a "bundle" (a directory whose extension
  is usually ".app" and whose contents are organized according to certain
  conventions.)  The code in the example will likely fail unless the
  executable is contained (along with some .nib files and an Info.plist
  xml file that describes things) in an application bundle.

If you get to the  point where the third problem is the stumbling block,
that's good news: there are several potential solutions.

   - change the code to create windows/menus without using .nib files
     (hard to follow tutorials that way.)

   - refer to the Cocoa example for more bloodcurdling hacks.

   - find a good way to get OpenMCL into an application bundle.  Mikel
     Evins - who posts here fairly regularly - has some tools that help
     to simplify this process, and has used them successfully to do
     Carbon programming in OpenMCL.

> (defun main ()
>    ;; Allow SLIME to connect
>    (when *swank-port*
>      (swank:create-server :port *swank-port* :dont-close t))
>    (setf *main-window* (ccl::make-record :<w>indow<r>ef))
>    (rlet ((nibref :<ibn>ib<r>ef)
>           (mainspec :<e>vent<t>ype<s>pec :event<c>lass
> #$kEventClassCommand :event<k>ind #$kEventCommandProcess))
>      (let ((err (#_CreateNibReference (const-cfstring "main") nibref)))
>        (assert (eql err #$noErr) ()
>                "unable to get the main menu nib reference")
>        (setf err (#_SetMenuBarFromNib (ccl::%get-ptr nibref)
> (const-cfstring "MainMenu")))
>        (assert (eql err #$noErr) ()
>                "Can't set the menubar!")
>        (setf err (#_CreateWindowFromNib (ccl::%get-ptr nibref)
> (const-cfstring "MainWindow") *main-window*))
>        (assert (eql err #$noErr) ()
>                "Can't create the main window!")
>        (#_ShowWindow (ccl::%get-ptr *main-window*))
>        (#_DisposeNibReference (ccl::%get-ptr nibref))
>        ;; Install event handler
>        (#_InstallEventHandler (#_GetWindowEventTarget *main-window*)
>                               (#_NewEventHandlerUPP
> main-window-event-handler)
>                               1 mainspec
>                               *main-window*
>                               (ccl::%null-ptr))
>        ;; Start the main event loop
>        (#_RunApplicationEventLoop)))
>    (#_free *main-window*) ; superfluous since we are about to exit anyway
>    (quit))
> (ccl::defcallback main-window-event-handler
>      (:<e>vent<h>andler<c>all<r>ef handler :<e>vent<r>ef event (:* t)
> user-data :<oss>tatus)
>    (declare (ignore handler))
>    (rlet ((command :<hic>ommand))
>      (#_GetEventParameter event #$kEventParamDirectObject #$typeHICommand
>                           (ccl::%null-ptr)
> (ccl::%foreign-type-or-record-size :<HIC>ommand :bytes)
>                           (ccl::%null-ptr) command)
>      (cond ((equal (ccl::pref command :<hic>ommand.command<id>)
>                    +compute-command+)
>             (compute-command-handler user-data)
>             #$noErr)
>            (t #$eventNotHandledErr))))
> (defun compute-command-handler (windowref)
>    (rlet ((mode-of-transport-button-group :<c>ontrol<h>andle)
>           (travel-time-field :<c>ontrol<h>andle)
>           (mode-of-transport-control-id :<c>ontrol<id>
>                                         :signature +application-signature+
>                                         :id
> +mode-of-transport-button-group-id+)
>           (travel-time-control-id :<c>ontrol<id>
>                                   :signature +application-signature+
>                                   :id +travel-time-field-id+))
>      (#_GetControlByID (ccl::%get-ptr windowref)
>                        mode-of-transport-control-id
> mode-of-transport-button-group)
>      (#_GetControlByID (ccl::%get-ptr windowref)
>                        travel-time-control-id travel-time-field)
>      (let* ((transport-mode-value (#_GetControl32BitValue
> mode-of-transport-button-group))
>             (travel-time (cond ((= transport-mode-value +foot-mode+)
>                                 (/ (/ +distance-to-moon+ (/ 4.0 0.62))
> +hours-per-day+))
>                                ((= transport-mode-value +car-mode+)
>                                 (/ (/ +distance-to-moon+ (/ 70.0 0.62))
> +hours-per-day+))
>                                ((= transport-mode-value
> +commercial-jet-mode+)
>                                 (/ (/ +distance-to-moon+ (/ 600.0 0.62))
> +hours-per-day+))
>                                ((= transport-mode-value
> +apollo-spacecraft-mode+)
>                                 4)
>                                (t -1.0))))
>        (with-cfstring (text (format nil "~,2F" travel-time))
>          (#_SetControlData travel-time-field #$kControlEntireControl
> #$kControlEditTextCFStringTag
>                            (ccl::%foreign-type-or-record-size
> :<cfs>tring<r>ef :bytes) text))
>        (#_Draw1Control travel-time-field))))
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list