summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberts Muktupāvels <alberts.muktupavels@gmail.com>2022-03-18 21:13:35 +0200
committerMarco Trevisan <mail@3v1n0.net>2022-09-16 11:06:20 +0000
commit34ced542636123c2832b341593741d4ee0a413c3 (patch)
treeeb44411dd1bcd71c26564ed81a9c95fe7e85a70e
parent6bb6cce5f97d4739dc7a3c4a21455409faaa1b24 (diff)
downloadlibwnck-34ced542636123c2832b341593741d4ee0a413c3.tar.gz
tests: add test-icon-cache tool
- Set button always sets new icon. This should emit icon-changed signal unless there is higher priority icon already set. - Nochange button is used to cause PropertyNotify event that should not cause changes to icon and/or should not emit icon-changed signal. - Unset button is used to remove icon. This should emit icon-changed signal only if highest priority icon is unset.
-rw-r--r--libwnck/meson.build1
-rw-r--r--libwnck/test-icon-cache.c554
2 files changed, 555 insertions, 0 deletions
diff --git a/libwnck/meson.build b/libwnck/meson.build
index ddbff6e..9bc1739 100644
--- a/libwnck/meson.build
+++ b/libwnck/meson.build
@@ -161,6 +161,7 @@ test_progs = [
'test-pager',
'test-urgent',
'test-shutdown',
+ 'test-icon-cache',
]
foreach prog: progs + test_progs
diff --git a/libwnck/test-icon-cache.c b/libwnck/test-icon-cache.c
new file mode 100644
index 0000000..2f3fc38
--- /dev/null
+++ b/libwnck/test-icon-cache.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2022 Alberts Muktupāvels
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cairo-xlib.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <libwnck/libwnck.h>
+#include <X11/Xatom.h>
+
+#define WINDOW_NAME "Test Icon Cache"
+#define NET_WM_ICON_NAME "face-cool"
+#define WM_HINTS_ICON_NAME "face-monkey"
+
+typedef struct
+{
+ GtkWidget *set_button;
+ GtkWidget *nochange_button;
+ GtkWidget *unset_button;
+
+ GtkWidget *status_label;
+} RowData;
+
+typedef struct
+{
+ Display *xdisplay;
+
+ Window xwindow;
+
+ Pixmap icon_pixmap;
+ Pixmap icon_mask;
+} IconCacheTest;
+
+static void
+unset_net_wm_icon (IconCacheTest *self)
+{
+ XDeleteProperty (self->xdisplay,
+ self->xwindow,
+ XInternAtom (self->xdisplay, "_NET_WM_ICON", False));
+}
+
+static void
+set_net_wm_icon (IconCacheTest *self)
+{
+ GdkPixbuf *pixbuf;
+ int width;
+ int height;
+ int size;
+ unsigned long *data;
+ unsigned long *p;
+
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ NET_WM_ICON_NAME,
+ 24,
+ GTK_ICON_LOOKUP_FORCE_SIZE,
+ NULL);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ size = 2 + width * height;
+ data = p = g_malloc (size * sizeof (unsigned long));
+
+ *p++ = width;
+ *p++ = height;
+
+ {
+ unsigned char *pixels;
+ int n_channels;
+ int stride;
+ int x;
+ int y;
+
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ stride = gdk_pixbuf_get_rowstride (pixbuf);
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ unsigned char a;
+
+ r = pixels[y * stride + x * n_channels + 0];
+ g = pixels[y * stride + x * n_channels + 1];
+ b = pixels[y * stride + x * n_channels + 2];
+
+ if (n_channels >= 4)
+ a = pixels[y * stride + x * n_channels + 3];
+ else
+ a = 255;
+
+ *p++ = a << 24 | r << 16 | g << 8 | b ;
+ }
+ }
+ }
+
+ XChangeProperty (self->xdisplay,
+ self->xwindow,
+ XInternAtom (self->xdisplay, "_NET_WM_ICON", False),
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ (unsigned char *) data,
+ size);
+
+ g_object_unref (pixbuf);
+ g_free (data);
+}
+
+static void
+create_icon_pixmaps (IconCacheTest *self)
+{
+ int screen;
+ GdkPixbuf *pixbuf;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ screen = DefaultScreen (self->xdisplay);
+
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ WM_HINTS_ICON_NAME,
+ 24,
+ GTK_ICON_LOOKUP_FORCE_SIZE,
+ NULL);
+
+ g_assert (self->icon_pixmap == None);
+ self->icon_pixmap = XCreatePixmap (self->xdisplay,
+ self->xwindow,
+ 24,
+ 24,
+ XDefaultDepth (self->xdisplay, screen));
+
+ surface = cairo_xlib_surface_create (self->xdisplay,
+ self->icon_pixmap,
+ XDefaultVisual (self->xdisplay, screen),
+ 24,
+ 24);
+
+ cr = cairo_create (surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+
+ if (gdk_pixbuf_get_has_alpha (pixbuf))
+ {
+ cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
+ cairo_paint (cr);
+ cairo_pop_group_to_source (cr);
+ }
+
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ if (gdk_pixbuf_get_has_alpha (pixbuf))
+ {
+ Screen *xscreen;
+
+ g_assert (self->icon_mask == None);
+ self->icon_mask = XCreatePixmap (self->xdisplay,
+ self->xwindow,
+ 24,
+ 24,
+ 1);
+
+ xscreen = DefaultScreenOfDisplay (self->xdisplay);
+ surface = cairo_xlib_surface_create_for_bitmap (self->xdisplay,
+ self->icon_mask,
+ xscreen,
+ 24,
+ 24);
+
+ cr = cairo_create (surface);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ }
+
+ g_object_unref (pixbuf);
+}
+
+static void
+free_icon_pixmaps (IconCacheTest *self)
+{
+ if (self->icon_pixmap != None)
+ {
+ XFreePixmap (self->xdisplay, self->icon_pixmap);
+ self->icon_pixmap = None;
+ }
+
+ if (self->icon_mask != None)
+ {
+ XFreePixmap (self->xdisplay, self->icon_mask);
+ self->icon_mask = None;
+ }
+}
+
+static void
+unset_wm_hints (IconCacheTest *self)
+{
+ XWMHints *hints;
+
+ hints = XGetWMHints (self->xdisplay, self->xwindow);
+ if (hints == NULL)
+ return;
+
+ free_icon_pixmaps (self);
+
+ hints->flags |= IconPixmapHint | IconMaskHint;
+ hints->icon_pixmap = None;
+ hints->icon_mask = None;
+
+ XSetWMHints (self->xdisplay, self->xwindow, hints);
+ XFree (hints);
+}
+
+static void
+set_wm_hints (IconCacheTest *self)
+{
+ XWMHints *hints;
+
+ hints = XGetWMHints (self->xdisplay, self->xwindow);
+ if (hints == NULL)
+ hints = XAllocWMHints ();
+
+ free_icon_pixmaps (self);
+ create_icon_pixmaps (self);
+
+ hints->flags |= IconPixmapHint | IconMaskHint;
+ hints->icon_pixmap = self->icon_pixmap;
+ hints->icon_mask = self->icon_mask;
+
+ XSetWMHints (self->xdisplay, self->xwindow, hints);
+ XFree (hints);
+}
+
+static void
+unset_net_wm_icon_cb (GtkButton *button,
+ IconCacheTest *self)
+{
+ RowData *data;
+
+ unset_net_wm_icon (self);
+
+ data = g_object_get_data (G_OBJECT (button), "row-data");
+ gtk_label_set_text (GTK_LABEL (data->status_label), "✘");
+ gtk_widget_set_sensitive (data->unset_button, FALSE);
+}
+
+static void
+set_net_wm_icon_cb (GtkButton *button,
+ IconCacheTest *self)
+{
+ RowData *data;
+
+ set_net_wm_icon (self);
+
+ data = g_object_get_data (G_OBJECT (button), "row-data");
+ gtk_label_set_text (GTK_LABEL (data->status_label), "✔");
+ gtk_widget_set_sensitive (data->unset_button, TRUE);
+}
+
+static void
+unset_wm_hints_cb (GtkButton *button,
+ IconCacheTest *self)
+{
+ RowData *data;
+
+ unset_wm_hints (self);
+
+ data = g_object_get_data (G_OBJECT (button), "row-data");
+ gtk_label_set_text (GTK_LABEL (data->status_label), "✘");
+ gtk_widget_set_sensitive (data->unset_button, FALSE);
+}
+
+static void
+set_wm_hints_cb (GtkButton *button,
+ IconCacheTest *self)
+{
+ RowData *data;
+
+ set_wm_hints (self);
+
+ data = g_object_get_data (G_OBJECT (button), "row-data");
+ gtk_label_set_text (GTK_LABEL (data->status_label), "✔");
+ gtk_widget_set_sensitive (data->unset_button, TRUE);
+}
+
+static void
+urgency_cb (GtkButton *button,
+ IconCacheTest *self)
+{
+ XWMHints *hints;
+
+ hints = XGetWMHints (self->xdisplay, self->xwindow);
+ if (hints == NULL)
+ hints = XAllocWMHints ();
+
+ hints->flags ^= XUrgencyHint;
+
+ XSetWMHints (self->xdisplay, self->xwindow, hints);
+ XFree (hints);
+}
+
+static GtkWidget *
+append_icon_row (const char *name,
+ const char *icon_name,
+ GCallback set_cb,
+ GCallback nochange_cb,
+ GCallback unset_cb,
+ IconCacheTest *self)
+{
+ GtkWidget *row;
+ RowData *data;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *btn;
+ GtkWidget *img;
+
+ row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+
+ data = g_new0 (RowData, 1);
+ g_object_set_data_full (G_OBJECT (row), "row-data", data, g_free);
+
+ label = gtk_label_new (name);
+ gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.);
+ gtk_widget_show (label);
+
+ label = data->status_label = gtk_label_new ("✘");
+ g_object_set_data (G_OBJECT (label), "row-data", data);
+ gtk_box_pack_end (GTK_BOX (row), label, FALSE, FALSE, 0);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.);
+ gtk_widget_show (label);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_end (GTK_BOX (row), box, FALSE, FALSE, 0);
+ gtk_widget_show (box);
+
+ img = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_container_add (GTK_CONTAINER (box), img);
+ gtk_widget_show (img);
+
+ btn = data->set_button = gtk_button_new_with_label ("set");
+ g_object_set_data (G_OBJECT (btn), "row-data", data);
+ gtk_container_add (GTK_CONTAINER (box), btn);
+ gtk_widget_show (btn);
+
+ g_signal_connect (btn, "clicked", set_cb, self);
+
+ btn = data->nochange_button = gtk_button_new_with_label ("nochange");
+ g_object_set_data (G_OBJECT (btn), "row-data", data);
+ gtk_container_add (GTK_CONTAINER (box), btn);
+ gtk_widget_show (btn);
+
+ if (nochange_cb != NULL)
+ g_signal_connect (btn, "clicked", nochange_cb, self);
+ else
+ gtk_widget_set_sensitive (btn, FALSE);
+
+ btn = data->unset_button = gtk_button_new_with_label ("unset");
+ g_object_set_data (G_OBJECT (btn), "row-data", data);
+ gtk_container_add (GTK_CONTAINER (box), btn);
+ gtk_widget_set_sensitive (btn, FALSE);
+ gtk_widget_show (btn);
+
+ g_signal_connect (btn, "clicked", unset_cb, self);
+
+ return row;
+}
+
+static void
+icon_changed_cb (WnckWindow *window,
+ GtkImage *image)
+{
+ GdkPixbuf *icon;
+
+ icon = wnck_window_get_icon (window);
+ gtk_image_set_from_pixbuf (image, icon);
+}
+
+static void
+mini_icon_changed_cb (WnckWindow *window,
+ GtkImage *image)
+{
+ GdkPixbuf *icon;
+
+ icon = wnck_window_get_mini_icon (window);
+ gtk_image_set_from_pixbuf (image, icon);
+}
+
+static void
+icon_changed_count_cb (WnckWindow *window,
+ GtkLabel *label)
+{
+ static int count = 0;
+ char *text;
+
+ text = g_strdup_printf ("%d", ++count);
+ gtk_label_set_text (label, text);
+ g_free (text);
+}
+
+static void
+append_result_box (GtkWidget *vbox,
+ WnckWindow *window)
+{
+ GtkWidget *box1;
+ GtkWidget *box2;
+ GtkWidget *image;
+ GtkWidget *label;
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), box1, TRUE, FALSE, 0);
+ gtk_widget_show (box1);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_end (GTK_BOX (box1), box2, TRUE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (box2), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ g_signal_connect (window,
+ "icon-changed",
+ G_CALLBACK (icon_changed_cb),
+ image);
+
+ icon_changed_cb (window, GTK_IMAGE (image));
+
+ image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (box2), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ g_signal_connect (window,
+ "icon-changed",
+ G_CALLBACK (mini_icon_changed_cb),
+ image);
+
+ mini_icon_changed_cb (window, GTK_IMAGE (image));
+
+ label = gtk_label_new ("0");
+ gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_signal_connect (window,
+ "icon-changed",
+ G_CALLBACK (icon_changed_count_cb),
+ label);
+}
+
+static void
+window_opened_cb (WnckScreen *screen,
+ WnckWindow *window,
+ GtkWidget *vbox)
+{
+ const char *name;
+
+ name = wnck_window_get_name (window);
+
+ if (g_strcmp0 (name, WINDOW_NAME) != 0)
+ return;
+
+ append_result_box (vbox, window);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ WnckScreen *screen;
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *separator;
+ GtkWidget *row;
+ IconCacheTest self;
+
+ gtk_init (&argc, &argv);
+
+ screen = wnck_screen_get_default ();
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), WINDOW_NAME);
+
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ g_object_set (G_OBJECT (vbox), "margin", 12, NULL);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_widget_show (vbox);
+
+ row = append_icon_row ("_NET_WM_ICON:",
+ NET_WM_ICON_NAME,
+ G_CALLBACK (set_net_wm_icon_cb),
+ NULL,
+ G_CALLBACK (unset_net_wm_icon_cb),
+ &self);
+
+ gtk_box_pack_start (GTK_BOX (vbox), row, FALSE, FALSE, 0);
+ gtk_widget_show (row);
+
+ row = append_icon_row ("WM_HINTS:",
+ WM_HINTS_ICON_NAME,
+ G_CALLBACK (set_wm_hints_cb),
+ G_CALLBACK (urgency_cb),
+ G_CALLBACK (unset_wm_hints_cb),
+ &self);
+
+ gtk_box_pack_start (GTK_BOX (vbox), row, FALSE, FALSE, 0);
+ gtk_widget_show (row);
+
+ separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_container_add (GTK_CONTAINER (vbox), separator);
+ gtk_widget_show (separator);
+
+ g_signal_connect (screen,
+ "window-opened",
+ G_CALLBACK (window_opened_cb),
+ vbox);
+
+ gtk_window_present (GTK_WINDOW (window));
+
+ self.xdisplay = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
+ self.xwindow = gdk_x11_window_get_xid (gtk_widget_get_window (window));
+ self.icon_pixmap = None;
+ self.icon_mask = None;
+
+ gtk_main ();
+
+ free_icon_pixmaps (&self);
+
+ return EXIT_SUCCESS;
+}