[comp.windows.x] CLX "hello world" example

Paul.Birkel@K.GP.CS.CMU.EDU (11/12/87)

What with various sites porting TIs' implementation of CLX, a CLX
version of the "hello worlds" example from David Rosenthal (at SUN)
(thank you David!) would be useful:

	a) As a simple test case for a port.

	b) As an example of how to use CLX to generate a simple
		X11 application.

	c) As an interesting comparison on how much Common Lisp code
		with CLX it would take to replicate a given functionality
		in C code with Xlib.

Any takers?

	Paul A. Birkel
	Dept. of Computer Science
	Carnegie-Mellon University
	Pittsburgh, PA  15213

	pab@K.CS.CMU.EDU

	(412) 268-8893

Oren@home.csc.ti.COM (LaMott Oren) (11/13/87)

   Any takers?

Here's one.  I tried to duplicate the features of David Rosenthal's
program.

;;; -*- Mode:Lisp; Syntax: Common-lisp; Package:XLIB; Base:10 -*-

(defun hello-world (host &rest args &key (string "Hello World") (font "vrb-25"))
  ;; CLX demo, says STRING using FONT in its own window on HOST
  (let* ((display (open-display host))
	 (screen (display-default-screen display))
	 (black (screen-black-pixel screen))
	 (white (screen-white-pixel screen))
	 (font (open-font display font))
	 (border 1)				; Minimum margin around the text
	 (width (+ (text-width font string) (* 2 border)))
	 (height (+ (max-char-ascent font) (max-char-descent font) (* 2 border)))
	 (x (truncate (- (screen-width screen) width) 2))
	 (y (truncate (- (screen-height screen) height) 2))
	 (window (create-window :parent (screen-root screen)
				:x x :y y :width width :height height
				:background black
				:border white
				:border-width 1
				:colormap (screen-default-colormap screen)
				:bit-gravity :center
				:event-mask '(:exposure)))
	 (gcontext (create-gcontext :drawable window
				    :background black
				    :foreground white
				    :font font)))
    ;; Set window manager hints
    (setf (wm-name window) string
	  (wm-icon-name window) string
	  (wm-command window) (list* 'hello-world host args)
	  (wm-normal-hints window) (make-wm-size-hints :x x :y y :width :width)
	  (wm-hints window) (make-wm-hints :input :off :initial-state :normal))
    (map-window window)
    ;; Handle events
    (unwind-protect
	(loop
	  (event-case (display :force-output-p t)
	    (exposure        ;; Come here on exposure events,
	      (window count) ;; binding WINDOW and COUNT from the event.
	      (when (zerop count) ;; Ignore all but the last exposure event.
		(with-state (window)
		  (let ((x (truncate (- (drawable-width window) width) 2))
			(y (truncate (- (+ (drawable-height window)
					   (max-char-ascent font))
					(max-char-descent font))
				     2)))
		    ;; Draw text centered in widnow
		    (clear-area window)
		    (draw-glyphs window gcontext x y string)))
		;; Returning non-nil causes event-case to exit
		t))))
      ;; Ensure display is closed when done
      (close-display display))))


;; The following isn't part of CLX, put should be soon (see discussions
;; in the cl-windows@sail.stanford.edu mailing list).
;; I've included the accessor functions, even though only the defsetf's
;; are used by the hello-world function.
;;-----------------------------------------------------------------------------
;; Window manager property functions

(defun wm-name (window)
  (declare (type window window)
	   (values string))
  (get-property window :wm_name :type :string :result-type 'string :transform #'xlib:card8->char))

(defsetf wm-name (window) (name)
  (declare (type window window)
	   (values string))
  `(set-string-property ,window :wm_name ,name))

(defun set-string-property (window property string)
  (declare (type window window)
	   (type keyword property)
	   (type stringable string))
  (change-property window property (string string) :string 8 :transform #'xlib:char->card8)
  string)

(defun wm-icon-name (window)
  (declare (type window window)
	   (values string))
  (get-property window :wm_icon_name :type :string
		:result-type 'string :transform #'xlib:card8->char))

(defsetf wm-icon-name (window) (name)
  `(set-string-property ,window :wm_icon_name ,name))

(defun wm-class (window)
  (declare (type window window)
	   (values string))
  (get-property window :wm_class :type :string
		:result-type 'string :transform #'xlib:card8->char))

(defsetf wm-class (window) (name)
  `(set-string-property ,window :wm_class ,name))

(defun wm-command (window)
  ;; Returns a list whose car is the command and 
  ;; whose cdr is the list of arguments
  (declare (type window window)
	   (values list))
  (do* ((command-string (get-property window :wm_command :type :string
				      :result-type 'string :transform #'xlib:card8->char))
	(command nil)
	(start 0 (1+ end))
	(end 0)
	(len (length command-string)))
       ((>= start len) (nreverse command))
    (setq end (position #.(int-char 0) command-string :start start))
    (push (subseq command-string start end) command)))

(defsetf wm-command set-wm-command)
(defun set-wm-command (window command)
  ;; Uses PRIN1 to a string-stream with the following bindings:
  ;; (*print-length* nil) (*print-level* nil) (*print-radix* nil)
  ;; (*print-base* 10.) (*print-array* t) (*package* (find-package 'lisp))
  ;; each element of command is seperated with NULL characters.
  ;; This enables (mapcar #'read-from-string (wm-command window))
  ;; to recover a lisp command.
  (declare (type window window)
	   (type list command))
  (set-string-property window :wm_command
		       (with-output-to-string (stream)
			 (let ((*print-length* nil)
			       (*print-level* nil)
			       (*print-radix* nil)
			       (*print-base* 10.)
			       (*print-array* t)
			       (*package* (find-package 'lisp))
			       #+ti (ticl:*print-structure* t))
			   (dolist (c command)
			     (prin1 c stream)
			     (write-char #.(int-char 0) stream)))))
  command)

;;-----------------------------------------------------------------------------
;; WM_HINTS

(defstruct wm-hints
  (input nil :type (or null (member :off :on)))
  (initial-state nil :type (or null (member :dont-care :normal :zoom :iconic :inactive)))
  (icon-pixmap nil :type (or null pixmap))
  (icon-window nil :type (or null window))
  (icon-x nil :type (or null card16))
  (icon-y nil :type (or null card16))
  (icon-mask nil :type (or null pixmap))
  (window-group nil :type (or null resource-id))
  ;; may be extended in the future
  )

(defun wm-hints (window)
  (declare (type window window)
	   (values wm-hints))
  (let ((prop (get-property window :wm_hints :type :wm_hints :result-type 'vector)))
    (when prop
      (decode-wm-hints prop (window-display window)))))

(defsetf wm-hints set-wm-hints)
(defun set-wm-hints (window wm-hints)
  (declare (type window window)
	   (type wm-hints wm-hints)
	   (values wm-hints))  
  (change-property window :wm_hints (encode-wm-hints wm-hints) :wm_hints 32)
  wm-hints)

(defun decode-wm-hints (vector display)
  (declare (type (simple-vector 9) vector)
	   (type display display)
	   (values wm-hints))
  (let ((input-hint 0)
	(state-hint 1)
	(icon-pixmap-hint 2)
	(icon-window-hint 3)
	(icon-position-hint 4)
	(icon-mask-hint 5)
	(window-group-hint 6)
	)
    (let ((flags (aref vector 0))
	  (hints (make-wm-hints)))
      (declare (type card16 flags)
	       (type wm-hints hints))
      (compiler-let ((*buffer* 'display))
	(when (logbitp input-hint flags)
	  (setf (wm-hints-input hints) (decode-type (member :off :on) (aref vector 1))))
	(when (logbitp state-hint flags)
	  (setf (wm-hints-initial-state hints)
		(decode-type (member :dont-care :normal :zoom :iconic :inactive)
			     (aref vector 2))))
	(when (logbitp icon-pixmap-hint flags)
	  (setf (wm-hints-icon-pixmap hints) (decode-type pixmap (aref vector 3))))
	(when (logbitp icon-window-hint flags)
	  (setf (wm-hints-icon-window hints) (decode-type window (aref vector 4))))
	(when (logbitp icon-position-hint flags)
	  (setf (wm-hints-icon-x hints) (aref vector 5)
		(wm-hints-icon-y hints) (aref vector 6)))
	(when (logbitp icon-mask-hint flags)
	  (setf (wm-hints-icon-mask hints) (decode-type pixmap (aref vector 7))))
	(when (logbitp window-group-hint flags)
	  (setf (wm-hints-window-group hints) (aref vector 8)))
	hints))))


(defun encode-wm-hints (wm-hints)
  (declare (type wm-hints wm-hints)
	   (values simple-vector))
  (let ((input-hint         #b1)
	(state-hint         #b10)
	(icon-pixmap-hint   #b100)
	(icon-window-hint   #b1000)
	(icon-position-hint #b10000)
	(icon-mask-hint     #b100000)
	(window-group-hint  #b1000000)
	)
    (let ((vector (make-array 9 :initial-element 0))
	  (flags 0))
      (declare (type (simple-vector 9) vector)
	       (type card16 flags))
      (when (wm-hints-input wm-hints)
	(setf flags input-hint
	      (aref vector 1) (encode-type (member :off :on) (wm-hints-input wm-hints))))
      (when (wm-hints-initial-state wm-hints)
	(setf flags (logior flags state-hint)
	      (aref vector 2) (encode-type (member :dont-care :normal :zoom :iconic :inactive)
					   (wm-hints-initial-state wm-hints))))
      (when (wm-hints-icon-pixmap wm-hints)
	(setf flags (logior flags icon-pixmap-hint)
	      (aref vector 3) (encode-type pixmap (wm-hints-icon-pixmap wm-hints))))
      (when (wm-hints-icon-window wm-hints)
	(setf flags (logior flags icon-window-hint)
	      (aref vector 4) (encode-type window (wm-hints-icon-window wm-hints))))
      (when (and (wm-hints-icon-x wm-hints) (wm-hints-icon-y wm-hints))
	(setf flags (logior flags icon-position-hint)
	      (aref vector 5) (encode-type card16 (wm-hints-icon-x wm-hints))
	      (aref vector 6) (encode-type card16 (wm-hints-icon-y wm-hints))))
      (when (wm-hints-icon-mask wm-hints)
	(setf flags (logior flags icon-mask-hint)
	      (aref vector 7) (encode-type pixmap (wm-hints-icon-mask wm-hints))))
      (when (wm-hints-window-group wm-hints)
	(setf flags (logior flags window-group-hint)
	      (aref vector 8) (wm-hints-window-group wm-hints)))
      (setf (aref vector 0) flags)
      vector)))

;;-----------------------------------------------------------------------------
;; WM_SIZE_HINTS

(defstruct wm-size-hints
  ;; Defaulted T to put the burden of remembering these on widget programmers.
  (user-specified-position-p t :type boolean) ;; True when user specified x y
  (user-specified-size-p t :type boolean)     ;; True when user specified width height
  (x nil :type (or null int16))
  (y nil :type (or null int16))
  (width nil :type (or null card16))
  (height nil :type (or null card16))
  (min-width nil :type (or null card16))
  (min-height nil :type (or null card16))
  (max-width nil :type (or null card16))
  (max-height nil :type (or null card16))
  (width-inc nil :type (or null card16))
  (height-inc nil :type (or null card16))
  (min-aspect nil :type (or null number))
  (max-aspect nil :type (or null number)))

(defun wm-normal-hints (window)
  (declare (type window window)
	   (values wm-size-hints))
  (decode-wm-size-hints (get-property window :wm_normal_hints :type :wm_size_hints :result-type 'vector)))

(defsetf wm-normal-hints set-wm-normal-hints)
(defun set-wm-normal-hints (window hints)
  (declare (type window window)
	   (type wm-size-hints hints)
	   (values wm-size-hints))
  (change-property window :wm_normal_hints (encode-wm-size-hints hints) :wm_size_hints 32)
  hints)

(defun wm-zoom-hints (window)
  (declare (type window window)
	   (values wm-size-hints))
  (decode-wm-size-hints (get-property window :wm_zoom_hints :type :wm_size_hints :result-type 'vector)))

(defsetf wm-zoom-hints set-wm-zoom-hints)
(defun set-wm-zoom-hints (window hints)
  (declare (type window window)
	   (type wm-size-hints hints)
	   (values wm-size-hints))
  (change-property window :wm_zoom_hints (encode-wm-size-hints hints) :wm_size_hints 32)
  hints)

(defun decode-wm-size-hints (vector)
  (declare (type (or null (simple-vector 15)) vector)
	   (values (or null wm-size-hints)))
  (when vector
    (let ((usposition 0)			;User Specified position
	  (ussize 1)				;User Specified size
	  (pposition 2)				;Program specified position
	  (psize 3)				;Program specified size
	  (pminsize 4)				;Program specified minimum size
	  (pmaxsize 5)				;Program specified maximum size
	  (presizeinc 6)			;Program specified resize increments
	  (paspect 7)				;Program specfied min and max aspect ratios
	  )
      (let ((flags (aref vector 0))
	    (hints (make-wm-size-hints)))
	(declare (type card16 flags)
		 (type wm-size-hints hints))
	(when (or (logbitp usposition flags)
		  (logbitp pposition flags))
	  (setf (wm-size-hints-user-specified-position-p hints) (logbitp usposition flags)
		(wm-size-hints-x hints) (aref vector 1)
		(wm-size-hints-y hints) (aref vector 2)))
	(when (or (logbitp ussize flags)
		  (logbitp psize flags))
	  (setf (wm-size-hints-user-specified-size-p hints) (logbitp usposition flags)
		(wm-size-hints-width hints) (aref vector 3)
		(wm-size-hints-height hints) (aref vector 4)))
	(when (logbitp pminsize flags)
	  (setf (wm-size-hints-min-width hints) (aref vector 5)
		(wm-size-hints-min-height hints) (aref vector 6)))
	(when (logbitp pmaxsize flags)
	  (setf (wm-size-hints-max-width hints) (aref vector 7)
		(wm-size-hints-max-height hints) (aref vector 8)))
	(when (logbitp presizeinc flags)
	  (setf (wm-size-hints-width-inc hints) (aref vector 9)
		(wm-size-hints-height-inc hints) (aref vector 10)))
	(when (logbitp paspect flags)
	  (setf (wm-size-hints-min-aspect hints) (/ (aref vector 11) (aref vector 12))
		(wm-size-hints-max-aspect hints) (/ (aref vector 13) (aref vector 14))))
	hints))))

(defun encode-wm-size-hints (hints)
  (declare (type wm-size-hints hints)
	   (values simple-vector))
  (let ((usposition #b1)			;User Specified position
	(ussize     #b10)			;User Specified size
	(pposition  #b100)			;Program specified position
	(psize      #b1000)			;Program specified size
	(pminsize   #b10000)			;Program specified minimum size
	(pmaxsize   #b100000)			;Program specified maximum size
	(presizeinc #b1000000)			;Program specified resize increments
	(paspect    #b10000000)			;Program specfied min and max aspect ratios
	)
    (let ((vector (make-array 15 :initial-element 0))
	  (flags 0))
      (declare (type (simple-vector 15) vector)
	       (type card16 flags))
      (when (and (wm-size-hints-x hints) (wm-size-hints-y hints))
	(setq flags (if (wm-size-hints-user-specified-position-p hints) usposition pposition))
	(setf (aref vector 1) (wm-size-hints-x hints)
	      (aref vector 2) (wm-size-hints-y hints)))
      (when (and (wm-size-hints-width hints) (wm-size-hints-height hints))
	(setf flags (logior flags (if (wm-size-hints-user-specified-position-p hints) ussize psize))
	      (aref vector 3) (wm-size-hints-width hints)
	      (aref vector 4) (wm-size-hints-height hints)))
      
      (when (and (wm-size-hints-min-width hints) (wm-size-hints-min-height hints))
	(setf flags (logior flags pminsize)
	      (aref vector 5) (wm-size-hints-min-width hints)
	      (aref vector 6) (wm-size-hints-min-height hints)))
      (when (and (wm-size-hints-max-width hints) (wm-size-hints-max-height hints))
	(setf flags (logior flags pmaxsize)
	      (aref vector 7) (wm-size-hints-max-width hints)
	      (aref vector 8) (wm-size-hints-max-height hints)))
      (when (and (wm-size-hints-width-inc hints) (wm-size-hints-height-inc hints))
	(setf flags (logior flags presizeinc)
	      (aref vector 9) (wm-size-hints-width-inc hints)
	      (aref vector 10) (wm-size-hints-height-inc hints)))
      (let ((min-aspect (wm-size-hints-min-aspect hints))
	    (max-aspect (wm-size-hints-max-aspect hints)))
	(when (and min-aspect max-aspect)
	  (setf flags (logior flags paspect)
		min-aspect (rationalize min-aspect)
		max-aspect (rationalize max-aspect)
		(aref vector 11) (numerator min-aspect)
		(aref vector 12) (denominator min-aspect)
		(aref vector 13) (numerator max-aspect)
		(aref vector 14) (denominator max-aspect))))
      (setf (aref vector 0) flags)
      vector)))