libxm

libxm provides s7, Ruby, and Forth (fth) bindings for Xlib, Xt, Xpm, and Xm (Motif); or alternatively (under the name libxg) Gtk, gdk, gdk-pixbuf, pango, cairo, and some of glib; and much of OpenGL. You can build it with just X, and so on — see README.libxm for details. There are several example files in the libxm package — anything with the extension "scm", "rb", or "fs". libxm can be used directly with s7, Ruby, or Forth, providing a graphical user-interface for scripts.

All libxm names are exactly the same as the C name except that a "." is prepended (in Scheme) to struct field accessors — this prefix (and the postfix) can be set via the macros XM_PREFIX, XM_FIELD_PREFIX and XM_POSTFIX to whatever you like — "x:" or "<" and ">", etc. In Ruby, I'm prepending "R"; in Forth "F". I chose not to try to make these names fit into the "Scheme culture" (i.e. adding random dashes and uncapitalizing everything) because it is already too hard to keep track of the thousands of names in these libraries. By using the exact C name, embedded caps and all, there's no uncertainty about what the corresponding libxm name is; XmScaleSetValue becomes XmScaleSetValue. There are several differences between the C versions and the libxm versions; these are listed at the start of xm.c and xg.c. Briefly, an Arg list (in Motif) is a lisp list of name/value pairs and the "len" arg associated with it is optional; ref args are usually returned by the procedure in a list, and not passed in unless an initial value is needed; array args are passed as lists, and returned as lists; pointers to structs are '(type val) where val is opaque except via accessors (that is, all non-simple types are lists in xm where the first element is a symbol describing the type and the second is usually the C value stored directly as an unsigned long); "Va" args are passed and returned as lists, and the list length argument is optional; XtCallback procedure args are passed by value; the various "client data" args are optional; XtCallbackLists are passed as lists of procedure/data pairs; where an explicit NULL is needed as an arg, use #f (or '() for list args); structs are accessed by the field name and the lisp variable (which contains the struct type) — (.pixel color) for example, or (.foreground gcvalue); blank structs, where needed, can be created via (Name) — (XColor) or (XGCValues) for example.

To use libxm from some existing program, you need only export the caller's XtAppContext and main shell widget (mainly to get the Display variable). In Snd, the g_main_widgets procedure passes back a list:

      return(XEN_CONS(XEN_WRAP_APPCONTEXT(MAIN_APP(ss)),
    	   XEN_CONS(XEN_WRAP_WIDGET(MAIN_SHELL(ss)),
                 XEN_CONS(XEN_WRAP_WIDGET(MAIN_PANE(ss)),...))));

The XEN entities are from the xen package that provides a wrapper for extension-language-specific functions and macros.

      (set! app (car (main-widgets)))

For many examples, see snd-motif.scm,new-effects.scm, effects.rb, snd-xm.rb, and snd-test.scm in the Snd tarball. There is also some further discussion of this stuff in Snd's grfsnd.html (Ruby examples, etc).

    (gtk_init 0 #f)
    (let ((window (gtk_window_new GTK_WINDOW_TOPLEVEL)))
      (g_signal_connect window "delete_event"
    		    (lambda (w e data)
    		      (gtk_main_quit)
    		      #t))
      (g_signal_connect window "destroy"
    		    (lambda (w data)
    		      (gtk_main_quit)))

      (gtk_container_set_border_width (GTK_CONTAINER window) 10)

      (let ((button (gtk_button_new_with_label "Click Me!")))
        (g_signal_connect button "clicked"
    		      (lambda (w data)
    			(display "Hello World")))

        (gtk_container_add (GTK_CONTAINER window) button)
        (gtk_widget_show button)
        (gtk_widget_show window)
        (gtk_main)
        ))

There's a longer Gtk example in s7.html in the Snd package.