"Fossies" - the Fresh Open Source Software Archive

Member "genius-1.0.24/vte/src/vtebg.c" (16 Aug 2011, 14045 Bytes) of package /linux/misc/genius-1.0.24.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "vtebg.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) 2003 Red Hat, Inc.
    3  *
    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.
    8  *
    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.
   13  *
   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.
   17  */
   18 
   19 #include <config.h>
   20 
   21 #include <stdio.h>
   22 #include <string.h>
   23 #include <gtk/gtk.h>
   24 #include "debug.h"
   25 #include "marshal.h"
   26 #include "vtebg.h"
   27 #include "vte-gtk-compat.h"
   28 
   29 #ifdef GDK_WINDOWING_X11
   30 #include <gdk/gdkx.h>
   31 #include <cairo-xlib.h>
   32 #endif
   33 
   34 G_DEFINE_TYPE(VteBg, vte_bg, G_TYPE_OBJECT)
   35 
   36 struct _VteBgPrivate {
   37     GList *cache;
   38     GdkScreen *screen;
   39 #ifdef GDK_WINDOWING_X11
   40     cairo_surface_t *root_surface;
   41         struct {
   42                 GdkDisplay *display;
   43                 GdkWindow *window;
   44                 XID native_window;
   45                 GdkAtom atom;
   46                 Atom native_atom;
   47         } native;
   48 #endif
   49 };
   50 
   51 typedef struct {
   52     VteBgSourceType source_type;
   53     GdkPixbuf *source_pixbuf;
   54     char *source_file;
   55 
   56     PangoColor tint_color;
   57     double saturation;
   58     cairo_surface_t *surface;
   59 } VteBgCacheItem;
   60 
   61 static void vte_bg_cache_item_free(VteBgCacheItem *item);
   62 static void vte_bg_cache_prune_int(VteBg *bg, gboolean root);
   63 static const cairo_user_data_key_t item_surface_key;
   64 
   65 #ifdef GDK_WINDOWING_X11
   66 
   67 static void
   68 _vte_bg_display_sync(VteBg *bg)
   69 {
   70         VteBgPrivate *pvt = bg->pvt;
   71 
   72     gdk_display_sync(pvt->native.display);
   73 }
   74 
   75 static gboolean
   76 _vte_property_get_pixmaps(GdkWindow *window, GdkAtom atom,
   77               GdkAtom *type, int *size,
   78               XID **pixmaps)
   79 {
   80     return gdk_property_get(window, atom, GDK_TARGET_PIXMAP,
   81                 0, INT_MAX - 3,
   82                 FALSE,
   83                 type, NULL, size,
   84                 (guchar**) pixmaps);
   85 }
   86 
   87 static cairo_surface_t *
   88 vte_bg_root_surface(VteBg *bg)
   89 {
   90         VteBgPrivate *pvt = bg->pvt;
   91     GdkAtom prop_type;
   92     int prop_size;
   93     Window root;
   94     XID *pixmaps;
   95     int x, y;
   96     unsigned int width, height, border_width, depth;
   97     cairo_surface_t *surface = NULL;
   98     Display *display;
   99     Screen *screen;
  100 
  101     pixmaps = NULL;
  102     gdk_error_trap_push();
  103     if (!_vte_property_get_pixmaps(pvt->native.window, pvt->native.atom,
  104                                        &prop_type, &prop_size,
  105                                        &pixmaps))
  106         goto out;
  107 
  108     if ((prop_type != GDK_TARGET_PIXMAP) ||
  109         (prop_size < (int)sizeof(XID) ||
  110          (pixmaps == NULL)))
  111         goto out_pixmaps;
  112         
  113     if (!XGetGeometry (GDK_DISPLAY_XDISPLAY (pvt->native.display),
  114                pixmaps[0], &root,
  115                &x, &y, &width, &height, &border_width, &depth))
  116         goto out_pixmaps;
  117 
  118     display = gdk_x11_display_get_xdisplay (pvt->native.display);
  119     screen = gdk_x11_screen_get_xscreen (pvt->screen);
  120     surface = cairo_xlib_surface_create (display,
  121                          pixmaps[0],
  122                          DefaultVisualOfScreen(screen),
  123                          width, height);
  124 
  125         _vte_debug_print(VTE_DEBUG_BG|VTE_DEBUG_EVENTS,
  126                          "VteBg new background image %dx%d\n", width, height);
  127 
  128  out_pixmaps:
  129     g_free(pixmaps);
  130  out:
  131     _vte_bg_display_sync(bg);
  132     gdk_error_trap_pop_ignored ();
  133 
  134     return surface;
  135 }
  136 
  137 static void
  138 vte_bg_set_root_surface(VteBg *bg, cairo_surface_t *surface)
  139 {
  140         VteBgPrivate *pvt = bg->pvt;
  141 
  142     if (pvt->root_surface != NULL) {
  143         cairo_surface_destroy (pvt->root_surface);
  144     }
  145     pvt->root_surface = surface;
  146     vte_bg_cache_prune_int (bg, TRUE);
  147     g_signal_emit_by_name(bg, "root-pixmap-changed");
  148 }
  149 
  150 static GdkFilterReturn
  151 vte_bg_root_filter(GdkXEvent *native, GdkEvent *event, gpointer data)
  152 {
  153     XEvent *xevent = (XEvent*) native;
  154     VteBg *bg;
  155         VteBgPrivate *pvt;
  156     cairo_surface_t *surface;
  157 
  158     switch (xevent->type) {
  159     case PropertyNotify:
  160         bg = VTE_BG(data);
  161                 pvt = bg->pvt;
  162         if ((xevent->xproperty.window == pvt->native.native_window) &&
  163             (xevent->xproperty.atom == pvt->native.native_atom)) {
  164             surface = vte_bg_root_surface(bg);
  165             vte_bg_set_root_surface(bg, surface);
  166         }
  167         break;
  168     default:
  169         break;
  170     }
  171     return GDK_FILTER_CONTINUE;
  172 }
  173 
  174 #endif /* GDK_WINDOWING_X11 */
  175 
  176 static void
  177 vte_bg_finalize (GObject *obj)
  178 {
  179     VteBg *bg = VTE_BG (obj);
  180         VteBgPrivate *pvt = bg->pvt;
  181 
  182         g_list_foreach (pvt->cache, (GFunc)vte_bg_cache_item_free, NULL);
  183         g_list_free (pvt->cache);
  184 
  185     G_OBJECT_CLASS(vte_bg_parent_class)->finalize (obj);
  186 }
  187 
  188 static void
  189 vte_bg_class_init(VteBgClass *klass)
  190 {
  191     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
  192 
  193     gobject_class->finalize = vte_bg_finalize;
  194 
  195     g_signal_new("root-pixmap-changed",
  196                      G_OBJECT_CLASS_TYPE(klass),
  197                      G_SIGNAL_RUN_LAST,
  198                      0,
  199                      NULL,
  200                      NULL,
  201                      g_cclosure_marshal_VOID__VOID,
  202                      G_TYPE_NONE, 0);
  203     g_type_class_add_private(klass, sizeof (VteBgPrivate));
  204 }
  205 
  206 static void
  207 vte_bg_init(VteBg *bg)
  208 {
  209     bg->pvt = G_TYPE_INSTANCE_GET_PRIVATE (bg, VTE_TYPE_BG, VteBgPrivate);
  210 }
  211 
  212 /**
  213  * vte_bg_get:
  214  * @screen: a #GdkScreen
  215  *
  216  * Returns the global #VteBg object for @screen, creating it if necessary.
  217  *
  218  * Returns: (transfer none): a #VteBg
  219  */
  220 VteBg *
  221 vte_bg_get_for_screen(GdkScreen *screen)
  222 {
  223     VteBg       *bg;
  224 
  225     bg = g_object_get_data(G_OBJECT(screen), "vte-bg");
  226     if (G_UNLIKELY(bg == NULL)) {
  227                 VteBgPrivate *pvt;
  228 
  229         bg = g_object_new(VTE_TYPE_BG, NULL);
  230         g_object_set_data_full(G_OBJECT(screen),
  231                 "vte-bg", bg, (GDestroyNotify)g_object_unref);
  232 
  233         /* connect bg to screen */
  234                 pvt = bg->pvt;
  235         pvt->screen = screen;
  236 #ifdef GDK_WINDOWING_X11
  237             {
  238                 GdkEventMask events;
  239                 GdkWindow   *window;
  240 
  241         window = gdk_screen_get_root_window(screen);
  242                 pvt->native.window = window;
  243 #if GTK_CHECK_VERSION (2, 91, 6)
  244                 pvt->native.native_window = GDK_WINDOW_XID (window);
  245                 pvt->native.display = gdk_window_get_display(window);
  246 #else
  247                 pvt->native.native_window = gdk_x11_drawable_get_xid(window);
  248                 pvt->native.display = gdk_drawable_get_display(GDK_DRAWABLE(window));
  249 #endif
  250                 pvt->native.native_atom = gdk_x11_get_xatom_by_name_for_display(pvt->native.display, "_XROOTPMAP_ID");
  251                 pvt->native.atom = gdk_x11_xatom_to_atom_for_display(pvt->native.display, pvt->native.native_atom);
  252         pvt->root_surface = vte_bg_root_surface(bg);
  253         events = gdk_window_get_events(window);
  254         events |= GDK_PROPERTY_CHANGE_MASK;
  255         gdk_window_set_events(window, events);
  256         gdk_window_add_filter(window, vte_bg_root_filter, bg);
  257             }
  258 #endif /* GDK_WINDOWING_X11 */
  259     }
  260 
  261     return bg;
  262 }
  263 
  264 static gboolean
  265 vte_bg_colors_equal(const PangoColor *a, const PangoColor *b)
  266 {
  267     return  (a->red >> 8) == (b->red >> 8) &&
  268         (a->green >> 8) == (b->green >> 8) &&
  269         (a->blue >> 8) == (b->blue >> 8);
  270 }
  271 
  272 static void
  273 vte_bg_cache_item_free(VteBgCacheItem *item)
  274 {
  275         _vte_debug_print(VTE_DEBUG_BG,
  276                          "VteBgCacheItem %p freed\n", item);
  277 
  278     /* Clean up whatever is left in the structure. */
  279     if (item->source_pixbuf != NULL) {
  280         g_object_remove_weak_pointer(G_OBJECT(item->source_pixbuf),
  281                 (gpointer*)(void*)&item->source_pixbuf);
  282     }
  283     g_free(item->source_file);
  284 
  285     if (item->surface != NULL)
  286         cairo_surface_set_user_data (item->surface,
  287                          &item_surface_key, NULL, NULL);
  288 
  289     g_slice_free(VteBgCacheItem, item);
  290 }
  291 
  292 static void
  293 vte_bg_cache_prune_int(VteBg *bg, gboolean root)
  294 {
  295     GList *i, *next;
  296     for (i = bg->pvt->cache; i != NULL; i = next) {
  297         VteBgCacheItem *item = i->data;
  298         next = g_list_next (i);
  299         /* Prune the item if either it is a "root pixmap" item and
  300          * we want to prune them, or its surface is NULL because
  301          * whichever object it created has been destroyed. */
  302         if ((root && (item->source_type == VTE_BG_SOURCE_ROOT)) ||
  303             item->surface == NULL) {
  304             vte_bg_cache_item_free (item);
  305             bg->pvt->cache = g_list_delete_link(bg->pvt->cache, i);
  306         }
  307     }
  308 }
  309 
  310 static void
  311 vte_bg_cache_prune(VteBg *bg)
  312 {
  313     vte_bg_cache_prune_int(bg, FALSE);
  314 }
  315 
  316 static void item_surface_destroy_func(void *data)
  317 {
  318     VteBgCacheItem *item = data;
  319 
  320         _vte_debug_print(VTE_DEBUG_BG,
  321                          "VteBgCacheItem %p surface destroyed\n", item);
  322 
  323     item->surface = NULL;
  324 }
  325 
  326 /*
  327  * vte_bg_cache_add:
  328  * @bg: a #VteBg
  329  * @item: a #VteBgCacheItem
  330  *
  331  * Adds @item to @bg's cache, instructing all of the objects therein to
  332  * clear the field which holds a pointer to the object upon its destruction.
  333  */
  334 static void
  335 vte_bg_cache_add(VteBg *bg, VteBgCacheItem *item)
  336 {
  337     vte_bg_cache_prune(bg);
  338     bg->pvt->cache = g_list_prepend(bg->pvt->cache, item);
  339     if (item->source_pixbuf != NULL) {
  340         g_object_add_weak_pointer(G_OBJECT(item->source_pixbuf),
  341                       (gpointer*)(void*)&item->source_pixbuf);
  342     }
  343 
  344         if (item->surface != NULL)
  345                 cairo_surface_set_user_data (item->surface, &item_surface_key, item,
  346                                             item_surface_destroy_func);
  347 }
  348 
  349 /*
  350  * vte_bg_cache_search:
  351  * @bg: a #VteBg
  352  * @source_type: a #VteBgSourceType
  353  * @source_pixbuf: a #GdkPixbuf, or %NULL
  354  * @source_file: path of an image file, or %NULL
  355  * @tint: a #PangoColor to use as tint color
  356  * @saturation: the saturation as a value between 0.0 and 1.0
  357  *
  358  * Returns: a reference to a #cairo_surface_t, or %NULL on if
  359  *   there is no matching item in the cache
  360  */
  361 static cairo_surface_t *
  362 vte_bg_cache_search(VteBg *bg,
  363             VteBgSourceType source_type,
  364             const GdkPixbuf *source_pixbuf,
  365             const char *source_file,
  366             const PangoColor *tint,
  367             double saturation)
  368 {
  369     GList *i;
  370 
  371     vte_bg_cache_prune(bg);
  372     for (i = bg->pvt->cache; i != NULL; i = g_list_next(i)) {
  373         VteBgCacheItem *item = i->data;
  374         if (vte_bg_colors_equal(&item->tint_color, tint) &&
  375             (saturation == item->saturation) &&
  376             (source_type == item->source_type)) {
  377             switch (source_type) {
  378             case VTE_BG_SOURCE_ROOT:
  379                 break;
  380             case VTE_BG_SOURCE_PIXBUF:
  381                 if (item->source_pixbuf != source_pixbuf) {
  382                     continue;
  383                 }
  384                 break;
  385             case VTE_BG_SOURCE_FILE:
  386                 if (strcmp(item->source_file, source_file)) {
  387                     continue;
  388                 }
  389                 break;
  390             default:
  391                 g_assert_not_reached();
  392                 break;
  393             }
  394 
  395             return cairo_surface_reference(item->surface);
  396         }
  397     }
  398     return NULL;
  399 }
  400 
  401 /*< private >
  402  * vte_bg_get_surface:
  403  * @bg: a #VteBg
  404  * @source_type: a #VteBgSourceType
  405  * @source_pixbuf: (allow-none): a #GdkPixbuf, or %NULL
  406  * @source_file: (allow-none): path of an image file, or %NULL
  407  * @tint: a #PangoColor to use as tint color
  408  * @saturation: the saturation as a value between 0.0 and 1.0
  409  * @other: a #cairo_surface_t
  410  *
  411  * Returns: a reference to a #cairo_surface_t, or %NULL on failure
  412  */
  413 cairo_surface_t *
  414 vte_bg_get_surface(VteBg *bg,
  415            VteBgSourceType source_type,
  416            GdkPixbuf *source_pixbuf,
  417            const char *source_file,
  418            const PangoColor *tint,
  419            double saturation,
  420            cairo_surface_t *other)
  421 {
  422         VteBgPrivate *pvt;
  423     VteBgCacheItem *item;
  424     GdkPixbuf *pixbuf;
  425     cairo_surface_t *cached;
  426     cairo_t *cr;
  427     int width, height;
  428 
  429         g_return_val_if_fail(VTE_IS_BG(bg), NULL);
  430         pvt = bg->pvt;
  431 
  432     if (source_type == VTE_BG_SOURCE_NONE) {
  433         return NULL;
  434     }
  435 #ifndef GDK_WINDOWING_X11
  436         if (source_type == VTE_BG_SOURCE_ROOT) {
  437                 return NULL;
  438         }
  439 #endif
  440 
  441     cached = vte_bg_cache_search(bg, source_type,
  442                      source_pixbuf, source_file,
  443                      tint, saturation);
  444     if (cached != NULL) {
  445         return cached;
  446     }
  447 
  448         /* FIXME: The above only returned a hit when the source *and*
  449          * tint and saturation matched. This means that for VTE_BG_SOURCE_FILE,
  450          * we will create below *another* #GdkPixbuf for the same source file,
  451          * wasting memory. We should instead look up the source pixbuf regardless
  452          * of tint and saturation, and just create a new #VteBgCacheItem
  453          * with a new surface for it.
  454          */
  455 
  456     item = g_slice_new(VteBgCacheItem);
  457     item->source_type = source_type;
  458     item->source_pixbuf = NULL;
  459     item->source_file = NULL;
  460     item->tint_color = *tint;
  461     item->saturation = saturation;
  462         item->surface = NULL;
  463     pixbuf = NULL;
  464 
  465     switch (source_type) {
  466     case VTE_BG_SOURCE_ROOT:
  467         break;
  468     case VTE_BG_SOURCE_PIXBUF:
  469         item->source_pixbuf = g_object_ref (source_pixbuf);
  470         pixbuf = g_object_ref (source_pixbuf);
  471         break;
  472     case VTE_BG_SOURCE_FILE:
  473         if (source_file != NULL && source_file[0] != '\0') {
  474             item->source_file = g_strdup(source_file);
  475             pixbuf = gdk_pixbuf_new_from_file(source_file, NULL);
  476         }
  477         break;
  478     default:
  479         g_assert_not_reached();
  480         break;
  481     }
  482 
  483     if (pixbuf) {
  484         width = gdk_pixbuf_get_width(pixbuf);
  485         height = gdk_pixbuf_get_height(pixbuf);
  486     }
  487 #ifdef GDK_WINDOWING_X11
  488         else if (source_type == VTE_BG_SOURCE_ROOT &&
  489                  pvt->root_surface != NULL) {
  490         width = cairo_xlib_surface_get_width(pvt->root_surface);
  491         height = cairo_xlib_surface_get_height(pvt->root_surface);
  492     }
  493 #endif
  494         else
  495                 goto out;
  496 
  497     item->surface =
  498         cairo_surface_create_similar(other, CAIRO_CONTENT_COLOR_ALPHA,
  499                          width, height);
  500 
  501     cr = cairo_create (item->surface);
  502     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  503     if (pixbuf)
  504         gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
  505 #ifdef GDK_WINDOWING_X11
  506     else if (source_type == VTE_BG_SOURCE_ROOT)
  507         cairo_set_source_surface (cr, pvt->root_surface, 0, 0);
  508 #endif
  509     cairo_paint (cr);
  510 
  511     if (saturation < 1.0) {
  512         cairo_set_source_rgba (cr, 
  513                        tint->red / 65535.,
  514                        tint->green / 65535.,
  515                        tint->blue / 65535.,
  516                        1 - saturation);
  517         cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
  518         cairo_paint (cr);
  519     }
  520     cairo_destroy (cr);
  521 
  522     out:
  523     vte_bg_cache_add(bg, item);
  524 
  525     if (pixbuf)
  526         g_object_unref (pixbuf);
  527 
  528     return item->surface;
  529 }