cl-cairo with X11 examples with mouse events handling

View: New views
5 Messages — Rating Filter:   Alert me  

cl-cairo with X11 examples with mouse events handling

by Taoufik Dachraoui :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear,

I am new to cl-cairo2 and would like to know how to write an X11  
application using CL-CAIRO2

I installed cl-cairo2 on openmcl (Clozure CL) and run the tests (e.g  
tutorial/x11-example.lisp)

The X11 tests works fine but I could not figure out how to use and  
handle mouse events (e.g. button-press, exposure,...)

I will appreciate if someone can send me an example of how to create a  
X11 window and to define lisp functions to handle mouse and keyboard  
events.

I prefer to use X window with cairo and not GTK with cairo.

Kind regards
Taoufik





_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo

Re: cl-cairo with X11 examples with mouse events handling

by Bill Spitzak-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I fully agree that such an example is very much needed. One of the big
points in favor of Cairo is that it is not tied to a toolkit, but all
the examples being tied to a toolkit mean that is not really true...

As FLTK can draw using Cairo, I think I may be able to extract the
necessary information and make a program that works that does not use
GTK (or FLTK). I hope this will be a useful example.

Taoufik Dachraoui wrote:

> Dear,
>
> I am new to cl-cairo2 and would like to know how to write an X11  
> application using CL-CAIRO2
>
> I installed cl-cairo2 on openmcl (Clozure CL) and run the tests (e.g  
> tutorial/x11-example.lisp)
>
> The X11 tests works fine but I could not figure out how to use and  
> handle mouse events (e.g. button-press, exposure,...)
>
> I will appreciate if someone can send me an example of how to create a  
> X11 window and to define lisp functions to handle mouse and keyboard  
> events.
>
> I prefer to use X window with cairo and not GTK with cairo.
>
> Kind regards
> Taoufik
>
>
>
>
>
> _______________________________________________
> cairo mailing list
> cairo@...
> http://lists.cairographics.org/mailman/listinfo/cairo
_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo

Re: cl-cairo with X11 examples with mouse events handling

by Taoufik Dachraoui :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I managed to write the following (this is not complete but I know how  
to handle xevents)

load the file and do >(cl-cairo2:runit)
then press mouse button on windows

 >>>> xclui.lisp
(in-package :cl-cairo2)

(defun my-create-window (display parent x y width height visual  
background-pixel
                      event-mask &optional (backing-store t))
   "Create an x11 window with the given attributes."
   ;; 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 x y width height
                   0 ; zero border width
                   0 ; depth - copy from parent
                   1 ;inputoutput ; class
                   visual
                   (logior cwbackpixel cwbackingstore cweventmask)
                   attributes)))

(defclass my-xlib-context (context)
   ((display :initarg :display)
    (window :initarg :window)
    (xlib-context :initarg :xlib-context :accessor xlib-context)
    (wm-delete-window :initarg :wm-delete-window :accessor wm-delete-
window)
    (width :initarg :width)
    (height :initarg :height)))

;create cairo context; link X Window to cairo surface
(defun my-create-context (display visual window width height)
   ;(xsynchronize display 1)
   (let* ((xlib-surface (cairo_xlib_surface_create display window visual
                                                  width height))
         (xlib-context (cairo_create xlib-surface)))
     (cairo_surface_destroy xlib-surface)
     (let*  ((surface (cairo_image_surface_create :CAIRO_FORMAT_RGB24
                                                  width height))
             (*context* (make-instance 'my-xlib-context :width  
width :height height :pixel-based-p t
                          :display display :window window :xlib-
context xlib-context
                          :wm-delete-window (xinternatom display  
"WM_DELETE_WINDOW" 1)
                          :pointer (cairo_create surface))))
       (with-foreign-object (prot 'xatom)
         (setf (mem-aref prot 'xatom) (wm-delete-window *context*))
         (xsetwmprotocols display window prot 1))
       (cairo_set_source_surface xlib-context surface 0 0) ; link  
window to surface
       (cairo_surface_destroy surface)
       ;(xsynchronize display 0)
       *context*)))

(defun destroy-context (context)
   (with-slots (display window pointer
                       xlib-context)
       context
     (force-output t)
     (let ((saved-pointer pointer))
       (setf pointer nil) ; invalidate first so it can't be used
       (cairo_destroy saved-pointer))
     (cairo_destroy xlib-context)
     ;; !! free xlib-context, surface
     (xdestroywindow display window)))


(defmacro application (x y width height window-name &rest body)
   `(let* ((*display* (xopendisplay ":0.0"))
           (*screen* (xdefaultscreen *display*))
           (*root* (xdefaultrootwindow *display*))
           (*visual* (xdefaultvisual *display* *screen*))
           (*depth* (xdefaultdepth *display* *screen*))
           (*whitepixel* (xwhitepixel *display* *screen*))
           (*window*
                   (my-create-window *display*  
*root* ,x ,y ,width ,height *visual*
                                  *whitepixel*
                                  (logior exposuremask buttonpressmask
                                          structurenotifymask)
                                  t))
          (*parent* *window*)
           (*context* (my-create-context *display* *visual*  
*window* ,width ,height))
          (*width* ,width) (*height* ,height)
          (*all-contexts* nil))
      (set-window-size-hints *display*  
*window* ,width ,width ,height ,height)
      (xstorename *display* *window* ,window-name)
      (xmapwindow *display* *window*)
      (progn
        ,@body)
      (labels
          (;; Repaint the xlib context with the image surface
               ;; (previously set as source during initialization.
               (refresh ()
                 (dolist (c (reverse *all-contexts*)) (cairo_paint  
(xlib-context c)))
                (cairo_paint (xlib-context *context*)))
               ;; The main event loop, started as a separate thread
               ;; when initialization is complete.  The main thread is
               ;; supposed to communicate with this one via X signals
               ;; using an unmapped InputOnly window (see
               ;; send-message-to-signal-window).
               (event-loop ()
                   (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)
                          (format t "event ~A window ~A *window* ~A~%" type window *window*)
                           ;; action based on event type
                           (cond
                            ;; expose events
                            ((and (= type 12) (= window *window*))  
(refresh))
                            ;; clientnotify event
                            ((= type 33)
                             (with-foreign-slots ((message-type data0)  
xev
                                                  xclientmessageevent)
                               (cond
                                ((or (= data0 +destroy-message+)
                                     (and (= window *window*)
                                          (= message-type wm-protocols)
                                          (= data0 (wm-delete-window  
*context*))))
                                 (setf got-close-signal t))
                                ((= data0 +refresh-message+)
                                 (refresh)))))))) ))
                 ;; close down everything
                (dolist (c (reverse *all-contexts*)) (destroy-context c))
                (xclosedisplay *display*)))
        (event-loop))))

(defmacro view (x y width height  &rest body)
   `(let* ((*window*
           (my-create-window *display* *parent* ,x ,y ,width ,height *visual*
                             *whitepixel*
                             (logior exposuremask buttonpressmask
                                     structurenotifymask)
                             t))
          (*context* (my-create-context *display* *visual*  
*window* ,width ,height))
          (*width* ,width) (*height* ,height)
          (*parent* *window*))
      (push *context* *all-contexts*)
      (xmapwindow *display* *window*)
      (progn
       ,@body)))


;
;
; view body fires at exposue event; draw

(defun x-on-window (width height)
       (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))

(defun runit ()
   (application 100 100 400 400 "test"
               (view 0 0 100 50
                     (rectangle 0 0 *width* *height*)
                     (set-source-rgb 0.2 0.2 1.0)
                     (fill-path)
                     (move-to 20 20)
                     (set-source-rgb 1.0 1.0 1.0)
                     (show-text "test"))
               (view 100 100 100 100 (x-on-window *width* *height*))
               (view 200 200 150 150 (x-on-window *width* *height*))))

(export 'runit)

 >>> end file

Regards
Taoufik

On Oct 12, 2009, at 6:53 PM, Bill Spitzak wrote:

>
> I fully agree that such an example is very much needed. One of the  
> big points in favor of Cairo is that it is not tied to a toolkit,  
> but all the examples being tied to a toolkit mean that is not really  
> true...
>
> As FLTK can draw using Cairo, I think I may be able to extract the  
> necessary information and make a program that works that does not  
> use GTK (or FLTK). I hope this will be a useful example.
>
> Taoufik Dachraoui wrote:
>> Dear,
>> I am new to cl-cairo2 and would like to know how to write an X11  
>> application using CL-CAIRO2
>> I installed cl-cairo2 on openmcl (Clozure CL) and run the tests  
>> (e.g  tutorial/x11-example.lisp)
>> The X11 tests works fine but I could not figure out how to use and  
>> handle mouse events (e.g. button-press, exposure,...)
>> I will appreciate if someone can send me an example of how to  
>> create a  X11 window and to define lisp functions to handle mouse  
>> and keyboard  events.
>> I prefer to use X window with cairo and not GTK with cairo.
>> Kind regards
>> Taoufik
>> _______________________________________________
>> cairo mailing list
>> cairo@...
>> http://lists.cairographics.org/mailman/listinfo/cairo
>



_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo

Re: cl-cairo with X11 examples with mouse events handling

by Bill Spitzak-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This works. The code is extracted from the fltk2.0 source code so it has
been somewhat tested.

   cc x11cairo.c -o x11cairo -lX11 -lcairo

It might be a good idea to add this to the Cairo examples, provided
everybody considers it ok.

Another nice idea would be to make a portable "toy" toolkit that lets
you create windows and draw with Cairo into them, and recompile on
Windows and Linux and maybe OS/X. Sort of like Glut, which I think is
part of the popularity of OpenGL.


/* Program to use Cairo to draw into an X11 window without any other libs */

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <cairo/cairo-xlib.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Produce less verbose message if connection drops: */
static int io_error_handler(Display* d) {
    fprintf(stderr, "X I/O error\n");
    exit(1);
}

/* Makes it continue on X errors rather than exit, and make readable message: */
static int xerror_handler(Display* d, XErrorEvent* e) {
  char buf1[128], buf2[128];
  sprintf(buf1, "XRequest.%d", e->request_code);
  XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
  XGetErrorText(d, e->error_code, buf1, 128);
  fprintf(stderr, "%s: %s 0x%lx\n", buf2, buf1, e->resourceid);
  return 0;
}

static Display* xdisplay = 0;
static int xscreen;
static Atom WM_DELETE_WINDOW;
static Atom WM_PROTOCOLS;
static Atom MOTIF_WM_HINTS;
static Atom XA_TEXT;
static Atom _NET_WM_NAME;
static Atom _NET_WM_ICON_NAME;
static Atom UTF8_STRING;

Display* open_display() {
  if (xdisplay) return xdisplay;

  XSetIOErrorHandler(io_error_handler);
  XSetErrorHandler(xerror_handler);

  xdisplay = XOpenDisplay(0);
  xscreen = DefaultScreen(xdisplay);

  /* Register all the Atoms we need */
#define MAX_ATOMS 30
  Atom* atom_ptr[MAX_ATOMS];
  const char* names[MAX_ATOMS];
  int i = 0;
#define atom(a,b) atom_ptr[i] = &a; names[i] = b; i++
  atom( WM_DELETE_WINDOW , "WM_DELETE_WINDOW");
  atom( WM_PROTOCOLS , "WM_PROTOCOLS");
  atom( MOTIF_WM_HINTS , "_MOTIF_WM_HINTS");
  atom( XA_TEXT , "TEXT");
  atom( _NET_WM_NAME , "_NET_WM_NAME");
  atom( _NET_WM_ICON_NAME , "_NET_WM_ICON_NAME");
  atom( UTF8_STRING , "UTF8_STRING");
#undef atom
  Atom atoms[MAX_ATOMS];
  XInternAtoms(xdisplay, (char**)names, i, 0, atoms);
  for (; i--;) *atom_ptr[i] = atoms[i];

  return xdisplay;
}

Window make_window(const char* name, int W, int H) {

    open_display();

    XSetWindowAttributes attr;
    attr.border_pixel = 0;
    attr.bit_gravity = StaticGravity;
    attr.event_mask =
      ExposureMask | StructureNotifyMask
      | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask
      | ButtonPressMask | ButtonReleaseMask
      | EnterWindowMask | LeaveWindowMask
      | PointerMotionMask;

    Window window =
        XCreateWindow(xdisplay, RootWindow(xdisplay, xscreen),
                      100, 100, W, H,
                      0, /* borderwidth */
                      DefaultDepth(xdisplay, xscreen),
                      InputOutput,
                      DefaultVisual(xdisplay, xscreen),
                      CWBorderPixel|CWEventMask|CWBitGravity,
                      &attr);

    /* Make it resizable */
    XSizeHints hints;
    hints.max_width = 2048;
    hints.max_height = 2048;
    hints.win_gravity = StaticGravity;
    hints.flags = PMaxSize|PWinGravity;
    XSetWMNormalHints(xdisplay, window, &hints);

    /* Makes the close button produce an event */
    XChangeProperty(xdisplay, window, WM_PROTOCOLS,
                    XA_ATOM, 32, 0, (unsigned char*)&WM_DELETE_WINDOW, 1);

    /* Set the name. */
    int l = strlen(name);
    XChangeProperty(xdisplay, window, _NET_WM_NAME,
                    UTF8_STRING, 8, 0, (unsigned char*)name, l);
    /* This is only used by old non-UTF-8 window managers: */
    XChangeProperty(xdisplay, window, XA_WM_NAME,
                    XA_STRING, 8, 0, (unsigned char*)name, l);

    return window;
}

int main(int argc, char** argv) {

    int W = 300;
    int H = 300;
    Window window = make_window(argv[0], W, H);

    cairo_surface_t* surface =
        cairo_xlib_surface_create(xdisplay, window,
                                  DefaultVisual(xdisplay, xscreen),
                                  W, H);
    cairo_t* cr = cairo_create(surface);

    XEvent xevent;

    XMapRaised(xdisplay, window);

    for (;;) {
        XNextEvent(xdisplay, &xevent);
        if (xevent.xany.window != window) {
            printf("Event for a different window\n");
            continue;
        }

        switch (xevent.type) {

        case MappingNotify:
            XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
            break;

        case ClientMessage:
            /* This is what the close-window comes in as */
            if (xevent.xclient.data.l[0] == WM_DELETE_WINDOW)
                return 0;
            break;

        case ConfigureNotify:
        case MapNotify: {
            /* Resize and window-move events. */
            /* event data is useless due to WM bugs, do a query instead */
            XWindowAttributes actual;
            XGetWindowAttributes(xdisplay, window, &actual);
            if (actual.width != W || actual.height != H) {
                W = actual.width; H = actual.height;
                printf("Resize to %d,%d\n", W, H);
                cairo_xlib_surface_set_size(surface, W, H);
            }
            break;};

        case Expose:
        case GraphicsExpose: {
            /* Spew and reset any previous errors */
            cairo_status_t cstatus = cairo_status(cr);
            if (cstatus) {
                printf("Cairo: %s", cairo_status_to_string(cstatus));
                exit(1);
                /* If you want to recover from errors, do this:
                   cairo_destroy(cr);
                   cairo_surface_destroy(surface);
                   Then recreate the cr and surface as shown above
                */
            }
            /* draw something: */
            cairo_set_source_rgb(cr, 1,1,1);
            cairo_rectangle(cr, 0, 0, W, H);
            cairo_fill(cr);
            cairo_set_source_rgb(cr, 1,0,0);
            cairo_rectangle(cr, W/4, H/4, W/2, H/2);
            cairo_fill(cr);
            break;}

        case ButtonPress:
            printf("Button %d pressed at %d %d\n",
                   xevent.xbutton.button, xevent.xbutton.x, xevent.xbutton.y);
            break;

        case MotionNotify:
            if (xevent.xbutton.state & 0x7f00)
                printf("Mouse drag to %d %d\n",
                       xevent.xbutton.x, xevent.xbutton.y);
            break;

        case ButtonRelease:
            printf("Button %d released at %d %d\n",
                   xevent.xbutton.button, xevent.xbutton.x, xevent.xbutton.y);
            break;

        case FocusIn:
            printf("Got keyboard focus\n");
            break;

        case FocusOut:
            printf("Lost keyboard focus\n");
            break;

        case KeyPress: {
            KeySym keysym;
            int length;
            char buffer[20];
            length = XLookupString(&(xevent.xkey), buffer, sizeof(buffer)-1,
                                   &keysym, 0);
            if (length>0 && buffer[0]>=' ' && buffer[0]!=127) {
                buffer[length] = 0;
                printf("'%s' typed on keyboard\n", buffer);
            } else {
                printf("key %#x typed\n", keysym);
            }
            break;}
        }
    }
}


_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo

Self-contained X11 source code to draw with Cairo

by Bill Spitzak-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This is a cleaned up version of my code to use Cairo without using gtk
or another toolkit.

In this version the code you may actually want to alter to change what
the program does has been extracted into functions at the bottom, making
it a lot easier to adapt. This has also been tested and works with pango
drawing (requires pkg-config to compile, however).

Hope some Cairo users enjoy this. Please send any feedback!

Bill Spitzak


/* Program to use Cairo to draw into an X11 window without any other libs.

   To compile:

   cc -o x11cairo x11cairo.c `pkg-config ==cflags --libs pangocairo`
*/

/*
  The top half of this file is the "tiny toolkit" that you should not
  have to alter. Under the divider are the "callback" functions and
  main(), which you want to alter to change what your program does.

  The "mini toolkit" currently only supports exactly one window. It probably
  should track the window size, too, rather than having the program do it.
*/

/* LICENSE:
  Copyright (c) 2009 Bill Spitzak (spitzak@...)

  All code is based on FLTK2.0.

  License is GPL2 or later, plus a linking exception that says you can
  use this code unchanged in any program no matter what the license of
  that program is. However if you modify this code then you must
  include the modified version as source code under this same license
  or (by removing the linking exception) under the GPL or LGPL.

  Please check with www.fltk.org for license details. However the
  intention is to allow this source code to be used for any purpose,
  as long as you don't "close" this source code, without propagating
  the restrictions to other code.
*/

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <cairo/cairo-xlib.h>
#include <pango/pangocairo.h> /* not needed if you don't use pango */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Produce less verbose message if connection drops: */
static int io_error_handler(Display* d) {
    fprintf(stderr, "X I/O error\n");
    exit(1);
}

/* Makes it continue on X errors rather than exit, and make readable message: */
static int xerror_handler(Display* d, XErrorEvent* e) {
  char buf1[128], buf2[128];
  sprintf(buf1, "XRequest.%d", e->request_code);
  XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
  XGetErrorText(d, e->error_code, buf1, 128);
  fprintf(stderr, "%s: %s 0x%lx\n", buf2, buf1, e->resourceid);
  return 0;
}

static Display* xdisplay = 0;
static int xscreen;
static Atom WM_DELETE_WINDOW;
static Atom WM_PROTOCOLS;
static Atom MOTIF_WM_HINTS;
static Atom XA_TEXT;
static Atom _NET_WM_NAME;
static Atom _NET_WM_ICON_NAME;
static Atom UTF8_STRING;

static Display* open_display() {
  if (xdisplay) return xdisplay;

  XSetIOErrorHandler(io_error_handler);
  XSetErrorHandler(xerror_handler);

  xdisplay = XOpenDisplay(0);
  xscreen = DefaultScreen(xdisplay);

  /* Register all the Atoms we need */
#define MAX_ATOMS 30
  Atom* atom_ptr[MAX_ATOMS];
  const char* names[MAX_ATOMS];
  int i = 0;
#define atom(a,b) atom_ptr[i] = &a; names[i] = b; i++
  atom( WM_DELETE_WINDOW , "WM_DELETE_WINDOW");
  atom( WM_PROTOCOLS , "WM_PROTOCOLS");
  atom( MOTIF_WM_HINTS , "_MOTIF_WM_HINTS");
  atom( XA_TEXT , "TEXT");
  atom( _NET_WM_NAME , "_NET_WM_NAME");
  atom( _NET_WM_ICON_NAME , "_NET_WM_ICON_NAME");
  atom( UTF8_STRING , "UTF8_STRING");
#undef atom
  Atom atoms[MAX_ATOMS];
  XInternAtoms(xdisplay, (char**)names, i, 0, atoms);
  for (; i--;) *atom_ptr[i] = atoms[i];

  return xdisplay;
}

Window make_window(int W, int H) {

  open_display();

  XSetWindowAttributes attr;
  attr.border_pixel = 0;
  attr.bit_gravity = StaticGravity;
  attr.event_mask =
    ExposureMask | StructureNotifyMask
    | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask
    | ButtonPressMask | ButtonReleaseMask
    | EnterWindowMask | LeaveWindowMask
    | PointerMotionMask;

  Window window =
    XCreateWindow(xdisplay, RootWindow(xdisplay, xscreen),
                  100, 100, W, H,
                  0, /* borderwidth */
                  DefaultDepth(xdisplay, xscreen),
                  InputOutput,
                  DefaultVisual(xdisplay, xscreen),
                  CWBorderPixel|CWEventMask|CWBitGravity,
                  &attr);

  /* Makes the close button produce an event */
  XChangeProperty(xdisplay, window, WM_PROTOCOLS,
                  XA_ATOM, 32, 0, (unsigned char*)&WM_DELETE_WINDOW, 1);

  /* Make it resizable */
  XSizeHints hints;
  hints.max_width = 2048;
  hints.max_height = 2048;
  hints.win_gravity = StaticGravity;
  hints.flags = PMaxSize|PWinGravity;
  XSetWMNormalHints(xdisplay, window, &hints);

  return window;
}

void set_window_name(Window window, const char* name) {
  /* Set the name. */
  int l = strlen(name);
  XChangeProperty(xdisplay, window, _NET_WM_NAME,
                  UTF8_STRING, 8, 0, (unsigned char*)name, l);
  /* This is only used by old non-UTF-8 window managers: */
  XChangeProperty(xdisplay, window, XA_WM_NAME,
                  XA_STRING, 8, 0, (unsigned char*)name, l);
}

/* Should take an array of bitflags for the state, for now this just maps */
void set_window_state(Window window, unsigned state) {
    XMapRaised(xdisplay, window);
}

/* Indicate that draw(window) must be called. Multiple windows nyi! */
static Window redraw_called = 0;
void redraw(Window window) {
    redraw_called = window;
}

/* Callbacks that the user program must supply */
void draw(Window window);
void resized(Window window, int X, int Y, int W, int H);
void closebutton(Window window);
void push(Window, int button, int X, int Y);
void drag(Window, int button, int X, int Y);
void release(Window, int button, int X, int Y);
void motion(Window, int X, int Y); /* Window may be 0 if mouse moves out */
void focus(Window);
void keyboard(Window, KeySym, char* text, int length);

int run() {
    if (!xdisplay) return 0;
    XEvent xevent;
    int sendmotion = 0;
    Window motionwindow;
    int motionX, motionY;
    for (;;) {
      if (sendmotion) {
        sendmotion = 0;
        motion(motionwindow, motionX, motionY);
      }
      if (redraw_called) {
        draw(redraw_called);
        redraw_called = 0;
      }
      XFlush(xdisplay);
      while (XEventsQueued(xdisplay,QueuedAfterReading)) {
        XNextEvent(xdisplay, &xevent);
        Window window = xevent.xany.window;

        switch (xevent.type) {

        case MappingNotify:
            XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
            break;

        case ClientMessage:
            if (xevent.xclient.data.l[0] == WM_DELETE_WINDOW)
                closebutton(window);
            break;

        case ConfigureNotify:
        case MapNotify: {
            /* event data is useless due to WM bugs, do a query instead */
            XWindowAttributes actual;
            XGetWindowAttributes(xdisplay, window, &actual);
            Window junk; int X, Y;
            XTranslateCoordinates(xdisplay, window, actual.root,
                                  0, 0, &X, &Y, &junk);
            resized(window, X, Y, actual.width, actual.height);
            /* Needs to do redraw if it gets smaller? */
            break;}

        case Expose:
        case GraphicsExpose:
            redraw(window);
            break;

        case ButtonPress:
            push(window,
                 xevent.xbutton.button, xevent.xbutton.x, xevent.xbutton.y);
            break;

        case MotionNotify:
            if (xevent.xbutton.state & 0x7F00) {
                sendmotion = 0;
                int button = 1;
                while (!(xevent.xbutton.state & (0x80<<button))) ++button;
                drag(window, button, xevent.xbutton.x, xevent.xbutton.y);
            } else {
                sendmotion = 1;
                motionwindow = window;
                motionX = xevent.xbutton.x;
                motionY = xevent.xbutton.y;
            }
            break;

        case EnterNotify:
            if (!(xevent.xcrossing.state & 0x7F00)) {
                sendmotion = 1;
                motionwindow = window;
                motionX = xevent.xbutton.x;
                motionY = xevent.xbutton.y;
            }
            break;

        case LeaveNotify:
            if (!(xevent.xcrossing.state & 0x7F00)) {
                sendmotion = 1;
                motionwindow = 0;
                motionX = xevent.xbutton.x_root;
                motionY = xevent.xbutton.y_root;
            }
            break;

        case ButtonRelease:
            release(window,
                    xevent.xbutton.button, xevent.xbutton.x, xevent.xbutton.y);
            break;

        case FocusIn:
            focus(window);
            break;

        case FocusOut:
            focus(0);
            break;

        case KeyPress: {
            KeySym keysym;
            int length;
            char buffer[20];
            length = XLookupString(&(xevent.xkey), buffer, sizeof(buffer)-1,
                                   &keysym, 0);
            buffer[length] = 0;
            keyboard(window, keysym, buffer, length);
            break;}
        }
      }
    }
}

/* Returns a cairo_t for drawing into window, reusing as much as possible. */
cairo_t* cairo_for(Window window, int W, int H) {
    static Window pwindow = 0;
    static cairo_surface_t* surface = 0;
    static cairo_t* cr = 0;
    static int pw, ph;
    /* Spew and reset any previous error */
    if (cr) {
        cairo_status_t cstatus = cairo_status(cr);
        if (cstatus) {
            printf("Cairo: %s", cairo_status_to_string(cstatus));
            pwindow = 0;
        }
    }
    if (pwindow != window) {
        if (cr) {
            cairo_destroy(cr);
            cairo_surface_destroy(surface);
        }
        surface =
            cairo_xlib_surface_create(xdisplay, window,
                                      DefaultVisual(xdisplay, xscreen),
                                      W, H);
        cr = cairo_create(surface);
        pwindow = window;
        pw = W;
        ph = H;
    } else if (pw != W || ph != H) {
        cairo_xlib_surface_set_size(surface, W, H);
        pw = W;
        ph = H;
    }
    return cr;
}

/* End of tiny toolkit */
/******************************************************************************/
/* Start of user program */

Window window;
int W, H; /* track the size of the window */

void resized(Window window, int x, int y, int w, int h) {
    /* lame-o fix for X not sending expose if smaller: */
    if (w < W && h <= H || w <= W && h < H) redraw(window);
    W = w;
    H = h;
}

/* Change the drawing code here */
void draw(Window window) {
    cairo_t* cr = cairo_for(window,W,H);
#if 1  /* Centered toy text sample */
    /* erase the window */
    cairo_set_source_rgb(cr, 1,1,1);
    cairo_rectangle(cr, 0, 0, W, H);
    cairo_fill(cr);

    /* draw something: */
    cairo_text_extents_t extents;

    const char *utf8 = "www.cairograpics.org";
    double x,y;

    cairo_select_font_face (cr, "Sans",
                            CAIRO_FONT_SLANT_NORMAL,
                            CAIRO_FONT_WEIGHT_NORMAL);

    cairo_set_font_size (cr, 52.0);
    cairo_text_extents (cr, utf8, &extents);
    x = (W-extents.width)/2 - extents.x_bearing;
    y = (H-extents.height)/2 - extents.y_bearing;

    cairo_move_to (cr, x, y);
    cairo_set_source_rgb(cr, 0,0,0);
    cairo_show_text (cr, utf8);

    /* draw helping lines */
    cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
    cairo_set_line_width (cr, 6.0);
    cairo_arc (cr, x, y, 10.0, 0, 2*M_PI);
    cairo_fill (cr);
    cairo_move_to (cr, W/2.0, 0);
    cairo_rel_line_to (cr, 0, H);
    cairo_move_to (cr, 0, H/2.0);
    cairo_rel_line_to (cr, W, 0);
    cairo_stroke (cr);
#endif
#if 0  /* Gradient sample */
    cairo_save(cr);
    if (W > H) {
        cairo_translate(cr, 0, .5*(H-W));
        cairo_scale(cr, W, W);
    } else {
        cairo_translate(cr, .5*(W-H), 0);
        cairo_scale(cr, H, H);
    }

    cairo_pattern_t *pat;

    pat = cairo_pattern_create_linear (0.0, 0.0,  0.0, 1.0);
    cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
    cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
    cairo_rectangle (cr, 0, 0, 1, 1);
    cairo_set_source (cr, pat);
    cairo_fill (cr);
    cairo_pattern_destroy (pat);

    pat = cairo_pattern_create_radial (115.2/256, 102.4/256, 25.6/256,
                                       102.4/256, 102.4/256, 128.0/256);
    cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
    cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
    cairo_set_source (cr, pat);
    cairo_arc (cr, .5, .5, .3, 0, 2 * M_PI);
    cairo_fill (cr);
    cairo_pattern_destroy (pat);

    cairo_restore(cr);
#endif
#if 0  /* pango sample */
    /* erase the window */
    cairo_set_source_rgb(cr, 1,1,1);
    cairo_rectangle(cr, 0, 0, W, H);
    cairo_fill(cr);

    PangoFontDescription *font_description = pango_font_description_new ();
    pango_font_description_set_family (font_description, "sans");
    pango_font_description_set_weight (font_description, PANGO_WEIGHT_BOLD);
    pango_font_description_set_absolute_size (font_description, 32 * PANGO_SCALE);

    PangoLayout *layout = pango_cairo_create_layout (cr);
    pango_layout_set_font_description (layout, font_description);
    pango_layout_set_text (layout, "www.cairographics.org", -1);

    cairo_set_source_rgb (cr, 0, 0, 0);
    cairo_move_to (cr, 10.0, 50.0);
    pango_cairo_show_layout (cr, layout);

    g_object_unref (layout);
    pango_font_description_free (font_description);
#endif
}

void closebutton(Window window) {
    exit(0);
}

void push(Window window, int button, int X, int Y) {
    /* printf("button %d pushed at %d %d\n", button, X, Y); */
}

void drag(Window window, int button, int X, int Y) {
    /* printf("button %d drag to %d %d\n", button, X, Y); */
}

void release(Window window, int button, int X, int Y) {
    /* printf("button %d release at %d %d\n", button, X, Y); */
}

void motion(Window window, int X, int Y) {
    /* printf("move %d to %d %d\n", window, X, Y); */
}

void focus(Window window) {
    /* if (window) printf("focus in\n"); else printf("focus out\n"); */
}

void keyboard(Window window, KeySym keysym, char* text, int length) {
    /*    if (text[0]>=' ' && text[0]!=127) {
        printf("'%s' typed on keyboard\n", text);
    } else {
        printf("key %#x typed\n", keysym);
        }*/
}

int main(int argc, char** argv) {
    W = H = 300;
    window = make_window(W, H);
    set_window_name(window, argv[0]);
    set_window_state(window, 1);
    return run();
}

_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo