All I wanted to do was have an x window open and be able to plot an arbitrary changeable image to it (no I am not writing yet another image viewer!). The XCB documentation seems to be a cross between "good luck with that" and some rather erratic doxygen output.
The utility libraries are rather skimpy on examples and its tiresome worrying that when you feed any of the xcb function names into Google (web or codesearch) there are very, very few hits beyond the freedesktop API docs.
My favourite has to be the xcb_image_create documentation, go on I challenge anyone to follow that logic without going and reading the sources! The answer (afaict) is that if you pass a pointer in base that pointer will be freed by xcb_image_destroy otherwise the data pointer will be used, unless the bytes value is too small in which case memory will be allocated with malloc and the passed pointer ignored.
Anyhow, I have succeeded and the result is below, mainly so I do not loose it :-)
/* XCB application drawing an updating bitmap in a window
*
* Inspired by the xcb black rectangle in a window example
*
* Copyright 2010 V. R. Sanders, released under the MIT licence
*/
/* compile with:
* gcc -Wall -lxcb-icccm -lxcb -lxcb-image -o disp disp.c
*/
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
static xcb_format_t *
find_format (xcb_connection_t * c, uint8_t depth, uint8_t bpp)
{
const xcb_setup_t *setup = xcb_get_setup(c);
xcb_format_t *fmt = xcb_setup_pixmap_formats(setup);
xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(setup);
for(; fmt != fmtend; ++fmt)
if((fmt->depth == depth) && (fmt->bits_per_pixel == bpp)) {
/* printf("fmt %p has pad %d depth %d, bpp %d\n",
fmt,fmt->scanline_pad, depth,bpp); */
return fmt;
}
return 0;
}
void
fillimage(unsigned char *p, int width, int height)
{
int i, j;
for(i=0; i < width; i++)
{
for(j=0; j < height; j++)
{
if((i < 256)&&(j < 256))
{
*p++=rand()%256; // blue
*p++=rand()%256; // green
*p++=rand()%256; // red
} else {
*p++=i%256; // blue
*p++=j%256; // green
if(i < 256)
*p++=i%256; // red
else if(j < 256)
*p++=j%256; // red
else
*p++=(256-j)%256; // red
}
p++; /* unused byte */
}
}
}
xcb_image_t *
CreateTrueColorImage(xcb_connection_t *c,
int width,
int height)
{
const xcb_setup_t *setup = xcb_get_setup(c);
unsigned char *image32=(unsigned char *)malloc(width*height*4);
xcb_format_t *fmt = find_format(c, 24, 32);
if (fmt == NULL)
return NULL;
fillimage(image32, width, height);
return xcb_image_create(width,
height,
XCB_IMAGE_FORMAT_Z_PIXMAP,
fmt->scanline_pad,
fmt->depth,
fmt->bits_per_pixel,
0,
setup->image_byte_order,
XCB_IMAGE_ORDER_LSB_FIRST,
image32,
width*height*4,
image32);
}
int
main (int argc, char **argv)
{
xcb_connection_t *c;
xcb_screen_t *s;
xcb_window_t w;
xcb_pixmap_t pmap;
xcb_gcontext_t gc;
xcb_generic_event_t *e;
uint32_t mask;
uint32_t values[2];
int done=0;
xcb_image_t *image;
uint8_t *image32;
xcb_expose_event_t *ee;
char *title="Hello World!";
xcb_size_hints_t *hints;
/* open connection with the server */
c = xcb_connect (NULL, NULL);
if (!c) {
printf ("Cannot open display\n");
exit (1);
}
s = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
/* printf("root depth %d\n",s->root_depth); */
/* create image */
image = CreateTrueColorImage(c, 640, 480);
if (image == NULL) {
printf ("Cannot create iamge\n");
xcb_disconnect(c);
return 1;
}
image32 = image->data;
/* create window */
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
values[0] = s->white_pixel;
values[1] = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_KEY_PRESS |
XCB_EVENT_MASK_BUTTON_PRESS;
w = xcb_generate_id (c);
xcb_create_window (c, XCB_COPY_FROM_PARENT, w, s->root,
10, 10, image->width, image->height, 1,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
s->root_visual,
mask, values);
/* set title on window */
xcb_set_wm_name(c, w, STRING, strlen(title), title);
/* set size hits on window */
hints = xcb_alloc_size_hints();
xcb_size_hints_set_max_size(hints, image->width,image->height);
xcb_size_hints_set_min_size(hints, image->width,image->height);
xcb_set_wm_size_hints(c, w, WM_NORMAL_HINTS, hints);
/* create backing pixmap */
pmap = xcb_generate_id(c);
xcb_create_pixmap(c, 24, pmap, w, image->width, image->height);
/* create pixmap plot gc */
mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
values[0] = s->black_pixel;
values[1] = 0xffffff;
gc = xcb_generate_id (c);
xcb_create_gc (c, gc, pmap, mask, values);
/* put the image into the pixmap */
xcb_image_put(c, pmap, gc, image, 0, 0, 0);
/* show the window */
xcb_map_window (c, w);
xcb_flush (c);
/* event loop */
while (!done && (e = xcb_wait_for_event (c))) {
switch (e->response_type) {
case XCB_EXPOSE:
ee=(xcb_expose_event_t *)e;
/* printf ("expose %d,%d - %d,%d\n",
ee->x,ee->y,ee->width,ee->height); */
xcb_copy_area(c, pmap, w, gc,
ee->x,
ee->y,
ee->x,
ee->y,
ee->width,
ee->height);
xcb_flush (c);
image32+=16;
break;
case XCB_KEY_PRESS:
/* exit on keypress */
done = 1;
break;
case XCB_BUTTON_PRESS:
fillimage(image->data, image->width, image->height);
memset(image->data, 0, image32 - image->data);
xcb_image_put(c, pmap, gc, image, 0, 0, 0);
xcb_copy_area(c, pmap, w, gc, 0,0,0,0,image->width,image->height);
xcb_flush (c);
break;
}
free (e);
}
/* free pixmap */
xcb_free_pixmap(c, pmap);
/* close connection to server */
xcb_disconnect (c);
return 0;
}