/* gdkcairo - replacing a gdkwindow with a cairo surface * * Copyright © 2003, 2004 Carl D. Worth * Evan Martin * Øyvind Kolås * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "gdkcairo.h" #include #ifdef CAIRO_HAS_XLIB_SURFACE #include #endif #ifdef CAIRO_HAS_GLITZ_SURFACE #include #endif static void gdkcairo_init (gdkcairo_t *self, GtkWidget *widget) { self->widget = widget; self->cr = NULL; self->backend = GDKCAIRO_BACKEND_IMAGE; #ifdef CAIRO_HAS_XLIB_SURFACE self->backend = GDKCAIRO_BACKEND_XLIB; #endif #ifdef USE_GL { char *GTKCAIRO_GL = getenv ("GTKCAIRO_GL"); if (GTKCAIRO_GL && atoi (GTKCAIRO_GL)) self->backend = GDKCAIRO_BACKEND_GL; } #endif { char *GDKCAIRO_BACKEND = getenv ("GTKCAIRO_BACKEND"); if (GDKCAIRO_BACKEND) { if (!strcmp (GDKCAIRO_BACKEND, "image")) { self->backend = GDKCAIRO_BACKEND_IMAGE; } #ifdef CAIRO_HAS_XLIB_SURFACE else if (!strcmp (GDKCAIRO_BACKEND, "xlib")) { self->backend = GDKCAIRO_BACKEND_XLIB; } #endif #ifdef USE_GL else if (!strcmp (GDKCAIRO_BACKEND, "gl")) { self->backend = GDKCAIRO_BACKEND_GL; } #endif else { self->backend = GDKCAIRO_BACKEND_IMAGE; #ifdef CAIRO_HAS_XLIB_SURFACE self->backend = GDKCAIRO_BACKEND_XLIB; #endif fprintf (stderr, "unknown GTKCAIRO_BACKEND '%s' falling back\n", GDKCAIRO_BACKEND); } } } switch (self->backend) { case GDKCAIRO_BACKEND_IMAGE: break; #ifdef CAIRO_HAS_XLIB_SURFACE case GDKCAIRO_BACKEND_XLIB: break; #endif #ifdef USE_GL case GDKCAIRO_BACKEND_GL: self->glitz_surface = NULL; break; #endif default: g_assert (0); break; } } gdkcairo_t * gdkcairo_new (GtkWidget *widget) { gdkcairo_t *self = malloc (sizeof (gdkcairo_t)); gdkcairo_init (self, widget); return self; } void gdkcairo_destroy (gdkcairo_t *self) { GtkWidget *widget = self->widget; if (self->cr != NULL) { cairo_destroy (self->cr); self->cr = NULL; } #ifdef USE_GL if (self->glitz_surface != NULL) { glitz_surface_destroy (self->glitz_surface); self->glitz_surface = NULL; } #endif /* FIXME: gtk_style_detach (self->widget->style) is missing */ /* FIXME: how is self freed? */ } void gdkcairo_realize (gdkcairo_t *self) { GtkWidget *widget = self->widget; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); GTK_WIDGET_SET_FLAGS (self->widget, GTK_REALIZED); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; attributes.visual = gtk_widget_get_visual (widget); retry: switch (self->backend) { case GDKCAIRO_BACKEND_IMAGE: break; #ifdef CAIRO_HAS_XLIB_SURFACE case GDKCAIRO_BACKEND_XLIB: attributes.colormap = gtk_widget_get_colormap (widget); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); break; #endif #ifdef USE_GL case GDKCAIRO_BACKEND_GL: { Display *dpy = gdk_x11_get_default_xdisplay (); int screen = gdk_x11_get_default_screen (); XVisualInfo *vinfo; glitz_drawable_format_t *dformat; glitz_drawable_format_t templ; unsigned long mask; char *GTKCAIRO_GL_DOUBLEBUFFER; char *GTKCAIRO_GL_SAMPLES; GTKCAIRO_GL_DOUBLEBUFFER = getenv ("GTKCAIRO_GL_DOUBLEBUFFER"); GTKCAIRO_GL_SAMPLES = getenv ("GTKCAIRO_GL_SAMPLES"); if (GTKCAIRO_GL_DOUBLEBUFFER) { if (atoi (GTKCAIRO_GL_DOUBLEBUFFER)) templ.doublebuffer = 1; else templ.doublebuffer = 0; mask |= GLITZ_FORMAT_DOUBLEBUFFER_MASK; } if (GTKCAIRO_GL_SAMPLES) { templ.samples = atoi (GTKCAIRO_GL_SAMPLES); /* less than 1 sample is not possible */ if (templ.samples < 1) templ.samples = 1; mask |= GLITZ_FORMAT_SAMPLES_MASK; } dformat = glitz_glx_find_window_format (dpy, screen, mask, &templ, 0); if (dformat) { glitz_drawable_t *drawable; glitz_format_t *format; XID xid; cairo_surface_t *cr_surface; vinfo = glitz_glx_get_visual_info_from_format (dpy, screen, dformat); gtk_widget_set_double_buffered (widget, FALSE); attributes.visual = gdkx_visual_get (vinfo->visualid); attributes.colormap = gdk_colormap_new (attributes.visual, TRUE); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); xid = gdk_x11_drawable_get_xid (widget->window); drawable = glitz_glx_create_drawable_for_window (dpy, screen, dformat, xid, attributes.width, attributes.height); format = glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32); self->glitz_surface = glitz_surface_create (drawable, format, attributes.width, attributes.height, 0, NULL); glitz_surface_attach (self->glitz_surface, drawable, (dformat->doublebuffer) ? GLITZ_DRAWABLE_BUFFER_BACK_COLOR : GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); glitz_drawable_destroy (drawable); cr_surface = cairo_glitz_surface_create (self->glitz_surface); self->cr = cairo_create (cr_surface); cairo_surface_destroy (cr_surface); } else { g_warning ("could not find a usable GL visual\n"); self->backend = GDKCAIRO_BACKEND_XLIB; goto retry; } } break; #endif default: break; } gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); gdk_window_set_user_data (widget->window, widget); } void gdkcairo_size_allocate (gdkcairo_t *self, gint x, gint y, gint width, gint height) { if (GTK_WIDGET_REALIZED (self->widget)) { gdk_window_move_resize (self->widget->window, x, y, width, height); switch (self->backend) { #ifdef CAIRO_HAS_XLIB_SURFACE case GDKCAIRO_BACKEND_XLIB: break; #endif #ifdef USE_GL case GDKCAIRO_BACKEND_GL: if (self->glitz_surface) { glitz_format_t *format; glitz_drawable_t *drawable; glitz_drawable_format_t *dformat; cairo_surface_t *cr_surface; format = glitz_surface_get_format (self->glitz_surface); drawable = glitz_surface_get_drawable (self->glitz_surface); glitz_drawable_reference (drawable); dformat = glitz_drawable_get_format (drawable); cairo_destroy (self->cr); glitz_surface_destroy (self->glitz_surface); glitz_drawable_update_size (drawable, width, height); self->glitz_surface = glitz_surface_create (drawable, format, width, height, 0, NULL); glitz_drawable_destroy(drawable); glitz_surface_attach (self->glitz_surface, drawable, (dformat->doublebuffer) ? GLITZ_DRAWABLE_BUFFER_BACK_COLOR : GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); cr_surface = cairo_glitz_surface_create (self->glitz_surface); self->cr = cairo_create (cr_surface); cairo_surface_destroy (cr_surface); } break; #endif default: g_assert (0); break; } } } gint gdkcairo_expose (gdkcairo_t *self, GdkEventExpose *event) { GtkWidget *widget = self->widget; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (event != NULL, FALSE); switch (self->backend) { #ifdef USE_GL case GDKCAIRO_BACKEND_GL: { glitz_drawable_t *drawable; glitz_drawable_format_t *dformat; cairo_save (self->cr); cairo_rectangle (self->cr, 0, 0, widget->allocation.width, widget->allocation.height); gtk_cairo_set_gdk_color (self->cr, &(self->widget->style->bg[GTK_STATE_NORMAL])); cairo_fill (self->cr); cairo_restore (self->cr); cairo_save (self->cr); g_signal_emit_by_name (self->widget, "paint", self->cr); cairo_restore (self->cr); /* FIXME: flush cairo first. */ drawable = glitz_surface_get_drawable (self->glitz_surface); dformat = glitz_drawable_get_format (drawable); glitz_surface_flush (self->glitz_surface); if (dformat->doublebuffer) glitz_drawable_swap_buffers (drawable); else glitz_drawable_flush (drawable); } break; #endif #ifdef CAIRO_HAS_XLIB_SURFACE case GDKCAIRO_BACKEND_XLIB: { GdkDrawable *gdkdrawable; gint x_off, y_off; gint width, height; cairo_surface_t *x11_surface; /* find drawable, offset and size */ gdk_window_get_internal_paint_info (widget->window, &gdkdrawable, &x_off, &y_off); gdk_drawable_get_size (gdkdrawable, &width, &height); x11_surface = cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay (gdkdrawable), gdk_x11_drawable_get_xid (gdkdrawable), gdk_x11_visual_get_xvisual (gdk_drawable_get_visual (gdkdrawable)), width, height); cairo_surface_set_device_offset (x11_surface, -x_off, -y_off); self->cr = cairo_create (x11_surface); cairo_surface_destroy (x11_surface); g_signal_emit_by_name (self->widget, "paint", self->cr); cairo_destroy (self->cr); self->cr = NULL; } break; #endif default: g_assert (0); } return FALSE; } /* vim: set ts=4 sw=4 et : */