Arthur W Cater | 26 Aug 10:46
Favicon

programmatic file chooser?

Hi, I'd like to have an equivalent in ClozureCL (for Mac & Cocoa) of MCL's choose-file-dialog.

Does such a thing exist already? or would I have to roll my own, and if so can
anyone please point me to the names of useful cocoa classes & methods? I hope I don't have
to go all the way to NSBrowser.

Arthur

_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Gary Byers | 26 Aug 12:25

Re: programmatic file chooser?

Enclosed is a very simple example showing one way to use NSOpenPanel.
It doesn't exercise all of NSOpenPanel's options or features, but
it does mention a few of them in passing and I think that this is
an example of "fairly typical" usage, and it seems to be ~10 lines of
code, for whatever that's worth.  (It seems to be safe to call it
from a listener.)

NSOpenPanel is a subclass of NSSavePanel (example left as an exercise.)
It may or may not be a little surprising that neither NSOpenPanel
nor NSSavePanel is mentioned directly in the IDE sources (which
obviously deal with Open and Save panels when ... opening and
saving documents.)  The editor sources (in "ccl:cococa-ide;cocoa-editor.lisp")
contain some methods which setup a popup menu which lists way too many
character encodings with often cryptic names; that menu is basically
used as the "accessory view" of an NSOpenPanel or NSSavePanel.

I would ordinarily point you to Apple's online documentation, but
the URL just changed and would probably change again by the time this
message reaches anyone.  Instead (and since it hasn't been plugged
in a while), it may be helpful to point out the AppKido program

<http://homepage.mac.com/aglee/downloads/appkido.html>

AppKido's just a little program that browses locally-installed
copies of Apple's Cocoa documentation (and works well, unless
Apple changes the file:/ URL of those locally-installed copies.)
The information's exactly the same as what's in Apple's HTML
files, but it may be easier to navigate through that information
with AppKido than it would be elsewise), and it may be easier
to find what you're looking for (when you don't know what it's
called) via AppKido's navigation and search features.

(So, rather than saying RTFM!, I'm recommending what seems to
be a good tool for R'ing TFM.)

On Tue, 26 Aug 2008, Arthur W Cater wrote:

> Hi, I'd like to have an equivalent in ClozureCL (for Mac & Cocoa) of MCL's choose-file-dialog.
> Does such a thing exist already? or would I have to roll my own, and if so can
> anyone please point me to the names of useful cocoa classes & methods? I hope I don't have
> to go all the way to NSBrowser.
>
> Arthur
>
>
>
;;; Very simple example of using NSOpenPanel to select a single filename.
;;; A few utilities for dealing with NSStrings are defined (like most
;;; of the IDE) in the GUI package; they may be generally useful enough
;;; to be moved to OBJC or some other package (and exported !).

(defun choose-lisp-source-file ()
  (gui::with-autorelease-pool 
      (let* ((panel (#/openPanel ns:ns-open-panel))) ; allocate an NSOpenPanel
        (#/setAllowsMultipleSelection: panel nil) ; return at most one filename
        (when (eql #$NSOKButton
                   (#/runModalForDirectory:file:types: panel
                                                       +null-ptr+ ; default to last dir used
                                                       +null-ptr+ ; no preselected file
                                                       ;; An array containing an NSString
                                                       ;; describing the single file type
                                                       ;; we're interested in
                                                       (#/arrayWithObject: ns:ns-array
                                                                           #@"lisp")))
          ;; Because we told the panel to disallow multiple selection,
          ;; there should be exactly one object in this array, an
          ;; NSString describing the selected file.
          (let* ((files (#/filenames panel)))
            (if (eql 1 (#/count files))
              (gui::lisp-string-from-nsstring (#/objectAtIndex: files 0))
              (error "Don't know why we didn't get an NSArray containing exactly 1 file here.")))))))
                                        
                                                            
_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Arthur W Cater | 26 Aug 13:26
Favicon

Re: programmatic file chooser? thanks!

Thank you to Gary for a quick and extremely helpful answer.


Arthur

_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Chris Van Dusen | 27 Aug 18:05

Re: programmatic file chooser? thanks!

Agreed.  I saw AppKiDo mentioned in the online documentation, and have been using it pretty heavily since.

One thing I've been curious about (and may produce on my own, if none exists) is a cheat sheet for the various ways of interacting with Objective-C.  Until I get used to it, when to use the appropriate reader macro is a source of frustration.

All of that said, does anyone have suggestions/recommendations as to how best to learn to commingle CCL & Objective-C?  I'm fairly comfortable with Lisp in general.  Would it be worth my while to devote my time to learning Objective-C, Cocoa, etc. to the point that I'm conversant in those things, then come back at it from the CCL perspective?

Thanks,
Chris.

On Tue, Aug 26, 2008 at 6:26 AM, Arthur W Cater <arthur.cater <at> ucd.ie> wrote:
Thank you to Gary for a quick and extremely helpful answer.

Arthur


_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel


_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
R. Matthew Emerson | 28 Aug 03:45

Re: programmatic file chooser? thanks!


On Aug 27, 2008, at 12:05 PM, Chris Van Dusen wrote:

> All of that said, does anyone have suggestions/recommendations as to  
> how best to learn to commingle CCL & Objective-C?  I'm fairly  
> comfortable with Lisp in general.  Would it be worth my while to  
> devote my time to learning Objective-C, Cocoa, etc. to the point  
> that I'm conversant in those things, then come back at it from the  
> CCL perspective?

My personal opinion is that it would be worthwhile to spend some time  
learning about Objective-C and Cocoa.

I suggest working through the Hillegass book (at least up through  
chapter 21 or so).

The book:
http://www.amazon.com/dp/0321503619
Gary Byers | 28 Aug 12:18

Re: programmatic file chooser? thanks!


On Wed, 27 Aug 2008, Chris Van Dusen wrote:

> Agreed.  I saw AppKiDo mentioned in the online documentation, and have been
> using it pretty heavily since.
> One thing I've been curious about (and may produce on my own, if none
> exists) is a cheat sheet for the various ways of interacting with
> Objective-C.  Until I get used to it, when to use the appropriate reader
> macro is a source of frustration.
>
> All of that said, does anyone have suggestions/recommendations as to how
> best to learn to commingle CCL & Objective-C?  I'm fairly comfortable with
> Lisp in general.  Would it be worth my while to devote my time to learning
> Objective-C, Cocoa, etc. to the point that I'm conversant in those things,
> then come back at it from the CCL perspective?
>
> Thanks,
> Chris.
>

When learning CL, there were probably some fairly major concepts
that needed to be absorbed, including:

   - macros
   - syntax (or the near total absence of it)
   - the general notion that everything's a function that returns
     some number of values, and that most primitive operations
     (arithmetic, array/string access) are expressed as function calls
   - closures and higher-order functions
   - special variables and dynamic binding
   - lambda-lists and variadic functions
   - garbage collection (which might or might not be a new concept)
   - CLOS
   - the reader, packages ..
   - the fact that there's a fairly rich library of standard functions
     for dealing with common data structures
   - ...

At some point - when enough of these things make enough sense - one
gets comfortable enough with enough of these concepts to be able to
program productively in CL.  Learning to program in CL probably has
more to do with becoming comfortable with a relatively small set of
core concepts (like those above) than it does with memorizing
reference material describing all of the library functions.  (If you
quizzed me right now, I probably couldn't correctly enumerate all of
the possible standard values that the :IF-EXISTS argument to OPEN can
take; since we all know what keyword arguments are and how they
can be used, we can look up those details in CLHS when we need
to and expect that reference material to make sense.  Well, usually.)

For ObjC and Cocoa, the number of core concepts that we need to
learn in order to start to make sense of things is (mostly) pretty
small; if we want to be able to read ObjC code/examples, there
are some additional syntactic details.  For ObjC, these concepts
include (some of this is a little circular):

  - certain data structures are "classes"; they serve a role
    very similar to that served by classes in CLOS and other
    object systems.  It's possible to add new classes to a
    running ObjC program (just as it is in CLOS), though ObjC
    is less flexible than CLOS/MOP is about redefining/removing
    classes procedurally.  ObjC classes are uniquely named (by
    a string); many of the predefined classes in Cocoa (which
    was derived from NeXT's NeXTStep framework) have names
    that start with the prefix "NS".

    ObjC classes are allocated in foreign memory (often as
    part of a shared library's initialized data.)  A class
    decribes the layout of its instances' instance variables
    ("slots" in CLOS) and describes methods which are specialized
    on its instances.

    Redefining ObjC classes at runtime probably doesn't work
    very well.  In CCL's ObjC bridge, accidentally mousing
    on an unchanged class definition is probably harmless.
    Adding/removing slots won't affect existing instances
    in general, and I don't know/remember what Bad Things
    might happen if the class hierarchy was changed.

  - classes can be instantiated (just as in CLOS and most other
    object systems); this involves allocating a (somewhat opaque)
    block of foreign memory.  If one knows that a block of
    foreign memory contains an ObjC instance, it is possible
    (and cheap) to determine the class of that instance at
    runtime.

  - unlike the case in CLOS - where classes are usually instances
    of the metaclass STANDARD-CLASS - ObjC classes of unique
    metaclasses.  (Among other things, this scheme allows methods
    on classes to be specialized and customized.)  An ObjC class
    is therefore an instance of its metaclass (which is itself
    an instance of a special root metaclass), and it's possible
    to consider any ObjC class, metaclass, or instance as being
    an "ObjC object".  All three kinds of ObjC object are implemented
    as "blocks of foreign memory"; the blocks occupied by classes
    and metaclasses are "permanent" (for the lifetime of the
    application), while those occupied by ObjC instances can be
    freed (and possibly reallocated) when it appears that they
    can no longer be referenced.  Apple is moving toward the
    use of real garbage collection to determine when instances
    can be reliably and automatically freed; traditionally, a
    reference-counting scheme has been used.

    A foreign type corresponding to "pointer to ObjC object"
    is predefined in ObjC; this type is named "id" (or :id from
    CCL).

  - some strings (usually in mixed case and often containing
    embedded and/or trailing colons) name ObjC "messages".  An
    ObjC message isn't quite a first-class object, but it can be
    thought of as being similar to a kind of generic function
    which is specialized on the class of its first argument
    (which must be an ObjC object) and which may contain
    additional arguments.  Those additional arguments may
    be ObjC objects or low-level C pointers or numbers.
    A string which names a message is sometimes called a
    "selector" (in some other ObjC implementations, a "selector"
    is distinct from the string which identifies it, but that's
    a separate issue.)

    The number of colons in a message name typically indicates
    the number of arguments (other than the first, special
    argument) that methods defined on the message should accept.

  - It's possible (and useful) to define methods on any message
    and any ObjC class or metaclass.  A large number of methods
    (on a large number of classes ...) are predefined in Cocoa
    and other ObjC libraries; methdods can also be added at
    runtime.  In order for ObjC code to be able to pass arguments
    (other than the first specialized argument of type "id")
    to and return results from an ObjC method, the (foreign)
    types of those arguments and return value must be specified.
    If a method shadows another method defined in a superclass
    (or is shadowed by a method defined in a subclass), the
    types of those arguments and return value must agree.
    (It's legal for methods defined on disjoint classes to
    have different type signatures; this happens occasionally
    in practice.)

    All methods defined on an ObjC message must take the same
    number of arguments, but (as noted above) the types of
    their non-specialized arguments may differe in some cases.

    It is possible to redefine an ObjC method at runtime; it
    generally works reliably to change the implementation (body)
    of the method, but may not work as well (or at all) to

  - From within the body of an ObjC method M, it's possible to
    invoke whatever method would have been applicable had M
    not been defined.  (Conceptually, this is similar to
    CALL-NEXT-METHOD in CLOS.)

  - In ObjC (as in SmallTalk), invoking a generic function on
    a specialized argument ("the receiver") and some other
    arguments is called "sending a message", and it's written
    in a sort of infix syntax:

[reciever messageName]   if the message accepts no other arguments
[receiver messageNameFirstArg: first-arg-value nextArg: next-arg-value]
otherwise.

    In the first case, the actual message name is "messageName"; in the
    second  case, it's "messageNameFirstArg:nextArg:"

  - Methods can be defined on classes (and therefore applicable to
    their instances) or on metaclasses (and therefore applicable to
    class methods).  When a class method is defined or described, it's
    usually prefixed with a "+" character; an instance method is
    usually prefixed with a "-".

That's a fair amount of stuff that really should be written down
someplace and in greater detail.  A short version is that ObjC offers
an object system with dynamically typed objects and at least some
amount of/some kinds of runtime extensibility, and a sort of
restricted form of (something like) CLOS method dispatch that's based
on the (runtime) class of the first/only specialized argument, and
that ObjC methods typically deal with some combination of run-time
typed Objc objects and statically-typed primitive C things (numbers,
pointers, structures.)

I suspect (I don't know for sure) that the thing that'd help most
people the most in getting up to speed with using Cocoa in CCL is
comfort and familiarity with CCL's FFI.  If that's true (and to the
extent that it is), it's somewhat unfortunate; when he first started
working on it, Randall Beer said that ideally he'd like the bridge to
expose as much of Cocoa as possible while hiding as much of ObjC as
possible.  It's gotten closer to that goal over the years, but I
suspect that a lot of things that people have trouble with could
be blamed on the fact that ObjC (and some of the arcana of dealing
with it) still isn't hidden well enough.  (If this is true, it's
probably the "C" part that's problematic; the "Obj" part seems
to fit a lot better into CL/CCL.)  Looking back at it, I think
that the bridge actually -does- do a good job of hiding a lot
of the problematic C stuff (though admittedly not all of it),
but a lot of it is ultimately nothing more than a few layers
of syntactic sugar around the FFI, and I imagine that it's
still hard to use unless one has some sense of what's going
on underneath.

For learning Cocoa itself ... well, it's a large class library (much
larger than the standard CL library) that provides default behavior
for a lot of things that an application needs to do (implemented as
instances of predefined classes and predefined methods on those
classes), and the general idea is that by subclassing some of those
classes and overriding some of those methods you turn this generic
application into something that offers custom behavior.  It's
generally hard to absorb all of the functionality that's available
in a library by reading the reference manual from cover to cover
(it might help to skim reference material to get a sense of
what's available and to read a few passages that seem interesting.)
It's often helpful - especially when starting out - to have a
specific goal in mind, and then try to learn what you need to
learn in order to achieve that.  After you repeat that process
a few times, it likely starts to get easier, and (as is often
the case) the first foray into something new is the hardest.

Suppose that we had a specific goal of writing an image-viewing
program, like a watered-down version of Apple's Preview.app)
(We could make the example more interesting, but let's not ...)
If we've never done this before in Cocoa, there are clearly
a bunch of specific questions we'd need answered, including:

  - what predefined support is there for loading files containing
    well-known image types (jpg, tiff, gif, png, ...) and displaying
    them ?  If we wanted to construct an image from an unsupported
    file type or procedurally, what's involved in doing that ?

  - how exactly do you display a window and get an image to appear
    in it ?  How do you control what happens when the window is
    resized (e.g., does the image scale ?  do you want it to ?)

  - how do you provide a user interface that allows images to
    be loaded (and other behavior that a more interesting
    application would want) ?

Apple does provide very good overview documentation (in the
form of "guides".)  At the moment, the Cocoa guides are
available at:

<http://developer.apple.com/documentation/Cocoa/index.html>

and looking at the "Graphics and Imaging" section there we find links
to guides describing "Image Views" (sounds promising) and a "View
Programming Guide".  The image view guide is a little terse (it
basically just says that NSImageViews are NSViews that display
NSImages, whatever they are.  The "View Programming Guide" is a lot
more to digest, and if we follow a link to NSImage we'd have even more
to digest.  If we follow the link from the Image View guide to the
NSImageView reference, we see that NSImageViews can be set to allow
cut copy and paste of images as well as drag-and-drop.  That's maybe
not ultimately what we want (if we want to load images from files),
but it may be an easier way to get started.

Let's make a window, put an NSImageView in it, set some options
that enable drag-and-drop and cut-and-paste to work with the
image view, center and display the window, and return the NSImageView
object:

(defun make-image-view-window ()
   "Make a window whose content view is an empty NSImageView.  Allow
the user to drag images to that view, and support transfer of images
to/from the view via the clipboard.  Return the NSImageView."
   (ccl::with-autorelease-pool
    (let* ((rect (ns:make-ns-rect 0 0 300 300))
 	  (w (make-instance 'ns:ns-window
 			    :with-content-rect rect
 			    :style-mask (logior #$NSTitledWindowMask
                                                 #$NSClosableWindowMask
                                                 #$NSMiniaturizableWindowMask
                                                 #$NSResizableWindowMask)
 			    :backing #$NSBackingStoreBuffered
 			    :defer t))
           (image-view (make-instance 'ns:ns-image-view)))
      ;; Set the window's title to a constant NSString (#@"...").
      (#/setTitle: w #@"Drag or paste an image here!")
      ;; Make the image view respond to drag-and-drop
      (#/setEditable: image-view t)
      ;; and make it support the clipboard
      (#/setAllowsCutCopyPaste: image-view t)
      ;; Make the image view be the window's "content view".
      (#/setContentView: w (#/autorelease image-view))
      ;; Center and display the window.
      (#/center w)
      (#/orderFront: w nil)
      image-view)))

If we call this function, an empty 300x300 window should appear on
the screen and an NSImageView object should be returned.

? (make-image-view-window)
#<NS-IMAGE-VIEW <NSImageView: 0x1279bf50> (#x1279BF50)>
? (defvar *v* *) ; so we can refer to the NS-IMAGE-VIEW later.
*V*

If we then switch to a web browser or other image-viewing application
and copy an image to the clipboard, we can switch back to the IDE and
paste that image into the image view.

I happened to copy a fairly small image:

? (#/image *v*)
#<NS-IMAGE NSImage 0x12795df0 Size={120, 80} Reps=(
     NSBitmapImageRep 0x1279cf30 Size={120, 80} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32
Pixels=120x80 Alpha=YES Planar=NO Format=2 CGImage=0x1279e340
) (#x12795DF0)>

When I resized the window, the image stayed at 120x80 and happened to
stay centered in the window.  It may be more impressive to let the image
scale itself to fit the view bounds:

? (#/setImageScaling: *v* #$NSScaleToFit)
NIL

Getting images from the clipboard is all well and good, but the original
goal was to display images that were loaded from files.   If only
NSImage had an "initWithContentsOfFile:" method, this would be trivially
easy in most cases.

Oh, wait: it does (as revealed by a little bit of poking around in the
NSImage reference doc.)  ObjC messages whose names start with
"init" are treated specially by the bridge: MAKE-INSTANCE with keyword
args derived from the rest of the message name will (among other
things) send that initialization message to a newly-allocated instance
of the class. So:

(let* ((image (make-instance 'ns:ns-image :with-contents-of-file #@"/Library/Desktop Pictures/Nature/Aurora.jpg")))
   (unless (%null-ptr-p image) ; unless the image couldn't be initialized
     (#/setImage: *v* (#/autorelease image))))

We know from an earlier message in this thread (if not otherwise)
how to use NSOpenPanel to select a file, so we're maybe around 75%
of the way towards the goal of developing a simple image-viewing
program.  That's probably enough for an off-topic reply, but it's
worth noting that so far it's been about 20 lines of code and that
NSImage and NSImageView and the rest of Cocoa are doing all of the
heavy lifting.

I'm sorry that this reply is so long and not entirely responsive. To
try to summarize:

  - I think that the best way to learn Cocoa (or anything similar)
    is probably from the bottom up and from the inside out, and that
    it helps to have a concrete, narrowly focused goal and to try
    to learn just what you need to learn in order to achieve that.
    After repeating that process a few times, more stuff tends to
    resonate and make sense.

    It's certainly not practical to go in the other directions,
    as in "here are several hundred classes and a few thousand
    methods, go learn them."

  - From your comments and others, I think that it's fair to say
    that the goal of hiding ObjC (the C part of it, especially)
    hasn't been fully reached.   It's still sometimes necessary
    to allocate foreign objects and pass pointers to them by
    reference, and that sort of thing is disconsonant with what's
    otherwise a very high-level and powerful framework (Cocoa.)
    That stuff is also inherently hard and un-lispy.

One of the problems that I personally have here is that while
I can believe that there are aspects of CCL's FFI that are hard
to use and/or understand, it's hard for me to guess exactly
what those things are (since it all makes perfect sense to me
for some reason.)
Chris Van Dusen | 28 Aug 15:50

Re: programmatic file chooser? thanks!

Wow!  I should have asked that a long time ago.

Actually, I'm glad your response was as in-depth as it was.  It gave me time to really think about the things that have been stumbling blocks, whether externally (the size and scope of ObjC & Cocoa), or internally (adapting my brain to ObjC's syntax).

After working through the currency converter (first in ObjC, then CCL), I could see how the things were working, but it wasn't what I would call "independent knowledge" (i.e., I couldn't have taken what I knew and written something from scratch).  Realizing this, I cast about for a small project to tinker with (as you suggested), and remembered a book that I had bought years ago (http://www.amazon.com/dp/0387202293) that had drawing exercises at the end of each chapter written in Basic.  (Hmm.  Basic, ObjC, Lisp.  There's a joke in there somewhere, but, at the moment, it escapes me.)

From what I've gathered, NSBezierPath will be my friend for accomplishing the drawing.

Given Matthew's suggestion, it sounds like working through the Hillegass book first, and translating the exercises from the aforementioned book to ObjC might be the most optimal approach.

Thank you for the thoughtful response,
Chris.

On Thu, Aug 28, 2008 at 5:18 AM, Gary Byers <gb <at> clozure.com> wrote:


On Wed, 27 Aug 2008, Chris Van Dusen wrote:

Agreed.  I saw AppKiDo mentioned in the online documentation, and have been
using it pretty heavily since.
One thing I've been curious about (and may produce on my own, if none
exists) is a cheat sheet for the various ways of interacting with
Objective-C.  Until I get used to it, when to use the appropriate reader
macro is a source of frustration.

All of that said, does anyone have suggestions/recommendations as to how
best to learn to commingle CCL & Objective-C?  I'm fairly comfortable with
Lisp in general.  Would it be worth my while to devote my time to learning
Objective-C, Cocoa, etc. to the point that I'm conversant in those things,
then come back at it from the CCL perspective?

Thanks,
Chris.


When learning CL, there were probably some fairly major concepts
that needed to be absorbed, including:

 - macros
 - syntax (or the near total absence of it)
 - the general notion that everything's a function that returns
   some number of values, and that most primitive operations
   (arithmetic, array/string access) are expressed as function calls
 - closures and higher-order functions
 - special variables and dynamic binding
 - lambda-lists and variadic functions
 - garbage collection (which might or might not be a new concept)
 - CLOS
 - the reader, packages ..
 - the fact that there's a fairly rich library of standard functions
   for dealing with common data structures
 - ...

At some point - when enough of these things make enough sense - one
gets comfortable enough with enough of these concepts to be able to
program productively in CL.  Learning to program in CL probably has
more to do with becoming comfortable with a relatively small set of
core concepts (like those above) than it does with memorizing
reference material describing all of the library functions.  (If you
quizzed me right now, I probably couldn't correctly enumerate all of
the possible standard values that the :IF-EXISTS argument to OPEN can
take; since we all know what keyword arguments are and how they
can be used, we can look up those details in CLHS when we need
to and expect that reference material to make sense.  Well, usually.)

For ObjC and Cocoa, the number of core concepts that we need to
learn in order to start to make sense of things is (mostly) pretty
small; if we want to be able to read ObjC code/examples, there
are some additional syntactic details.  For ObjC, these concepts
include (some of this is a little circular):

 - certain data structures are "classes"; they serve a role
  very similar to that served by classes in CLOS and other
  object systems.  It's possible to add new classes to a
  running ObjC program (just as it is in CLOS), though ObjC
  is less flexible than CLOS/MOP is about redefining/removing
  classes procedurally.  ObjC classes are uniquely named (by
  a string); many of the predefined classes in Cocoa (which
  was derived from NeXT's NeXTStep framework) have names
  that start with the prefix "NS".

  ObjC classes are allocated in foreign memory (often as
  part of a shared library's initialized data.)  A class
  decribes the layout of its instances' instance variables
  ("slots" in CLOS) and describes methods which are specialized
  on its instances.

  Redefining ObjC classes at runtime probably doesn't work
  very well.  In CCL's ObjC bridge, accidentally mousing
  on an unchanged class definition is probably harmless.
  Adding/removing slots won't affect existing instances
  in general, and I don't know/remember what Bad Things
  might happen if the class hierarchy was changed.

 - classes can be instantiated (just as in CLOS and most other
  object systems); this involves allocating a (somewhat opaque)
  block of foreign memory.  If one knows that a block of
  foreign memory contains an ObjC instance, it is possible
  (and cheap) to determine the class of that instance at
  runtime.

 - unlike the case in CLOS - where classes are usually instances
  of the metaclass STANDARD-CLASS - ObjC classes of unique
  metaclasses.  (Among other things, this scheme allows methods
  on classes to be specialized and customized.)  An ObjC class
  is therefore an instance of its metaclass (which is itself
  an instance of a special root metaclass), and it's possible
  to consider any ObjC class, metaclass, or instance as being
  an "ObjC object".  All three kinds of ObjC object are implemented
  as "blocks of foreign memory"; the blocks occupied by classes
  and metaclasses are "permanent" (for the lifetime of the
  application), while those occupied by ObjC instances can be
  freed (and possibly reallocated) when it appears that they
  can no longer be referenced.  Apple is moving toward the
  use of real garbage collection to determine when instances
  can be reliably and automatically freed; traditionally, a
  reference-counting scheme has been used.

  A foreign type corresponding to "pointer to ObjC object"
  is predefined in ObjC; this type is named "id" (or :id from
  CCL).

 - some strings (usually in mixed case and often containing
  embedded and/or trailing colons) name ObjC "messages".  An
  ObjC message isn't quite a first-class object, but it can be
  thought of as being similar to a kind of generic function
  which is specialized on the class of its first argument
  (which must be an ObjC object) and which may contain
  additional arguments.  Those additional arguments may
  be ObjC objects or low-level C pointers or numbers.
  A string which names a message is sometimes called a
  "selector" (in some other ObjC implementations, a "selector"
  is distinct from the string which identifies it, but that's
  a separate issue.)

  The number of colons in a message name typically indicates
  the number of arguments (other than the first, special
  argument) that methods defined on the message should accept.

 - It's possible (and useful) to define methods on any message
  and any ObjC class or metaclass.  A large number of methods
  (on a large number of classes ...) are predefined in Cocoa
  and other ObjC libraries; methdods can also be added at
  runtime.  In order for ObjC code to be able to pass arguments
  (other than the first specialized argument of type "id")
  to and return results from an ObjC method, the (foreign)
  types of those arguments and return value must be specified.
  If a method shadows another method defined in a superclass
  (or is shadowed by a method defined in a subclass), the
  types of those arguments and return value must agree.
  (It's legal for methods defined on disjoint classes to
  have different type signatures; this happens occasionally
  in practice.)

  All methods defined on an ObjC message must take the same
  number of arguments, but (as noted above) the types of
  their non-specialized arguments may differe in some cases.

  It is possible to redefine an ObjC method at runtime; it
  generally works reliably to change the implementation (body)
  of the method, but may not work as well (or at all) to


 - From within the body of an ObjC method M, it's possible to
  invoke whatever method would have been applicable had M
  not been defined.  (Conceptually, this is similar to
  CALL-NEXT-METHOD in CLOS.)

 - In ObjC (as in SmallTalk), invoking a generic function on
  a specialized argument ("the receiver") and some other
  arguments is called "sending a message", and it's written
  in a sort of infix syntax:

[reciever messageName]   if the message accepts no other arguments
[receiver messageNameFirstArg: first-arg-value nextArg: next-arg-value]
otherwise.

  In the first case, the actual message name is "messageName"; in the
  second  case, it's "messageNameFirstArg:nextArg:"

 - Methods can be defined on classes (and therefore applicable to
  their instances) or on metaclasses (and therefore applicable to
  class methods).  When a class method is defined or described, it's
  usually prefixed with a "+" character; an instance method is
  usually prefixed with a "-".

That's a fair amount of stuff that really should be written down
someplace and in greater detail.  A short version is that ObjC offers
an object system with dynamically typed objects and at least some
amount of/some kinds of runtime extensibility, and a sort of
restricted form of (something like) CLOS method dispatch that's based
on the (runtime) class of the first/only specialized argument, and
that ObjC methods typically deal with some combination of run-time
typed Objc objects and statically-typed primitive C things (numbers,
pointers, structures.)

I suspect (I don't know for sure) that the thing that'd help most
people the most in getting up to speed with using Cocoa in CCL is
comfort and familiarity with CCL's FFI.  If that's true (and to the
extent that it is), it's somewhat unfortunate; when he first started
working on it, Randall Beer said that ideally he'd like the bridge to
expose as much of Cocoa as possible while hiding as much of ObjC as
possible.  It's gotten closer to that goal over the years, but I
suspect that a lot of things that people have trouble with could
be blamed on the fact that ObjC (and some of the arcana of dealing
with it) still isn't hidden well enough.  (If this is true, it's
probably the "C" part that's problematic; the "Obj" part seems
to fit a lot better into CL/CCL.)  Looking back at it, I think
that the bridge actually -does- do a good job of hiding a lot
of the problematic C stuff (though admittedly not all of it),
but a lot of it is ultimately nothing more than a few layers
of syntactic sugar around the FFI, and I imagine that it's
still hard to use unless one has some sense of what's going
on underneath.

For learning Cocoa itself ... well, it's a large class library (much
larger than the standard CL library) that provides default behavior
for a lot of things that an application needs to do (implemented as
instances of predefined classes and predefined methods on those
classes), and the general idea is that by subclassing some of those
classes and overriding some of those methods you turn this generic
application into something that offers custom behavior.  It's
generally hard to absorb all of the functionality that's available
in a library by reading the reference manual from cover to cover
(it might help to skim reference material to get a sense of
what's available and to read a few passages that seem interesting.)
It's often helpful - especially when starting out - to have a
specific goal in mind, and then try to learn what you need to
learn in order to achieve that.  After you repeat that process
a few times, it likely starts to get easier, and (as is often
the case) the first foray into something new is the hardest.

Suppose that we had a specific goal of writing an image-viewing
program, like a watered-down version of Apple's Preview.app)
(We could make the example more interesting, but let's not ...)
If we've never done this before in Cocoa, there are clearly
a bunch of specific questions we'd need answered, including:

 - what predefined support is there for loading files containing
  well-known image types (jpg, tiff, gif, png, ...) and displaying
  them ?  If we wanted to construct an image from an unsupported
  file type or procedurally, what's involved in doing that ?

 - how exactly do you display a window and get an image to appear
  in it ?  How do you control what happens when the window is
  resized (e.g., does the image scale ?  do you want it to ?)

 - how do you provide a user interface that allows images to
  be loaded (and other behavior that a more interesting
  application would want) ?

Apple does provide very good overview documentation (in the
form of "guides".)  At the moment, the Cocoa guides are
available at:

<http://developer.apple.com/documentation/Cocoa/index.html>

and looking at the "Graphics and Imaging" section there we find links
to guides describing "Image Views" (sounds promising) and a "View
Programming Guide".  The image view guide is a little terse (it
basically just says that NSImageViews are NSViews that display
NSImages, whatever they are.  The "View Programming Guide" is a lot
more to digest, and if we follow a link to NSImage we'd have even more
to digest.  If we follow the link from the Image View guide to the
NSImageView reference, we see that NSImageViews can be set to allow
cut copy and paste of images as well as drag-and-drop.  That's maybe
not ultimately what we want (if we want to load images from files),
but it may be an easier way to get started.

Let's make a window, put an NSImageView in it, set some options
that enable drag-and-drop and cut-and-paste to work with the
image view, center and display the window, and return the NSImageView
object:

(defun make-image-view-window ()
 "Make a window whose content view is an empty NSImageView.  Allow
the user to drag images to that view, and support transfer of images
to/from the view via the clipboard.  Return the NSImageView."
 (ccl::with-autorelease-pool
  (let* ((rect (ns:make-ns-rect 0 0 300 300))
         (w (make-instance 'ns:ns-window
                           :with-content-rect rect
                           :style-mask (logior #$NSTitledWindowMask
                                               #$NSClosableWindowMask
                                               #$NSMiniaturizableWindowMask
                                               #$NSResizableWindowMask)
                           :backing #$NSBackingStoreBuffered
                           :defer t))
         (image-view (make-instance 'ns:ns-image-view)))
    ;; Set the window's title to a constant NSString (# <at> "...").
    (#/setTitle: w # <at> "Drag or paste an image here!")
    ;; Make the image view respond to drag-and-drop
    (#/setEditable: image-view t)
    ;; and make it support the clipboard
    (#/setAllowsCutCopyPaste: image-view t)
    ;; Make the image view be the window's "content view".
    (#/setContentView: w (#/autorelease image-view))
    ;; Center and display the window.
    (#/center w)
    (#/orderFront: w nil)
    image-view)))

If we call this function, an empty 300x300 window should appear on
the screen and an NSImageView object should be returned.

? (make-image-view-window)
#<NS-IMAGE-VIEW <NSImageView: 0x1279bf50> (#x1279BF50)>
? (defvar *v* *) ; so we can refer to the NS-IMAGE-VIEW later.
*V*

If we then switch to a web browser or other image-viewing application
and copy an image to the clipboard, we can switch back to the IDE and
paste that image into the image view.

I happened to copy a fairly small image:

? (#/image *v*)
#<NS-IMAGE NSImage 0x12795df0 Size={120, 80} Reps=(
   NSBitmapImageRep 0x1279cf30 Size={120, 80} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=120x80 Alpha=YES Planar=NO Format=2 CGImage=0x1279e340
) (#x12795DF0)>

When I resized the window, the image stayed at 120x80 and happened to
stay centered in the window.  It may be more impressive to let the image
scale itself to fit the view bounds:

? (#/setImageScaling: *v* #$NSScaleToFit)
NIL

Getting images from the clipboard is all well and good, but the original
goal was to display images that were loaded from files.   If only
NSImage had an "initWithContentsOfFile:" method, this would be trivially
easy in most cases.

Oh, wait: it does (as revealed by a little bit of poking around in the
NSImage reference doc.)  ObjC messages whose names start with
"init" are treated specially by the bridge: MAKE-INSTANCE with keyword
args derived from the rest of the message name will (among other
things) send that initialization message to a newly-allocated instance
of the class. So:

(let* ((image (make-instance 'ns:ns-image :with-contents-of-file # <at> "/Library/Desktop Pictures/Nature/Aurora.jpg")))
 (unless (%null-ptr-p image) ; unless the image couldn't be initialized
   (#/setImage: *v* (#/autorelease image))))

We know from an earlier message in this thread (if not otherwise)
how to use NSOpenPanel to select a file, so we're maybe around 75%
of the way towards the goal of developing a simple image-viewing
program.  That's probably enough for an off-topic reply, but it's
worth noting that so far it's been about 20 lines of code and that
NSImage and NSImageView and the rest of Cocoa are doing all of the
heavy lifting.

I'm sorry that this reply is so long and not entirely responsive. To
try to summarize:

 - I think that the best way to learn Cocoa (or anything similar)
  is probably from the bottom up and from the inside out, and that
  it helps to have a concrete, narrowly focused goal and to try
  to learn just what you need to learn in order to achieve that.
  After repeating that process a few times, more stuff tends to
  resonate and make sense.

  It's certainly not practical to go in the other directions,
  as in "here are several hundred classes and a few thousand
  methods, go learn them."

 - From your comments and others, I think that it's fair to say
  that the goal of hiding ObjC (the C part of it, especially)
  hasn't been fully reached.   It's still sometimes necessary
  to allocate foreign objects and pass pointers to them by
  reference, and that sort of thing is disconsonant with what's
  otherwise a very high-level and powerful framework (Cocoa.)
  That stuff is also inherently hard and un-lispy.

One of the problems that I personally have here is that while
I can believe that there are aspects of CCL's FFI that are hard
to use and/or understand, it's hard for me to guess exactly
what those things are (since it all makes perfect sense to me
for some reason.)

_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Alexander Repenning | 28 Aug 21:34
Favicon

Debugging Cocoa (was: programmatic file chooser? thanks!)

Thanks Gary for the detailed response. I have just spent some time to write some basic GUI code based on Cocoa in LispWorks and Clozure CL. I could go on about the philosophical differences between LW and CCL wrt Cocoa but that is perhaps left best for some other time. At the end of the day both approaches work. 

CCL gets high scores for having defined interfaces, based on reading the header files I presume. LW includes no such information. As developer I have to define my own function/method/constant definitions for Cocoa and other APIs in LW. Not very practical for large APIs. 

The main problem, we found with CCL and Cocoa, especially vis-a-vis LW, is debugging. Specifically, CCL makes it hard to debug Cocoa call backs. Example: Say I have a window (NSWindow containing NSView) with a buggy event handler. 

(defmethod VIEW-LEFT-MOUSE-DOWN-EVENT-HANDLER ((Self event-test-Window) X Y)
  (format t "click: x=~A, y=~A~%"  x y)
  (setf (x self) x)
  (setf (y self) y)
  (/ x 0) ;; division-by-zero!!!
  (print *Current-process*)
  (view-draw-contents Self))

1) output to OS X console does not work as interactive debugging tool. There is too much lag. The print/format statements, if you start CCL 1.2 by double clicking the OS X app will produce output in the OS X console with great lag time. Output from processes such as the APPKIT-PROCESS do not write to the listener. It can sometimes take quite some while for that output to show up. The solution is to start CCL via a terminal window. In that case the output will show up in that terminal window and will do so about 1000 times faster. The MCL had its problem too. It opened up a listener per thread in case of output. For CCL I think it would be good to have just ALL the output be routed to the listener. Perhaps there could be a before method called with the current-process allowing developers to customize output, e.g., by preceding output from certain processes with some prefix prompt.


2) notice the bug in the method above (division by zero). In the main thread an error would be raised. In the APPKIT-PROCESS there is no evidence of the bug. No error message. No user prompting with continuation. No opportunity to enter backtrace. Is there some implicit error handler in the APPKIT-PROCESS that simply ignores all the errors raised? This makes things hard to debug because one does not even suspect a problem. LW, in contrast, not only catches the error in the usual way (raising a division-by-zero condition) but also allows a backtrace including getting to the point in the source causing the problem. This is very nice. Is there a simple way to get around this?


- detail: Why is the backtrace window in CCL 1.2 a drop shadow less window? Looks like a bug to me.
- ps: a good way to explore Cocoa functionality is via XCode. It includes some nice documentation browsers

all the best,  Alex



On Aug 28, 2008, at 4:18 AM, Gary Byers wrote:

When learning CL, there were probably some fairly major concepts
that needed to be absorbed, including:

  - macros
  - syntax (or the near total absence of it)
  - the general notion that everything's a function that returns
    some number of values, and that most primitive operations
    (arithmetic, array/string access) are expressed as function calls
  - closures and higher-order functions
  - special variables and dynamic binding
  - lambda-lists and variadic functions
  - garbage collection (which might or might not be a new concept)
  - CLOS
  - the reader, packages ..
  - the fact that there's a fairly rich library of standard functions
    for dealing with common data structures
  - ...



Prof. Alexander Repenning


University of Colorado

Computer Science Department

Boulder, CO 80309-430


vCard: http://www.cs.colorado.edu/~ralex/AlexanderRepenning.vcf



_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Rainer Joswig | 28 Aug 23:11

Re: Debugging Cocoa (was: programmatic file chooser? thanks!)


Am 28.08.2008 um 21:34 schrieb Alexander Repenning:

> Thanks Gary for the detailed response. I have just spent some time  
> to write some basic GUI code based on Cocoa in LispWorks and Clozure  
> CL. I could go on about the philosophical differences between LW and  
> CCL wrt Cocoa but that is perhaps left best for some other time. At  
> the end of the day both approaches work.
>
> CCL gets high scores for having defined interfaces, based on reading  
> the header files I presume. LW includes no such information. As  
> developer I have to define my own function/method/constant  
> definitions for Cocoa and other APIs in LW. Not very practical for  
> large APIs.
>
> The main problem, we found with CCL and Cocoa, especially vis-a-vis  
> LW, is debugging. Specifically, CCL makes it hard to debug Cocoa  
> call backs. Example: Say I have a window (NSWindow containing  
> NSView) with a buggy event handler.
>
> (defmethod VIEW-LEFT-MOUSE-DOWN-EVENT-HANDLER ((Self event-test- 
> Window) X Y)
>   (format t "click: x=~A, y=~A~%"  x y)
>   (setf (x self) x)
>   (setf (y self) y)
>   (/ x 0) ;; division-by-zero!!!
>   (print *Current-process*)
>   (view-draw-contents Self))
>
> 1) output to OS X console does not work as interactive debugging  
> tool. There is too much lag. The print/format statements, if you  
> start CCL 1.2 by double clicking the OS X app will produce output in  
> the OS X console with great lag time.

Probably not related to this, but I'll mention it anyway, since I have  
seen this in other debugging contexts:
Sometimes the output streams are buffered (and the display gets  
updated sometime later) and one needs to
call FINISH-OUTPUT or FORCE-OUTPUT after printing something to see any  
debug information immediately.
Please ignore above if it does not apply here...

...

Rainer Joswig, Hamburg, Germany
http://lispm.dyndns.org/
mailto:joswig <at> lisp.de
Gary Byers | 3 Sep 03:19

Re: Debugging Cocoa (was: programmatic file chooser? thanks!)


On Thu, 28 Aug 2008, Rainer Joswig wrote:

>
> Am 28.08.2008 um 21:34 schrieb Alexander Repenning:
>
>> Thanks Gary for the detailed response. I have just spent some time
>> to write some basic GUI code based on Cocoa in LispWorks and Clozure
>> CL. I could go on about the philosophical differences between LW and
>> CCL wrt Cocoa but that is perhaps left best for some other time. At
>> the end of the day both approaches work.
>>
>> CCL gets high scores for having defined interfaces, based on reading
>> the header files I presume. LW includes no such information. As
>> developer I have to define my own function/method/constant
>> definitions for Cocoa and other APIs in LW. Not very practical for
>> large APIs.
>>
>> The main problem, we found with CCL and Cocoa, especially vis-a-vis
>> LW, is debugging. Specifically, CCL makes it hard to debug Cocoa
>> call backs. Example: Say I have a window (NSWindow containing
>> NSView) with a buggy event handler.
>>
>> (defmethod VIEW-LEFT-MOUSE-DOWN-EVENT-HANDLER ((Self event-test-
>> Window) X Y)
>>   (format t "click: x=~A, y=~A~%"  x y)
>>   (setf (x self) x)
>>   (setf (y self) y)
>>   (/ x 0) ;; division-by-zero!!!
>>   (print *Current-process*)
>>   (view-draw-contents Self))

I couldn't reproduce this (e.g., I got a DIVISION-BY-ZERO error
and a backtrace on standard output.)

In what lisp version does this happen ?

>>
>> 1) output to OS X console does not work as interactive debugging
>> tool. There is too much lag. The print/format statements, if you
>> start CCL 1.2 by double clicking the OS X app will produce output in
>> the OS X console with great lag time.
>
>
> Probably not related to this, but I'll mention it anyway, since I have
> seen this in other debugging contexts:
> Sometimes the output streams are buffered (and the display gets
> updated sometime later) and one needs to
> call FINISH-OUTPUT or FORCE-OUTPUT after printing something to see any
> debug information immediately.
> Please ignore above if it does not apply here...

I think that what actually happens is something like:

- the main event thread gets an error in its event loop and writes
a bunch of stuff to file descriptor 1 (or 2), which is open on
a pipe (or something similar; I don't remember.)

- a background thread reads the other side of that connection and
packages up the incoming data, then "posts a notification" to the
event thread (the data is an attribute of the notification object.)

- the event thread is supposed to receive that notification (the next
time its in its event loop), extract the data from the notification
object and insert it in the console window.  This part would work a
lot better if the event thread wasn't too busy pumping out data to
listen for notifications of incoming data (e.g., the same thread is
trying to produce and consume the data at the same time.)

It's tempting to say that the right solution here is to simply cut out
the middle man (to write directly to the console window or a
listener-like window that "belongs to" the event thread.)  I'm sure
that that'd be better, but you basically want the event thread to be
responsive to events (and time spent generating large amounts of
output is time not spent being responsive to events.)  Generating a
large text backtrace from the event thread is never likely to
be blindingly fast (or at least there's always going to be some
latency between the time that output is generated and the time
that redisplay occurs and the output is visible.)  Fortunately,
dumping a large amount of text output to a console window or
listener isn't really an ideal response to debugging an error
in the event thread

Of course, as Alex's message suggested, a more nearly ideal response
to an error in the event thread is to have access to a break loop,
backtrace dialog, etc. in the (dynamic) context in which the error
occurred.  That dynamic context is typically in an event handler
(or a #/drawRect: method, or something similar), and supporting
event-driven interaction (typing into that listener, mousing
around in the backtrace dialog) in that context basically involves
running a recursive event loop.  There are some subtle issues there
and some work obviously needs to be done to address them.

>
> ...
>
> Rainer Joswig, Hamburg, Germany
> http://lispm.dyndns.org/
> mailto:joswig <at> lisp.de
>
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel <at> clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>
>
Brent Fulgham | 29 Aug 01:22

Re: programmatic file chooser? thanks!



On Thu, Aug 28, 2008 at 3:18 AM, Gary Byers <gb <at> clozure.com> wrote:
I suspect (I don't know for sure) that the thing that'd help most
people the most in getting up to speed with using Cocoa in CCL is
comfort and familiarity with CCL's FFI.  If that's true (and to the
extent that it is), it's somewhat unfortunate; when he first started
working on it, Randall Beer said that ideally he'd like the bridge to
expose as much of Cocoa as possible while hiding as much of ObjC as
possible.  It's gotten closer to that goal over the years, but I
suspect that a lot of things that people have trouble with could
be blamed on the fact that ObjC (and some of the arcana of dealing
with it) still isn't hidden well enough.

Of all the language embeddings/FFI's I've worked with, I think that Clozure CL's
implementation is the easiest I've worked with.  When getting started on the
Open Agent Engine port, it was pretty amazing how simple (and straightforward)
it was to translate a snippet of Objective C code from Apple's documentation
into something that would work in CCL.

The only 'complaint' or suggestion I would have regarding the FFI would be to
make it easier to generate the CDB files.  This is generally easy, but in some
cases (especially with the release of Leopard with the new ObjC 2.0 syntax)
it requires some clever declarations to ignore various things that Lisp doesn't
care about.

Perhaps you ship the FFI generator with Clozure; previously this had to be
obtained from Gary or others (perhaps a valid first step in embarking on the
journey of accessing foreign data -- you will probably be chatting with Gary at some
point!)

I wish other Lisp's had Clozure's FFI facilities.  Well, actually I don't really care
since I don't use them.  ;-P

-Brent

_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Samantha  Atkins | 29 Aug 01:39

Re: programmatic file chooser? thanks!


On Aug 28, 2008, at 4:22 PM, Brent Fulgham wrote:



On Thu, Aug 28, 2008 at 3:18 AM, Gary Byers <gb <at> clozure.com> wrote:
I suspect (I don't know for sure) that the thing that'd help most
people the most in getting up to speed with using Cocoa in CCL is
comfort and familiarity with CCL's FFI.  If that's true (and to the
extent that it is), it's somewhat unfortunate; when he first started
working on it, Randall Beer said that ideally he'd like the bridge to
expose as much of Cocoa as possible while hiding as much of ObjC as
possible.  It's gotten closer to that goal over the years, but I
suspect that a lot of things that people have trouble with could
be blamed on the fact that ObjC (and some of the arcana of dealing
with it) still isn't hidden well enough.

Of all the language embeddings/FFI's I've worked with, I think that Clozure CL's
implementation is the easiest I've worked with.  When getting started on the
Open Agent Engine port, it was pretty amazing how simple (and straightforward)
it was to translate a snippet of Objective C code from Apple's documentation
into something that would work in CCL.

The only 'complaint' or suggestion I would have regarding the FFI would be to
make it easier to generate the CDB files.  This is generally easy, but in some
cases (especially with the release of Leopard with the new ObjC 2.0 syntax)
it requires some clever declarations to ignore various things that Lisp doesn't
care about.

Perhaps you ship the FFI generator with Clozure; previously this had to be
obtained from Gary or others (perhaps a valid first step in embarking on the
journey of accessing foreign data -- you will probably be chatting with Gary at some
point!)

I wish other Lisp's had Clozure's FFI facilities.  Well, actually I don't really care
since I don't use them.  ;-P


Well I do use them, especially sbcl.  What is so different about the Clozure FFI than say UFFI or other efforts?  Since this is Open Source is there an effort to make this FFI, if that superior, available to other Lisps?

- samantha
_______________________________________________
Openmcl-devel mailing list
Openmcl-devel <at> clozure.com
http://clozure.com/mailman/listinfo/openmcl-devel
Brent Fulgham | 29 Aug 19:33

Re: programmatic file chooser? thanks!

Samantha,

The thing I like about the Clozure FFI is that the creation of the
stubs are automated.  So, unlike CFFI (and perhaps UFFI) where you are
required to write wrapper functions defining the input types and
return values for functions:

(cffi:defcfun ("ccl_chol_decomp_front" ccl-chol-decomp-front)
    :int (x :pointer) (y :long) (z :pointer))
(defun chol-decomp-front (x y z)
  (ccl-chol-decomp-front x y z))

CL-USER> (chol-decomp-front #2A((2 3 4) (1 2 4) (2 4 5)) 3 3)
... does stuff ...

Instead, the Clozure FFI produces a linking database based on parsing
the header files of any C/Objective C library, so if the corresponding
C library in the above example had been processed by the 'ffigen'
utility, I could then just do the following:

[... no declarations made, just load the library ...]
CL-USER> (#_chol-decomp-front #2A((2 3 4) (1 2 4) (2 4 5)) 3 3)
... does same stuff ...

So, I like the fact that I don't have to write and maintain C->Lisp<-C
wrapper functions.  Even if something like SWIG is able to produce the
output, it still

On Thu, Aug 28, 2008 at 4:39 PM, Samantha  Atkins <sjatkins <at> mac.com> wrote:
>
> On Aug 28, 2008, at 4:22 PM, Brent Fulgham wrote:
>
>
> On Thu, Aug 28, 2008 at 3:18 AM, Gary Byers <gb <at> clozure.com> wrote:
>>
>> I suspect (I don't know for sure) that the thing that'd help most
>> people the most in getting up to speed with using Cocoa in CCL is
>> comfort and familiarity with CCL's FFI.  If that's true (and to the
>> extent that it is), it's somewhat unfortunate; when he first started
>> working on it, Randall Beer said that ideally he'd like the bridge to
>> expose as much of Cocoa as possible while hiding as much of ObjC as
>> possible.  It's gotten closer to that goal over the years, but I
>> suspect that a lot of things that people have trouble with could
>> be blamed on the fact that ObjC (and some of the arcana of dealing
>> with it) still isn't hidden well enough.
>
> Of all the language embeddings/FFI's I've worked with, I think that Clozure CL's
> implementation is the easiest I've worked with.  When getting started on the
> Open Agent Engine port, it was pretty amazing how simple (and straightforward)
> it was to translate a snippet of Objective C code from Apple's documentation
> into something that would work in CCL.
> The only 'complaint' or suggestion I would have regarding the FFI would be to
> make it easier to generate the CDB files.  This is generally easy, but in some
> cases (especially with the release of Leopard with the new ObjC 2.0 syntax)
> it requires some clever declarations to ignore various things that Lisp doesn't
> care about.
> Perhaps you ship the FFI generator with Clozure; previously this had to be
> obtained from Gary or others (perhaps a valid first step in embarking on the
> journey of accessing foreign data -- you will probably be chatting with Gary at some
> point!)
> I wish other Lisp's had Clozure's FFI facilities.  Well, actually I don't really care
> since I don't use them.  ;-P
>
> Well I do use them, especially sbcl.  What is so different about the Clozure FFI than say UFFI or other
efforts?  Since this is Open Source is there an effort to make this FFI, if that superior, available to other Lisps?
> - samantha
Gary Byers | 30 Aug 04:38

Re: programmatic file chooser? thanks!


On Thu, 28 Aug 2008, Brent Fulgham wrote:

> The only 'complaint' or suggestion I would have regarding the FFI would be
> to
> make it easier to generate the CDB files.  This is generally easy, but in
> some
> cases (especially with the release of Leopard with the new ObjC 2.0 syntax)
> it requires some clever declarations to ignore various things that Lisp
> doesn't
> care about.
>
> Perhaps you ship the FFI generator with Clozure; previously this had to be
> obtained from Gary or others (perhaps a valid first step in embarking on the
> journey of accessing foreign data -- you will probably be chatting with Gary
> at some
> point!)

For a long time (during the Leopard beta period) we couldn't
distribute the .cdb files for Leopard (out of paranoid concern that
this would violate Apple's NDA somehow)  and couldn't distribute
an ffigen (a hacked-up version of GCC) that fully understood the
new ObjC 2.0 features because Apple hadn't released the sources
to those changes.  They were actually fairly good about releasing
sources shortly after Leopard was released this time, and we got
an ffigen based on that Apple-modified GCC out shortly thereafter.
('ffigen' is basically just some changes to the GCC frontend, so
that it spits out type/structure/function declarations in an
S-expression syntax that's easier for lisp code to parse.  It
doesn't have to be based on the latest version of GCC, but it
does have to understand the syntax of the language it's compiling
and has to understand the alignment rules of the platform it's
compiling for.)

On many platforms, just using GCC 4.0.0 has the base works
fine.  Darwin (with the ObjC 2.0 changes especially) requires
a slightly different set of patches to a slightly different
version of GCC; win64 support for GCC is still being added
(and tends to only be present in recent 4.3/4.4 versions ...)
This is getting (more) confusing, so the right thing is probably
to set up a Trac page for it, keep that up to date, and link
to it from the main page (so that you can understand what you
need and find it without having to ask me ...)
Brent Fulgham | 29 Aug 07:17

Re: programmatic file chooser? thanks!

Just to take the example a bit further, if you are on Leopard you can  
even play with rudimentary CoreAnimation:

> (defun make-image-view-window ()
>   "Make a window whose content view is an empty NSImageView.  Allow
> the user to drag images to that view, and support transfer of images
> to/from the view via the clipboard.  Return the NSImageView."
>   (ccl::with-autorelease-pool
>    (let* ((rect (ns:make-ns-rect 0 0 300 300))
> 	  (w (make-instance 'ns:ns-window
> 			    :with-content-rect rect
> 			    :style-mask (logior #$NSTitledWindowMask
>                                                 #$NSClosableWindowMask
>                                                 # 
> $NSMiniaturizableWindowMask
>                                                 # 
> $NSResizableWindowMask)
> 			    :backing #$NSBackingStoreBuffered
> 			    :defer t))
>           (image-view (make-instance 'ns:ns-image-view)))
>      ;; Set the window's title to a constant NSString (#@"...").
>      (#/setTitle: w #@"Drag or paste an image here!")
>      ;; Make the image view respond to drag-and-drop
>      (#/setEditable: image-view t)
>      ;; and make it support the clipboard
>      (#/setAllowsCutCopyPaste: image-view t)
>      ;; Make the image view be the window's "content view".
>      (#/setContentView: w (#/autorelease image-view))
>      ;; Center and display the window.
>      (#/center w)
>      (#/orderFront: w nil)
>      image-view)))

As before, load an image and tell the view to scale it:

? (defvar *view* (make-image-view-window))
*VIEW*
? (#/image *view*)
#<NS-IMAGE NSImage 0x21eac40 Size={1600, 1280} Reps=(
     NSBitmapImageRep 0x21f4e30 Size={1600, 1280}  
ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=24 Pixels=1600x1280  
Alpha=NO Planar=NO Format=0) (#x21EAC40)>
? (#/setImageScaling: *view* #$NSScaleToFit)
NIL

Define a new NSRect that has a different location (and shape):

? (defvar *nf* (ns:make-ns-rect 100 350 400 400))
*NF*
? *NF*
#<NS-RECT 400 X 400 @ 100,350 [gcable] (#xF2BA750) #x30004213FACD>

Get the View's containing window so we can access its animation proxy:
? (defvar *W* (#/window *view*))
*W*

Now, tell Cocoa to move it around!
? (#/setFrame:display:animate: *w* *nf* #$YES #$YES)

Whee!

-Brent

Gmane