[Openmcl-devel] Carbon FFI: How can I debug this?
Gary Byers
gb at clozure.com
Fri Dec 24 14:48:02 PST 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
created.)
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