source: pkg/main/gnome-panel/branches/upstream/current/gnome-panel/panel-background-monitor.c @ 180

Revision 180, 11.8 KB checked in by alanbach-guest, 6 years ago (diff)

[svn-inject] Installing original source of gnome-panel

Line 
1/*
2 * panel-background-monitor.c:
3 *
4 * Copyright (C) 2001, 2002 Ian McKellar <yakk@yakk.net>
5 *                     2002 Sun Microsystems, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 * Authors:
23 *      Ian McKellar <yakk@yakk.net>
24 *      Mark McLoughlin <mark@skynet.ie>
25 */
26
27#include <glib.h>
28#include <glib-object.h>
29#include <gdk/gdk.h>
30#include <gdk/gdkx.h>
31#include <X11/Xlib.h>
32#include <X11/Xatom.h>
33
34#include "panel-background-monitor.h"
35#include "panel-util.h"
36
37enum {
38        CHANGED,
39        LAST_SIGNAL
40};
41
42static void panel_background_monitor_changed (PanelBackgroundMonitor *monitor);
43
44static GdkFilterReturn panel_background_monitor_xevent_filter (GdkXEvent *xevent,
45                                                               GdkEvent  *event,
46                                                               gpointer   data);
47
48struct _PanelBackgroundMonitorClass {
49        GObjectClass   parent_class;
50        void         (*changed) (PanelBackgroundMonitor *monitor);
51};
52
53struct _PanelBackgroundMonitor {
54        GObject    parent_instance;
55
56        GdkScreen *screen;
57
58        Window     xwindow;
59        GdkWindow *gdkwindow;
60
61        Atom       xatom;
62        GdkAtom    gdkatom;
63
64        GdkPixmap *gdkpixmap;
65        GdkPixbuf *gdkpixbuf;
66
67        int        width;
68        int        height;
69
70        gboolean   display_grabbed;
71};
72
73static GObjectClass *parent_class;
74
75static PanelBackgroundMonitor **global_background_monitors = NULL;
76
77static guint signals [LAST_SIGNAL] = { 0 };
78
79static void
80panel_background_monitor_finalize (GObject *object)
81{
82        PanelBackgroundMonitor *monitor;
83
84        monitor = PANEL_BACKGROUND_MONITOR (object);
85
86        gdk_window_remove_filter (
87                monitor->gdkwindow, panel_background_monitor_xevent_filter, monitor);
88        g_signal_handlers_disconnect_by_func (monitor->screen, 
89                panel_background_monitor_changed, monitor);
90
91        if (monitor->gdkpixmap)
92                g_object_unref (monitor->gdkpixmap);
93        monitor->gdkpixmap = NULL;
94
95        if (monitor->gdkpixbuf)
96                g_object_unref (monitor->gdkpixbuf);
97        monitor->gdkpixbuf = NULL;
98
99        parent_class->finalize (object);
100}
101
102static void
103panel_background_monitor_class_init (PanelBackgroundMonitorClass *klass)
104{
105        GObjectClass *object_class = G_OBJECT_CLASS (klass);
106
107        parent_class = g_type_class_peek_parent (klass);
108
109        signals [CHANGED] = 
110                g_signal_new ("changed",
111                              G_OBJECT_CLASS_TYPE (object_class),
112                              G_SIGNAL_RUN_LAST,
113                              G_STRUCT_OFFSET (PanelBackgroundMonitorClass, changed),
114                              NULL, NULL,
115                              g_cclosure_marshal_VOID__VOID,
116                              G_TYPE_NONE, 0);
117
118        object_class->finalize = panel_background_monitor_finalize;
119}
120
121static void
122panel_background_monitor_init (PanelBackgroundMonitor *monitor)
123{
124        monitor->screen = NULL;
125
126        monitor->gdkwindow = NULL;
127        monitor->xwindow   = None;
128
129        monitor->gdkatom = gdk_atom_intern ("_XROOTPMAP_ID", FALSE);
130        monitor->xatom   = gdk_x11_atom_to_xatom (monitor->gdkatom);
131
132        monitor->gdkpixmap = NULL;
133        monitor->gdkpixbuf = NULL;
134
135        monitor->display_grabbed = FALSE;
136}
137
138GType
139panel_background_monitor_get_type (void)
140{
141        static GType object_type = 0;
142
143        if (!object_type) {
144                static const GTypeInfo object_info = {
145                        sizeof (PanelBackgroundMonitorClass),
146                        (GBaseInitFunc) NULL,
147                        (GBaseFinalizeFunc) NULL,
148                        (GClassInitFunc) panel_background_monitor_class_init,
149                        NULL,           /* class_finalize */
150                        NULL,           /* class_data */
151                        sizeof (PanelBackgroundMonitor),
152                        0,              /* n_preallocs */
153                        (GInstanceInitFunc) panel_background_monitor_init,
154                };
155
156                object_type = g_type_register_static (
157                                        G_TYPE_OBJECT, "PanelBackgroundMonitor", &object_info, 0);
158        }
159
160        return object_type;
161}
162
163static void
164panel_background_monitor_connect_to_screen (PanelBackgroundMonitor *monitor,
165                                            GdkScreen              *screen)
166{
167        if (monitor->screen != NULL && monitor->gdkwindow != NULL) {
168                gdk_window_remove_filter (monitor->gdkwindow,
169                                          panel_background_monitor_xevent_filter,
170                                          monitor);
171        }
172
173        monitor->screen = screen;
174        g_signal_connect_swapped (screen, "size-changed", 
175            G_CALLBACK (panel_background_monitor_changed), monitor);
176
177        monitor->gdkwindow = gdk_screen_get_root_window (screen);
178        monitor->xwindow   = gdk_x11_drawable_get_xid (monitor->gdkwindow);
179
180        gdk_window_add_filter (
181                monitor->gdkwindow, panel_background_monitor_xevent_filter, monitor);
182
183        gdk_window_set_events (
184                monitor->gdkwindow, 
185                gdk_window_get_events (monitor->gdkwindow) | GDK_PROPERTY_CHANGE_MASK);
186}
187
188static PanelBackgroundMonitor *
189panel_background_monitor_new (GdkScreen *screen)
190{
191        PanelBackgroundMonitor *monitor;
192
193        monitor = g_object_new (PANEL_TYPE_BACKGROUND_MONITOR, NULL);
194
195        panel_background_monitor_connect_to_screen (monitor, screen);
196
197        return monitor;
198}
199
200PanelBackgroundMonitor *
201panel_background_monitor_get_for_screen (GdkScreen *screen)
202{
203        int screen_number;
204
205        screen_number = gdk_screen_get_number (screen);
206
207        if (!global_background_monitors) {
208                int n_screens;
209
210                n_screens = gdk_display_get_n_screens (gdk_display_get_default ());
211
212                global_background_monitors = g_new0 (PanelBackgroundMonitor *, n_screens);
213        }
214
215        if (!global_background_monitors [screen_number]) {
216                global_background_monitors [screen_number] =
217                                panel_background_monitor_new (screen);
218
219                g_object_add_weak_pointer (
220                        G_OBJECT (global_background_monitors [screen_number]),
221                        (void **) &global_background_monitors [screen_number]);
222
223                return global_background_monitors [screen_number];
224        }
225
226        return g_object_ref (global_background_monitors [screen_number]);
227}
228
229static void
230panel_background_monitor_changed (PanelBackgroundMonitor *monitor)
231{
232        if (monitor->gdkpixmap)
233                g_object_unref (monitor->gdkpixmap);
234        monitor->gdkpixmap = NULL;
235
236        if (monitor->gdkpixbuf)
237                g_object_unref (monitor->gdkpixbuf);
238        monitor->gdkpixbuf = NULL;
239
240        g_signal_emit (monitor, signals [CHANGED], 0);
241}
242
243static GdkFilterReturn
244panel_background_monitor_xevent_filter (GdkXEvent *xevent,
245                                        GdkEvent  *event,
246                                        gpointer   data)
247{
248        PanelBackgroundMonitor *monitor;
249        XEvent                 *xev;
250
251        g_return_val_if_fail (PANEL_IS_BACKGROUND_MONITOR (data), GDK_FILTER_CONTINUE);
252
253        monitor = PANEL_BACKGROUND_MONITOR (data);
254        xev     = (XEvent *) xevent;
255
256        if (xev->type == PropertyNotify &&
257            xev->xproperty.atom == monitor->xatom &&
258            xev->xproperty.window == monitor->xwindow)
259                panel_background_monitor_changed (monitor);
260
261        return GDK_FILTER_CONTINUE;
262}
263
264static void
265panel_background_monitor_setup_pixmap (PanelBackgroundMonitor *monitor)
266{
267        Pixmap  *prop_data = NULL;
268        GdkAtom  prop_type;
269
270        g_assert (monitor->display_grabbed);
271
272        if (!gdk_property_get (
273                monitor->gdkwindow, monitor->gdkatom,
274                gdk_x11_xatom_to_atom (XA_PIXMAP), 0, 10, 
275                FALSE, &prop_type, NULL, NULL, (gpointer) &prop_data))
276                return;
277
278        if ((prop_type == GDK_TARGET_PIXMAP) && prop_data && prop_data [0]) {
279                GdkDisplay *display;
280
281                g_assert (monitor->gdkpixmap == NULL);
282
283                display = gdk_screen_get_display (monitor->screen);
284
285                monitor->gdkpixmap = gdk_pixmap_foreign_new_for_display (display,
286                                                                         prop_data [0]);
287
288                if (!monitor->gdkpixmap)
289                        g_warning ("couldn't get background pixmap\n");
290        }
291
292        g_free (prop_data);
293}
294
295static GdkPixbuf *
296panel_background_monitor_tile_background (PanelBackgroundMonitor *monitor,
297                                          int                     width,
298                                          int                     height)
299{
300        GdkPixbuf *retval;
301        int        tilewidth, tileheight;
302
303        retval = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
304
305        tilewidth  = gdk_pixbuf_get_width (monitor->gdkpixbuf);
306        tileheight = gdk_pixbuf_get_height (monitor->gdkpixbuf);
307
308        if (tilewidth == 1 && tileheight == 1) {
309                guchar  *pixels;
310                int      n_channels;
311                guint32  pixel = 0;
312
313                n_channels = gdk_pixbuf_get_n_channels (monitor->gdkpixbuf);
314                pixels     = gdk_pixbuf_get_pixels (monitor->gdkpixbuf);
315
316                if (pixels) {
317                        if (n_channels == 4)
318                                pixel = ((guint32 *) pixels) [0];
319                        else if (n_channels == 3)
320                                pixel = pixels [0] << 24 | pixels [1] << 16 | pixels [2] << 8;
321                }
322
323                gdk_pixbuf_fill (retval, pixel);
324        } else {
325                unsigned char   *data;
326                cairo_t         *cr;
327                cairo_surface_t *surface;
328                cairo_pattern_t *pattern;
329
330                data = g_malloc (width * height * 4);
331                if (!data)
332                        return NULL;
333
334                surface = cairo_image_surface_create_for_data (data,
335                                                               CAIRO_FORMAT_RGB24,
336                                                               width, height,
337                                                               width * 4);
338                cr = cairo_create (surface);
339                cairo_set_source_rgb (cr, 1, 1, 1);
340                cairo_paint (cr);
341
342                gdk_cairo_set_source_pixbuf (cr, monitor->gdkpixbuf, 0, 0);
343                pattern = cairo_get_source (cr);
344                cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
345                cairo_rectangle (cr, 0, 0, width, height);
346                cairo_fill (cr);
347
348                cairo_destroy (cr);
349                cairo_surface_destroy (surface);
350
351                retval = panel_util_cairo_rgbdata_to_pixbuf (data,
352                                                             width, height);
353
354                g_free (data);
355        }
356
357        return retval;
358}
359
360static void 
361panel_background_monitor_setup_pixbuf (PanelBackgroundMonitor *monitor)
362{
363        GdkColormap *colormap = NULL;
364        GdkDisplay  *display;
365        int          rwidth, rheight;
366        int          pwidth, pheight;
367
368        display = gdk_screen_get_display (monitor->screen);
369
370        gdk_x11_display_grab (display);
371        monitor->display_grabbed = TRUE;
372
373        if (!monitor->gdkpixmap)
374                panel_background_monitor_setup_pixmap (monitor);
375
376        if (!monitor->gdkpixmap) {
377                gdk_x11_display_ungrab (display);
378                monitor->display_grabbed = FALSE;
379                return;
380        }
381
382        gdk_drawable_get_size (
383                GDK_DRAWABLE (monitor->gdkpixmap), &pwidth, &pheight);
384
385        gdk_window_get_geometry (monitor->gdkwindow,
386                                 NULL, NULL, &rwidth, &rheight, NULL);
387
388        monitor->width  = MIN (pwidth,  rwidth);
389        monitor->height = MIN (pheight, rheight);
390
391        colormap = gdk_drawable_get_colormap (monitor->gdkwindow);
392
393        g_assert (monitor->gdkpixbuf == NULL);
394        monitor->gdkpixbuf = gdk_pixbuf_get_from_drawable (
395                                        NULL, monitor->gdkpixmap, colormap,
396                                        0, 0, 0, 0, 
397                                        monitor->width, monitor->height);
398
399        gdk_x11_display_ungrab (display);
400        monitor->display_grabbed = FALSE;
401
402        if (monitor->gdkpixbuf == NULL)
403                return;
404
405        if ((monitor->width < rwidth || monitor->height < rheight)) {
406                GdkPixbuf *tiled;
407
408                tiled = panel_background_monitor_tile_background (
409                                                monitor, rwidth, rheight);
410                g_object_unref (monitor->gdkpixbuf);
411                monitor->gdkpixbuf = tiled;
412
413                monitor->width  = rwidth;
414                monitor->height = rheight;
415        }
416}
417
418GdkPixbuf *
419panel_background_monitor_get_region (PanelBackgroundMonitor *monitor,
420                                     int                     x,
421                                     int                     y,
422                                     int                     width,
423                                     int                     height)
424{
425        GdkPixbuf *pixbuf, *tmpbuf;
426        int        subwidth, subheight;
427        int        subx, suby;
428
429        if (!monitor->gdkpixbuf)
430                panel_background_monitor_setup_pixbuf (monitor);
431
432        if (!monitor->gdkpixbuf)
433                return NULL;
434
435        subwidth  = MIN (width,  monitor->width - x);
436        subheight = MIN (height, monitor->height - y);
437        /* if x or y are negative numbers */
438        subwidth  = MIN (subwidth, width + x);
439        subheight  = MIN (subheight, height + y);
440
441        subx = MAX (x, 0);
442        suby = MAX (y, 0);
443
444        if ((subwidth <= 0) || (subheight <= 0) ||
445            (monitor->width-x < 0) || (monitor->height-y < 0) )
446                /* region is completely offscreen */
447                return gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
448                                       width, height);
449
450        pixbuf = gdk_pixbuf_new_subpixbuf (
451                        monitor->gdkpixbuf, subx, suby, subwidth, subheight);
452
453        if ((subwidth < width) || (subheight < height)) {
454                tmpbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
455                                         width, height);
456                gdk_pixbuf_copy_area (pixbuf, 0, 0, subwidth, subheight,
457                                      tmpbuf, (x < 0) ? -x : 0, (y < 0) ? -y : 0);
458                g_object_unref (pixbuf);
459                pixbuf = tmpbuf;
460        }
461
462        return pixbuf;
463}
Note: See TracBrowser for help on using the repository browser.