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.