You are not logged in.
Pages: 1
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.
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
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
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…
Xfce maintainer: https://gravatar.com/gaelbonithon
Offline
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/21If 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
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.
Xfce maintainer: https://gravatar.com/gaelbonithon
Offline
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.
Do you want to exit the Circus?
https://www.youtube.com/watch?v=ZJwQicZHp_c
Offline
It's interesting that the icon is shown in the tooltip but not in the dock.
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.
Xfce maintainer: https://gravatar.com/gaelbonithon
Offline
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
Pages: 1
[ Generated in 0.010 seconds, 7 queries executed - Memory usage: 615.15 KiB (Peak: 631.99 KiB) ]