[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Using mp:process-wait to service X

Running a separate process to handle X11 input events is the only
acceptable way to do it without freezing up your lisp (no user input)
or spinning away cycles polling the server.

Here is a simplified version of what I have used for a few years.

It is trivially ported to Lucid CL (strictly a mechanical transform).

For CMU CL, see system:serve-event and *display-event-handlers.  *Most*
uses of MP for display and/or input/output functions can be ported to CMU CL
using this functionality (I ported CLM to CMUCL in this manner).
Most other functionality can be had with alarm signals & handlers,
and closures, though it can get rather messy.  Hopefully threads will come soon. 

Here is a brief summary of the functions here:

 xapp -- starts up xapp-server function in a separate process,
	 restarting and/or killing existing process.

 xapp-server  -- the function that waits for X events,
		 and then calls default-event-handler on them

 default-event-handler  -- dispatch according to the event type.
			   notice these other functions (exposure, buttonpress, etc)
			   are not defined here.

This skeleton should be suitable for simple applications.
(I believe a copy was once in garnet.)
Things to watch out for:

 - multiple processes drawing in a window can cause locking problems.
   I suggest you do your own scheduling if necessary

 - debugging MP's can be tricky and confusing.
   You probably want to add some error handling and/or catching in xapp-server

 - you should be familiar with xlib:event-case, and understand the comment
   ";; every clause should return T, not NIL!"
   (experts may ignore)

Free code, enjoy.

Disclaimer:  this code has worked for over 3 years in allegro, in versions
             3.x and 4.0, probably 4.1.  It assumes CLX is loaded, and the
             obvious specials (eg, *display*) are set and functional.
             Derived and simplified from working code; only the names have
             been changed to protect the innocent.  Some settling may have
             occurred during posting.  Void where prohibited.  Compile for
             best results.  Not responsible for generated garbage or your
             failings.  I don't have time to look at it, but bribery works.

 Todd Kaufmann
 Center for Machine Translation 
 Carnegie Mellon University
 +1 412 268 7130     fax:  +1 412 268 6298

;;; Culled from other code by Todd Kaufmann on or about 26-Jan-93, 02:04.
;;; Assumes CLX loaded & running, *display* is valid.
;;; You must define event-handling functions.

(defvar *xapp-proc-name* "Xapp-window")

(require :process)
(defun xapp (&key restart)
  (let ((proc (find *xapp-proc-name* mp:*all-processes*
		    :key #'mp:process-name
		    :test #'string=)))
    (cond ((and restart proc)
	   (format *debug-io* "~&Killing old process..")
	   (mp:process-kill proc)
	   (format *debug-io* ". done.~%"))
	   (format *debug-io* "~&Process ~s wasn't running.  Creating..."))
	   (format *debug-io* "~&Restarting existing ~s." *xapp-proc-name*)
	   (return-from xapp (mp:process-reset proc)))
	   (format *debug-io* "Starting up process to serve the window."))
    (mp:process-run-function *xapp-proc-name*

(defun xapp-server (display)
     (when (xlib:event-listen display 20)
       (default-event-handler display)))

(defvar *event-debug* nil
  "When non-nil, produces debugging info from default-event-handler.")

(defun default-event-handler (display)
  "Event handler for the fg-windows"
  ;; every clause should return T, not NIL!
  (when *event-debug* (format t "In default-event-handler~%"))
  (if (xlib:display-p display) 
      (xlib:event-case (display :discard-p t :force-output-p t)
	(:EXPOSURE (event-window count)
	  (Exposure *event-debug* event-window count display))
	(:ENTER-NOTIFY  (event-window)
	  (Enter-Notify *event-debug* event-window))
	(:NO-EXPOSURE () t)
	(:Button-Press (event-window x y code)
	  (ButtonPress *event-debug* event-window x y code))
	(:Button-Release (event-window x y)
			 (ButtonRelease *event-debug* event-window x y))
	(:Motion-notify ()
	  (when *event-debug* 
	    (format t "Motion Notify in default-event-handler~%"))
	(:key-press (event-window state code x y)
		    (call-window-key-function display event-window state
					      code x y)
	(OTHERWISE (event-key)
		   (when *event-debug*
		     (format t "Illegal event: ~S~%" event-key))
      ; else
      (when *EVENT-DEBUG* (format t "Bad Display value:  ~A~%" display)))
  (when *event-debug* (format t "Exiting default-event-handler~%")