libxm provides Guile/Ruby bindings for Xlib, Xt, Xp, Xpm, and Xm (Motif). 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". libxm can be used directly with Guile (or Ruby), providing a graphical user-interface for scripts and so on.
All libxm names are exactly the same as the C name except that a "|" is prepended (in Guile) -- this can be set via the macros XM_PREFIX and XM_POSTFIX to whatever you like -- "x:" or "<" and ">", etc. In Ruby, I'm prepending "R". There are several differences between the C versions and the libxm versions; these are listed in detail at the start of xm.c. Briefly, an Arg list is lisp list of name/value pairs and the "len" arg associated with it is optional; ref args are usually returned by proc, and not passed in unless init val 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 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.
For example, load this into guile after loading libxm.so:
(let* ((shell-app (|XtVaOpenApplication
"Test" 0 0 0 '() 0 |applicationShellWidgetClass
(list |XmNallowShellResize #t)))
(app (cadr shell-app))
(shell (car shell-app))
(black (|BlackPixelOfScreen
(|DefaultScreenOfDisplay
(|XtDisplay shell)))))
(if (not (|XtIsApplicationShell shell))
(display "not appshell"?))
(|XtSetValues shell (list |XmNtitle "Hi!") 1)
(let* ((main-pane
(|XtVaCreateManagedWidget
"main-pane" |xmFormWidgetClass shell
(list |XmNforeground black
|XmNtopAttachment |XmATTACH_FORM
|XmNbottomAttachment |XmATTACH_FORM
|XmNleftAttachment |XmATTACH_FORM
|XmNrightAttachment |XmATTACH_FORM
|XmNallowResize #t)))
(button (|XtCreateManagedWidget
"push me" |xmPushButtonWidgetClass main-pane '() 0)))
(|XtAddCallback button |XmNactivateCallback
(lambda (widget context event-info)
(display widget)
(display (|reason event-info))
(display context))
123)
(|XtRealizeWidget shell)
(|XtAppMainLoop app)))
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_C_POINTER(MAIN_APP(ss)),
XEN_CONS(XEN_WRAP_C_POINTER(MAIN_SHELL(ss)),
XEN_CONS(XEN_WRAP_C_POINTER(MAIN_PANE(ss)),...))));
The XEN entities are from the xen package that provides a wrapper for Guile-specific (or Ruby-specific) functions and macros. Once exported, we import them into the libxm world via the C-style casting procedures |Widget and |XtAppContext:
(set! app (|XtAppContext (car (main-widgets))))
And now "app" can be used wherever a libxm procedure expects to get an XtAppContext argument. There are several other "casters": |Pixel, etc. See snd-motif.scm and friends.