Author: tpapp
Date: Thu Jun 21 05:07:42 2007
New Revision: 6
Added:
README.xlib-context
cffi/
cffi/Makefile
cffi/xlib-context.c
tutorial/xlib-example.lisp
xlib-context.lisp
Modified:
Makefile
cairo.lisp
cl-cairo2.asd
context.lisp
surface.lisp
text.lisp
transformations.lisp
tutorial/example.lisp
tutorial/tutorial.tex
Log:
added svg and xlib support
Modified: Makefile
==============================================================================
--- Makefile (original)
+++ Makefile Thu Jun 21 05:07:42 2007
@@ -9,4 +9,4 @@
mkdir /tmp/cl-cairo2-latest
cp * -R /tmp/cl-cairo2-latest
tar -cvzf /tmp/cl-cairo2-latest.tar.gz -C /tmp cl-cairo2-latest
- gpg -b -a /tmp/cl-cairo2-latest.tar.gz
\ No newline at end of file
+ gpg -b -a /tmp/cl-cairo2-latest.tar.gz
Added: README.xlib-context
==============================================================================
--- (empty file)
+++ README.xlib-context Thu Jun 21 05:07:42 2007
@@ -0,0 +1,14 @@
+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). For the latter, it uses the XDamage
+extension.
+
+The shared library that contains the glue code has to be compiled and
+installed manually in /usr/local/lib/. Running
+
+make install
+
+as root from the cffi subdirectory should take care of this. You need
+to have the XDamage library and header files (eg package
+libxdamage-dev on Debian) installed.
Modified: cairo.lisp
==============================================================================
--- cairo.lisp (original)
+++ cairo.lisp Thu Jun 21 05:07:42 2007
@@ -18,13 +18,15 @@
;;;; commonly used macros/functions
;;;;
-(defun prepend-intern (prefix name &optional (replace-dash t))
+(defun prepend-intern (prefix name &key (replace-dash t) (suffix ""))
"Create and intern symbol PREFIXNAME from NAME, optionally
- replacing dashes in name. PREFIX is converted to upper case."
+ replacing dashes in name. PREFIX is converted to upper case.
+ If given, suffix is appended at the end."
(let ((name-as-string (symbol-name name)))
(when replace-dash
(setf name-as-string (substitute #\_ #\- name-as-string)))
- (intern (concatenate 'string (string-upcase prefix) name-as-string))))
+ (intern (concatenate 'string (string-upcase prefix)
+ name-as-string (string-upcase suffix)))))
(defun copy-double-vector-to-pointer (vector pointer)
"Copies vector of double-floats to a memory location."
Added: cffi/Makefile
==============================================================================
--- (empty file)
+++ cffi/Makefile Thu Jun 21 05:07:42 2007
@@ -0,0 +1,10 @@
+INSTALL=/usr/local/lib
+
+install: xlib-context.so
+ cp xlib-context.so $(INSTALL)
+
+xlib-context.so: xlib-context.o
+ gcc -shared -o xlib-context.so xlib-context.o -lXdamage
+
+xlib-context.o: xlib-context.c
+ gcc -c -fPIC xlib-context.c -I /usr/include/cairo
Added: cffi/xlib-context.c
==============================================================================
--- (empty file)
+++ cffi/xlib-context.c Thu Jun 21 05:07:42 2007
@@ -0,0 +1,210 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xdamage.h>
+#include <cairo-xlib.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/* #define DEBUG */
+
+/* error codes */
+#define ERROR_OUTOFMEMORY 1
+#define ERROR_OPENDISPLAY 2
+#define ERROR_XDAMAGEMISSING 3
+
+#define RETURN_ERROR(err) { free(xc); return (err); }
+
+/* structur */
+typedef struct {
+ unsigned int width;
+ unsigned int height;
+ Display *display;
+ int screen;
+ Window window;
+ Pixmap pixmap;
+ GC gc;
+} xlib_context_data;
+
+/************************************************************************
+ * create_xlib_context -- create an X11 window that acts as a context *
+ * *
+ * Parameters *
+ * display_name -- name of the X11 display, eg ":0" *
+ * window_name -- name of the X11 window *
+ * width, height -- width and height in pixels *
+ * xc_pointer -- see below *
+ * context_pointer -- points to the location which contains a pointer *
+ * to a cairo_t structure *
+ * *
+ * Notes *
+ * This function is meant to be called in a separate thread. If *
+ * successful, it allocates an xlib_context_data structure and puts *
+ * the pointer in *xc_pointer, and also initializes its contents with *
+ * the relevant data. A cairo_t context is created and assigned to *
+ * *context_pointer. *
+ * *
+ * The window has fixed size (width x height). The context is *
+ * attached to an X11 pixmap, which is copied onto the screen when *
+ * 1) X asks for the window contents to be redrawn, 2) the pixmap is *
+ * changed by cairo. The latter is handled via the X Damage *
+ * extension, which needs to be available. *
+ * *
+ * *
+ * *
+ ************************************************************************/
+int
+create_xlib_context(char *display_name,
+ char *window_name,
+ unsigned int width,
+ unsigned int height,
+ xlib_context_data **xc_pointer,
+ cairo_t **context_pointer)
+{
+ Window root; /* root window */
+ Visual *visual; /* visual */
+ int depth; /* depth */
+ XEvent ev; /* event */
+ cairo_surface_t *surface; /* surface */
+ Atom prots[1];
+ int damage_event, damage_error; /* for querying extension */
+ Damage damage; /* damage notification handler */
+ XDamageNotifyEvent *dev;
+ cairo_t *context;
+ XSizeHints *hints;
+
+ xlib_context_data *xc = (void *)NULL;
+ /* initialize pointer with NULL */
+ *xc_pointer = NULL;
+ /* allocate xlib_context */
+ xc = malloc( sizeof(xlib_context_data) );
+ if (!xc)
+ RETURN_ERROR(ERROR_OUTOFMEMORY);
+ /* open display, get screen, root, visual, and depth */
+ xc->display = XOpenDisplay(display_name);
+ if (!xc->display)
+ RETURN_ERROR( ERROR_OPENDISPLAY );
+ xc->screen = DefaultScreen(xc->display);
+ root = RootWindow(xc->display, xc->screen);
+ visual = DefaultVisual(xc->display, xc->screen);
+ depth = DefaultDepth(xc->display, xc->screen);
+ /* check X damage extension */
+ if (!XDamageQueryExtension( xc->display, &damage_event, &damage_error ))
+ RETURN_ERROR( ERROR_XDAMAGEMISSING );
+ /* create window and pixmap */
+ xc->window = XCreateSimpleWindow(xc->display, root, 0, 0, width, height, 0, 0,
+ WhitePixel (xc->display, xc->screen));
+ xc->pixmap = XCreatePixmap(xc->display, xc->window, width, height, depth);
+ /* size hints */
+ hints = XAllocSizeHints();
+ hints->min_width = width;
+ hints->min_height = height;
+ hints->max_width = width;
+ hints->max_height = height;
+ hints->flags = PMinSize | PMaxSize;
+ XSetWMNormalHints(xc->display, xc->window, hints);
+ XFree(hints);
+ /* window name */
+ XStoreName(xc->display,xc->window,window_name);
+ /* graphics context */
+ xc->gc = XCreateGC(xc->display, xc->pixmap, 0, 0);
+ /* setup damage notification */
+ damage = XDamageCreate( xc->display, xc->pixmap, XDamageReportNonEmpty );
+ /* select events, map window */
+ XSelectInput( xc->display, xc->window,
+ ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
+ SubstructureNotifyMask );
+ /* handle window closing */
+ prots[0] = XInternAtom(xc->display, "WM_DELETE_WINDOW", FALSE);
+ XSetWMProtocols(xc->display, xc->window, prots, 1);
+ /* map window */
+ XMapWindow(xc->display, xc->window);
+ /* allocate structure, create cairo surface */
+ surface = cairo_xlib_surface_create( xc->display, xc->pixmap, visual,
+ width, height );
+ context = cairo_create(surface);
+ cairo_surface_destroy(surface);
+ /* set pointers before we start loop */
+#ifdef DEBUG
+ FILE *debug = fopen("/tmp/debug","w");
+ fprintf(debug, "before setting: xc_pointer=%p *xc_pointer=%p context_pointer=%p *context_pointer=%p\n", xc_pointer, *xc_pointer, context_pointer, *context_pointer);
+#endif /* DEBUG */
+ *xc_pointer = xc;
+ *context_pointer = context;
+#ifdef DEBUG
+ fprintf(debug, "xc=%p=%p context=%p=%p\n", xc, *xc_pointer,
+ context, *context_pointer);
+ fprintf(debug, "damage_event=%d, XDamageNotify=%d\n", damage_event, XDamageNotify);
+ fflush(debug);
+#endif /* DEBUG */
+ /* main loop */
+ for (;;) {
+ XNextEvent(xc->display, &ev);
+#ifdef DEBUG
+ fprintf(debug, "event of type %d\n", ev.type);
+ fflush(debug);
+#endif /* DEBUG */
+ if (ev.type == (damage_event + XDamageNotify)) {
+ dev = (XDamageNotifyEvent *) &ev;
+#ifdef DEBUG
+ fprintf(debug, "damage event received\n");
+ fflush(debug);
+#endif /* DEBUG */
+ /* !!!! should only update the rectangle */
+ XCopyArea(xc->display, xc->pixmap, xc->window, xc->gc, 0, 0,
+ width, height, 0, 0);
+ XDamageSubtract( xc->display, dev->damage, None, None );
+ } else {
+ switch (ev.type) {
+ case Expose:
+#ifdef DEBUG
+ fprintf(debug, "expose event received\n");
+ fflush(debug);
+#endif /* DEBUG */
+ if (ev.xexpose.count > 0)
+ break;
+ /* !!!! should only update the rectangle */
+ XCopyArea(xc->display, xc->pixmap, xc->window, xc->gc, 0, 0,
+ width, height, 0, 0);
+ break;
+ case DestroyNotify:
+ case ClientMessage:
+ /* cleanup & close */
+ XDamageDestroy( xc->display, damage );
+ XDestroyWindow( xc->display, xc->window );
+ XCloseDisplay( xc->display );
+#ifdef DEBUG
+ fprintf(debug,"cleaning up\n");
+ fclose(debug);
+#endif /* DEBUG */
+ free(xc);
+ return(0);
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/************************************************************************
+ * close_xlib_context -- send a destroy even to the window *
+ * *
+ * Note *
+ * We just send the event, all the cleanup will be done by the *
+ * event handler loop. *
+ ************************************************************************/
+void close_xlib_context(xlib_context_data *xc)
+{
+ XEvent ev;
+ ev.type = DestroyNotify;
+ XSendEvent(xc->display, xc->window, FALSE, 0, &ev);
+ XFlush(xc->display);
+}
+
+void sync_xlib(xlib_context_data *xc)
+{
+ XFlush(xc->display);
+}
Modified: cl-cairo2.asd
==============================================================================
--- cl-cairo2.asd (original)
+++ cl-cairo2.asd Thu Jun 21 05:07:42 2007
@@ -1,6 +1,6 @@
(defsystem cl-cairo2
:description "Cairo 1.4 bindings"
- :version "0.1"
+ :version "0.2"
:author "Tamas K Papp"
:license "GPL"
:components ((:file "package")
@@ -11,5 +11,8 @@
(:file "context" :depends-on ("surface" "cl-cairo2-swig"))
(:file "path" :depends-on ("context")) ; "cl-cairo2-swig"))
(:file "text" :depends-on ("context")) ; "cl-cairo2-swig"))
- (:file "transformations" :depends-on ("context"))) ; "cl-cairo2-swig")))
+ (:file "transformations" :depends-on ("context")) ; "cl-cairo2-swig")))
+ (:file "xlib-context" :depends-on ("context")
+ :in-order-to ((load-op (feature :unix))
+ (compile-op (feature :unix)))))
:depends-on (:cffi))
Modified: context.lisp
==============================================================================
--- context.lisp (original)
+++ context.lisp Thu Jun 21 05:07:42 2007
@@ -38,6 +38,10 @@
(cairo_destroy pointer)
(setf pointer nil))))
+(defmethod sync ((object context))
+ ;; most contexts don't need syncing
+ )
+
;;;;
;;;; default context and convenience macros
;;;;
@@ -66,6 +70,16 @@
(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))))
+
(defmacro define-flexible ((name pointer &rest args) &body body)
"Like define-with-default context, but with arbitrary body,
pointer will point to the context."
@@ -85,20 +99,23 @@
(defmacro define-get-set (property)
"Define set-property and get-property functions."
`(progn
- (define-with-default-context ,(prepend-intern "get-" property nil))
- (define-with-default-context ,(prepend-intern "set-" property nil) ,property)))
+ (define-with-default-context ,(prepend-intern "get-" property :replace-dash nil))
+ (define-with-default-context ,(prepend-intern "set-" property :replace-dash nil)
+ ,property)))
(defmacro define-get-set-using-table (property)
"Define set-property and get-property functions, where property
is looked up in table-property for conversion into Cairo's enum
constants."
`(progn
- (define-flexible (,(prepend-intern "get-" property nil) pointer)
- (lookup-cairo-enum (,(prepend-intern "cairo_get_" property) pointer)
- ,(prepend-intern "table-" property nil)))
- (define-flexible (,(prepend-intern "set-" property nil) pointer ,property)
- (,(prepend-intern "cairo_set_" property) pointer
- (lookup-enum ,property ,(prepend-intern "table-" property nil))))))
+ (define-flexible (,(prepend-intern "get-" property :replace-dash nil) pointer)
+ (lookup-cairo-enum (,(prepend-intern "cairo_get_" property) pointer)
+ ,(prepend-intern "table-" property :replace-dash nil)))
+ (define-flexible (,(prepend-intern "set-" property :replace-dash nil)
+ pointer ,property)
+ (,(prepend-intern "cairo_set_" property) pointer
+ (lookup-enum ,property ,(prepend-intern "table-"
+ property :replace-dash nil))))))
;;;;
;;;; simple functions using context
@@ -116,14 +133,15 @@
(clip)
(clip-preserve)
(reset-clip)
- (fill-preserve)
- (paint)
- (paint-with-alpha alpha)
- (stroke)
- (stroke-preserve)
(copy-page)
(show-page))
+(define-with-default-context-sync fill-preserve)
+(define-with-default-context-sync paint)
+(define-with-default-context-sync paint-with-alpha alpha)
+(define-with-default-context-sync stroke)
+(define-with-default-context-sync stroke-preserve)
+
;;;;
;;;; functions that get/set a property without any conversion
;;;;
@@ -142,11 +160,11 @@
(define-get-set-using-table line-join)
(define-get-set-using-table operator)
-
;; fill-path: it should simply be fill, but it is renamed so it does
;; not clash with cl-user:fill
(define-flexible (fill-path pointer)
- (cairo_fill pointer))
+ (cairo_fill pointer)
+ (sync context))
(define-flexible (set-dash pointer offset dashes)
(let ((num-dashes (length dashes)))
Modified: surface.lisp
==============================================================================
--- surface.lisp (original)
+++ surface.lisp Thu Jun 21 05:07:42 2007
@@ -13,7 +13,7 @@
;;;; class surface
;;;;
-(defclass surface () ((pointer :initform nil)))
+(defclass surface () ((pointer :initarg :pointer :initform nil)))
(defmacro with-alive-surface ((surface pointer) &body body)
"Execute body with pointer pointing to cairo surface, if nil,
@@ -52,46 +52,54 @@
(setf pointer nil)))
;;;;
-;;;; PDF surface
+;;;; Macros to create surfaces (that are written into files) and
+;;;; direct creation of contexts for these surfaces.
;;;;
-(export
- (defun create-pdf-surface (filename width-in-points height-in-points)
- (new-surface-with-check
- (cairo_pdf_surface_create filename
- width-in-points
- height-in-points))))
+(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)))))
+
+(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
+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))))
-(export
- (defun create-pdf-context (filename width-in-points height-in-points)
- "Create a surface, then a context for a pdf file, then
- destroy (dereference) the surface. The user only needs to
- destroy the context when done."
- (let* ((surface (create-pdf-surface filename width-in-points height-in-points))
- (context (create-context surface)))
- (destroy surface)
- context)))
+
+;;;;
+;;;; PDF surface
+;;;;
+
+(define-create-surface pdf)
+(define-create-context pdf)
;;;;
;;;; PostScript surface
;;;;
-(export
- (defun create-ps-surface (filename width-in-points height-in-points)
- (new-surface-with-check
- (cairo_ps_surface_create filename
- width-in-points
- height-in-points))))
+(define-create-surface ps)
+(define-create-context ps)
-(export
- (defun create-ps-context (filename width-in-points height-in-points)
- "Create a surface, then a context for a postscript file, then
- destroy (dereference) the surface. The user only needs to
- destroy the context when done."
- (let* ((surface (create-ps-surface filename width-in-points height-in-points))
- (context (create-context surface)))
- (destroy surface)
- context)))
+;;;;
+;;;; SVG surface
+;;;;
+
+(define-create-surface svg)
+(define-create-context svg)
;;;;
;;;; image surface
Modified: text.lisp
==============================================================================
--- text.lisp (original)
+++ text.lisp Thu Jun 21 05:07:42 2007
@@ -37,4 +37,4 @@
extents-pointer cairo_text_extents_t)
(values x_bearing y_bearing width height x_advance y_advance))))
-(define-with-default-context show-text text)
+(define-with-default-context-sync show-text text)
Modified: transformations.lisp
==============================================================================
--- transformations.lisp (original)
+++ transformations.lisp Thu Jun 21 05:07:42 2007
@@ -123,7 +123,7 @@
"Define a matrix initializer function with args, which returns the
new matrix."
`(export
- (defun ,(prepend-intern "trans-matrix-init-" name nil) ,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
@@ -138,7 +138,7 @@
"Define a matrix transformation function with matrix and args,
which returns the new matrix."
`(export
- (defun ,(prepend-intern "trans-matrix-init-" name nil) (matrix ,@args)
+ (defun ,(prepend-intern "trans-matrix-init-" name :replace-dash nil) (matrix ,@args)
(with-trans-matrix-in-out matrix matrix-pointer
(,(prepend-intern "cairo_matrix_" name)
matrix-pointer
Modified: tutorial/example.lisp
==============================================================================
--- tutorial/example.lisp (original)
+++ tutorial/example.lisp Thu Jun 21 05:07:42 2007
@@ -6,7 +6,29 @@
(in-package :cairo-example)
+;;;;
+;;;; short example for the tutorial
+;;;;
+
+(defparameter *surface* (create-ps-surface "example.ps" 200 100))
+(setf *context* (create-context *surface*))
+(destroy *surface*)
+;; clear the whole canvas with blue
+(rectangle 0 0 200 100)
+(set-source-rgb 0.2 0.2 1)
+(fill-path)
+;; draw a white diagonal line
+(move-to 200 0)
+(line-to 0 100)
+(set-source-rgb 1 1 1)
+(set-line-width 5)
+(stroke)
+;; destroy context, this also destroys the surface and closes the file
+(destroy *context*)
+
+;;;;
;;;; helper functions
+;;;;
(defun show-text-aligned (text x y &optional (x-align 0.5) (y-align 0.5)
(context *context*))
@@ -150,25 +172,3 @@
(scale scaling scaling) ; scale
(rotate (deg-to-rad (- (random (* 2 max-angle)) max-angle 180))) ; rotate
(heart (+ 0.1 (random 0.7))))))
-
-
-;;;;
-;;;; short example for the tutorial
-;;;;
-
-(defparameter *surface* (create-ps-surface "example.ps" 200 100))
-(setf *context* (create-context *surface*))
-(destroy *surface*)
-;; clear the whole canvas with blue
-(rectangle 0 0 200 100)
-(set-source-rgb 0.2 0.2 1)
-(fill-path)
-;; draw a white diagonal line
-(move-to 0 0)
-(line-to 200 100)
-(set-source-rgb 1 1 1)
-(set-line-width 5)
-(stroke)
-;; destroy context, this also destroys the surface and closes the file
-(destroy *context*)
-
Modified: tutorial/tutorial.tex
==============================================================================
--- tutorial/tutorial.tex (original)
+++ tutorial/tutorial.tex Thu Jun 21 05:07:42 2007
@@ -135,7 +135,7 @@
on a blue background, using a Postscript file -- the result is shown
in Figure~\ref{fig:example}.
-\lstinputlisting[firstline=159,lastline=173]{example.lisp}
+\lstinputlisting[firstline=13,lastline=27]{example.lisp}
\begin{figure}[htbp]
\centering
@@ -223,9 +223,12 @@
\lstinline!new-surface-with-check! makes a new surface object from a
pointer, checking its status first.
-Currently, only Postscript, PDF and image surfaces (which can be
+Currently, only Postscript, PDF, SVG and image surfaces (which can be
written to PNG files) are supported.
+Drawing in X11 windows is implemented using the
+\lstinline!xlib-context! class --- see Section~\ref{sec:xlib-context}
+for more information.
\subsection{Contexts}
\label{sec:contexts}
@@ -318,6 +321,35 @@
with \lstinline!trans-matrix-!, and other a few other functions have
been renamed to avoid conflicts with linear algebra packages.
+\subsection{Xlib Contexts}
+\label{sec:xlib-context}
+
+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). For the latter, it uses the XDamage
+extension.
+
+The X11 event loop runs in a separate thread, so you need a Lisp
+implementation that supports threads. The surface is not exposed to
+the user, who is only allowed to see the context. This makes memory
+management and proper cleanup easier.
+
+\textbf{Important:} before proceeding, make sure that you read
+\verb!README.xlib-context! on how to install the shared library with
+the necessary code.
+
+You can create Xlib contexts with
+\begin{lstlisting}
+ (create-xlib-context width height)
+\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.
+
+Example code can be found in \verb!tutorial/xlib-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.
\subsection{To Do}
\label{sec:todo}
@@ -325,9 +357,7 @@
The list below reflects my priorities. If you need something, please
let me know.
\begin{itemize}
-\item X-Window surface
\item patterns (should be easy)
-\item SVG surfaces (should be quick \& easy)
\item Win32 surfaces (I can't do it, I don't use Windows)
\item CLOS integration for fonts (as suggested
\href{http://www.cairographics.org/manual/bindings-fonts.html}{here})
@@ -370,18 +400,6 @@
\label{fig:hearts}
\end{figure}
-
-
-
-
-
-
-
-
-
-% \bibliographystyle{apalike}
-% \bibliography{/home/tpapp/doc/general.bib}
-
\end{document}
%%% Local Variables:
Added: tutorial/xlib-example.lisp
==============================================================================
--- (empty file)
+++ tutorial/xlib-example.lisp Thu Jun 21 05:07:42 2007
@@ -0,0 +1,57 @@
+(asdf:operate 'asdf:load-op :cl-cairo2)
+
+;;;; Make a test package
+(defpackage :cairo-xlib-example
+ (:use :common-lisp :cl-cairo2))
+
+(in-package :cairo-xlib-example)
+
+(let ((width 400)
+ (height 300))
+ (setf *context* (create-xlib-context width height))
+ ;; clear the whole canvas with blue
+ (rectangle 0 0 width height)
+ (set-source-rgb 0.2 0.2 0.5)
+ (fill-path)
+ ;; draw a white diagonal line
+ (move-to width 0)
+ (line-to 0 height)
+ (set-source-rgb 1 1 1)
+ (set-line-width 5)
+ (stroke)
+ ;; draw a green diagonal line
+ (move-to 0 0)
+ (line-to width height)
+ (set-source-rgb 0 1 0)
+ (set-line-width 5)
+ (stroke))
+;; need to close window manually
+
+
+(defun random-square (alpha)
+ "Draw a blue rectangle with fixed size and the given transparency alpha."
+ (move-to 1 1)
+ (line-to -1 1)
+ (line-to -1 -1)
+ (line-to 1 -1)
+ (close-path)
+ (set-source-rgba 0 0 1 alpha)
+ (fill-path))
+
+(defparameter width 800)
+(defparameter height 600)
+(defparameter max-angle 90d0)
+(setf *context* (create-xlib-context width height))
+;; fill with white
+(rectangle 0 0 width height)
+(set-source-rgb 1 1 1)
+(fill-path)
+;; draw the rectangles
+(dotimes (i 500)
+ (let ((scaling (+ 5d0 (random 40d0))))
+ (reset-matrix) ; reset matrix
+ (translate (random width) (random height)) ; move the origin
+ (scale scaling scaling) ; scale
+ (rotate (deg-to-rad (random max-angle))) ; rotate
+ (random-square (+ 0.1 (random 0.4)))))
+;; need to close window manually
Added: xlib-context.lisp
==============================================================================
--- (empty file)
+++ xlib-context.lisp Thu Jun 21 05:07:42 2007
@@ -0,0 +1,80 @@
+(in-package :cl-cairo2)
+
+;;;;
+;;;; a little glue code loaded as a shared library
+;;;;
+
+;; modify path if needed
+(load-foreign-library "/usr/local/lib/xlib-context.so")
+
+;; 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))
+
+;;;;
+;;;; CFFI interface
+;;;;
+
+(cffi:defcfun ("create_xlib_context" create_xlib_context) :int
+ (display_name :string)
+ (window_name :string)
+ (width :unsigned-int)
+ (height :unsigned-int)
+ (xc-pointer :pointer)
+ (context-pointer :pointer))
+
+(cffi:defcfun ("close_xlib_context" close_xlib_context) :void
+ (xc-pointer :pointer))
+
+(cffi:defcfun ("sync_xlib" sync_xlib) :void
+ (xc-pointer :pointer))
+
+
+;;;; xlib-context class
+
+(defclass xlib-context (context) (xc thread))
+
+(defun create-xlib-context (width height &key (display-name ":0")
+ (window-name "cl-cairo2"))
+ "Create a cairo context that draws to an X11 window of specified size."
+ (let ((xlc (make-instance 'xlib-context))
+ (xc-pointer (foreign-alloc :pointer))
+ (context-pointer (foreign-alloc :pointer)))
+ ;; we will detect changes with null-pointer-p
+ (setf (mem-ref xc-pointer :pointer) (null-pointer))
+ (setf (mem-ref context-pointer :pointer) (null-pointer))
+ ;; start and save thread
+ (setf (slot-value xlc 'thread)
+ (start-thread
+ (lambda ()
+ (let ((err (create_xlib_context display-name window-name width height
+ xc-pointer context-pointer)))
+ (unless (zerop err)
+ (error "Error ~a when creating xlib-context." err)))
+ ;; set slots to nil when done
+ (with-slots (xc pointer) xlc
+ (setf xc nil)
+ (setf pointer nil)))
+ "cl-cairo2"))
+ ;; extract slots
+ (do () ; wait for thread to fill pointers
+ ((not (null-pointer-p (mem-ref context-pointer :pointer)))))
+ (setf (slot-value xlc 'xc) (mem-ref xc-pointer :pointer))
+ (setf (slot-value xlc 'pointer) (mem-ref context-pointer :pointer))
+ (foreign-free xc-pointer)
+ (foreign-free context-pointer)
+ xlc))
+
+(export 'create-xlib-context)
+
+(defmethod destroy ((object xlib-context))
+ (close_xlib_context (slot-value object 'xc)))
+
+(defmethod sync ((object xlib-context))
+ (sync_xlib (slot-value object 'xc)))