diff --git a/Makefile b/Makefile index 122a85e6..abfb256f 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,8 @@ libwayland.so : $(libwayland_objs) $(compositors) $(clients) : CFLAGS += $(shell pkg-config --cflags libdrm) egl_compositor_objs = egl-compositor.o -egl-compositor.so : CFLAGS += $(EAGLE_CFLAGS) -egl-compositor.so : LDLIBS += $(EAGLE_LDLIBS) -rdynamic +egl-compositor.so : CFLAGS += $(EAGLE_CFLAGS) $(shell pkg-config --cflags libpng) +egl-compositor.so : LDLIBS += $(EAGLE_LDLIBS) $(shell pkg-config --libs libpng) -rdynamic egl-compositor.so : $(egl_compositor_objs) diff --git a/egl-compositor.c b/egl-compositor.c index 5c90a670..580dcbf2 100644 --- a/egl-compositor.c +++ b/egl-compositor.c @@ -2,12 +2,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include +#include #include "wayland.h" @@ -24,6 +26,7 @@ struct egl_compositor { EGLConfig config; struct wl_display *wl_display; int gem_fd; + int width, height; }; struct surface_data { @@ -32,6 +35,142 @@ struct surface_data { EGLSurface surface; }; +static int do_screenshot; + +static void +handle_sigusr1(int s) +{ + do_screenshot = 1; +} + +static void +die(const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + vfprintf(stderr, msg, ap); + va_end (ap); + + exit(EXIT_FAILURE); +} + +static void +stdio_write_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp; + size_t ret; + + fp = png_get_io_ptr (png); + while (size) { + ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) + die("write: %m\n"); + } +} + +static void +png_simple_output_flush_fn (png_structp png_ptr) +{ +} + +static void +png_simple_error_callback (png_structp png, + png_const_charp error_msg) +{ + die("png error: %s\n", error_msg); +} + +static void +png_simple_warning_callback (png_structp png, + png_const_charp error_msg) +{ + fprintf(stderr, "png warning: %s\n", error_msg); +} + +static void +convert_pixels(png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + + memcpy (&pixel, b, sizeof (uint32_t)); + b[0] = (pixel & 0xff0000) >> 16; + b[1] = (pixel & 0x00ff00) >> 8; + b[2] = (pixel & 0x0000ff) >> 0; + b[3] = 0; + } +} + +static void +screenshot(struct egl_compositor *ec) +{ + png_struct *png; + png_info *info; + png_byte **volatile rows = NULL; + png_color_16 white; + int depth, i; + FILE *fp; + uint8_t *data; + GLuint stride; + static const char filename[] = "wayland-screenshot.png"; + + data = eglReadBuffer(ec->display, ec->surface, GL_FRONT_LEFT, &stride); + if (data == NULL) + die("eglReadBuffer failed\n"); + rows = malloc(ec->height * sizeof rows[0]); + if (rows == NULL) + die("malloc failed\n"); + + for (i = 0; i < ec->height; i++) + rows[i] = (png_byte *) data + i * stride; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + png_simple_error_callback, + png_simple_warning_callback); + if (png == NULL) + die("png_create_write_struct failed\n"); + + info = png_create_info_struct(png); + if (info == NULL) + die("png_create_info_struct failed\n"); + + fp = fopen(filename, "w"); + if (fp == NULL) + die("fopen failed: %m\n"); + + png_set_write_fn(png, fp, stdio_write_func, png_simple_output_flush_fn); + + depth = 8; + png_set_IHDR(png, info, + ec->width, + ec->height, depth, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + white.gray = (1 << depth) - 1; + white.red = white.blue = white.green = white.gray; + png_set_bKGD(png, info, &white); + png_write_info (png, info); + png_set_write_user_transform_fn(png, convert_pixels); + + png_set_filler(png, 0, PNG_FILLER_AFTER); + png_write_image(png, rows); + png_write_end(png, info); + + png_destroy_write_struct(&png, &info); + fclose(fp); + free(rows); + free(data); +} + static void repaint(void *data) { @@ -83,6 +222,17 @@ repaint(void *data) glFlush(); eglSwapBuffers(ec->display, ec->surface); + + if (do_screenshot) { + glFinish(); + /* FIXME: There's a bug somewhere so that glFinish() + * doesn't actually wait for all rendering to finish. + * I *think* it's fixed in upstream drm, but for my + * kernel I need this sleep now... */ + sleep(1); + screenshot(ec); + do_screenshot = 0; + } } static void @@ -232,12 +382,14 @@ wl_compositor_create(struct wl_display *display) EGLConfig configs[64]; EGLint major, minor, count; struct egl_compositor *ec; - const int width = 1280, height = 800; ec = malloc(sizeof *ec); if (ec == NULL) return NULL; + ec->width = 1280; + ec->height = 800; + ec->base.interface = &interface; ec->wl_display = display; @@ -259,7 +411,7 @@ wl_compositor_create(struct wl_display *display) ec->config = configs[24]; ec->surface = eglCreateSurfaceNative(ec->display, ec->config, - 0, 0, width, height); + 0, 0, ec->width, ec->height); if (ec->surface == NULL) { fprintf(stderr, "failed to create surface\n"); return NULL; @@ -276,10 +428,10 @@ wl_compositor_create(struct wl_display *display) return NULL; } - glViewport(0, 0, width, height); + glViewport(0, 0, ec->width, ec->height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, width, height, 0, 0, 1000.0); + glOrtho(0, ec->width, ec->height, 0, 0, 1000.0); glMatrixMode(GL_MODELVIEW); glClearColor(0.0, 0.05, 0.2, 0.0); @@ -289,6 +441,8 @@ wl_compositor_create(struct wl_display *display) return NULL; } + signal(SIGUSR1, handle_sigusr1); + schedule_repaint(ec); return &ec->base;