xm


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.