diff options
Diffstat (limited to 'mate-panel/panel-background-monitor.c')
-rw-r--r-- | mate-panel/panel-background-monitor.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/mate-panel/panel-background-monitor.c b/mate-panel/panel-background-monitor.c new file mode 100644 index 00000000..1b4428dd --- /dev/null +++ b/mate-panel/panel-background-monitor.c @@ -0,0 +1,440 @@ +/* + * panel-background-monitor.c: + * + * Copyright (C) 2001, 2002 Ian McKellar <[email protected]> + * 2002 Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Authors: + * Ian McKellar <[email protected]> + * Mark McLoughlin <[email protected]> + */ + +#include <glib.h> +#include <glib-object.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#include "panel-background-monitor.h" +#include "panel-util.h" + +enum { + CHANGED, + LAST_SIGNAL +}; + +static void panel_background_monitor_changed (PanelBackgroundMonitor *monitor); + +static GdkFilterReturn panel_background_monitor_xevent_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data); + +struct _PanelBackgroundMonitorClass { + GObjectClass parent_class; + void (*changed) (PanelBackgroundMonitor *monitor); +}; + +struct _PanelBackgroundMonitor { + GObject parent_instance; + + GdkScreen *screen; + + Window xwindow; + GdkWindow *gdkwindow; + + Atom xatom; + GdkAtom gdkatom; + + GdkPixmap *gdkpixmap; + GdkPixbuf *gdkpixbuf; + + int width; + int height; + + gboolean display_grabbed; +}; + +G_DEFINE_TYPE (PanelBackgroundMonitor, panel_background_monitor, G_TYPE_OBJECT) + +static PanelBackgroundMonitor **global_background_monitors = NULL; + +static guint signals [LAST_SIGNAL] = { 0 }; + +static void +panel_background_monitor_finalize (GObject *object) +{ + PanelBackgroundMonitor *monitor; + + monitor = PANEL_BACKGROUND_MONITOR (object); + + gdk_window_remove_filter ( + monitor->gdkwindow, panel_background_monitor_xevent_filter, monitor); + g_signal_handlers_disconnect_by_func (monitor->screen, + panel_background_monitor_changed, monitor); + + if (monitor->gdkpixmap) + g_object_unref (monitor->gdkpixmap); + monitor->gdkpixmap = NULL; + + if (monitor->gdkpixbuf) + g_object_unref (monitor->gdkpixbuf); + monitor->gdkpixbuf = NULL; + + G_OBJECT_CLASS (panel_background_monitor_parent_class)->finalize (object); +} + +static void +panel_background_monitor_class_init (PanelBackgroundMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + signals [CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PanelBackgroundMonitorClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = panel_background_monitor_finalize; +} + +static void +panel_background_monitor_init (PanelBackgroundMonitor *monitor) +{ + monitor->screen = NULL; + + monitor->gdkwindow = NULL; + monitor->xwindow = None; + + monitor->gdkatom = gdk_atom_intern_static_string ("_XROOTPMAP_ID"); + monitor->xatom = gdk_x11_atom_to_xatom (monitor->gdkatom); + + monitor->gdkpixmap = NULL; + monitor->gdkpixbuf = NULL; + + monitor->display_grabbed = FALSE; +} + +static void +panel_background_monitor_connect_to_screen (PanelBackgroundMonitor *monitor, + GdkScreen *screen) +{ + if (monitor->screen != NULL && monitor->gdkwindow != NULL) { + gdk_window_remove_filter (monitor->gdkwindow, + panel_background_monitor_xevent_filter, + monitor); + } + + monitor->screen = screen; + g_signal_connect_swapped (screen, "size-changed", + G_CALLBACK (panel_background_monitor_changed), monitor); + + monitor->gdkwindow = gdk_screen_get_root_window (screen); + monitor->xwindow = gdk_x11_drawable_get_xid (monitor->gdkwindow); + + gdk_window_add_filter ( + monitor->gdkwindow, panel_background_monitor_xevent_filter, monitor); + + gdk_window_set_events ( + monitor->gdkwindow, + gdk_window_get_events (monitor->gdkwindow) | GDK_PROPERTY_CHANGE_MASK); +} + +static PanelBackgroundMonitor * +panel_background_monitor_new (GdkScreen *screen) +{ + PanelBackgroundMonitor *monitor; + + monitor = g_object_new (PANEL_TYPE_BACKGROUND_MONITOR, NULL); + + panel_background_monitor_connect_to_screen (monitor, screen); + + return monitor; +} + +PanelBackgroundMonitor * +panel_background_monitor_get_for_screen (GdkScreen *screen) +{ + int screen_number; + + screen_number = gdk_screen_get_number (screen); + + if (!global_background_monitors) { + int n_screens; + + n_screens = gdk_display_get_n_screens (gdk_display_get_default ()); + + global_background_monitors = g_new0 (PanelBackgroundMonitor *, n_screens); + } + + if (!global_background_monitors [screen_number]) { + global_background_monitors [screen_number] = + panel_background_monitor_new (screen); + + g_object_add_weak_pointer ( + G_OBJECT (global_background_monitors [screen_number]), + (void **) &global_background_monitors [screen_number]); + + return global_background_monitors [screen_number]; + } + + return g_object_ref (global_background_monitors [screen_number]); +} + +static void +panel_background_monitor_changed (PanelBackgroundMonitor *monitor) +{ + if (monitor->gdkpixmap) + g_object_unref (monitor->gdkpixmap); + monitor->gdkpixmap = NULL; + + if (monitor->gdkpixbuf) + g_object_unref (monitor->gdkpixbuf); + monitor->gdkpixbuf = NULL; + + g_signal_emit (monitor, signals [CHANGED], 0); +} + +static GdkFilterReturn +panel_background_monitor_xevent_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + PanelBackgroundMonitor *monitor; + XEvent *xev; + + g_return_val_if_fail (PANEL_IS_BACKGROUND_MONITOR (data), GDK_FILTER_CONTINUE); + + monitor = PANEL_BACKGROUND_MONITOR (data); + xev = (XEvent *) xevent; + + if (xev->type == PropertyNotify && + xev->xproperty.atom == monitor->xatom && + xev->xproperty.window == monitor->xwindow) + panel_background_monitor_changed (monitor); + + return GDK_FILTER_CONTINUE; +} + +static void +panel_background_monitor_setup_pixmap (PanelBackgroundMonitor *monitor) +{ + Pixmap *prop_data = NULL; + GdkAtom prop_type; + + g_assert (monitor->display_grabbed); + + if (!gdk_property_get ( + monitor->gdkwindow, monitor->gdkatom, + gdk_x11_xatom_to_atom (XA_PIXMAP), 0, 10, + FALSE, &prop_type, NULL, NULL, (gpointer) &prop_data)) + return; + + if ((prop_type == GDK_TARGET_PIXMAP) && prop_data && prop_data [0]) { + GdkDisplay *display; + + g_assert (monitor->gdkpixmap == NULL); + + display = gdk_screen_get_display (monitor->screen); + + monitor->gdkpixmap = gdk_pixmap_foreign_new_for_display (display, + prop_data [0]); + + if (!monitor->gdkpixmap) + g_warning ("couldn't get background pixmap\n"); + } + + g_free (prop_data); +} + +static GdkPixbuf * +panel_background_monitor_tile_background (PanelBackgroundMonitor *monitor, + int width, + int height) +{ + GdkPixbuf *retval; + int tilewidth, tileheight; + + retval = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height); + + tilewidth = gdk_pixbuf_get_width (monitor->gdkpixbuf); + tileheight = gdk_pixbuf_get_height (monitor->gdkpixbuf); + + if (tilewidth == 1 && tileheight == 1) { + guchar *pixels; + int n_channels; + guint32 pixel = 0; + + n_channels = gdk_pixbuf_get_n_channels (monitor->gdkpixbuf); + pixels = gdk_pixbuf_get_pixels (monitor->gdkpixbuf); + + if (pixels) { + if (n_channels == 4) + pixel = ((guint32 *) pixels) [0]; + else if (n_channels == 3) + pixel = pixels [0] << 24 | pixels [1] << 16 | pixels [2] << 8; + } + + gdk_pixbuf_fill (retval, pixel); + } else { + unsigned char *data; + cairo_t *cr; + cairo_surface_t *surface; + cairo_pattern_t *pattern; + + data = g_malloc (width * height * 4); + if (!data) + return NULL; + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_RGB24, + width, height, + width * 4); + cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + gdk_cairo_set_source_pixbuf (cr, monitor->gdkpixbuf, 0, 0); + pattern = cairo_get_source (cr); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + retval = panel_util_cairo_rgbdata_to_pixbuf (data, + width, height); + + g_free (data); + } + + return retval; +} + +static void +panel_background_monitor_setup_pixbuf (PanelBackgroundMonitor *monitor) +{ + GdkColormap *colormap = NULL; + GdkDisplay *display; + int rwidth, rheight; + int pwidth, pheight; + + display = gdk_screen_get_display (monitor->screen); + + gdk_x11_display_grab (display); + monitor->display_grabbed = TRUE; + + if (!monitor->gdkpixmap) + panel_background_monitor_setup_pixmap (monitor); + + if (!monitor->gdkpixmap) { + gdk_x11_display_ungrab (display); + monitor->display_grabbed = FALSE; + return; + } + + #if GTK_CHECK_VERSION(3, 0, 0) + pwidth = gdk_window_get_width(monitor->gdkpixmap); + pheight = gdk_window_get_height(monitor->gdkpixmap); + #else + gdk_drawable_get_size(GDK_DRAWABLE(monitor->gdkpixmap), &pwidth, &pheight); + #endif + + gdk_window_get_geometry (monitor->gdkwindow, + NULL, NULL, &rwidth, &rheight, NULL); + + monitor->width = MIN (pwidth, rwidth); + monitor->height = MIN (pheight, rheight); + + colormap = gdk_drawable_get_colormap (monitor->gdkwindow); + + g_assert (monitor->gdkpixbuf == NULL); + monitor->gdkpixbuf = gdk_pixbuf_get_from_drawable ( + NULL, monitor->gdkpixmap, colormap, + 0, 0, 0, 0, + monitor->width, monitor->height); + + gdk_x11_display_ungrab (display); + monitor->display_grabbed = FALSE; + + if (monitor->gdkpixbuf == NULL) + return; + + if ((monitor->width < rwidth || monitor->height < rheight)) { + GdkPixbuf *tiled; + + tiled = panel_background_monitor_tile_background ( + monitor, rwidth, rheight); + g_object_unref (monitor->gdkpixbuf); + monitor->gdkpixbuf = tiled; + + monitor->width = rwidth; + monitor->height = rheight; + } +} + +GdkPixbuf * +panel_background_monitor_get_region (PanelBackgroundMonitor *monitor, + int x, + int y, + int width, + int height) +{ + GdkPixbuf *pixbuf, *tmpbuf; + int subwidth, subheight; + int subx, suby; + + if (!monitor->gdkpixbuf) + panel_background_monitor_setup_pixbuf (monitor); + + if (!monitor->gdkpixbuf) + return NULL; + + subwidth = MIN (width, monitor->width - x); + subheight = MIN (height, monitor->height - y); + /* if x or y are negative numbers */ + subwidth = MIN (subwidth, width + x); + subheight = MIN (subheight, height + y); + + subx = MAX (x, 0); + suby = MAX (y, 0); + + if ((subwidth <= 0) || (subheight <= 0) || + (monitor->width-x < 0) || (monitor->height-y < 0) ) + /* region is completely offscreen */ + return gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + width, height); + + pixbuf = gdk_pixbuf_new_subpixbuf ( + monitor->gdkpixbuf, subx, suby, subwidth, subheight); + + if ((subwidth < width) || (subheight < height)) { + tmpbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + width, height); + gdk_pixbuf_copy_area (pixbuf, 0, 0, subwidth, subheight, + tmpbuf, (x < 0) ? -x : 0, (y < 0) ? -y : 0); + g_object_unref (pixbuf); + pixbuf = tmpbuf; + } + + return pixbuf; +} |