Author: tpapp Date: Wed Aug 22 12:13:14 2007 New Revision: 13
Added: xlib-context.lisp Removed: x11-context.lisp Modified: cl-cairo2.asd context.lisp package.lisp surface.lisp transformations.lisp tutorial/tutorial.tex xlib.lisp Log: another major revamping of X11 code, also put exported symbols in package.lisp where they belong
Modified: cl-cairo2.asd ============================================================================== --- cl-cairo2.asd (original) +++ cl-cairo2.asd Wed Aug 22 12:13:14 2007 @@ -15,7 +15,7 @@ (:file "xlib" :depends-on ("context") :in-order-to ((load-op (feature :unix)) (compile-op (feature :unix)))) - (:file "x11-context" :depends-on ("xlib") + (:file "xlib-context" :depends-on ("xlib") :in-order-to ((load-op (feature :unix)) (compile-op (feature :unix))))) :depends-on (:cffi :cl-colors :cl-utilities))
Modified: context.lisp ============================================================================== --- context.lisp (original) +++ context.lisp Wed Aug 22 12:13:14 2007 @@ -22,20 +22,24 @@ ;;;; context class ;;;;
-(defclass context () ((pointer :initform nil))) - -(export - (defun create-context (surface) - (with-surface (surface pointer) - (let ((context (make-instance 'context))) - (setf (slot-value context 'pointer) (cairo_create pointer)) - ;; register finalizer -;; (let ((context-pointer (slot-value context 'pointer))) -;; (finalize context -;; #'(lambda () -;; (cairo_destroy context-pointer)))) - ;; return context - context)))) +(defclass context () + ((pointer :initform nil :initarg :pointer) + (width :initarg :width :reader get-width) + (height :initarg :height :reader get-height))) + +(defun create-context (surface) + (with-surface (surface pointer) + (let ((context (make-instance 'context + :pointer (cairo_create pointer) + :width (get-width surface) + :height (get-height surface)))) + ;; register finalizer + (let ((context-pointer (slot-value context 'pointer))) + (finalize context + #'(lambda () + (cairo_destroy context-pointer)))) + ;; return context + context)))
(defmethod destroy ((object context)) (with-slots (pointer) object @@ -45,11 +49,23 @@ ;; deregister finalizer (cancel-finalization object))
-(defgeneric sync (object)) - -(defmethod sync ((object context)) - ;; most contexts don't need syncing - ) +(defgeneric sync (object) + (:documentation "Synchronize contents of the object with the + physical device if needed.")) +(defgeneric sync-lock (object) + (:documentation "Suspend syncing (ie sync will have no effect) until + sync-unlock is called. Calls to sync-lock nest.")) +(defgeneric sync-unlock (object) + (:documentation "Undo a call to sync-lock.")) +(defgeneric sync-reset (object) + (:documentation "Undo all calls to sync, ie object will be +synced (if necessary) no matter how many times sync was called before.")) + +;; most contexts don't need syncing +(defmethod sync ((object context))) +(defmethod sync-lock ((object context))) +(defmethod sync-unlock ((object context))) +(defmethod sync-reset ((object context)))
;;;; ;;;; default context and convenience macros @@ -74,28 +90,25 @@ "Define cairo function with *context* as its first argument and args as the rest, automatically mapping name to the appropriate cairo function." - `(export - (defun ,name (,@args &optional (context *context*)) - (with-context (context pointer) - (,(prepend-intern "cairo_" name) pointer ,@args))))) + `(defun ,name (,@args &optional (context *context*)) + (with-context (context pointer) + (,(prepend-intern "cairo_" name) pointer ,@args))))
(defmacro define-with-default-context-sync (name &rest args) "Define cairo function with *context* as its first argument and args as the rest, automatically mapping name to the appropriate cairo function. sync will be called after the operation." - `(export - (defun ,name (,@args &optional (context *context*)) - (with-context (context pointer) - (,(prepend-intern "cairo_" name) pointer ,@args)) - (sync context)))) + `(defun ,name (,@args &optional (context *context*)) + (with-context (context pointer) + (,(prepend-intern "cairo_" name) pointer ,@args)) + (sync context)))
(defmacro define-flexible ((name pointer &rest args) &body body) "Like define-with-default context, but with arbitrary body, pointer will point to the context." - `(export - (defun ,name (,@args &optional (context *context*)) - (with-context (context ,pointer) - ,@body)))) + `(defun ,name (,@args &optional (context *context*)) + (with-context (context ,pointer) + ,@body)))
(defmacro define-many-with-default-context (&body args) "Apply define-with-default context to a list. Each item is @@ -156,18 +169,15 @@
(defgeneric set-source-color (color &optional context))
-(defmethod set-source-color - ((color rgb) &optional (context *context*)) +(defmethod set-source-color ((color rgb) &optional (context *context*)) (with-slots (red green blue) color (set-source-rgb red green blue context)))
-(defmethod set-source-color - ((color rgba) &optional (context *context*)) +(defmethod set-source-color ((color rgba) &optional (context *context*)) (with-slots (red green blue alpha) color (set-source-rgb red green blue alpha context)))
-(defmethod set-source-color - ((color hsv) &optional (context *context*)) +(defmethod set-source-color ((color hsv) &optional (context *context*)) (with-slots (red green blue) (hsv->rgb color) (set-source-rgb red green blue context)))
@@ -228,3 +238,11 @@
(define-flexible (in-stroke pointer x y) (not (zerop (cairo_in_stroke pointer x y)))) + +;;;; +;;;; convenience functions for creating contexts directly +;;;; + +(define-create-context ps) +(define-create-context pdf) +(define-create-context svg)
Modified: package.lisp ============================================================================== --- package.lisp (original) +++ package.lisp Wed Aug 22 12:13:14 2007 @@ -1,15 +1,54 @@ (defpackage :cl-cairo2 (:use :common-lisp :cffi :cl-colors :cl-utilities) - (:export ; !!! when the interface - ; stabilizes, remove export's - ; from all other places and - ; list them here - ;; utility functions - deg-to-rad + (:export + + ;; cairo + + destroy deg-to-rad + + ;; surface + + get-width get-height destroy create-image-surface + image-surface-get-format image-surface-get-width + image-surface-get-height image-surface-create-from-png + surface-write-to-png with-png-file + ;; context - *context* set-source-color + + create-context sync sync-lock sync + sync-unlock sync-reset *context* save restore push-group pop-group + pop-group-to-source set-source-rgb set-source-rgba clip + clip-preserve reset-clip copy-page show-page fill-preserve paint + paint-with-alpha stroke stroke-preserve set-source-color + get-line-width set-line-width get-miter-limit set-miter-limit + get-antialias set-antialias get-fill-rule set-fill-rule + get-line-cap set-line-cap get-line-join set-line-join get-operator + set-operator fill-path set-dash get-dash clip-extents fill-extents + in-fill in-stoke create-ps-context create-pdf-context + create-svg-context + + ;; path + + new-path new-sub-path close-path arc arc-negative curve-to line-to + move-to rectangle rel-move-to rel-curve-to rel-line-to text-path + get-current-point + + ;; text + + select-font-face set-font-size text-extents show-text + ;; transformations - make-trans-matrix trans-matrix-xx trans-matrix-yx trans-matrix-xy - trans-matrix-yy trans-matrix-x0 trans-matrix-y0 trans-matrix-p - ;; x11-context - x11-context x11-display open-x11-display create-x11-context)) + + translate scale rotate reset-trans-matrix make-trans-matrix + trans-matrix-xx trans-matrix-yx trans-matrix-xy trans-matrix-yy + trans-matrix-x0 trans-matrix-y0 trans-matrix-p transform + set-trans-matrix get-trans-matrix user-to-device + user-to-device-distance device-to-user device-to-user-distance + trans-matrix-init-translate trans-matrix-init-scale + trans-matrix-init-rotate trans-matrix-rotate trans-matrix-scale + trans-matrix-rotate trans-matrix-invert trans-matrix-multiply + trans-matrix-distance transform-point + + ;; xlib-context + + xlib-context xlib-display open-xlib-display create-xlib-context))
Modified: surface.lisp ============================================================================== --- surface.lisp (original) +++ surface.lisp Wed Aug 22 12:13:14 2007 @@ -13,7 +13,10 @@ ;;;; class surface ;;;;
-(defclass surface () ((pointer :initarg :pointer :initform nil))) +(defclass surface () + ((pointer :initarg :pointer :initform nil) + (width :initarg :width :reader get-width) + (height :initarg :height :reader get-height)))
(defmacro with-alive-surface ((surface pointer) &body body) "Execute body with pointer pointing to cairo surface, if nil, @@ -39,9 +42,9 @@ (check-surface-pointer-status ,pointer ,@body)))
-(defun new-surface-with-check (pointer) +(defun new-surface-with-check (pointer width height) "Check if the creation of new surface was successful, if so, return new class." - (let ((surface (make-instance 'surface))) + (let ((surface (make-instance 'surface :width width :height height))) (check-surface-pointer-status pointer (setf (slot-value surface 'pointer) pointer) ;; register finalizer @@ -62,97 +65,93 @@ ;;;;
(defmacro define-create-surface (type) - `(export - (defun ,(prepend-intern "create-" type :replace-dash nil :suffix "-surface") - (filename width-in-points height-in-points) - (new-surface-with-check - (,(prepend-intern "cairo_" type :replace-dash nil - :suffix "_surface_create") - filename width-in-points height-in-points))))) + "Define the function create-<type>-surface." + `(defun ,(prepend-intern "create-" type :replace-dash nil :suffix "-surface") + (filename width height) + (new-surface-with-check + (,(prepend-intern "cairo_" type :replace-dash nil + :suffix "_surface_create") + filename width height) + width height)))
(defmacro define-create-context (type) - `(export - (defun ,(prepend-intern "create-" type :replace-dash nil :suffix "-context") - (filename width-in-points height-in-points) - "Create a surface, then a context for a file, then + `(defun ,(prepend-intern "create-" type :replace-dash nil :suffix "-context") + (filename width height) + "Create a surface, then a context for a file, then destroy (dereference) the surface. The user only needs to destroy the context when done." - (let* ((surface (,(prepend-intern "create-" - type :replace-dash nil :suffix "-surface") - filename width-in-points height-in-points)) - (context (create-context surface))) - (destroy surface) - context)))) - + (let* ((surface (,(prepend-intern "create-" + type :replace-dash nil :suffix "-surface") + filename width height)) + (context (create-context surface))) + (destroy surface) + context)))
;;;; ;;;; PDF surface ;;;;
(define-create-surface pdf) -(define-create-context pdf)
;;;; ;;;; PostScript surface ;;;;
(define-create-surface ps) -(define-create-context ps)
;;;; ;;;; SVG surface ;;;;
(define-create-surface svg) -(define-create-context svg)
;;;; ;;;; image surface ;;;;
-(export - (defun create-image-surface (format width height) - (new-surface-with-check - (cairo_image_surface_create (lookup-enum format table-format) - width height)))) - -(export - (defun image-surface-get-format (surface) - (with-surface (surface pointer) - (lookup-cairo-enum (cairo_image_surface_get_format pointer) table-format)))) - -(export - (defun image-surface-get-width (surface) - (with-surface (surface pointer) - (cairo_image_surface_get_width pointer)))) - -(export - (defun image-surface-get-height (surface) - (with-surface (surface pointer) - (cairo_image_surface_get_height pointer)))) +(defun create-image-surface (format width height) + (new-surface-with-check + (cairo_image_surface_create (lookup-enum format table-format) + width height) + width height)) + +(defun image-surface-get-format (surface) + (with-surface (surface pointer) + (lookup-cairo-enum (cairo_image_surface_get_format pointer) table-format))) + +(defun image-surface-get-width (surface) + (with-surface (surface pointer) + (cairo_image_surface_get_width pointer))) + +(defun image-surface-get-height (surface) + (with-surface (surface pointer) + (cairo_image_surface_get_height pointer)))
;;;; ;;;; PNG surfaces ;;;;
-(export - (defun image-surface-create-from-png (filename) - (new-surface-with-check (cairo_image_surface_create_from_png filename)))) - -(export - (defun surface-write-to-png (surface filename) - (with-surface (surface pointer) - (cairo_surface_write_to_png pointer filename)))) - -(export - (defmacro with-png-file ((filename format width height) &body body) - "Execute the body with context bound to a newly created png +(defun image-surface-create-from-png (filename) + (let ((surface + (new-surface-with-check (cairo_image_surface_create_from_png filename) + 0 0))) + (with-slots (width height) surface + (setf width (image-surface-get-width surface) + height (image-surface-get-height surface)) + surface))) + +(defun surface-write-to-png (surface filename) + (with-surface (surface pointer) + (cairo_surface_write_to_png pointer filename))) + +(defmacro with-png-file ((filename format width height) &body body) + "Execute the body with context bound to a newly created png file, and close it after executing body." - (let ((surface-name (gensym))) - `(let* ((,surface-name (create-image-surface ,format ,width ,height)) - (*context* (create-context ,surface-name))) - (progn - ,@body - (surface-write-to-png ,surface-name ,filename) - (destroy ,surface-name) - (destroy *context*)))))) + (let ((surface-name (gensym))) + `(let* ((,surface-name (create-image-surface ,format ,width ,height)) + (*context* (create-context ,surface-name))) + (progn + ,@body + (surface-write-to-png ,surface-name ,filename) + (destroy ,surface-name) + (destroy *context*)))))
Modified: transformations.lisp ============================================================================== --- transformations.lisp (original) +++ transformations.lisp Wed Aug 22 12:13:14 2007 @@ -131,12 +131,11 @@ (defmacro define-matrix-init (name &rest args) "Define a matrix initializer function with args, which returns the new matrix." - `(export - (defun ,(prepend-intern "trans-matrix-init-" name :replace-dash nil) ,args - (with-trans-matrix-out matrix-pointer - (,(prepend-intern "cairo_matrix_init_" name) - matrix-pointer - ,@args))))) + `(defun ,(prepend-intern "trans-matrix-init-" name :replace-dash nil) ,args + (with-trans-matrix-out matrix-pointer + (,(prepend-intern "cairo_matrix_init_" name) + matrix-pointer + ,@args))))
(define-matrix-init translate tx ty) (define-matrix-init scale sx sy) @@ -157,23 +156,20 @@ (define-matrix-transformation rotate radians) (define-matrix-transformation invert)
-(export - (defun trans-matrix-multiply (a b) - (with-trans-matrix-in a a-pointer - (with-trans-matrix-in b b-pointer - (with-trans-matrix-out result-pointer - (cairo_matrix_multiply result-pointer - a-pointer - b-pointer)))))) - -(export - (defun transform-distance (matrix x y) - (with-trans-matrix-in matrix matrix-pointer - (with-x-y - (cairo_matrix_transform_distance matrix-pointer xp yp))))) - -(export - (defun transform-point (matrix x y) - (with-trans-matrix-in matrix matrix-pointer - (with-x-y - (cairo_matrix_transform_point matrix-pointer xp yp))))) +(defun trans-matrix-multiply (a b) + (with-trans-matrix-in a a-pointer + (with-trans-matrix-in b b-pointer + (with-trans-matrix-out result-pointer + (cairo_matrix_multiply result-pointer + a-pointer + b-pointer))))) + +(defun transform-distance (matrix x y) + (with-trans-matrix-in matrix matrix-pointer + (with-x-y + (cairo_matrix_transform_distance matrix-pointer xp yp)))) + +(defun transform-point (matrix x y) + (with-trans-matrix-in matrix matrix-pointer + (with-x-y + (cairo_matrix_transform_point matrix-pointer xp yp))))
Modified: tutorial/tutorial.tex ============================================================================== --- tutorial/tutorial.tex (original) +++ tutorial/tutorial.tex Wed Aug 22 12:13:14 2007 @@ -128,14 +128,15 @@ CLOS wrappers, and can be closed (\emph{destroyed}) with \lstinline!destroy!.
-When the context is created from a surface, the reference count of the -latter is incremented. You can immediately destroy the surface: it -will not be destroyed (ie the file will not be closed) until you -destroy the context.\footnote{The file will also be closed if the - wrapper object is garbage collected. However, you should not rely - on this, as calling the garbage collector is not portable.} The -following code draws a white diagonal line on a blue background, using -a Postscript file -- the result is shown in Figure~\ref{fig:example}. +When the context is created from a surface, the reference count (in +the internals of Cairo) of the latter is incremented. You can +immediately destroy the surface: it will not be destroyed (ie the file +will not be closed) until you destroy the context.\footnote{The file + will also be closed if the wrapper object is garbage collected. + However, you should not rely on this, as calling the garbage + collector is not portable.} The following code draws a white +diagonal line on a blue background, using a Postscript file -- the +result is shown in Figure~\ref{fig:example}.
\lstinputlisting[firstline=13,lastline=27]{example.lisp}
@@ -153,6 +154,10 @@ \begin{lstlisting} (setf *context* (create-ps-context "example.ps" 200 100)) \end{lstlisting} +Unlike the original Cairo API, surfaces and contexts in +\lstinline!cl-cairo2! remember their width and height. Use the +generic functions \lstinline!get-width! and \lstinline!get-height! to +extract these.
When you want to write the output into a bitmap file (for example, in PNG format), you first need to create an \emph{image surface}, then @@ -230,7 +235,7 @@ written to PNG files) are supported.
Drawing in X11 windows is implemented using the -\lstinline!x11-context! class --- see Section~\ref{sec:x11-context} +\lstinline!x11-context! class --- see Section~\ref{sec:xlib-context} for more information.
\subsection{Contexts} @@ -334,45 +339,50 @@ with \lstinline!trans-matrix-!, and other a few other functions have been renamed to avoid conflicts with linear algebra packages.
-\subsection{X11 Contexts} -\label{sec:x11-context} +\subsection{Xlib Contexts} +\label{sec:xlib-context}
-The x11 context is not part of cairo -- it is a bit of glue code that +The xlib context is not part of cairo -- it is a bit of glue code that uses cairo's X11 surface on a pixmap, and displays this pixmap when needed (when X11 asks for the window contents to be redrawn or when cairo draws on the pixmap).
-In order to open an \lstinline!x11-context!, first you need to open an -\lstinline!x11-display!, for example, -\begin{lstlisting} -(defparameter *display* (open-x11-display ":0")) -\end{lstlisting} -opens a display on the local host. Each display runs an event loop in -a separate thread, and you can open several display and several -windows on each simultaneously. The X11 event loop runs in a separate -thread, so you need a Lisp implementation that supports threads. You -can close displays with \lstinline!destroy!, all open windows will be -closed and the contexts mapping into these windows will be destroyed -(drawing on them will be an invalid operation). - -For cl-cairo2, each window maps to a context. The surface is not +In cl-cairo2, each window maps to a context. The surface is not exposed to the user, who is only allowed to see the context. This -makes memory management and proper cleanup easier. - -You can create Xlib contexts with +makes memory management and proper cleanup easier. For example, you +can create an \lstinline!xlib-context! with \begin{lstlisting} - (create-x11-context width height display) +(setf *context* (create-xlib-context 500 400 + :display-name "localhost:0" + :window-name "my pretty drawing")) \end{lstlisting} -When \lstinline!destroy!ed, the window is closed. This works the -other way too: when the window is closed, the context is destroyed. -The windows are double-buffered using a pixmap on the X11 server, -therefore redrawing exposed windows is fast. However, this +If you give \lstinline!nil! for \lstinline!display-name!, Xlib fill +probably figure out a reasonable default, usually from your +\verb!$DISPLAY! environment variable. + +The X11 event loop runs in a separate thread, so you need a Lisp +implementation that supports threads. + +When the context \lstinline!destroy!ed, the window is closed. This +works the other way too: when the window is closed, the context is +destroyed. The windows are double-buffered using a pixmap on the X11 +server, therefore redrawing exposed windows is fast. However, this implementation precludes the resizing of the window.
Example code can be found in \verb!tutorial/x11-example.lisp!. The current implementation is not optimized for speed (the whole window is -redrawn all the time) but it is fast enough for me. If you need speed -improvements desperately, please contact the author. +redrawn all the time) but it is fast enough. If you draw a lot of +objects at the same time, it is suggested that you suspend +synchronizing with the X-window server using + \lstinline!(sync-lock context)!. + When you are done, you can call \lstinline!(sync-unlock context)!, which will automatically sync the buffer and the window. +You can nest calls to \lstinline!sync-lock! and +\lstinline!sync-unlock!, and if you want to restore syncing +unconditionally, use \lstinline!sync-reset!, which also performs +syncing too. These are generic functions which do nothing for other +contexts. + +
\subsection{To Do} \label{sec:todo}
Added: xlib-context.lisp ============================================================================== --- (empty file) +++ xlib-context.lisp Wed Aug 22 12:13:14 2007 @@ -0,0 +1,238 @@ +(in-package :cl-cairo2) + +;; constants for communicating with the signal window +(defconstant +destroy-message+ 4072) ; just some random constant +(defconstant +refresh-message+ 2495) ; ditto + +(defvar *xlib-context-count* 0 "window counter for autogenerating names") + +(defun next-xlib-context-name () + "Return an autogenerated window name using *xlib-context-count*." + (format nil "cl-cairo2 ~a" (incf *xlib-context-count*))) + +;; code to make threads, please extend with your own Lisp if needed +;; testing is welcome, I only tested cmucl and sbcl +(defun start-thread (function name) + #+allegro (mp:process-run-function name function) + #+armedbear (ext:make-thread function :name name) + #+cmu (mp:make-process function :name name) + #+lispworks (mp:process-run-function name nil function) + #+openmcl (ccl:process-run-function name function) + #+sbcl (sb-thread:make-thread function :name name)) + +;; we create this definition manually, SWIG just messes things up +(defcfun ("cairo_xlib_surface_create" cairo_xlib_surface_create) cairo_surface_t + (display display) + (drawable drawable) + (visual visual) + (width :int) + (height :int)) + +;; The class for an x11 context. Each context has a separate display +;; queue, window and an event loop in a separate thread. Once the +;; event loop is started, communication with the thread is done via +;; X11 ClientNotify events (see wacky constants above). + +(defclass xlib-context (context) + ((display :initarg :display) + (wm-delete-window) + (window) + (signal-window) + (pixmap) + (graphics-context) + (thread) + (sync-counter :initform 0 :accessor sync-counter))) + +(defun refresh-xlib-context (xlib-context) + "Copy the contents of the pixmap to the window. This function is +meant for internal use in the cl-cairo2 package." + (with-slots (display width height window pixmap graphics-context) xlib-context + (xcopyarea display pixmap window graphics-context + 0 0 width height 0 0) + (xsync display 1))) + +(defun create-window (display parent width height class visual background-pixel + event-mask &optional (backing-store t)) + "Create an x11 window, placed at 0 0, with the given attributes. +For internal use in the cl-cairo2 package." + ;; call xcreatewindow with attributes + (with-foreign-object (attributes 'xsetwindowattributes) + (setf (foreign-slot-value attributes 'xsetwindowattributes 'event-mask) + event-mask + (foreign-slot-value attributes 'xsetwindowattributes 'background-pixel) + background-pixel + (foreign-slot-value attributes 'xsetwindowattributes 'backing-store) + (if backing-store 1 0)) + (xcreatewindow display parent 0 0 width height + 0 ; zero border width + 0 ; depth - copy from parent + (ecase class + (copyfromparent 0) + (inputoutput 1) + (inputonly 2)) ; class + visual + (if (eq class 'inputonly) + cweventmask + (logior cwbackpixel cwbackingstore cweventmask)) + attributes))) + +(defun create-xlib-context (width height &key + (display-name nil) + (window-name (next-xlib-context-name))) + (let ((display (xopendisplay (if display-name display-name (null-pointer))))) + (when (null-pointer-p display) + (error "couldn't open display ~a" display-name)) + (let ((xlib-context (make-instance 'xlib-context + :display display + :width width + :height height))) + (flet ((event-loop () + (with-slots (display (this-window window) signal-window + pixmap + wm-delete-window graphics-context) + xlib-context + (let ((wm-protocols (xinternatom display "WM_PROTOCOLS" 1))) + (with-foreign-object (xev :long 24) + (do ((got-close-signal nil)) + (got-close-signal) + ;; get next event + (xnextevent display xev) + ;; decipher structure, at least partially + (with-foreign-slots ((type window serial) xev xanyevent) + ;; action based on event type + (cond + ;; expose and configurenotify events + ((and (= type 12) (= window this-window)) + (refresh-xlib-context xlib-context)) + ;; clientnotify event + ((= type 33) + (with-foreign-slots ((message-type data0) xev + xclientmessageevent) + (cond + ((or (and (= window signal-window) + (= data0 +destroy-message+)) + (and (= window this-window) + (= message-type wm-protocols) + (= data0 wm-delete-window))) + (setf got-close-signal t)) + ((and (= window signal-window) + (= data0 +refresh-message+)) + (refresh-xlib-context xlib-context))))))))))) + ;; close down everything + (with-slots (display pixmap window signal-window pointer) + xlib-context + (let ((saved-pointer pointer)) + (setf pointer nil) ; invalidate first so it can't be used + (cairo_destroy saved-pointer)) + (xfreepixmap display pixmap) + (xdestroywindow display window) + (xdestroywindow display signal-window) + (xclosedisplay display)))) + ;; initialize + (xsynchronize display 1) + (let* ((screen (xdefaultscreen display)) + (root (xdefaultrootwindow display)) + (visual (xdefaultvisual display screen)) + (depth (xdefaultdepth display screen)) + (whitepixel (xwhitepixel display screen))) + (with-slots (window pixmap signal-window thread wm-delete-window + pointer graphics-context) xlib-context + ;; create signal window and window + (setf window + (create-window display root width height 'inputoutput visual + whitepixel + (logior exposuremask + structurenotifymask) + t)) + (setf signal-window + (create-window display root 1 1 'inputonly visual + whitepixel 0 nil)) + ;; create pixmap + (setf pixmap + (xcreatepixmap display window width height depth)) + ;; create graphics-context + (setf graphics-context + (xcreategc display pixmap 0 (null-pointer))) + ;; set size hints on window (most window managers will respect this) + (let ((hints (xallocsizehints))) + (with-foreign-slots ((flags x y min-width min-height + max-width max-height) + hints + xsizehints) + ;; we only set the first four values because old WM's might + ;; get confused if we don't, they should be ignored + (setf flags (logior pminsize pmaxsize) + x 0 + y 0 + (foreign-slot-value hints 'xsizehints 'width) width + (foreign-slot-value hints 'xsizehints 'height) height + min-width width + max-width width + min-height height + max-height height) + (xsetwmnormalhints display window hints) + (xfree hints))) + ;; intern atom for window closing, set protocol on window + (setf wm-delete-window + (xinternatom display "WM_DELETE_WINDOW" 1)) + (with-foreign-object (prot 'atom) + (setf (mem-aref prot 'atom) wm-delete-window) + (xsetwmprotocols display window prot 1)) + ;; store name + (xstorename display window window-name) + ;; create cairo context + (let ((surface (cairo_xlib_surface_create display pixmap visual + width height))) + (setf pointer (cairo_create surface)) + ;; !!! error checking + (cairo_surface_destroy surface)) + ;; map window + (xmapwindow display window) + ;; end of synchronizing + (xsynchronize display 0) + ;; start thread + (setf thread + (start-thread + #'event-loop + (format nil "thread for display ~a" display-name)))))) + ;; return context + xlib-context))) + + +(defun send-message-to-signal-window (xlib-context message) + "Send the desired message to the context window." + (with-slots ((display-pointer display) signal-window) xlib-context + (with-foreign-object (xev :long 24) + (with-foreign-slots + ((type display window message-type format data0) + xev xclientmessageevent) + (setf type 33) ; clientnotify + (setf display display-pointer) + (setf window signal-window) + (setf message-type 0) + (setf format 32) + (setf data0 message) + (xsendevent display-pointer signal-window 0 0 xev)) + (xflush display-pointer)))) + + +(defmethod destroy ((object xlib-context)) + (send-message-to-signal-window object +destroy-message+)) + +(defmethod sync ((object xlib-context)) + (when (zerop (sync-counter object)) + (send-message-to-signal-window object +refresh-message+))) + +(defmethod sync-lock ((object xlib-context)) + (incf (sync-counter object))) + +(defmethod sync-unlock ((object xlib-context)) + (with-slots (sync-counter) object + (when (plusp sync-counter) + (decf sync-counter))) + (sync object)) + +(defmethod sync-reset ((object xlib-context)) + (setf (sync-counter object) 0) + (sync object)) +
Modified: xlib.lisp ============================================================================== --- xlib.lisp (original) +++ xlib.lisp Wed Aug 22 12:13:14 2007 @@ -13,6 +13,8 @@ (defctype drawable xid) (defctype window xid) (defctype pixmap xid) +(defctype cursor xid) +(defctype colormap xid) (defctype graphics-context xid) (defctype visual :pointer) (defctype atom :unsigned-long) @@ -62,7 +64,6 @@ ownergrabbuttonmask 24)
- ;;;; error code handling (defmacro check-status (call) "Check the return calue of call, if nonzero, display an error message." @@ -139,6 +140,60 @@ (border :unsigned-long) (background :unsigned-long))
+(defcfun ("XCreateWindow" xcreatewindow) window + (display display) + (parent window) + (x :int) + (y :int) + (width :unsigned-int) + (height :unsigned-int) + (border-width :unsigned-int) + (depth :int) + (class :unsigned-int) + (visual visual) + (valuemask :unsigned-long) + (attributes :pointer)) + +(defcstruct xsetwindowattributes + (background-pixmap pixmap) + (background-pixel :unsigned-long) + (border-pixmap pixmap) + (border-pixel :unsigned-long) + (bit-gravity :int) + (win-gravity :int) + (backing-store :int) + (backing-planes :unsigned-long) + (backing-pixel :unsigned-long) + (save-under bool) + (event-mask :long) + (do-not-propagate_mask :long) + (override-redirect bool) + (colormap colormap) + (cursor cursor)) + +(define-bitmask-constants + CWBackPixmap 0 + CWBackPixel 1 + CWBorderPixmap 2 + CWBorderPixel 3 + CWBitGravity 4 + CWWinGravity 5 + CWBackingStore 6 + CWBackingPlanes 7 + CWBackingPixel 8 + CWOverrideRedirect 9 + CWSaveUnder 10 + CWEventMask 11 + CWDontPropagate 12 + CWColormap 13 + CWCursor 14) + +(defcfun ("XChangeWindowAttributes" xchangewindowattributes) :int + (display display) + (window window) + (valuemask :unsigned-long) + (attributes :pointer)) + (defcfun ("XDestroyWindow" xdestroywindow) :int (display display) (window window)) @@ -170,12 +225,23 @@ (height :unsigned-int) (destination-x :int) (destination-y :int)) -
+(defcfun ("XSetGraphicsExposures" xsetgraphicsexposures) :int + (display display) + (graphics-context graphics-context) + (graphics-exposures bool)) + + ;; synchronization & threads
(defcfun ("XInitThreads" xinitthreads) :int)
+(defcfun ("XLockDisplay" xlockdisplay) :int + (display display)) + +(defcfun ("XUnlockDisplay" xunlockdisplay) :int + (display display)) + (defcfun ("XSynchronize" xsynchronize) :int (display display) (onoff :int)) @@ -243,6 +309,14 @@ ;; we only use first field, union of message data is not included (data0 :unsigned-long))
+(defcstruct xvisibilityevent + (type :int) + (serial :unsigned-long) + (send-event bool) + (display display) + (window window) + (state :int)) + (defcfun ("XNextEvent" xnextevent) :int (display display) (event-return :pointer))