Xfce Forum

Sub domains
 

You are not logged in.

#1 2025-04-23 06:02:27

Boku
Member
Registered: 2025-04-23
Posts: 2
LinuxFirefox 137.0

Docklike doesn't show some icons as expected

Hello

I noticed that docklike-plugin doesn't show some application icons related to panel as expected. they are displayed just like 'icon=application-x-executable'.
But Plank show them correctly.
Screenshot-2025-04-23.png

When I tried to edit icon right-clicking on the item on docklike the context menu only shows 'delete'. so I can't edit.

Is there a way to show correct icons on docklike-plugin ?

Arch Linux
panel 4.20.4-1
docklike-plugin 0.4.3-1

Thanks

Offline

#2 2025-05-05 10:28:58

ToZ
Administrator
From: Canada
Registered: 2011-06-02
Posts: 12,012
LinuxFirefox 138.0

Re: Docklike doesn't show some icons as expected


Mark solved threads as [SOLVED] to make it easier for others to find solutions.
--- How To Ask For Help | FAQ | Developer Wiki  |  Community | Contribute ---

Online

#3 2025-05-05 13:37:19

Tamaranch
Member
From: France
Registered: 2020-12-31
Posts: 389
Website
LinuxFirefox 138.0

Re: Docklike doesn't show some icons as expected

No it's another issue specific to docklike, because of the way it retrieves icons from WM_CLASS:
* https://gitlab.xfce.org/panel-plugins/x … /issues/93
* https://gitlab.xfce.org/panel-plugins/x … /issues/21

If you run e.g. the panel as

xfce4-panel --class=panel --name=panel

it'll solve this issue for the panel preferences, but not for systray… that would need to be improved…

Offline

#4 2025-05-05 19:38:38

Misko_2083
Member
Registered: 2015-10-13
Posts: 223
Website
LinuxFirefox 128.0

Re: Docklike doesn't show some icons as expected

Tamaranch wrote:

No it's another issue specific to docklike, because of the way it retrieves icons from WM_CLASS:
* https://gitlab.xfce.org/panel-plugins/x … /issues/93
* https://gitlab.xfce.org/panel-plugins/x … /issues/21

If you run e.g. the panel as

xfce4-panel --class=panel --name=panel

it'll solve this issue for the panel preferences, but not for systray… that would need to be improved…

It should use fetch the icons from _NET_WM_ICON instead, and fallback to WM_HINTS for older apps.
_NET_WM_ICON has several icon sizes and the app selects the best size for it.

Here is a full working example app for gtk3

// gcc window_icon_viewer.c -o window_icon_viewer `pkg-config --cflags --libs gtk+-3.0 gdk-x11-3.0` -lX11

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

#define MAX_ICONS 50

// Structure to store icon details
typedef struct {
    guint width;
    guint height;
    gulong *data; // Points to ARGB data in prop
} IconInfo;

// ----------------- NET_WM_ICON Extraction -----------------
static GdkPixbuf *get_net_wm_icon(Display *dpy, Window win, int target_size, int verbose) {
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytes_after;
    unsigned char *prop = NULL;
    Atom net_wm_icon = XInternAtom(dpy, "_NET_WM_ICON", False);

    if (XGetWindowProperty(dpy, win, net_wm_icon, 0, (~0L), False, XA_CARDINAL,
                           &actual_type, &actual_format, &nitems, &bytes_after, &prop) != Success || !prop) {
        printf("_NET_WM_ICON not found or failed\n");
        return NULL;
    }

    printf("_NET_WM_ICON received with %lu items\n", nitems);
    gulong *icon_data = (gulong *)prop;

    IconInfo icons[MAX_ICONS];
    int valid_icon_count = 0;

    // Parse all icons
    for (unsigned long i = 0; i + 1 < nitems && valid_icon_count < MAX_ICONS; ) {
        guint width = icon_data[i++];
        guint height = icon_data[i++];
        guint64 len = (guint64)width * height;

        printf("Icon %d: %ux%u\n", valid_icon_count + 1, width, height);

        if (width == 0 || height == 0 || len > nitems - i) {
            printf("Invalid icon segment: width=%u height=%u\n", width, height);
            continue; // Skip to next icon
        }

        // Test pixbuf creation to ensure valid data
        GdkPixbuf *test_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
        if (!test_pixbuf) {
            printf("Failed to create pixbuf for icon %ux%u\n", width, height);
            i += len;
            continue;
        }
        g_object_unref(test_pixbuf);

        // Store valid icon
        icons[valid_icon_count].width = width;
        icons[valid_icon_count].height = height;
        icons[valid_icon_count].data = &icon_data[i];
        valid_icon_count++;
        i += len;
    }

    printf("Found %d valid icons in _NET_WM_ICON\n", valid_icon_count);

    // Select best icon
    if (valid_icon_count > 0) {
        int best_idx = 0;
        int best_diff = INT_MAX;
        unsigned long best_area = 0;
        int default_target = (target_size > 0) ? target_size : 64;

        for (int i = 0; i < valid_icon_count; i++) {
            int diff = abs((int)icons[i].width - default_target);
            unsigned long area = (unsigned long)icons[i].width * icons[i].height;
            if (verbose) {
                printf("Icon %d: %ux%u, diff=%d, area=%lu\n", i + 1, icons[i].width, icons[i].height, diff, area);
            }
            // Prefer smallest diff; for equal diff, prefer larger area
            if (diff < best_diff || (diff == best_diff && area > best_area)) {
                best_diff = diff;
                best_area = area;
                best_idx = i;
            }
        }

        // Create pixbuf for best icon
        GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, icons[best_idx].width, icons[best_idx].height);
        if (!pixbuf) {
            printf("Failed to create pixbuf for best icon %ux%u\n", icons[best_idx].width, icons[best_idx].height);
            XFree(prop);
            return NULL;
        }

        guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
        int rowstride = gdk_pixbuf_get_rowstride(pixbuf);
        gulong *data = icons[best_idx].data;

        for (guint y = 0; y < icons[best_idx].height; y++) {
            for (guint x = 0; x < icons[best_idx].width; x++) {
                guint32 pixel = data[y * icons[best_idx].width + x];
                guchar *p = pixels + y * rowstride + x * 4;
                p[0] = (pixel >> 16) & 0xFF; // Red
                p[1] = (pixel >> 8) & 0xFF;  // Green
                p[2] = pixel & 0xFF;         // Blue
                p[3] = (pixel >> 24) & 0xFF; // Alpha
            }
        }

        printf("Selected best _NET_WM_ICON: %ux%u (target size: %d)\n",
               icons[best_idx].width, icons[best_idx].height, default_target);
        XFree(prop);
        return pixbuf;
    }

    printf("No valid icons found in _NET_WM_ICON\n");
    XFree(prop);
    return NULL;
}

// ----------------- WMHints Extraction -----------------
static GdkPixbuf *get_wm_hints_icon(Display *dpy, Window win, int verbose) {
    XWMHints *hints = XGetWMHints(dpy, win);
    if (!hints || !(hints->flags & IconPixmapHint)) {
        printf("No WMHints or no IconPixmapHint\n");
        if (hints) XFree(hints);
        return NULL;
    }

    printf("Found WMHints with IconPixmapHint\n");

    // Query pixmap geometry
    Window root;
    int x, y;
    unsigned int width, height, border_width, depth;
    Status geo_result = XGetGeometry(dpy, hints->icon_pixmap, &root, &x, &y, &width, &height, &border_width, &depth);
    if (!geo_result) {
        printf("Failed to get pixmap geometry\n");
        XFree(hints);
        return NULL;
    }

    printf("Pixmap geometry: %ux%u, depth=%u\n", width, height, depth);

    // Validate pixmap dimensions
    if (width == 0 || height == 0 || width > 1024 || height > 1024) {
        printf("Invalid pixmap dimensions\n");
        XFree(hints);
        return NULL;
    }

    XImage *img = XGetImage(dpy, hints->icon_pixmap, 0, 0, width, height, AllPlanes, ZPixmap);
    if (!img) {
        printf("Failed to get XImage for pixmap\n");
        XFree(hints);
        return NULL;
    }

    printf("Got XImage: %dx%d, depth=%d\n", img->width, img->height, img->depth);
    GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, img->width, img->height);
    if (!pixbuf) {
        printf("Failed to create pixbuf from XImage\n");
        XDestroyImage(img);
        XFree(hints);
        return NULL;
    }

    guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
    int rowstride = gdk_pixbuf_get_rowstride(pixbuf);

    // Handle different depths
    for (int y = 0; y < img->height; y++) {
        for (int x = 0; x < img->width; x++) {
            unsigned long pixel = XGetPixel(img, x, y);
            guchar *p = pixels + y * rowstride + x * 4;
            if (img->depth == 24 || img->depth == 32) {
                p[0] = (pixel >> 16) & 0xFF;
                p[1] = (pixel >> 8) & 0xFF;
                p[2] = pixel & 0xFF;
                p[3] = 255;
                if (hints->flags & IconMaskHint) {
                    XImage *mask = XGetImage(dpy, hints->icon_mask, 0, 0, width, height, 1, ZPixmap);
                    if (mask) {
                        p[3] = XGetPixel(mask, x, y) ? 255 : 0;
                        XDestroyImage(mask);
                    }
                }
            } else if (img->depth == 1) {
                p[0] = p[1] = p[2] = pixel ? 255 : 0;
                p[3] = 255;
            } else {
                printf("Unsupported pixmap depth: %d\n", img->depth);
                g_object_unref(pixbuf);
                pixbuf = NULL;
                break;
            }
        }
    }

    XDestroyImage(img);
    XFree(hints);

    if (pixbuf) {
        printf("Converted XImage to pixbuf\n");
    }
    return pixbuf;
}

// ----------------- Icon Extraction Logic -----------------
GdkPixbuf* get_window_icon(Display *dpy, Window win, int try_wm_hint_icon, int verbose, int target_size) {
    Atom wm_class = XInternAtom(dpy, "WM_CLASS", False);
    Atom net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", False);
    Atom utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
    GdkPixbuf *pixbuf = NULL;

    // Log WM_CLASS and _NET_WM_NAME
    if (verbose) {
        unsigned char *prop;
        unsigned long nitems, bytes_after;
        int actual_format;
        Atom actual_type;

        // WM_CLASS
        if (XGetWindowProperty(dpy, win, wm_class, 0, 1024, False, AnyPropertyType,
                               &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && prop) {
            printf("WM_CLASS: %s\n", (char *)prop);
            XFree(prop);
        } else {
            printf("WM_CLASS: Not found\n");
        }

        // _NET_WM_NAME
        if (XGetWindowProperty(dpy, win, net_wm_name, 0, 1024, False, utf8_string,
                               &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && prop) {
            printf("_NET_WM_NAME: %s\n", (char *)prop);
            XFree(prop);
        } else {
            printf("_NET_WM_NAME: Not found\n");
        }
    }

    // Try WMHints if --try-wm-hint-icon is specified
    if (try_wm_hint_icon) {
        printf("Flag --try-wm-hint-icon set, attempting WMHints\n");
        pixbuf = get_wm_hints_icon(dpy, win, verbose);
        if (pixbuf) {
            return pixbuf;
        }
        printf("WMHints failed, falling back to _NET_WM_ICON\n");
    }

    // Try _NET_WM_ICON (default)
    pixbuf = get_net_wm_icon(dpy, win, target_size, verbose);
    if (pixbuf) {
        return pixbuf;
    }

    // Fallback to WMHints if _NET_WM_ICON fails and not already tried
    if (!try_wm_hint_icon) {
        pixbuf = get_wm_hints_icon(dpy, win, verbose);
    }

    return pixbuf;
}

// ----------------- GTK3 Test App -----------------
int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    if (argc < 2 || argc > 5) {
        g_print("Usage: %s <X11 window id in hex or decimal> [--try-wm-hint-icon] [--verbose] [--target-size <size>]\n", argv[0]);
        return 1;
    }

    Window win = strtoul(argv[1], NULL, 0);
    int try_wm_hint_icon = 0;
    int verbose = 0;
    int target_size = 0;
    for (int i = 2; i < argc; i++) {
        if (strcmp(argv[i], "--try-wm-hint-icon") == 0) {
            try_wm_hint_icon = 1;
        } else if (strcmp(argv[i], "--verbose") == 0) {
            verbose = 1;
        } else if (strcmp(argv[i], "--target-size") == 0 && i + 1 < argc) {
            target_size = atoi(argv[++i]);
            if (target_size <= 0) {
                g_printerr("Invalid target size\n");
                return 1;
            }
        }
    }

    printf("Attempting to fetch icon for window ID: 0x%lx\n", win);
    if (try_wm_hint_icon) {
        printf("Flag --try-wm-hint-icon set, prioritizing WMHints\n");
    }
    if (verbose) {
        printf("Verbose mode enabled\n");
    }
    if (target_size > 0) {
        printf("Target icon size: %d\n", target_size);
    }

    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) {
        g_printerr("Cannot open X display\n");
        return 1;
    }

    // Verify window exists
    XWindowAttributes attr;
    if (!XGetWindowAttributes(dpy, win, &attr)) {
        g_printerr("Invalid or inaccessible window ID\n");
        XCloseDisplay(dpy);
        return 1;
    }
    printf("Window exists: %dx%d\n", attr.width, attr.height);

    GdkPixbuf *icon = get_window_icon(dpy, win, try_wm_hint_icon, verbose, target_size);
    if (!icon) {
        g_printerr("Failed to get window icon\n");
        XCloseDisplay(dpy);
        return 1;
    }

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Window Icon Viewer");
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    gtk_window_set_default_size(GTK_WINDOW(window), gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon));

    GtkWidget *image = gtk_image_new_from_pixbuf(icon);
    gtk_container_add(GTK_CONTAINER(window), image);

    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    gtk_widget_show_all(window);
    gtk_main();

    g_object_unref(icon);
    XCloseDisplay(dpy);
    return 0;
}

Used like this

./window_icon_viewer 0x3c020ab --target-size 64 --verbose
./window_icon_viewer 0x3c020ab --try-wm-hint-icon --verbose

Do you want to exit the Circus?
https://www.youtube.com/watch?v=ZJwQicZHp_c

Offline

#5 2025-05-05 20:23:45

Tamaranch
Member
From: France
Registered: 2020-12-31
Posts: 389
Website
LinuxFirefox 138.0

Re: Docklike doesn't show some icons as expected

Misko_2083 wrote:

It should use fetch the icons from _NET_WM_ICON instead, and fallback to WM_HINTS for older apps.

No it shouldn't, libxfce4windowing already does this, but this plugin wants to work differently.

Offline

#6 2025-05-05 22:07:26

Misko_2083
Member
Registered: 2015-10-13
Posts: 223
Website
LinuxFirefox 128.0

Re: Docklike doesn't show some icons as expected

8woOCxy.png
OK, athough the small GTK3 example app fetched it after I found the window ID.
It's interesting that the icon is shown in the tooltip but not in the dock.  smile


Do you want to exit the Circus?
https://www.youtube.com/watch?v=ZJwQicZHp_c

Offline

#7 2025-05-06 07:58:41

Tamaranch
Member
From: France
Registered: 2020-12-31
Posts: 389
Website
LinuxFirefox 138.0

Re: Docklike doesn't show some icons as expected

Misko_2083 wrote:

It's interesting that the icon is shown in the tooltip but not in the dock.  smile

Because it comes from libxfce4windowing, which can only do this on X11. Once again, the idea of this plugin is not to fall back into the pitfall of different code paths depending on the windowing system, but to use a method that is independent of it.

Offline

#8 2025-05-06 21:31:32

Boku
Member
Registered: 2025-04-23
Posts: 2
LinuxFirefox 138.0

Re: Docklike doesn't show some icons as expected

ToZ Tamaranch Misko_2083
Thank you for replying and I will wait for the enhancement.

Again, thanks all the members of Xfce Development Team for your best efforts and hard work.

Offline

Registered users online in this topic: 0, guests: 2
[Bot] ClaudeBot,
[Bot] MyEducationalCrawler

Board footer

Powered by FluxBB
Modified by Visman

[ Generated in 0.010 seconds, 7 queries executed - Memory usage: 615.15 KiB (Peak: 631.99 KiB) ]