2 * Copyright (C) 2003 Red Hat, Inc.
4 * This is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Library General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #ifdef GDK_WINDOWING_X11
30 #include <cairo-xlib.h>
33 G_DEFINE_TYPE(VteBg, vte_bg, G_TYPE_OBJECT)
35 struct _VteBgPrivate {
38 #ifdef GDK_WINDOWING_X11
39 cairo_surface_t *root_surface;
51 VteBgSourceType source_type;
52 GdkPixbuf *source_pixbuf;
55 PangoColor tint_color;
57 cairo_surface_t *surface;
60 static void vte_bg_cache_item_free(VteBgCacheItem *item);
61 static void vte_bg_cache_prune_int(VteBg *bg, gboolean root);
62 static const cairo_user_data_key_t item_surface_key;
64 #ifdef GDK_WINDOWING_X11
67 _vte_bg_display_sync(VteBg *bg)
69 VteBgPrivate *pvt = bg->pvt;
71 gdk_display_sync(pvt->native.display);
75 _vte_property_get_pixmaps(GdkWindow *window, GdkAtom atom,
76 GdkAtom *type, int *size,
79 return gdk_property_get(window, atom, GDK_TARGET_PIXMAP,
86 static cairo_surface_t *
87 vte_bg_root_surface(VteBg *bg)
89 VteBgPrivate *pvt = bg->pvt;
96 unsigned int width, height, border_width, depth;
97 cairo_surface_t *surface = NULL;
103 gdk_error_trap_push();
104 if (!_vte_property_get_pixmaps(pvt->native.window, pvt->native.atom,
105 &prop_type, &prop_size,
109 if ((prop_type != GDK_TARGET_PIXMAP) ||
110 (prop_size < (int)sizeof(XID) ||
114 if (!XGetGeometry (GDK_DISPLAY_XDISPLAY (pvt->native.display),
116 &x, &y, &width, &height, &border_width, &depth))
119 display = gdk_x11_display_get_xdisplay (pvt->native.display);
120 screen = gdk_x11_screen_get_xscreen (pvt->screen);
121 surface = cairo_xlib_surface_create (display,
123 DefaultVisualOfScreen(screen),
126 _vte_debug_print(VTE_DEBUG_BG|VTE_DEBUG_EVENTS,
127 "VteBg new background image %dx%d\n", width, height);
132 _vte_bg_display_sync(bg);
133 gdk_error_trap_pop();
139 vte_bg_set_root_surface(VteBg *bg, cairo_surface_t *surface)
141 VteBgPrivate *pvt = bg->pvt;
143 if (pvt->root_surface != NULL) {
144 cairo_surface_destroy (pvt->root_surface);
146 pvt->root_surface = surface;
147 vte_bg_cache_prune_int (bg, TRUE);
148 g_signal_emit_by_name(bg, "root-pixmap-changed");
151 static GdkFilterReturn
152 vte_bg_root_filter(GdkXEvent *native, GdkEvent *event, gpointer data)
154 XEvent *xevent = (XEvent*) native;
157 cairo_surface_t *surface;
159 switch (xevent->type) {
163 if ((xevent->xproperty.window == pvt->native.native_window) &&
164 (xevent->xproperty.atom == pvt->native.native_atom)) {
165 surface = vte_bg_root_surface(bg);
166 vte_bg_set_root_surface(bg, surface);
172 return GDK_FILTER_CONTINUE;
175 #endif /* GDK_WINDOWING_X11 */
178 vte_bg_finalize (GObject *obj)
180 VteBg *bg = VTE_BG (obj);
181 VteBgPrivate *pvt = bg->pvt;
183 g_list_foreach (pvt->cache, (GFunc)vte_bg_cache_item_free, NULL);
184 g_list_free (pvt->cache);
186 G_OBJECT_CLASS(vte_bg_parent_class)->finalize (obj);
190 vte_bg_class_init(VteBgClass *klass)
192 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
194 gobject_class->finalize = vte_bg_finalize;
196 g_signal_new("root-pixmap-changed",
197 G_OBJECT_CLASS_TYPE(klass),
202 g_cclosure_marshal_VOID__VOID,
204 g_type_class_add_private(klass, sizeof (VteBgPrivate));
208 vte_bg_init(VteBg *bg)
210 bg->pvt = G_TYPE_INSTANCE_GET_PRIVATE (bg, VTE_TYPE_BG, VteBgPrivate);
215 * @screen: a #GdkScreen
217 * Returns the global #VteBg object for @screen, creating it if necessary.
219 * Returns: (transfer none): a #VteBg
222 vte_bg_get_for_screen(GdkScreen *screen)
226 bg = g_object_get_data(G_OBJECT(screen), "vte-bg");
227 if (G_UNLIKELY(bg == NULL)) {
230 bg = g_object_new(VTE_TYPE_BG, NULL);
231 g_object_set_data_full(G_OBJECT(screen),
232 "vte-bg", bg, (GDestroyNotify)g_object_unref);
234 /* connect bg to screen */
236 pvt->screen = screen;
237 #ifdef GDK_WINDOWING_X11
242 window = gdk_screen_get_root_window(screen);
243 pvt->native.window = window;
244 pvt->native.native_window = gdk_x11_drawable_get_xid(window);
245 pvt->native.display = gdk_drawable_get_display(GDK_DRAWABLE(window));
246 pvt->native.native_atom = gdk_x11_get_xatom_by_name_for_display(pvt->native.display, "_XROOTPMAP_ID");
247 pvt->native.atom = gdk_x11_xatom_to_atom_for_display(pvt->native.display, pvt->native.native_atom);
248 pvt->root_surface = vte_bg_root_surface(bg);
249 events = gdk_window_get_events(window);
250 events |= GDK_PROPERTY_CHANGE_MASK;
251 gdk_window_set_events(window, events);
252 gdk_window_add_filter(window, vte_bg_root_filter, bg);
254 #endif /* GDK_WINDOWING_X11 */
261 vte_bg_colors_equal(const PangoColor *a, const PangoColor *b)
263 return (a->red >> 8) == (b->red >> 8) &&
264 (a->green >> 8) == (b->green >> 8) &&
265 (a->blue >> 8) == (b->blue >> 8);
269 vte_bg_cache_item_free(VteBgCacheItem *item)
271 _vte_debug_print(VTE_DEBUG_BG,
272 "VteBgCacheItem %p freed\n", item);
274 /* Clean up whatever is left in the structure. */
275 if (item->source_pixbuf != NULL) {
276 g_object_remove_weak_pointer(G_OBJECT(item->source_pixbuf),
277 (gpointer*)(void*)&item->source_pixbuf);
279 g_free(item->source_file);
281 if (item->surface != NULL)
282 cairo_surface_set_user_data (item->surface,
283 &item_surface_key, NULL, NULL);
285 g_slice_free(VteBgCacheItem, item);
289 vte_bg_cache_prune_int(VteBg *bg, gboolean root)
292 for (i = bg->pvt->cache; i != NULL; i = next) {
293 VteBgCacheItem *item = i->data;
294 next = g_list_next (i);
295 /* Prune the item if either it is a "root pixmap" item and
296 * we want to prune them, or its surface is NULL because
297 * whichever object it created has been destroyed. */
298 if ((root && (item->source_type == VTE_BG_SOURCE_ROOT)) ||
299 item->surface == NULL) {
300 vte_bg_cache_item_free (item);
301 bg->pvt->cache = g_list_delete_link(bg->pvt->cache, i);
307 vte_bg_cache_prune(VteBg *bg)
309 vte_bg_cache_prune_int(bg, FALSE);
312 static void item_surface_destroy_func(void *data)
314 VteBgCacheItem *item = data;
316 _vte_debug_print(VTE_DEBUG_BG,
317 "VteBgCacheItem %p surface destroyed\n", item);
319 item->surface = NULL;
325 * @item: a #VteBgCacheItem
327 * Adds @item to @bg's cache, instructing all of the objects therein to
328 * clear the field which holds a pointer to the object upon its destruction.
331 vte_bg_cache_add(VteBg *bg, VteBgCacheItem *item)
333 vte_bg_cache_prune(bg);
334 bg->pvt->cache = g_list_prepend(bg->pvt->cache, item);
335 if (item->source_pixbuf != NULL) {
336 g_object_add_weak_pointer(G_OBJECT(item->source_pixbuf),
337 (gpointer*)(void*)&item->source_pixbuf);
340 if (item->surface != NULL)
341 cairo_surface_set_user_data (item->surface, &item_surface_key, item,
342 item_surface_destroy_func);
346 * vte_bg_cache_search:
348 * @source_type: a #VteBgSourceType
349 * @source_pixbuf: a #GdkPixbuf, or %NULL
350 * @source_file: path of an image file, or %NULL
351 * @tint: a #PangoColor to use as tint color
352 * @saturation: the saturation as a value between 0.0 and 1.0
354 * Returns: a reference to a #cairo_surface_t, or %NULL on if
355 * there is no matching item in the cache
357 static cairo_surface_t *
358 vte_bg_cache_search(VteBg *bg,
359 VteBgSourceType source_type,
360 const GdkPixbuf *source_pixbuf,
361 const char *source_file,
362 const PangoColor *tint,
367 vte_bg_cache_prune(bg);
368 for (i = bg->pvt->cache; i != NULL; i = g_list_next(i)) {
369 VteBgCacheItem *item = i->data;
370 if (vte_bg_colors_equal(&item->tint_color, tint) &&
371 (saturation == item->saturation) &&
372 (source_type == item->source_type)) {
373 switch (source_type) {
374 case VTE_BG_SOURCE_ROOT:
376 case VTE_BG_SOURCE_PIXBUF:
377 if (item->source_pixbuf != source_pixbuf) {
381 case VTE_BG_SOURCE_FILE:
382 if (strcmp(item->source_file, source_file)) {
387 g_assert_not_reached();
391 return cairo_surface_reference(item->surface);
398 * vte_bg_get_surface:
400 * @source_type: a #VteBgSourceType
401 * @source_pixbuf: (allow-none): a #GdkPixbuf, or %NULL
402 * @source_file: (allow-none): path of an image file, or %NULL
403 * @tint: a #PangoColor to use as tint color
404 * @saturation: the saturation as a value between 0.0 and 1.0
405 * @other: a #cairo_surface_t
407 * Returns: a reference to a #cairo_surface_t, or %NULL on failure
410 vte_bg_get_surface(VteBg *bg,
411 VteBgSourceType source_type,
412 GdkPixbuf *source_pixbuf,
413 const char *source_file,
414 const PangoColor *tint,
416 cairo_surface_t *other)
419 VteBgCacheItem *item;
421 cairo_surface_t *cached;
425 g_return_val_if_fail(VTE_IS_BG(bg), NULL);
428 if (source_type == VTE_BG_SOURCE_NONE) {
431 #ifndef GDK_WINDOWING_X11
432 if (source_type == VTE_BG_SOURCE_ROOT) {
437 cached = vte_bg_cache_search(bg, source_type,
438 source_pixbuf, source_file,
440 if (cached != NULL) {
444 /* FIXME: The above only returned a hit when the source *and*
445 * tint and saturation matched. This means that for VTE_BG_SOURCE_FILE,
446 * we will create below *another* #GdkPixbuf for the same source file,
447 * wasting memory. We should instead look up the source pixbuf regardless
448 * of tint and saturation, and just create a new #VteBgCacheItem
449 * with a new surface for it.
452 item = g_slice_new(VteBgCacheItem);
453 item->source_type = source_type;
454 item->source_pixbuf = NULL;
455 item->source_file = NULL;
456 item->tint_color = *tint;
457 item->saturation = saturation;
458 item->surface = NULL;
461 switch (source_type) {
462 case VTE_BG_SOURCE_ROOT:
464 case VTE_BG_SOURCE_PIXBUF:
465 item->source_pixbuf = g_object_ref (source_pixbuf);
466 pixbuf = g_object_ref (source_pixbuf);
468 case VTE_BG_SOURCE_FILE:
469 if (source_file != NULL && source_file[0] != '\0') {
470 item->source_file = g_strdup(source_file);
471 pixbuf = gdk_pixbuf_new_from_file(source_file, NULL);
475 g_assert_not_reached();
480 width = gdk_pixbuf_get_width(pixbuf);
481 height = gdk_pixbuf_get_height(pixbuf);
483 #ifdef GDK_WINDOWING_X11
484 else if (source_type == VTE_BG_SOURCE_ROOT &&
485 pvt->root_surface != NULL) {
486 width = cairo_xlib_surface_get_width(pvt->root_surface);
487 height = cairo_xlib_surface_get_height(pvt->root_surface);
494 cairo_surface_create_similar(other, CAIRO_CONTENT_COLOR_ALPHA,
497 cr = cairo_create (item->surface);
498 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
500 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
501 #ifdef GDK_WINDOWING_X11
502 else if (source_type == VTE_BG_SOURCE_ROOT)
503 cairo_set_source_surface (cr, pvt->root_surface, 0, 0);
507 if (saturation < 1.0) {
508 cairo_set_source_rgba (cr,
510 tint->green / 65535.,
513 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
519 vte_bg_cache_add(bg, item);
522 g_object_unref (pixbuf);
524 return item->surface;