diff --git a/data/config/default.in b/data/config/default.in index 4adbb27..6076bd0 100644 --- a/data/config/default.in +++ b/data/config/default.in @@ -263,7 +263,21 @@ Plugin { Plugin { type = pager config { + # show wallpaper background in pager showwallpaper = true + + # show icons on windows in pager + ShowIcon = true + IconSize = 24 + IconMinSize = 8 + + # show desktop names in pager + ShowNamesInPager = true + ShowNamesInTooltip = true + NamesHeight = 12 + NamesFmt = %s + NamesBackgroundNormal = #cccccc + NamesBackgroundActive = #808080 } } diff --git a/plugins/pager/pager.c b/plugins/pager/pager.c index ebe2052..43d3664 100644 --- a/plugins/pager/pager.c +++ b/plugins/pager/pager.c @@ -28,6 +28,11 @@ #include +#include +#include +#include +#include + #include "panel.h" #include "misc.h" #include "plugin.h" @@ -47,6 +52,7 @@ typedef struct _task { guint stacking; guint desktop; char *name, *iname; + GdkPixbuf *icon; net_wm_state nws; net_wm_window_type nwwt; } task; @@ -58,6 +64,9 @@ typedef struct _pager_priv pager_priv; /* map of a desktop */ struct _desk { GtkWidget *da; + GtkWidget *da_name_box; + GtkWidget *da_name_text; + GtkWidget *da_vbox; Pixmap xpix; GdkPixmap *gpix; GdkPixmap *pix; @@ -73,6 +82,17 @@ struct _pager_priv { guint desknum; guint curdesk; gint wallpaper; + gint icon_show; + gint icon_size; + gint icon_min_size; + gint names_show_in_pager; + gint names_show_in_tooltip; + gint names_height; + char *names_fmt; + GdkColor names_color_normal; + GdkColor names_color_active; + char **dnames; + gint dnames_num; //int dw, dh; gfloat /*scalex, scaley, */ratio; Window *wins; @@ -80,14 +100,136 @@ struct _pager_priv { GHashTable* htable; task *focusedtask; FbBg *fbbg; - gint dah, daw; + gint dah, daw, dah2, daw2; }; - - #define TASK_VISIBLE(tk) \ (!( (tk)->nws.hidden || (tk)->nws.skip_pager )) +#define ICON_MARGIN 1 + +// from taskbar.c BEGIN // + +// TODO probably good idea is move this code from this file and from taskbar.c to panel/misc.c + +static void +free_pixels (guchar *pixels, gpointer data) +{ + ENTER; + g_free (pixels); + RET(); +} + +static guchar * +argbdata_to_pixdata (gulong *argb_data, int len) +{ + guchar *p, *ret; + int i; + + ENTER; + ret = p = g_new (guchar, len * 4); + if (!ret) + RET(NULL); + /* One could speed this up a lot. */ + i = 0; + while (i < len) { + guint32 argb; + guint32 rgba; + + argb = argb_data[i]; + rgba = (argb << 8) | (argb >> 24); + + *p = rgba >> 24; + ++p; + *p = (rgba >> 16) & 0xff; + ++p; + *p = (rgba >> 8) & 0xff; + ++p; + *p = rgba & 0xff; + ++p; + + ++i; + } + RET(ret); +} + +static GdkPixbuf * +get_netwm_icon(Window tkwin, int iw, int ih) +{ + gulong *data; + GdkPixbuf *ret = NULL; + int n; + guchar *p; + GdkPixbuf *src; + int w, h; + + ENTER; + data = get_xaproperty(tkwin, a_NET_WM_ICON, XA_CARDINAL, &n); + if (!data) + RET(NULL); + + /* loop through all icons in data to find best fit */ + if (0) { + gulong *tmp; + int len; + + len = n/sizeof(gulong); + tmp = data; + while (len > 2) { + int size = tmp[0] * tmp[1]; + DBG("sub-icon: %dx%d %d bytes\n", tmp[0], tmp[1], size * 4); + len -= size + 2; + tmp += size; + } + } + + if (0) { + int i, j, nn; + + nn = MIN(10, n); + p = (guchar *) data; + for (i = 0; i < nn; i++) { + for (j = 0; j < sizeof(gulong); j++) + ERR("%02x ", (guint) p[i*sizeof(gulong) + j]); + ERR("\n"); + } + } + + /* check that data indeed represents icon in w + h + ARGB[] format + * with 16x16 dimension at least */ + if (n < (16 * 16 + 1 + 1)) { + ERR("win %lx: icon is too small or broken (size=%d)\n", tkwin, n); + goto out; + } + w = data[0]; + h = data[1]; + /* check that sizes are in 64-256 range */ + if (w < 16 || w > 256 || h < 16 || h > 256) { + ERR("win %lx: icon size (%d, %d) is not in 64-256 range\n", + tkwin, w, h); + goto out; + } + + DBG("orig %dx%d dest %dx%d\n", w, h, iw, ih); + p = argbdata_to_pixdata(data + 2, w * h); + if (!p) + goto out; + src = gdk_pixbuf_new_from_data (p, GDK_COLORSPACE_RGB, TRUE, + 8, w, h, w * 4, free_pixels, NULL); + if (src == NULL) + goto out; + ret = src; + if (w != iw || h != ih) { + ret = gdk_pixbuf_scale_simple(src, iw, ih, GDK_INTERP_HYPER); + g_object_unref(src); + } + +out: + XFree(data); + RET(ret); +} + +// from taskbar.c END // static void pager_rebuild_all(FbEv *ev, pager_priv *pg); static void desk_draw_bg(pager_priv *pg, desk *d1); @@ -142,6 +284,8 @@ task_remove_stale(Window *win, task *t, pager_priv *p) if (p->focusedtask == t) p->focusedtask = NULL; DBG("del %lx\n", t->win); + if(t->icon) + g_object_unref(t->icon); g_free(t); return TRUE; } @@ -152,6 +296,8 @@ task_remove_stale(Window *win, task *t, pager_priv *p) static gboolean task_remove_all(Window *win, task *t, pager_priv *p) { + if(t->icon) + g_object_unref(t->icon); g_free(t); return TRUE; } @@ -192,6 +338,8 @@ task_update_pix(task *t, desk *d) { int x, y, w, h; GtkWidget *widget; + GdkPixbuf *icon_pixbuf; + gint icon_size; ENTER; g_return_if_fail(d->pix != NULL); @@ -222,6 +370,28 @@ task_update_pix(task *t, desk *d) widget->style->fg_gc[GTK_STATE_NORMAL], FALSE, x, y, w, h); + if (d->pg->icon_show + && w > d->pg->icon_min_size + 2 * ICON_MARGIN + && h > d->pg->icon_min_size + 2 * ICON_MARGIN) { + if (w < d->pg->icon_size + 2 * ICON_MARGIN + || h < d->pg->icon_size + 2 * ICON_MARGIN) { + icon_size = MIN(w - 2 * ICON_MARGIN, h - 2 * ICON_MARGIN); + icon_pixbuf = gdk_pixbuf_scale_simple(t->icon,icon_size, icon_size, GDK_INTERP_HYPER); + } else { + icon_size = d->pg->icon_size; + icon_pixbuf = t->icon; + } + gdk_draw_pixbuf(d->pix, + (d->pg->focusedtask == t) ? + widget->style->fg_gc[GTK_STATE_SELECTED] : + widget->style->fg_gc[GTK_STATE_NORMAL], + icon_pixbuf, + 0, 0, + x + w/2 - icon_size/2, y + h/2 - icon_size/2, + icon_size, icon_size, + GDK_RGB_DITHER_NONE, + 0, 0); + } RET(); } @@ -457,8 +627,36 @@ desk_new(pager_priv *pg, int i) d->no = i; d->da = gtk_drawing_area_new(); - gtk_widget_set_size_request(d->da, pg->daw, pg->dah); - gtk_box_pack_start(GTK_BOX(pg->box), d->da, TRUE, TRUE, 0); + + if (pg->names_show_in_pager) { + d->da_vbox = gtk_vbox_new(TRUE, 1); + + d->da_name_text = gtk_label_new("name"); /// FIXME + gtk_misc_set_alignment(GTK_MISC(d->da_name_text), 0.5, 0.5); + gtk_misc_set_padding(GTK_MISC(d->da_name_text), 1, 0); + gtk_label_set_justify(GTK_LABEL(d->da_name_text), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap(GTK_LABEL(d->da_name_text), TRUE); + gtk_label_set_ellipsize(GTK_LABEL(d->da_name_text), PANGO_ELLIPSIZE_START); + + gtk_box_set_homogeneous(GTK_BOX(d->da_vbox), FALSE); + gtk_widget_set_size_request(d->da, pg->daw2, pg->dah2); + gtk_widget_set_size_request(d->da_name_text, pg->daw2, pg->names_height); + + d->da_name_box = gtk_bgbox_new(); + gtk_widget_modify_bg(d->da_name_box, GTK_STATE_NORMAL, &pg->names_color_normal); + + gtk_box_pack_start(GTK_BOX(pg->box), d->da_vbox, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(d->da_vbox), d->da, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(d->da_vbox), d->da_name_box, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(d->da_name_box), d->da_name_text); + + gtk_widget_show_all(d->da_name_text); + gtk_widget_show_all(d->da_vbox); + } else { + gtk_widget_set_size_request(d->da, pg->daw, pg->dah); + gtk_box_pack_start(GTK_BOX(pg->box), d->da, TRUE, TRUE, 0); + } + gtk_widget_add_events (d->da, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); @@ -469,6 +667,7 @@ desk_new(pager_priv *pg, int i) g_signal_connect (G_OBJECT (d->da), "button_press_event", (GCallback) desk_button_press_event, (gpointer)d); gtk_widget_show_all(d->da); + RET(); } @@ -530,12 +729,18 @@ do_net_current_desktop(FbEv *ev, pager_priv *pg) desk_set_dirty(pg->desks[pg->curdesk]); gtk_widget_set_state(pg->desks[pg->curdesk]->da, GTK_STATE_NORMAL); //pager_paint_frame(pg, pg->curdesk, GTK_STATE_NORMAL); + if (pg->names_show_in_pager) + gtk_widget_modify_bg(pg->desks[pg->curdesk]->da_name_box, + GTK_STATE_NORMAL, &pg->names_color_normal); pg->curdesk = get_net_current_desktop (); if (pg->curdesk >= pg->desknum) pg->curdesk = 0; desk_set_dirty(pg->desks[pg->curdesk]); gtk_widget_set_state(pg->desks[pg->curdesk]->da, GTK_STATE_SELECTED); //pager_paint_frame(pg, pg->curdesk, GTK_STATE_SELECTED); + if (pg->names_show_in_pager) + gtk_widget_modify_bg(pg->desks[pg->curdesk]->da_name_box, + GTK_STATE_NORMAL, &pg->names_color_active); RET(); } @@ -571,6 +776,10 @@ do_net_client_list_stacking(FbEv *ev, pager_priv *p) t->desktop = get_net_wm_desktop(t->win); get_net_wm_state(t->win, &t->nws); get_net_wm_window_type(t->win, &t->nwwt); + if (p->icon_show) + t->icon = get_netwm_icon(t->win, p->icon_size, p->icon_size); + else + t->icon = NULL; task_get_sizepos(t); g_hash_table_insert(p->htable, &t->win, t); DBG("add %lx\n", t->win); @@ -703,6 +912,30 @@ pager_bg_changed(FbBg *bg, pager_priv *pg) RET(); } +static void +update_names(GtkWidget *widget, pager_priv *pg) +{ + int i; + gchar *buf; + + ENTER; + pg->desknum = get_net_number_of_desktops(); + if (pg->dnames) + g_strfreev (pg->dnames); + pg->dnames = get_utf8_property_list(GDK_ROOT_WINDOW(), a_NET_DESKTOP_NAMES, &(pg->dnames_num)); + + for (i = 0; i < MIN(pg->desknum, pg->dnames_num); i++) { + if (pg->names_show_in_tooltip) { + gtk_widget_set_tooltip_markup(pg->desks[i]->da, pg->dnames[i]); + } + if (pg->names_show_in_pager) { + buf = g_strdup_printf(pg->names_fmt, pg->dnames[i]); + gtk_label_set_markup(GTK_LABEL(pg->desks[i]->da_name_text), buf); + g_free(buf); + } + } + RET(); +} static void pager_rebuild_all(FbEv *ev, pager_priv *pg) @@ -740,17 +973,21 @@ pager_rebuild_all(FbEv *ev, pager_priv *pg) desk_new(pg, i); } g_hash_table_foreach_remove(pg->htable, (GHRFunc) task_remove_all, (gpointer)pg); + if (pg->names_show_in_pager || pg->names_show_in_tooltip) + update_names(NULL, pg); do_net_current_desktop(NULL, pg); do_net_client_list_stacking(NULL, pg); RET(); } + #define BORDER 1 static int pager_constructor(plugin_instance *plug) { pager_priv *pg; + gchar *color_normal = NULL, *color_active = NULL; ENTER; pg = (pager_priv *) plug; @@ -769,24 +1006,62 @@ pager_constructor(plugin_instance *plug) gtk_container_set_border_width (GTK_CONTAINER (plug->pwid), BORDER); gtk_container_add(GTK_CONTAINER(plug->pwid), pg->box); + pg->wallpaper = 1; + pg->icon_show = 1; + pg->icon_size = 24; + pg->icon_min_size = 10; + pg->names_show_in_tooltip = 1; + pg->names_show_in_pager = 0; + pg->names_height = 17; + pg->names_fmt = "%s"; + pg->dnames = NULL; + //pg->scaley = (gfloat)pg->dh / (gfloat)gdk_screen_height(); + //pg->scalex = (gfloat)pg->dw / (gfloat)gdk_screen_width(); + XCG(plug->xc, "showwallpaper", &pg->wallpaper, enum, bool_enum); + XCG(plug->xc, "ShowIcon", &pg->icon_show, enum, bool_enum); + XCG(plug->xc, "IconSize", &pg->icon_size, int); + XCG(plug->xc, "IconMinSize", &pg->icon_min_size, int); + XCG(plug->xc, "ShowNamesInTooltip", &pg->names_show_in_tooltip, enum, bool_enum); + XCG(plug->xc, "ShowNamesInPager", &pg->names_show_in_pager, enum, bool_enum); + if (pg->names_show_in_pager) { + XCG(plug->xc, "NamesHeight", &pg->names_height, int); + XCG(plug->xc, "NamesFmt", &pg->names_fmt, str); + + XCG(plug->xc, "NamesBackgroundNormal", &color_normal, str); + if (color_normal) { + gdk_color_parse(color_normal, &pg->names_color_normal); + } else { + gdk_color_parse("#cccccc", &pg->names_color_normal); + } + + XCG(plug->xc, "NamesBackgroundActive", &color_active, str); + if (color_active) { + gdk_color_parse(color_active, &pg->names_color_active); + } else { + gdk_color_parse("#808080", &pg->names_color_active); + } + } + pg->ratio = (gfloat)gdk_screen_width() / (gfloat)gdk_screen_height(); if (plug->panel->orientation == GTK_ORIENTATION_HORIZONTAL) { pg->dah = plug->panel->ah - 2 * BORDER; + pg->dah2 = pg->dah - pg->names_height; pg->daw = (gfloat) pg->dah * pg->ratio; + pg->daw2 = (gfloat) pg->dah2 * pg->ratio; } else { pg->daw = plug->panel->aw - 2 * BORDER; + pg->daw2 = pg->daw; pg->dah = (gfloat) pg->daw / pg->ratio; + pg->dah2 = pg->dah; } - pg->wallpaper = 1; - //pg->scaley = (gfloat)pg->dh / (gfloat)gdk_screen_height(); - //pg->scalex = (gfloat)pg->dw / (gfloat)gdk_screen_width(); - XCG(plug->xc, "showwallpaper", &pg->wallpaper, enum, bool_enum); + if (pg->wallpaper) { pg->fbbg = fb_bg_get_for_display(); DBG("get fbbg %p\n", pg->fbbg); g_signal_connect(G_OBJECT(pg->fbbg), "changed", G_CALLBACK(pager_bg_changed), pg); } + pager_rebuild_all(fbev, pg); gdk_window_add_filter(NULL, (GdkFilterFunc)pager_event_filter, pg ); @@ -799,6 +1074,9 @@ pager_constructor(plugin_instance *plug) G_CALLBACK (pager_rebuild_all), (gpointer) pg); g_signal_connect (G_OBJECT (fbev), "client_list_stacking", G_CALLBACK (do_net_client_list_stacking), (gpointer) pg); + if (pg->names_show_in_pager || pg->names_show_in_tooltip) + g_signal_connect (G_OBJECT (fbev), "desktop_names", + G_CALLBACK (update_names), (gpointer) pg); RET(1); } @@ -820,6 +1098,8 @@ pager_destructor(plugin_instance *p) while (pg->desknum--) { desk_free(pg, pg->desknum); } + if (pg->dnames) + g_strfreev(pg->dnames); g_hash_table_foreach_remove(pg->htable, (GHRFunc) task_remove_all, (gpointer)pg); g_hash_table_destroy(pg->htable);