Add GTK+3 support code into paint_root_pixmap(), never tested.
[lxde/lxpanel.git] / src / panel.c
index b98ccf8..58cb199 100644 (file)
@@ -36,7 +36,6 @@
 
 #include "private.h"
 #include "misc.h"
-#include "bg.h"
 
 #include "lxpanelctl.h"
 #include "dbg.h"
@@ -55,12 +54,12 @@ static gulong monitors_handler = 0;
 static void panel_start_gui(LXPanel *p, config_setting_t *list);
 static void ah_start(LXPanel *p);
 static void ah_stop(LXPanel *p);
-static void on_root_bg_changed(FbBg *bg, LXPanel* p);
 static void _panel_update_background(LXPanel * p);
 
 enum
 {
     ICON_SIZE_CHANGED,
+    PANEL_FONT_CHANGED,
     N_SIGNALS
 };
 
@@ -77,7 +76,7 @@ static void lxpanel_finalize(GObject *object)
         lxpanel_config_save( self );
     config_destroy(p->config);
 
-    XFree(p->workarea);
+    //XFree(p->workarea);
     g_free( p->background_file );
     g_slist_free( p->system_menus );
 
@@ -104,12 +103,6 @@ static void panel_stop_gui(LXPanel *self)
         /* just close the dialog, it will do all required cleanup */
         gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
 
-    if (p->bg != NULL)
-    {
-        g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, self);
-        g_object_unref(p->bg);
-        p->bg = NULL;
-    }
 
     if (p->initialized)
     {
@@ -120,6 +113,11 @@ static void panel_stop_gui(LXPanel *self)
         XSync(xdisplay, True);
         p->initialized = FALSE;
     }
+    if (p->surface != NULL)
+    {
+        cairo_surface_destroy(p->surface);
+        p->surface = NULL;
+    }
 
     if (p->background_update_queued)
     {
@@ -269,16 +267,11 @@ static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e
 {
     Panel *p = LXPANEL(widget)->priv;
 
-    if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
-        goto ok;
     p->cw = e->width;
     p->ch = e->height;
     p->cx = e->x;
     p->cy = e->y;
 
-    if (p->transparent)
-        fb_bg_notify_changed_bg(p->bg);
-ok:
     return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
 }
 
@@ -327,6 +320,15 @@ static void lxpanel_class_init(PanelToplevelClass *klass)
                      NULL, NULL,
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE, 0, G_TYPE_NONE);
+
+    signals[PANEL_FONT_CHANGED] =
+        g_signal_new("panel-font-changed",
+                     G_TYPE_FROM_CLASS(klass),
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET(PanelToplevelClass, panel_font_changed),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE, 0, G_TYPE_NONE);
 }
 
 static void lxpanel_init(PanelToplevel *self)
@@ -382,6 +384,11 @@ void _panel_emit_icon_size_changed(LXPanel *p)
     g_signal_emit(p, signals[ICON_SIZE_CHANGED], 0);
 }
 
+void _panel_emit_font_changed(LXPanel *p)
+{
+    g_signal_emit(p, signals[PANEL_FONT_CHANGED], 0);
+}
+
 /* Normalize panel configuration after load from file or reconfiguration. */
 static void panel_normalize_configuration(Panel* p)
 {
@@ -432,6 +439,8 @@ gboolean _panel_edge_can_strut(LXPanel *panel, int edge, gint monitor, gulong *s
     default: /* error! */
         return FALSE;
     }
+    if (s == 0)
+        return FALSE; /* nothing to strut here */
 
     if (monitor < 0) /* screen span */
     {
@@ -586,11 +595,68 @@ void _panel_set_wm_strut(LXPanel *panel)
  *         panel's handlers for GTK events          *
  ****************************************************/
 
+static void paint_root_pixmap(LXPanel *panel, cairo_t *cr)
+{
+    /*
+     * this code was extracted from code for FbBg object
+     *
+     * Copyright (C) 2001, 2002 Ian McKellar <yakk@yakk.net>
+     *                     2002 Sun Microsystems, Inc.
+     */
+    XGCValues gcv;
+    uint mask;
+    Window xroot;
+    GC gc;
+    Display *dpy;
+    Pixmap *prop;
+#if GTK_CHECK_VERSION(3, 0, 0)
+    cairo_surface_t *surface;
+#else
+    GdkPixmap *pixmap;
+#endif
+    Pixmap xpixmap;
+    Panel *p = panel->priv;
 
-static void
-on_root_bg_changed(FbBg *bg, LXPanel* p)
-{
-    _panel_update_background( p );
+    dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+    xroot = DefaultRootWindow(dpy);
+    gcv.ts_x_origin = 0;
+    gcv.ts_y_origin = 0;
+    gcv.fill_style = FillTiled;
+    mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle;
+    prop = get_xaproperty(xroot, a_XROOTPMAP_ID, XA_PIXMAP, NULL);
+    if (prop)
+    {
+        gcv.tile = *prop;
+        mask |= GCTile;
+        XFree(prop);
+    }
+    gc = XCreateGC(dpy, xroot, mask, &gcv);
+#if GTK_CHECK_VERSION(3, 0, 0)
+    xpixmap = XCreatePixmap(dpy, xroot, p->aw, p->ah,
+                            DefaultDepth(dpy, DefaultScreen(dpy)));
+    surface = cairo_xlib_surface_create(dpy, xpixmap,
+                                        DefaultVisual(dpy, DefaultScreen(dpy)),
+                                        p->aw, p->ah);
+#else
+    pixmap = gdk_pixmap_new(gtk_widget_get_window(GTK_WIDGET(panel)),
+                            p->aw, p->ah, -1);
+    xpixmap = gdk_x11_drawable_get_xid(pixmap);
+#endif
+    XSetTSOrigin(dpy, gc, -p->ax, -p->ay);
+    XFillRectangle(dpy, xpixmap, gc, 0, 0, p->aw, p->ah);
+    XFreeGC(dpy, gc);
+#if GTK_CHECK_VERSION(3, 0, 0)
+    cairo_set_source_surface(cr, surface, 0, 0);
+#else
+    gdk_cairo_set_source_pixmap(cr, pixmap, 0, 0);
+#endif
+    cairo_paint(cr);
+#if GTK_CHECK_VERSION(3, 0, 0)
+    cairo_surface_destroy(surface);
+    XFreePixmap(xpixmap);
+#else
+    g_object_unref(pixmap);
+#endif
 }
 
 void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
@@ -603,43 +669,74 @@ void _panel_determine_background_pixmap(LXPanel * panel, GtkWidget * widget)
     GdkPixmap * pixmap = NULL;
     GdkWindow * window = gtk_widget_get_window(widget);
     Panel * p = panel->priv;
-
-    /* Free p->bg if it is not going to be used. */
-    if (( ! p->transparent) && (p->bg != NULL))
+    cairo_t *cr;
+    GtkAllocation alloc;
+    gint x = 0, y = 0;
+
+    if (!p->background && !p->transparent)
+        goto not_paintable;
+    else if (p->aw <= 1 || p->ah <= 1)
+        goto not_paintable;
+    else if (p->surface == NULL)
     {
-        g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, panel);
-        g_object_unref(p->bg);
-        p->bg = NULL;
-    }
+        GdkPixbuf *pixbuf = NULL;
 
-    if (p->background)
-    {
-        /* User specified background pixmap. */
-        if (p->background_file != NULL)
-            pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
-    }
+        p->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->aw, p->ah);
+        cr = cairo_create(p->surface);
+        if (p->background)
+        {
+            /* User specified background pixmap. */
+            pixbuf = gdk_pixbuf_new_from_file(p->background_file, NULL);
+        }
+        if ((p->transparent && p->alpha != 255) || /* ignore it for opaque panel */
+            (pixbuf != NULL && gdk_pixbuf_get_has_alpha(pixbuf)))
+        {
+            /* Transparent.  Determine the appropriate value from the root pixmap. */
+            paint_root_pixmap(panel, cr);
+        }
+        if (pixbuf != NULL)
+        {
+            gint w = gdk_pixbuf_get_width(pixbuf);
+            gint h = gdk_pixbuf_get_height(pixbuf);
 
-    else if (p->transparent)
-    {
-        /* Transparent.  Determine the appropriate value from the root pixmap. */
-        if (p->bg == NULL)
+            /* Tile the image */
+            for (y = 0; y < p->ah; y += h)
+                for (x = 0; x < p->aw; x += w)
+                {
+                    gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
+                    cairo_paint(cr);
+                }
+            y = 0;
+            g_object_unref(pixbuf);
+        }
+        else
         {
-            p->bg = fb_bg_get_for_display();
-            g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), panel);
+            /* Either color is set or image is invalid, fill the background */
+            gdk_cairo_set_source_color(cr, &p->gtintcolor);
+            cairo_paint_with_alpha(cr, p->transparent ? (double)p->alpha/255 : 1.0);
         }
-        pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
-        if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
-            fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
+        cairo_destroy(cr);
     }
 
-    if (pixmap != NULL)
+    if (p->surface != NULL)
     {
-        gtk_widget_set_app_paintable(widget, TRUE );
+        gtk_widget_set_app_paintable(widget, TRUE);
+        gtk_widget_get_allocation(widget, &alloc);
+        pixmap = gdk_pixmap_new(window, alloc.width, alloc.height, -1);
+        cr = gdk_cairo_create(pixmap);
+        gtk_widget_translate_coordinates(widget, GTK_WIDGET(panel), 0, 0, &x, &y);
+        cairo_set_source_surface(cr, p->surface, 0.0 - x, 0.0 - y);
+        cairo_paint(cr);
+        cairo_destroy(cr);
         gdk_window_set_back_pixmap(window, pixmap, FALSE);
         g_object_unref(pixmap);
     }
     else
+    {
+not_paintable:
         gtk_widget_set_app_paintable(widget, FALSE);
+        /* Free p->bg if it is not going to be used. */
+    }
 }
 
 /* Update the background of the entire panel.
@@ -654,6 +751,13 @@ static void _panel_update_background(LXPanel * p)
     GtkWidget *w = GTK_WIDGET(p);
     GList *plugins = NULL, *l;
 
+    /* reset background image */
+    if (p->priv->surface != NULL)
+    {
+        cairo_surface_destroy(p->priv->surface);
+        p->priv->surface = NULL;
+    }
+
     /* Redraw the top level widget. */
     _panel_determine_background_pixmap(p, w);
     gdk_window_clear(gtk_widget_get_window(w));
@@ -766,17 +870,20 @@ static gboolean ah_state_hide_timeout(gpointer p)
 static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
 {
     Panel *p = panel->priv;
+    GdkRectangle rect;
 
     ENTER;
     if (p->ah_state != ah_state) {
         p->ah_state = ah_state;
         switch (ah_state) {
         case AH_STATE_VISIBLE:
+            p->visible = TRUE;
+            _calculate_position(panel, &rect);
+            gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
             gtk_widget_show(GTK_WIDGET(panel));
             gtk_widget_show(p->box);
             gtk_widget_queue_resize(GTK_WIDGET(panel));
             gtk_window_stick(GTK_WINDOW(panel));
-            p->visible = TRUE;
             break;
         case AH_STATE_WAITING:
             if (p->hide_timeout)
@@ -1299,6 +1406,7 @@ panel_start_gui(LXPanel *panel, config_setting_t *list)
     Panel *p = panel->priv;
     GtkWidget *w = GTK_WIDGET(panel);
     config_setting_t *s;
+    GdkRectangle rect;
     int i;
 
     ENTER;
@@ -1306,7 +1414,7 @@ panel_start_gui(LXPanel *panel, config_setting_t *list)
     g_debug("panel_start_gui on '%s'", p->name);
     p->curdesk = get_net_current_desktop();
     p->desknum = get_net_number_of_desktops();
-    p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
+    //p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
     p->ax = p->ay = p->aw = p->ah = 0;
 
     p->display = gdk_display_get_default();
@@ -1351,6 +1459,9 @@ panel_start_gui(LXPanel *panel, config_setting_t *list)
     panel_set_dock_type(p);
 
     /* window mapping point */
+    p->visible = TRUE;
+    _calculate_position(panel, &rect);
+    gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
     gtk_window_present(GTK_WINDOW(panel));
 
     /* the settings that should be done after window is mapped */