Fall back to cairo image backend and shm surface if we don't have cairo gl

dev
Kristian Høgsberg 14 years ago
parent 6866856dfd
commit d0c3b9da22
  1. 406
      clients/window.c
  2. 12
      clients/window.h
  3. 6
      compositor/shm.c
  4. 7
      configure.ac

@ -20,6 +20,8 @@
* OF THIS SOFTWARE. * OF THIS SOFTWARE.
*/ */
#include "../config.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -33,13 +35,17 @@
#include <glib-object.h> #include <glib-object.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <sys/mman.h>
#define EGL_EGLEXT_PROTOTYPES 1 #define EGL_EGLEXT_PROTOTYPES 1
#define GL_GLEXT_PROTOTYPES 1 #define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h> #include <GL/gl.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#ifdef HAVE_CAIRO_GL
#include <cairo-gl.h> #include <cairo-gl.h>
#endif
#include <X11/extensions/XKBcommon.h> #include <X11/extensions/XKBcommon.h>
@ -56,6 +62,7 @@ struct display {
struct wl_compositor *compositor; struct wl_compositor *compositor;
struct wl_shell *shell; struct wl_shell *shell;
struct wl_drm *drm; struct wl_drm *drm;
struct wl_shm *shm;
struct wl_output *output; struct wl_output *output;
struct rectangle screen_allocation; struct rectangle screen_allocation;
int authenticated; int authenticated;
@ -89,6 +96,7 @@ struct window {
struct input *grab_device; struct input *grab_device;
struct input *keyboard_device; struct input *keyboard_device;
uint32_t name; uint32_t name;
enum window_buffer_type buffer_type;
EGLImageKHR *image; EGLImageKHR *image;
cairo_surface_t *cairo_surface, *pending_surface; cairo_surface_t *cairo_surface, *pending_surface;
@ -148,33 +156,110 @@ rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
cairo_close_path(cr); cairo_close_path(cr);
} }
static int static const cairo_user_data_key_t surface_data_key;
texture_from_png(const char *filename, int width, int height) struct surface_data {
struct wl_buffer *buffer;
};
#ifdef HAVE_CAIRO_GL
struct drm_surface_data {
struct surface_data data;
EGLImageKHR image;
GLuint texture;
EGLDisplay dpy;
};
static void
drm_surface_data_destroy(void *p)
{
struct drm_surface_data *data = p;
glDeleteTextures(1, &data->texture);
eglDestroyImageKHR(data->dpy, data->image);
wl_buffer_destroy(data->data.buffer);
}
cairo_surface_t *
display_create_drm_surface(struct display *display,
struct rectangle *rectangle)
{
struct drm_surface_data *data;
EGLDisplay dpy = display->dpy;
cairo_surface_t *surface;
struct wl_visual *visual;
struct wl_buffer *buffer;
EGLint name, stride;
EGLint image_attribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 0,
EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA,
EGL_NONE
};
data = malloc(sizeof *data);
if (data == NULL)
return NULL;
image_attribs[1] = rectangle->width;
image_attribs[3] = rectangle->height;
data->image = eglCreateDRMImageMESA(dpy, image_attribs);
glGenTextures(1, &data->texture);
data->dpy = dpy;
glBindTexture(GL_TEXTURE_2D, data->texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, data->image);
eglExportDRMImageMESA(display->dpy, data->image, &name, NULL, &stride);
visual = wl_display_get_premultiplied_argb_visual(display->display);
data->data.buffer =
wl_drm_create_buffer(display->drm, name, rectangle->width,
rectangle->height, stride, visual);
surface = cairo_gl_surface_create_for_texture(display->device,
CAIRO_CONTENT_COLOR_ALPHA,
data->texture,
rectangle->width,
rectangle->height);
cairo_surface_set_user_data (surface, &surface_data_key,
data, drm_surface_data_destroy);
return surface;
}
cairo_surface_t *
display_create_drm_surface_from_file(struct display *display,
const char *filename,
struct rectangle *rect)
{ {
cairo_surface_t *surface;
GdkPixbuf *pixbuf; GdkPixbuf *pixbuf;
GError *error = NULL; GError *error = NULL;
int stride, i; int stride, i;
unsigned char *pixels, *p, *end; unsigned char *pixels, *p, *end;
pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
width, height, rect->width, rect->height,
FALSE, &error); FALSE, &error);
if (error != NULL) if (error != NULL)
return -1; return NULL;
if (!gdk_pixbuf_get_has_alpha(pixbuf) || if (!gdk_pixbuf_get_has_alpha(pixbuf) ||
gdk_pixbuf_get_n_channels(pixbuf) != 4) { gdk_pixbuf_get_n_channels(pixbuf) != 4) {
gdk_pixbuf_unref(pixbuf); gdk_pixbuf_unref(pixbuf);
return -1; return NULL;
} }
stride = gdk_pixbuf_get_rowstride(pixbuf); stride = gdk_pixbuf_get_rowstride(pixbuf);
pixels = gdk_pixbuf_get_pixels(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf);
for (i = 0; i < height; i++) { for (i = 0; i < rect->height; i++) {
p = pixels + i * stride; p = pixels + i * stride;
end = p + width * 4; end = p + rect->width * 4;
while (p < end) { while (p < end) {
unsigned int t; unsigned int t;
@ -189,12 +274,181 @@ texture_from_png(const char *filename, int width, int height)
} }
} }
surface = display_create_drm_surface(display, rect);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); rect->width, rect->height,
0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
gdk_pixbuf_unref(pixbuf); gdk_pixbuf_unref(pixbuf);
return 0; return surface;
}
#endif
struct wl_buffer *
display_get_buffer_for_surface(struct display *display,
cairo_surface_t *surface)
{
struct surface_data *data;
data = cairo_surface_get_user_data (surface, &surface_data_key);
return data->buffer;
}
struct shm_surface_data {
struct surface_data data;
void *map;
size_t length;
};
void
shm_surface_data_destroy(void *p)
{
struct shm_surface_data *data = p;
wl_buffer_destroy(data->data.buffer);
munmap(data->map, data->length);
}
cairo_surface_t *
display_create_shm_surface(struct display *display,
struct rectangle *rectangle)
{
struct shm_surface_data *data;
cairo_surface_t *surface;
struct wl_visual *visual;
int stride, alloc, fd;
char filename[] = "/tmp/wayland-shm-XXXXXX";
data = malloc(sizeof *data);
if (data == NULL)
return NULL;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
rectangle->width);
data->length = stride * rectangle->height;
fd = mkstemp(filename);
if (fd < 0) {
fprintf(stderr, "open %s failed: %m", filename);
return NULL;
}
if (ftruncate(fd, data->length) < 0) {
fprintf(stderr, "ftruncate failed: %m");
close(fd);
return NULL;
}
data->map = mmap(NULL, data->length,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
unlink(filename);
if (data->map == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m");
close(fd);
return NULL;
}
surface = cairo_image_surface_create_for_data (data->map,
CAIRO_FORMAT_ARGB32,
rectangle->width,
rectangle->height,
stride);
cairo_surface_set_user_data (surface, &surface_data_key,
data, shm_surface_data_destroy);
visual = wl_display_get_premultiplied_argb_visual(display->display);
data->data.buffer = wl_shm_create_buffer(display->shm,
fd,
rectangle->width,
rectangle->height,
stride, visual);
close(fd);
return surface;
}
cairo_surface_t *
display_create_shm_surface_from_file(struct display *display,
const char *filename,
struct rectangle *rect)
{
cairo_surface_t *surface;
GdkPixbuf *pixbuf;
GError *error = NULL;
int stride, i;
unsigned char *pixels, *p, *end, *dest_data;
int dest_stride;
uint32_t *d;
pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
rect->width, rect->height,
FALSE, &error);
if (error != NULL)
return NULL;
if (!gdk_pixbuf_get_has_alpha(pixbuf) ||
gdk_pixbuf_get_n_channels(pixbuf) != 4) {
gdk_pixbuf_unref(pixbuf);
return NULL;
}
stride = gdk_pixbuf_get_rowstride(pixbuf);
pixels = gdk_pixbuf_get_pixels(pixbuf);
surface = display_create_shm_surface(display, rect);
dest_data = cairo_image_surface_get_data (surface);
dest_stride = cairo_image_surface_get_stride (surface);
for (i = 0; i < rect->height; i++) {
d = (uint32_t *) (dest_data + i * dest_stride);
p = pixels + i * stride;
end = p + rect->width * 4;
while (p < end) {
unsigned int t;
unsigned char a, r, g, b;
#define MULT(_d,c,a,t) \
do { t = c * a + 0x7f; _d = ((t >> 8) + t) >> 8; } while (0)
a = p[3];
MULT(r, p[0], a, t);
MULT(g, p[1], a, t);
MULT(b, p[2], a, t);
p += 4;
*d++ = (a << 24) | (r << 16) | (g << 8) | b;
}
}
gdk_pixbuf_unref(pixbuf);
return surface;
}
cairo_surface_t *
display_create_surface(struct display *display,
struct rectangle *rectangle)
{
#ifdef HAVE_CAIRO_GL
display_create_drm_surface(display, rectangle);
#else
display_create_shm_surface(display, rectangle);
#endif
}
cairo_surface_t *
display_create_surface_from_file(struct display *display,
const char *filename,
struct rectangle *rectangle)
{
#ifdef HAVE_CAIRO_GL
display_create_drm_surface_from_file(display, filename, rectangle);
#else
display_create_shm_surface_from_file(display, filename, rectangle);
#endif
} }
static const struct { static const struct {
@ -229,94 +483,11 @@ create_pointer_surfaces(struct display *display)
rect.height = height; rect.height = height;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
display->pointer_surfaces[i] = display->pointer_surfaces[i] =
display_create_surface(display, &rect); display_create_surface_from_file(display,
texture_from_png(pointer_images[i].filename, width, height); pointer_images[i].filename,
} &rect);
}
static const cairo_user_data_key_t surface_data_key;
struct surface_data {
EGLImageKHR image;
GLuint texture;
EGLDisplay dpy;
struct wl_buffer *buffer;
};
static void
surface_data_destroy(void *p)
{
struct surface_data *data = p;
glDeleteTextures(1, &data->texture);
eglDestroyImageKHR(data->dpy, data->image);
if (data->buffer)
wl_buffer_destroy(data->buffer);
}
cairo_surface_t *
display_create_surface(struct display *display,
struct rectangle *rectangle)
{
struct surface_data *data;
EGLDisplay dpy = display->dpy;
cairo_surface_t *surface;
EGLint image_attribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 0,
EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA,
EGL_NONE
};
data = malloc(sizeof *data);
image_attribs[1] = rectangle->width;
image_attribs[3] = rectangle->height;
data->image = eglCreateDRMImageMESA(dpy, image_attribs);
glGenTextures(1, &data->texture);
data->dpy = dpy;
glBindTexture(GL_TEXTURE_2D, data->texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, data->image);
data->buffer = NULL;
surface = cairo_gl_surface_create_for_texture(display->device,
CAIRO_CONTENT_COLOR_ALPHA,
data->texture,
rectangle->width,
rectangle->height);
cairo_surface_set_user_data (surface, &surface_data_key,
data, surface_data_destroy);
return surface;
} }
struct wl_buffer *
display_get_buffer_for_surface(struct display *display,
cairo_surface_t *surface)
{
struct surface_data *data;
struct wl_visual *visual;
struct wl_buffer *buffer;
EGLint name, stride;
int width, height;
data = cairo_surface_get_user_data (surface, &surface_data_key);
if (data->buffer)
return data->buffer;
width = cairo_gl_surface_get_width (surface);
height = cairo_gl_surface_get_height (surface);
eglExportDRMImageMESA(display->dpy, data->image, &name, NULL, &stride);
visual = wl_display_get_premultiplied_argb_visual(display->display);
buffer = wl_drm_create_buffer(display->drm,
name, width, height, stride, visual);
data->buffer = buffer;
return buffer;
} }
cairo_surface_t * cairo_surface_t *
@ -382,6 +553,23 @@ window_flush(struct window *window)
window_attach_surface(window); window_attach_surface(window);
} }
static void
window_create_surface(struct window *window, struct rectangle *allocation)
{
switch (window->buffer_type) {
#ifdef HAVE_CAIRO_GL
case WINDOW_BUFFER_TYPE_DRM:
window->cairo_surface =
display_create_surface(window->display, allocation);
break;
#endif
case WINDOW_BUFFER_TYPE_SHM:
window->cairo_surface =
display_create_shm_surface(window->display, allocation);
break;
}
}
static void static void
window_draw_decorations(struct window *window) window_draw_decorations(struct window *window)
{ {
@ -391,8 +579,8 @@ window_draw_decorations(struct window *window)
cairo_surface_t *frame; cairo_surface_t *frame;
int width, height, shadow_dx = 3, shadow_dy = 3; int width, height, shadow_dx = 3, shadow_dy = 3;
window->cairo_surface = window_create_surface(window, &window->allocation);
display_create_surface(window->display, &window->allocation);
width = window->allocation.width; width = window->allocation.width;
height = window->allocation.height; height = window->allocation.height;
@ -447,8 +635,7 @@ display_flush_cairo_device(struct display *display)
static void static void
window_draw_fullscreen(struct window *window) window_draw_fullscreen(struct window *window)
{ {
window->cairo_surface = window_create_surface(window, &window->allocation);
display_create_surface(window->display, &window->allocation);
} }
void void
@ -985,6 +1172,13 @@ window_move(struct window *window, int32_t x, int32_t y)
window->allocation.height); window->allocation.height);
} }
void
window_damage(struct window *window, int32_t x, int32_t y,
int32_t width, int32_t height)
{
wl_surface_damage(window->surface, x, y, width, height);
}
struct window * struct window *
window_create(struct display *display, const char *title, window_create(struct display *display, const char *title,
int32_t x, int32_t y, int32_t width, int32_t height) int32_t x, int32_t y, int32_t width, int32_t height)
@ -1007,12 +1201,24 @@ window_create(struct display *display, const char *title,
window->margin = 16; window->margin = 16;
window->decoration = 1; window->decoration = 1;
#ifdef HAVE_CAIRO_GL
window->buffer_type = WINDOW_BUFFER_TYPE_DRM;
#else
window->buffer_type = WINDOW_BUFFER_TYPE_SHM;
#endif
wl_surface_set_user_data(window->surface, window); wl_surface_set_user_data(window->surface, window);
wl_list_insert(display->window_list.prev, &window->link); wl_list_insert(display->window_list.prev, &window->link);
return window; return window;
} }
void
window_set_buffer_type(struct window *window, enum window_buffer_type type)
{
window->buffer_type = type;
}
static void static void
drm_handle_device(void *data, struct wl_drm *drm, const char *device) drm_handle_device(void *data, struct wl_drm *drm, const char *device)
{ {
@ -1091,6 +1297,8 @@ display_handle_global(struct wl_display *display, uint32_t id,
} else if (strcmp(interface, "drm") == 0) { } else if (strcmp(interface, "drm") == 0) {
d->drm = wl_drm_create(display, id); d->drm = wl_drm_create(display, id);
wl_drm_add_listener(d->drm, &drm_listener, d); wl_drm_add_listener(d->drm, &drm_listener, d);
} else if (strcmp(interface, "shm") == 0) {
d->shm = wl_shm_create(display, id);
} else if (strcmp(interface, "drag_offer") == 0) { } else if (strcmp(interface, "drag_offer") == 0) {
offer = wl_drag_offer_create(display, id); offer = wl_drag_offer_create(display, id);
d->drag_offer_handler(offer, d); d->drag_offer_handler(offer, d);

@ -138,6 +138,10 @@ window_schedule_redraw(struct window *window);
void void
window_move(struct window *window, int32_t x, int32_t y); window_move(struct window *window, int32_t x, int32_t y);
void
window_damage(struct window *window, int32_t x, int32_t y,
int32_t width, int32_t height);
cairo_surface_t * cairo_surface_t *
window_get_surface(struct window *window); window_get_surface(struct window *window);
@ -149,6 +153,14 @@ window_copy_surface(struct window *window,
void void
window_flush(struct window *window); window_flush(struct window *window);
enum window_buffer_type {
WINDOW_BUFFER_TYPE_DRM,
WINDOW_BUFFER_TYPE_SHM,
};
void
window_set_buffer_type(struct window *window, enum window_buffer_type type);
void void
window_set_fullscreen(struct window *window, int fullscreen); window_set_fullscreen(struct window *window, int fullscreen);

@ -52,6 +52,12 @@ shm_buffer_attach(struct wl_buffer *buffer_base, struct wl_surface *surface)
(struct wlsc_shm_buffer *) buffer_base; (struct wlsc_shm_buffer *) buffer_base;
glBindTexture(GL_TEXTURE_2D, es->texture); glBindTexture(GL_TEXTURE_2D, es->texture);
/* Unbind any EGLImage texture that may be bound, so we don't
* overwrite it.*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
0, 0, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
buffer->base.width, buffer->base.height, 0, buffer->base.width, buffer->base.height, 0,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer->data); GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer->data);

@ -3,6 +3,7 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2])
AC_PROG_CC AC_PROG_CC
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
PKG_PROG_PKG_CONFIG() PKG_PROG_PKG_CONFIG()
@ -10,8 +11,12 @@ PKG_CHECK_MODULES(FFI, [libffi])
PKG_CHECK_MODULES(COMPOSITOR, PKG_CHECK_MODULES(COMPOSITOR,
[egl glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.17] xcb-dri2 xcb-xfixes) [egl glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.17] xcb-dri2 xcb-xfixes)
PKG_CHECK_MODULES(CLIENT, [egl gl cairo-gl gdk-pixbuf-2.0 glib-2.0 gobject-2.0 xkbcommon libdrm]) PKG_CHECK_MODULES(CLIENT, [egl gl cairo gdk-pixbuf-2.0 glib-2.0 gobject-2.0 xkbcommon libdrm])
PKG_CHECK_MODULES(POPPLER, [poppler-glib gdk-2.0]) PKG_CHECK_MODULES(POPPLER, [poppler-glib gdk-2.0])
PKG_CHECK_MODULES(CAIRO_GL, [cairo-gl],
[have_cairo_gl=yes], [have_cairo_gl=no])
AS_IF([test "x$have_cairo_gl" = "xyes"],
[AC_DEFINE([HAVE_CAIRO_GL], [1], [Have cairo-gl])])
if test $CC = gcc; then if test $CC = gcc; then
GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden"

Loading…
Cancel
Save