summaryrefslogtreecommitdiff
path: root/util/cairo-sphinx/sphinx.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/cairo-sphinx/sphinx.c')
-rw-r--r--util/cairo-sphinx/sphinx.c1545
1 files changed, 0 insertions, 1545 deletions
diff --git a/util/cairo-sphinx/sphinx.c b/util/cairo-sphinx/sphinx.c
deleted file mode 100644
index 238d40064..000000000
--- a/util/cairo-sphinx/sphinx.c
+++ /dev/null
@@ -1,1545 +0,0 @@
-/*
- * The intention for sphinx is for detection of rendering errors inside
- * applications by simultaneously rendering on to the target device and on
- * an image surface and comparing the two. If it found a discrepancy, it
- * would then dump the trace that reproduces the error. (Then apply
- * delta-debugging to reduce that down to a minimal trace.)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/poll.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <errno.h>
-#include <assert.h>
-#include <pthread.h>
-
-#include <cairo.h>
-#include <cairo-script.h>
-#include <cairo-script-interpreter.h>
-#include <cairo-boilerplate.h>
-
-#include <glib.h> /* for checksumming */
-
-#ifndef CAIRO_HAS_REAL_PTHREAD
-# error "cairo-sphinx needs real pthreads"
-#endif
-
-#ifndef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
-
-#define DATA_SIZE (256 << 20)
-#define SHM_PATH_XXX "/shmem-cairo-sphinx"
-
-struct client {
- int sk;
- const cairo_boilerplate_target_t *target;
- cairo_surface_t *surface;
- void *base;
-
- cairo_script_interpreter_t *csi;
- struct context_closure {
- struct context_closure *next;
- unsigned long id;
- cairo_t *context;
- cairo_surface_t *surface;
- cairo_surface_t *original;
- } *contexts;
-
- unsigned long context_id;
-};
-
-struct surface_tag {
- long width, height;
-};
-static const cairo_user_data_key_t surface_tag;
-
-static int
-client_socket (const char *socket_path);
-
-static int
-writen (int fd, const void *ptr, int len)
-{
-#if 1
- const uint8_t *data = ptr;
- while (len) {
- int ret = write (fd, data, len);
- if (ret < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- continue;
- default:
- return FALSE;
- }
- } else if (ret == 0) {
- return FALSE;
- } else {
- data += ret;
- len -= ret;
- }
- }
- return TRUE;
-#else
- int ret = send (fd, ptr, len, 0);
- return ret == len;
-#endif
-}
-
-static int
-readn (int fd, void *ptr, int len)
-{
-#if 0
- uint8_t *data = ptr;
- while (len) {
- int ret = read (fd, data, len);
- if (ret < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- continue;
- default:
- return FALSE;
- }
- } else if (ret == 0) {
- return FALSE;
- } else {
- data += ret;
- len -= ret;
- }
- }
- return TRUE;
-#else
- int ret = recv (fd, ptr, len, MSG_WAITALL);
- return ret == len;
-#endif
-}
-static int
-open_devnull_to_fd (int want_fd, int flags)
-{
- int error;
- int got_fd;
-
- close (want_fd);
-
- got_fd = open("/dev/null", flags | O_CREAT, 0700);
- if (got_fd == -1)
- return -1;
-
- error = dup2 (got_fd, want_fd);
- close (got_fd);
-
- return error;
-}
-
-static int
-daemonize (void)
-{
- void (*oldhup) (int);
-
- /* Let the parent go. */
- switch (fork ()) {
- case -1: return -1;
- case 0: break;
- default: _exit (0);
- }
-
- /* Become session leader. */
- if (setsid () == -1)
- return -1;
-
- /* Refork to yield session leadership. */
- oldhup = signal (SIGHUP, SIG_IGN);
- switch (fork ()) {
- case -1: return -1;
- case 0: break;
- default: _exit (0);
- }
- signal (SIGHUP, oldhup);
-
- /* Establish stdio. */
- if (open_devnull_to_fd (0, O_RDONLY) == -1)
- return -1;
- if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
- return -1;
- if (dup2 (1, 2) == -1)
- return -1;
-
- return 0;
-}
-
-static int
-server_socket (const char *socket_path)
-{
- long flags;
- struct sockaddr_un addr;
- int sk;
-
- unlink (socket_path);
-
- sk = socket (PF_UNIX, SOCK_STREAM, 0);
- if (sk == -1)
- return -1;
-
- memset (&addr, 0, sizeof (addr));
- addr.sun_family = AF_UNIX;
- strcpy (addr.sun_path, socket_path);
- if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
- close (sk);
- return -1;
- }
-
- flags = fcntl (sk, F_GETFL);
- if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
- close (sk);
- return -1;
- }
-
- if (listen (sk, 5) == -1) {
- close (sk);
- return -1;
- }
-
- return sk;
-}
-
-static int
-readline (int fd, char *line, int max)
-{
- int len = 0;
- do {
- int ret = read (fd, &line[len], 1);
- if (ret <= 0)
- return -1;
- } while (line[len] != '\n' && ++len < max);
- line[len] = '\0';
- return len;
-}
-
-struct clients {
- int count, size;
- int complete;
-
- cairo_surface_t *recording;
- unsigned long serial;
-
- struct client_info {
- int sk;
- int trace;
- unsigned long image_serial;
- cairo_surface_t *image;
- char *name;
- char *target;
- char *reference;
-
- uint8_t *out_buf;
- int out_len;
- int out_size;
- } *clients;
- const char *shm_path;
- unsigned long offset;
- uint8_t *base;
-};
-
-static void *
-clients_shm (const char *shm_path)
-{
- void *base;
- int fd;
-
- shm_unlink (shm_path);
- fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
- if (fd == -1)
- return MAP_FAILED;
-
- if (ftruncate (fd, DATA_SIZE) == -1) {
- close (fd);
- return MAP_FAILED;
- }
-
- base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- close (fd);
-
- return base;
-}
-
-static int
-clients_init (struct clients *clients)
-{
- clients->count = 0;
- clients->complete = 0;
- clients->size = 4;
- clients->clients = xmalloc (clients->size * sizeof (struct client_info));
-
- clients->shm_path = SHM_PATH_XXX;
- clients->base = clients_shm (clients->shm_path);
- if (clients->base == MAP_FAILED)
- return -1;
- clients->offset = 0;
-
- clients->recording = NULL;
- clients->serial = 0;
-
- return 0;
-}
-
-static void
-clients_add_command (struct clients *clients, int fd, char *info)
-{
- struct client_info *c;
- char buf[1024];
- int len;
- char *str;
-
- if (clients->count == clients->size) {
- clients->size *= 2;
- clients->clients = xrealloc (clients->clients,
- clients->size * sizeof (struct client_info));
- }
-
- c = &clients->clients[clients->count++];
- c->sk = fd;
- c->trace = -1;
- c->image_serial = 0;
- c->image = NULL;
- c->name = c->target = c->reference = NULL;
-
- c->out_size = 8192;
- c->out_buf = xmalloc (c->out_size);
- c->out_len = 0;
-
- str = strstr (info, "name=");
- if (str != NULL) {
- char *sp = strchr (str + 5, ' ');
- int len;
- if (sp)
- len = sp - str - 5;
- else
- len = strlen (str + 5);
- c->name = xmalloc (len + 1);
- memcpy (c->name, str + 5, len);
- c->name[len] = '\0';
- }
-
- str = strstr (info, "target=");
- if (str != NULL) {
- char *sp = strchr (str + 7, ' ');
- int len;
- if (sp)
- len = sp - str - 7;
- else
- len = strlen (str + 7);
- c->target = xmalloc (len + 1);
- memcpy (c->target, str + 7, len);
- c->target[len] = '\0';
- }
-
- str = strstr (info, "reference=");
- if (str != NULL) {
- char *sp = strchr (str + 10, ' ');
- int len;
- if (sp)
- len = sp - str - 10;
- else
- len = strlen (str + 10);
- c->reference = xmalloc (len + 1);
- memcpy (c->reference, str + 10, len);
- c->reference[len] = '\0';
- }
-
- len = sprintf (buf, "%s\n", clients->shm_path);
- writen (fd, buf, len);
-}
-
-static void
-clients_add_trace (struct clients *clients, int fd, char *info)
-{
- char *str, *sp;
- char *name;
- int i;
-
- str = strstr (info, "name=");
- assert (str != NULL);
- sp = strchr (str + 5, ' ');
- if (sp)
- i = sp - str - 5;
- else
- i = strlen (str + 5);
-
- name = xmalloc (i + 1);
- memcpy (name, str + 5, i);
- name[i] = '\0';
-
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
- if (strcmp (name, c->name) == 0) {
- c->trace = fd;
- break;
- }
- }
-
- free (name);
-}
-
-static int
-clients_image (struct clients *clients, int fd, char *info)
-{
- struct client_info *c = NULL;
- int format, width, height, stride, size;
- int i;
-
- for (i = 0; i < clients->count; i++) {
- if (clients->clients[i].sk == fd) {
- c = &clients->clients[i];
- break;
- }
- }
-
- if (c == NULL)
- return 0;
-
- if (sscanf (info, "%lu %d %d %d %d",
- &c->image_serial, &format, &width, &height, &stride) != 5)
- {
- return 0;
- }
-
- size = height * stride;
- size = (size + 4095) & -4096;
- assert (clients->offset + size <= DATA_SIZE);
-
- c->image =
- cairo_image_surface_create_for_data (clients->base + clients->offset,
- format, width, height, stride);
-
- if (! writen (fd, &clients->offset, sizeof (clients->offset)))
- return 0;
-
- clients->offset += size;
-
- return 1;
-}
-
-static int
-u8_cmp (const void *A, const void *B)
-{
- const uint8_t *a = A, *b = B;
- return (int) *a - (int) *b;
-}
-
-static uint8_t
-median (uint8_t *values, int count)
-{
- /* XXX could use a fast median here if we cared */
- qsort (values, count, 1, u8_cmp);
- return values[count/2];
-}
-
-static uint32_t
-get_pixel32 (int x, int y, const uint8_t *data, int stride)
-{
- return ((uint32_t *)(data + y * stride))[x];
-}
-
-static uint8_t
-get_median_32 (int x, int y, int channel,
- const uint8_t *data, int width, int height, int stride)
-{
- uint8_t neighbourhood[25];
- int cnt = 0;
- int xx, yy;
-
- for (yy = y - 2; yy <= y + 2; yy++) {
- if (yy < 0)
- continue;
- if (yy >= height)
- continue;
-
- for (xx = x - 2; xx <= x + 2; xx++) {
- if (xx < 0)
- continue;
- if (xx >= width)
- continue;
-
- neighbourhood[cnt++] = (get_pixel32 (xx, yy, data, stride) >> (channel*8)) & 0xff;
- }
- }
-
- return median (neighbourhood, cnt);
-}
-
-static uint8_t
-get_pixel8 (int x, int y, const uint8_t *data, int stride)
-{
- return data[y * stride + x];
-}
-
-static uint8_t
-get_median_8 (int x, int y, const uint8_t *data, int width, int height, int stride)
-{
- uint8_t neighbourhood[25];
- int cnt = 0;
- int xx, yy;
-
- for (yy = y - 2; yy <= y + 2; yy++) {
- if (yy < 0)
- continue;
- if (yy >= height)
- continue;
-
- for (xx = x - 2; xx <= x + 2; xx++) {
- if (xx < 0)
- continue;
- if (xx >= width)
- continue;
-
- neighbourhood[cnt++] = get_pixel8 (xx, yy, data, stride);
- }
- }
-
- return median (neighbourhood, cnt);
-}
-
-static cairo_bool_t
-compare_images (cairo_surface_t *a,
- cairo_surface_t *b)
-{
- int width, height, stride;
- const uint8_t *aa, *bb;
- int x, y;
-
- if (cairo_surface_status (a) || cairo_surface_status (b))
- return FALSE;
-
- if (cairo_surface_get_type (a) != cairo_surface_get_type (b))
- return FALSE;
-
- if (cairo_image_surface_get_format (a) != cairo_image_surface_get_format (b))
- return FALSE;
-
- if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
- return FALSE;
-
- if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
- return FALSE;
-
- if (cairo_image_surface_get_stride (a) != cairo_image_surface_get_stride (b))
- return FALSE;
-
-
- width = cairo_image_surface_get_width (a);
- height = cairo_image_surface_get_height (a);
- stride = cairo_image_surface_get_stride (a);
-
- aa = cairo_image_surface_get_data (a);
- bb = cairo_image_surface_get_data (b);
- switch (cairo_image_surface_get_format (a)) {
- case CAIRO_FORMAT_ARGB32:
- for (y = 0; y < height; y++) {
- const uint32_t *ua = (uint32_t *) aa;
- const uint32_t *ub = (uint32_t *) bb;
- for (x = 0; x < width; x++) {
- if (ua[x] != ub[x]) {
- int channel;
-
- for (channel = 0; channel < 4; channel++) {
- unsigned va, vb, diff;
-
- va = (ua[x] >> (channel*8)) & 0xff;
- vb = (ub[x] >> (channel*8)) & 0xff;
- diff = abs (va - vb);
- if (diff > 1) {
- va = get_median_32 (x, y, channel, aa, width, height, stride);
- vb = get_median_32 (x, y, channel, bb, width, height, stride);
- diff = abs (va - vb);
- if (diff > 1)
- return FALSE;
- }
- }
- }
- }
- aa += stride;
- bb += stride;
- }
- break;
-
- case CAIRO_FORMAT_RGB24:
- for (y = 0; y < height; y++) {
- const uint32_t *ua = (uint32_t *) aa;
- const uint32_t *ub = (uint32_t *) bb;
- for (x = 0; x < width; x++) {
- if ((ua[x] & 0x00ffffff) != (ub[x] & 0x00ffffff)) {
- int channel;
-
- for (channel = 0; channel < 3; channel++) {
- unsigned va, vb, diff;
-
- va = (ua[x] >> (channel*8)) & 0xff;
- vb = (ub[x] >> (channel*8)) & 0xff;
- diff = abs (va - vb);
- if (diff > 1) {
- va = get_median_32 (x, y, channel, aa, width, height, stride);
- vb = get_median_32 (x, y, channel, bb, width, height, stride);
- diff = abs (va - vb);
- if (diff > 1)
- return FALSE;
- }
- }
- }
- }
- aa += stride;
- bb += stride;
- }
- break;
-
- case CAIRO_FORMAT_A8:
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- if (aa[x] != bb[x]) {
- unsigned diff = abs (aa[x] - bb[x]);
- if (diff > 1) {
- uint8_t va, vb;
-
- va = get_median_8 (x, y, aa, width, height, stride);
- vb = get_median_8 (x, y, bb, width, height, stride);
- diff = abs (va - vb);
- if (diff > 1)
- return FALSE;
- }
-
- }
- }
- aa += stride;
- bb += stride;
- }
- break;
-
- case CAIRO_FORMAT_A1:
- width /= 8;
- for (y = 0; y < height; y++) {
- if (memcmp (aa, bb, width))
- return FALSE;
- aa += stride;
- bb += stride;
- }
- break;
-
- case CAIRO_FORMAT_INVALID:
- case CAIRO_FORMAT_RGB16_565: /* XXX */
- break;
- }
-
- return TRUE;
-}
-
-static int
-check_images (struct clients *clients)
-{
- int i, j;
-
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
-
- if (c->reference == NULL)
- continue;
-
- for (j = 0; j < clients->count; j++) {
- struct client_info *ref = &clients->clients[j];
-
- if (strcmp (c->reference, ref->name))
- continue;
-
- if (! compare_images (c->image, ref->image))
- return 0;
- }
- }
-
- return 1;
-}
-
-static gchar *
-checksum (const char *filename)
-{
- gchar *str = NULL;
- gchar *data;
- gsize len;
-
- if (g_file_get_contents (filename, &data, &len, NULL)) {
- str = g_compute_checksum_for_data (G_CHECKSUM_SHA1, (guchar *) data, len);
- g_free (data);
- }
-
- return str;
-}
-
-static void
-write_trace (struct clients *clients)
-{
- cairo_device_t *ctx;
- gchar *csum;
- char buf[4096];
- int i;
-
- mkdir ("output", 0777);
-
- ctx = cairo_script_create ("output/cairo-sphinx.trace");
- cairo_script_from_recording_surface (ctx, clients->recording);
- cairo_device_destroy (ctx);
-
- csum = checksum ("output/cairo-sphinx.trace");
-
- sprintf (buf, "output/%s.trace", csum);
- if (! g_file_test (buf, G_FILE_TEST_EXISTS)) {
- rename ("output/cairo-sphinx.trace", buf);
-
- sprintf (buf, "output/%s.recording.png", csum);
- cairo_surface_write_to_png (clients->recording, buf);
-
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
- if (c->image != NULL) {
- sprintf (buf, "output/%s.%s.png", csum, c->name);
- cairo_surface_write_to_png (c->image, buf);
- }
- }
- }
-}
-
-static void
-clients_complete (struct clients *clients, int fd)
-{
- int i;
-
- for (i = 0; i < clients->count; i++) {
- if (clients->clients[i].sk == fd) {
- break;
- }
- }
- if (i == clients->count)
- return;
-
- if (++clients->complete != clients->count)
- return;
-
- clients->offset = 0;
- clients->complete = 0;
-
- if (! check_images (clients))
- write_trace (clients);
-
- /* ack */
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
-
- cairo_surface_destroy (c->image);
- c->image = NULL;
-
- if (! writen (c->sk, &clients->serial, sizeof (clients->serial)))
- continue;
-
- c->image_serial = 0;
- }
-
- clients->recording = NULL;
- clients->serial = 0;
-}
-
-static void
-clients_recording (struct clients *clients, int fd, char *info)
-{
- sscanf (info, "%p %lu", &clients->recording, &clients->serial);
- clients_complete (clients, fd);
-}
-
-static void
-clients_remove (struct clients *clients, int fd)
-{
- int i, j;
-
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
- if (c->sk == fd) {
- free (c->out_buf);
- break;
- }
- }
-
- for (j = i++; i < clients->count; i++)
- clients->clients[j] = clients->clients[i];
-
- clients->count = j;
-}
-
-static void
-clients_send_trace (struct clients *clients,
- const char * const line, const int len)
-{
- int i;
-
- for (i = 0; i < clients->count; i++) {
- struct client_info *c = &clients->clients[i];
- int ret, rem = len;
-
- if (c->trace == -1)
- continue;
-
- if (c->out_len) {
- ret = write (c->trace, c->out_buf, c->out_len);
- if (ret > 0) {
- c->out_len -= ret;
- if (c->out_len)
- memmove (c->out_buf, c->out_buf + ret, c->out_len);
- }
- }
-
- if (! c->out_len) {
- ret = write (c->trace, line, rem);
- if (ret > 0)
- rem -= ret;
- }
-
- if (rem) {
- if (c->out_len + rem > c->out_size) {
- c->out_size *= 2;
- c->out_buf = xrealloc (c->out_buf, c->out_size);
- }
-
- memcpy (c->out_buf + c->out_len, line, rem);
- c->out_len += rem;
- }
- }
-}
-
-static void
-clients_fini (struct clients *clients)
-{
- shm_unlink (clients->shm_path);
- munmap (clients->base, DATA_SIZE);
- free (clients->clients);
-}
-
-static int
-nonblocking (int fd)
-{
- long flags;
-
- flags = fcntl (fd, F_GETFL);
- if (flags == -1)
- return -1;
-
- return fcntl (fd, F_SETFL, flags | O_NONBLOCK);
-}
-
-static void *
-request_image (struct client *c,
- struct context_closure *closure,
- cairo_format_t format,
- int width, int height, int stride)
-{
- char buf[1024];
- unsigned long offset = -1;
- int len;
-
- assert (format != CAIRO_FORMAT_INVALID);
-
- len = sprintf (buf, ".image %lu %d %d %d %d\n",
- closure->id, format, width, height, stride);
- writen (c->sk, buf, len);
-
- readn (c->sk, &offset, sizeof (offset));
- if (offset == (unsigned long) -1)
- return NULL;
-
- return (uint8_t *) c->base + offset;
-}
-
-static cairo_format_t
-format_for_content (cairo_content_t content)
-{
- switch (content) {
- case CAIRO_CONTENT_ALPHA:
- return CAIRO_FORMAT_A8;
- case CAIRO_CONTENT_COLOR:
- return CAIRO_FORMAT_RGB24;
- default:
- case CAIRO_CONTENT_COLOR_ALPHA:
- return CAIRO_FORMAT_ARGB32;
- }
-}
-
-static void
-get_surface_size (cairo_surface_t *surface,
- int *width, int *height,
- cairo_format_t *format)
-{
- if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE) {
- *width = cairo_image_surface_get_width (surface);
- *height = cairo_image_surface_get_height (surface);
- *format = cairo_image_surface_get_format (surface);
- } else {
- struct surface_tag *tag;
-
- tag = cairo_surface_get_user_data (surface, &surface_tag);
- if (tag != NULL) {
- *width = tag->width;
- *height = tag->height;
- } else {
- double x0, x1, y0, y1;
- cairo_t *cr;
-
- /* presumably created using cairo_surface_create_similar() */
- cr = cairo_create (surface);
- cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
- cairo_destroy (cr);
-
- tag = xmalloc (sizeof (*tag));
- *width = tag->width = ceil (x1 - x0);
- *height = tag->height = ceil (y1 - y0);
-
- if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
- exit (-1);
- }
- }
-}
-
-
-static void
-send_surface (struct client *c,
- struct context_closure *closure)
-{
- cairo_surface_t *source = closure->surface;
- cairo_surface_t *image;
- cairo_format_t format = CAIRO_FORMAT_INVALID;
- cairo_t *cr;
- int width, height, stride;
- void *data;
- unsigned long serial;
-
- get_surface_size (source, &width, &height, &format);
- if (format == CAIRO_FORMAT_INVALID)
- format = format_for_content (cairo_surface_get_content (source));
-
- stride = cairo_format_stride_for_width (format, width);
-
- data = request_image (c, closure, format, width, height, stride);
- if (data == NULL)
- exit (-1);
-
- image = cairo_image_surface_create_for_data (data,
- format,
- width, height,
- stride);
- cr = cairo_create (image);
- cairo_surface_destroy (image);
-
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_set_source_surface (cr, source, 0, 0);
- cairo_paint (cr);
- cairo_destroy (cr);
-
- /* signal completion */
- writen (c->sk, ".complete\n", strlen (".complete\n"));
-
- /* wait for image check */
- serial = 0;
- readn (c->sk, &serial, sizeof (serial));
- if (serial != closure->id)
- exit (-1);
-}
-
-static void
-send_recording (struct client *c,
- struct context_closure *closure)
-{
- cairo_surface_t *source = closure->surface;
- char buf[1024];
- int len;
- unsigned long serial;
-
- assert (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_RECORDING);
- len = sprintf (buf, ".recording %p %lu\n", source, closure->id);
- writen (c->sk, buf, len);
-
- /* wait for image check */
-
- serial = 0;
- readn (c->sk, &serial, sizeof (serial));
- if (serial != closure->id)
- exit (-1);
-}
-
-static cairo_surface_t *
-_surface_create (void *closure,
- cairo_content_t content,
- double width, double height,
- long uid)
-{
- struct client *c = closure;
- cairo_surface_t *surface;
-
- surface = cairo_surface_create_similar (c->surface,
- content, width, height);
- if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) {
- struct surface_tag *tag;
-
- tag = xmalloc (sizeof (*tag));
- tag->width = width;
- tag->height = height;
- if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
- exit (-1);
- }
-
- return surface;
-}
-
-static cairo_t *
-_context_create (void *closure, cairo_surface_t *surface)
-{
- struct client *c = closure;
- struct context_closure *l;
- cairo_bool_t foreign = FALSE;
-
- l = xmalloc (sizeof (*l));
- l->next = c->contexts;
- l->surface = surface;
- l->original = cairo_surface_reference (surface);
- l->id = ++c->context_id;
- if (l->id == 0)
- l->id = ++c->context_id;
- c->contexts = l;
-
- /* record everything, including writes to images */
- if (c->target == NULL) {
- if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_RECORDING) {
- cairo_format_t format;
- int width, height;
-
- get_surface_size (surface, &width, &height, &format);
- l->surface = cairo_surface_create_similar (c->surface,
- cairo_surface_get_content (surface),
- width, height);
- foreign = TRUE;
- }
- }
-
- l->context = cairo_create (l->surface);
- if (foreign) {
- cairo_set_source_surface (l->context, surface, 0, 0);
- cairo_paint (l->context);
- }
-
- return l->context;
-}
-
-static void
-_context_destroy (void *closure, void *ptr)
-{
- struct client *c = closure;
- struct context_closure *l, **prev = &c->contexts;
-
- while ((l = *prev) != NULL) {
- if (l->context == ptr) {
- if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
- if (c->target == NULL)
- send_recording (c, l);
- else
- send_surface (c, l);
- } else {
- exit (-1);
- }
-
- cairo_surface_destroy (l->original);
- *prev = l->next;
- free (l);
- return;
- }
- prev = &l->next;
- }
-}
-
-static void *
-recorder (void *arg)
-{
- struct client client;
- const cairo_script_interpreter_hooks_t hooks = {
- .closure = &client,
- .surface_create = _surface_create,
- .context_create = _context_create,
- .context_destroy = _context_destroy,
- };
- char *buf;
- int buf_size;
- int len = 0, ret;
- struct pollfd pfd;
-
- client.target = NULL;
- client.sk = client_socket ("/tmp/cairo-sphinx");
- if (client.sk < 0)
- return NULL;
-
- buf_size = 65536;
- buf = xmalloc (buf_size);
-
- len = sprintf (buf, "client-command target=recording name=.recorder\n");
- if (! writen (client.sk, buf, len))
- return NULL;
-
- /* drain the shm_path */
- len = readline (client.sk, buf, buf_size);
-
- pfd.fd = client_socket ("/tmp/cairo-sphinx");
- if (pfd.fd < 0)
- return NULL;
-
- len = sprintf (buf, "client-trace name=.recorder\n");
- if (! writen (pfd.fd, buf, len))
- return NULL;
-
- client.surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
- NULL);
-
- client.context_id = 0;
- client.csi = cairo_script_interpreter_create ();
- cairo_script_interpreter_install_hooks (client.csi, &hooks);
-
- nonblocking (pfd.fd);
- pfd.events = POLLIN;
- len = 0;
- while (poll (&pfd, 1, -1) > 0) {
- while ((ret = read (pfd.fd, buf + len, buf_size - len)) > 0) {
- int end;
-
- if (ret == buf_size - len) {
- buf_size *= 2;
- buf = xrealloc (buf, buf_size);
- }
- len += ret;
-
- for (end = len; end > 0 && buf[--end] != '\n'; )
- ;
- if (end > 0) {
- buf[end] = '\0';
- cairo_script_interpreter_feed_string (client.csi, buf, end);
-
- len -= end + 1;
- if (len)
- memmove (buf, buf + end + 1, len);
- }
- }
- if (ret == 0)
- break;
- if (! (errno == EAGAIN || errno == EINTR))
- break;
- }
-
- cairo_script_interpreter_finish (client.csi);
- cairo_script_interpreter_destroy (client.csi);
-
- cairo_surface_destroy (client.surface);
- return NULL;
-}
-
-static int
-do_server (const char *path)
-{
- pthread_t thread;
- struct clients clients;
- char line[4096];
- struct pollfd *pfd;
- int num_pfd, size_pfd;
- int n, cnt, ret = 1;
- int sk, source = -1;
- int waiter = -1, waiter_count = 0;
- int len;
-
- signal (SIGPIPE, SIG_IGN);
-
- if (clients_init (&clients) < 0) {
- fprintf (stderr, "Failed to initialise clients structure\n");
- return -1;
- }
-
- sk = server_socket (path);
- if (sk < 0) {
- fprintf (stderr, "Failed to create server socket\n");
- return 1;
- }
-
- if (daemonize () < 0)
- return 1;
-
- if (pthread_create (&thread, NULL, recorder, NULL) < 0) {
- fprintf (stderr, "Failed to create spawn recording thread\n");
- return 1;
- }
-
- size_pfd = 4;
- pfd = xmalloc (sizeof (*pfd) * size_pfd);
- pfd[0].fd = sk;
- pfd[0].events = POLLIN;
- num_pfd = 1;
-
- while ((cnt = poll (pfd, num_pfd, -1)) > 0) {
- int have_source;
-
- if (pfd[0].revents) {
- while ((sk = accept (pfd[0].fd, NULL, NULL)) != -1) {
- len = readline (sk, line, sizeof (line));
- if (strcmp (line, "source") == 0) {
-
- if (source != -1)
- exit (1);
-
- source = sk;
- if (nonblocking (sk) < 0) {
- close (sk);
- continue;
- }
- } else if (strncmp (line, "client-command", 14) == 0) {
- if (source == -1)
- clients_add_command (&clients, sk, line);
- } else if (strncmp (line, "client-trace", 12) == 0) {
- if (source == -1) {
- clients_add_trace (&clients, sk, line);
- if (nonblocking (sk) < 0) {
- close (sk);
- continue;
- }
-
- if (clients.count == waiter_count) {
- for (n = 1; n < num_pfd; n++) {
- if (pfd[n].fd == waiter) {
- pfd[n].fd = -1;
- break;
- }
- }
- close (waiter);
- waiter_count = -1;
- }
- }
- } else if (strncmp (line, "wait", 4) == 0) {
- int count = atoi (line + 5) + 1;
- if (clients.count == count) {
- close (sk);
- continue;
- } else {
- waiter = sk;
- waiter_count = count;
- }
- }
-
- if (num_pfd == size_pfd) {
- size_pfd *= 2;
- pfd = xrealloc (pfd, sizeof (*pfd) * size_pfd);
- }
-
- pfd[num_pfd].fd = sk;
- pfd[num_pfd].events = POLLIN;
- pfd[num_pfd].revents = 0;
- num_pfd++;
- }
- cnt--;
- }
-
- have_source = 0;
- for (n = 1; cnt && n < num_pfd; n++) {
- if (! pfd[n].revents)
- continue;
- cnt--;
-
- if (pfd[n].fd == -1)
- continue;
-
- if (source == pfd[n].fd) {
- have_source = n;
- } else {
- len = readline (pfd[n].fd, line, sizeof (line));
- if (len < 0) {
- clients_remove (&clients, pfd[n].fd);
- close (pfd[n].fd);
- pfd[n].fd = -1;
- continue;
- }
-
- if (strncmp (line, ".image", 6) == 0) {
- if (! clients_image (&clients, pfd[n].fd, line + 7)) {
- clients_remove (&clients, pfd[n].fd);
- close (pfd[n].fd);
- pfd[n].fd = -1;
- continue;
- }
- } else if (strncmp (line, ".complete", 9) == 0) {
- clients_complete (&clients, pfd[n].fd);
- } else if (strncmp (line, ".recording", 10) == 0) {
- clients_recording (&clients, pfd[n].fd, line + 6);
- } else {
- printf ("do_command (%s)\n", line);
- }
- }
- }
-
- if (have_source) {
- do {
- len = read (source, line, sizeof (line));
- if (len > 0) {
- clients_send_trace (&clients, line, len);
- } else if (len == 0) {
- close (source);
- pfd[have_source].fd = source = -1;
- goto done;
- } else
- break;
- } while (1);
- }
-
- for (n = cnt = 1; n < num_pfd; n++) {
- if (pfd[n].fd != -1) {
- if (cnt != n)
- pfd[cnt] = pfd[n];
- cnt++;
- }
- }
- num_pfd = cnt;
- }
-
-done:
- ret = 0;
- for (n = 0; n < num_pfd; n++) {
- if (pfd[n].fd != -1)
- close (pfd[n].fd);
- }
- free (pfd);
- clients_fini (&clients);
-
- return ret;
-}
-
-static void *
-client_shm (const char *shm_path)
-{
- void *base;
- int fd;
-
- fd = shm_open (shm_path, O_RDWR, 0);
- if (fd == -1)
- return MAP_FAILED;
-
- base = mmap (NULL, DATA_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_NORESERVE,
- fd, 0);
- close (fd);
-
- return base;
-}
-
-static int
-client_socket (const char *socket_path)
-{
- struct sockaddr_un addr;
- int sk;
-
- sk = socket (PF_UNIX, SOCK_STREAM, 0);
- if (sk == -1)
- return -1;
-
- memset (&addr, 0, sizeof (addr));
- addr.sun_family = AF_UNIX;
- strcpy (addr.sun_path, socket_path);
-
- if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1)
- return -1;
-
- return sk;
-}
-
-static int
-do_client (int fd,
- const char *target,
- const char *name,
- const char *reference,
- cairo_content_t content)
-{
- struct client client;
- const cairo_script_interpreter_hooks_t hooks = {
- .closure = &client,
- .surface_create = _surface_create,
- .context_create = _context_create,
- .context_destroy = _context_destroy,
- };
- void *closure;
- char *buf;
- int buf_size;
- int len = 0, ret;
- struct pollfd pfd;
-
- client.sk = fd;
- client.target = cairo_boilerplate_get_target_by_name (target, content);
- client.context_id = 0;
-
- client.surface = client.target->create_surface (NULL, content, 1, 1, 1, 1,
- CAIRO_BOILERPLATE_MODE_TEST,
- &closure);
- if (client.surface == NULL) {
- fprintf (stderr, "Failed to create target surface: %s.\n",
- client.target->name);
- return 1;
- }
-
- buf_size = 65536;
- buf = xmalloc (buf_size);
-
- if (reference != NULL) {
- len = sprintf (buf,
- "client-command name=%s target=%s reference=%s\n",
- name, target, reference);
- } else {
- len = sprintf (buf,
- "client-command name=%s target=%s\n",
- name, target);
- }
- if (! writen (fd, buf, len))
- return 1;
-
- len = readline (fd, buf, buf_size);
- client.base = client_shm (buf);
- if (client.base == MAP_FAILED) {
- fprintf (stderr, "Failed to map shared memory segment '%s'.\n", buf);
- return 1;
- }
-
- if (daemonize () < 0)
- return 1;
-
- pfd.fd = client_socket ("/tmp/cairo-sphinx");
- if (pfd.fd < 0)
- return 1;
-
- len = sprintf (buf, "client-trace name=%s\n", name);
- if (! writen (pfd.fd, buf, len))
- return 1;
-
- client.csi = cairo_script_interpreter_create ();
- cairo_script_interpreter_install_hooks (client.csi, &hooks);
-
- nonblocking (pfd.fd);
- pfd.events = POLLIN;
- len = 0;
- while (poll (&pfd, 1, -1) > 0) {
- while ((ret = read (pfd.fd, buf + len, buf_size - len)) > 0) {
- int end;
-
- if (ret == buf_size - len) {
- buf_size *= 2;
- buf = xrealloc (buf, buf_size);
- }
- len += ret;
-
- for (end = len; end > 0 && buf[--end] != '\n'; )
- ;
- if (end > 0) {
- buf[end] = '\0';
- cairo_script_interpreter_feed_string (client.csi, buf, end);
-
- len -= end + 1;
- if (len)
- memmove (buf, buf + end + 1, len);
- }
- }
- if (ret == 0)
- break;
- if (! (errno == EAGAIN || errno == EINTR))
- break;
- }
-
- cairo_script_interpreter_finish (client.csi);
- cairo_script_interpreter_destroy (client.csi);
-
- cairo_surface_destroy (client.surface);
- close (fd);
-
- return 0;
-}
-
-static int
-do_exec (int fd, char **argv)
-{
- char buf[4096];
-
- if (*argv == NULL)
- return 0;
-
- snprintf (buf, sizeof (buf), "%s/cairo-trace.so", LIBDIR);
- setenv ("LD_PRELOAD", buf, 1);
-
- snprintf (buf, sizeof (buf), "0");
- setenv ("CAIRO_TRACE_LINE_INFO", buf, 1);
-
- snprintf (buf, sizeof (buf), "%d", fd);
- setenv ("CAIRO_TRACE_FD", buf, 1);
- putenv (buf);
-
- return execvp (argv[0], argv);
-}
-
-static int
-do_wait (int fd)
-{
- char buf;
- int ret = read (fd, &buf, 1);
- return ret != 0;
-}
-
-int
-main (int argc, char **argv)
-{
- char buf[4096];
- int len;
- int fd;
-
- if (argc == 1)
- return do_server ("/tmp/cairo-sphinx");
-
- fd = client_socket ("/tmp/cairo-sphinx");
- if (fd < 0)
- return 1;
-
- if (strcmp (argv[1], "client") == 0) {
- return do_client (fd, argv[2], argv[3], argv[4],
- CAIRO_CONTENT_COLOR_ALPHA);
- }
-
- if (strcmp (argv[1], "wait") == 0) {
- len = snprintf (buf, sizeof (buf), "wait %s\n", argv[2]);
- if (! writen (fd, buf, len))
- return 1;
-
- return do_wait (fd);
- }
-
- if (strcmp (argv[1], "exec") == 0) {
- len = snprintf (buf, sizeof (buf), "source\n");
- if (! writen (fd, buf, len))
- return 1;
-
- return do_exec (fd, argv+2);
- }
-
- if (strcmp (argv[1], "replay") == 0) {
- len = snprintf (buf, sizeof (buf), "replay %s\n", argv[2]);
- return ! writen (fd, buf, len);
- }
-
- return 0;
-}