[PATCH] wmaker: add window app icon to the window list

9 views
Skip to first unread message

david.m...@gmail.com

unread,
Feb 8, 2026, 5:38:00 PMFeb 8
to Window Maker Development
This patch is adding the app icon in between the flags icon and
the window name from the window list.
Feature request from https://github.com/window-maker/wmaker/issues/19
and reddit.
---
 src/menu.c       | 31 ++++++++++++++++++++++++++++++
 src/menu.h       |  1 +
 src/switchmenu.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)

diff --git a/src/menu.c b/src/menu.c
index e84b1caa..7806372a 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -267,6 +267,7 @@ WMenuEntry *wMenuInsertCallback(WMenu *menu, int index, const char *text,
  entry = wmalloc(sizeof(WMenuEntry));
  entry->flags.enabled = 1;
  entry->text = wstrdup(text);
+ entry->icon = NULL;
  entry->cascade = -1;
  entry->clientdata = clientdata;
  entry->callback = callback;
@@ -369,6 +370,9 @@ void wMenuRemoveItem(WMenu * menu, int index)
  if (menu->entries[index]->text)
  wfree(menu->entries[index]->text);
 
+ if (menu->entries[index]->icon)
+ wPixmapDestroy(menu->entries[index]->icon);
+
  if (menu->entries[index]->rtext)
  wfree(menu->entries[index]->rtext);
 
@@ -499,6 +503,10 @@ void wMenuRealize(WMenu * menu)
  text = menu->entries[i]->text;
  width = WMWidthOfString(scr->menu_entry_font, text, strlen(text)) + 10;
 
+ if (menu->entries[i]->icon) {
+ width += menu->entries[i]->icon->width + 4;
+ }
+
  if (menu->entries[i]->flags.indicator) {
  width += MENU_INDICATOR_SPACE;
  }
@@ -562,6 +570,9 @@ void wMenuDestroy(WMenu * menu, int recurse)
 
  wfree(menu->entries[i]->text);
 
+ if (menu->entries[i]->icon)
+ wPixmapDestroy(menu->entries[i]->icon);
+
  if (menu->entries[i]->rtext)
  wfree(menu->entries[i]->rtext);
 #ifdef USER_MENU
@@ -711,6 +722,26 @@ static void paintEntry(WMenu * menu, int index, int selected)
  if (entry->flags.indicator)
  x += MENU_INDICATOR_SPACE + 2;
 
+ if (entry->icon && entry->icon->image != None) {
+ int ix = x;
+ int iy = y + (h - entry->icon->height) / 2;
+
+ if (entry->icon->mask != None) {
+ XSetClipMask(dpy, scr->copy_gc, entry->icon->mask);
+ XSetClipOrigin(dpy, scr->copy_gc, ix, iy);
+ } else {
+ XSetClipMask(dpy, scr->copy_gc, None);
+ XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
+ }
+
+ XCopyArea(dpy, entry->icon->image, win, scr->copy_gc,
+   0, 0, entry->icon->width, entry->icon->height, ix, iy);
+ XSetClipMask(dpy, scr->copy_gc, None);
+ XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
+
+ x += entry->icon->width + 4;
+ }
+
  WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
       x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text));
 
diff --git a/src/menu.h b/src/menu.h
index 60ff9555..036403bd 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -46,6 +46,7 @@ typedef struct WMenuEntry {
  int order;
  char *text;        /* entry text */
  char *rtext;        /* text to show in the right part */
+ struct WPixmap *icon; /* optional icon displayed before the text */
  void (*callback)(struct WMenu *menu, struct WMenuEntry *entry);
  void (*free_cdata)(void *data);    /* proc to be used to free clientdata */
  void *clientdata;        /* data to pass to callback */
diff --git a/src/switchmenu.c b/src/switchmenu.c
index 551b4e58..a8c3937f 100644
--- a/src/switchmenu.c
+++ b/src/switchmenu.c
@@ -48,6 +48,50 @@ static int initialized = 0;
 static void observer(void *self, WMNotification * notif);
 static void wsobserver(void *self, WMNotification * notif);
 
+static WPixmap *switchMenuIconForWindow(WScreen *scr, WWindow *wwin)
+{
+ RImage *image = NULL;
+ WPixmap *pix;
+ WApplication *wapp;
+ int max_size;
+
+ if (!scr || !wwin)
+ return NULL;
+
+ max_size = WMFontHeight(scr->menu_entry_font) + 2;
+ if (max_size < 12)
+ max_size = 12;
+
+ /* Prefer the actual appicon image when available */
+ wapp = wApplicationOf(wwin->main_window);
+ if (wapp && wapp->app_icon && wapp->app_icon->icon && wapp->app_icon->icon->file_image) {
+ image = RRetainImage(wapp->app_icon->icon->file_image);
+ }
+
+ /* Fall back to _NET_WM_ICON, then the default icon */
+ if (!image && !WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
+ image = RRetainImage(wwin->net_icon_image);
+ if (!image)
+ image = get_icon_image(scr, wwin->wm_instance, wwin->wm_class, max_size);
+
+ if (!image)
+ return NULL;
+
+ image = wIconValidateIconSize(image, max_size);
+ if (!image)
+ return NULL;
+
+ pix = wmalloc(sizeof(WPixmap));
+ memset(pix, 0, sizeof(WPixmap));
+ RConvertImageMask(scr->rcontext, image, &pix->image, &pix->mask, 128);
+ pix->width = image->width;
+ pix->height = image->height;
+ pix->depth = scr->w_depth;
+
+ RReleaseImage(image);
+ return pix;
+}
+
 /*
  * FocusWindow
  *
@@ -216,6 +260,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
  entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
  wfree(t);
 
+ entry->icon = switchMenuIconForWindow(scr, wwin);
+
  entry->flags.indicator = 1;
  entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8);
  if (IS_OMNIPRESENT(wwin))
@@ -273,6 +319,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
  if (entry->rtext) {
  int idx = -1;
  char *t, *rt;
+ WPixmap *ipix;
  int it, ion;
 
  if (IS_OMNIPRESENT(wwin)) {
@@ -285,6 +332,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
 
  rt = entry->rtext;
  entry->rtext = NULL;
+ ipix = entry->icon;
+ entry->icon = NULL;
  t = entry->text;
  entry->text = NULL;
 
@@ -300,6 +349,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
  entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
  wfree(t);
  entry->rtext = rt;
+ entry->icon = ipix;
  entry->flags.indicator = 1;
  entry->flags.indicator_type = it;
  entry->flags.indicator_on = ion;
--
2.43.0
0001-wmaker-add-window-app-icon-to-the-window-list.patch

Carlos R. Mafra

unread,
Feb 9, 2026, 4:22:29 AMFeb 9
to wmake...@googlegroups.com
On Sun, 8 Feb 2026 at 14:38:00 -0800, david.m...@gmail.com wrote:
> This patch is adding the app icon in between the flags icon and
> the window name from the window list.
> Feature request from https://github.com/window-maker/wmaker/issues/19
> and reddit.

I don't mind this patch, there are clearly people who want this.
But this is changing the look of how F11 works in a fundamental way
that maybe is not what old-timers want.

Can you make this an option under the Expert Panel of WPrefs?

david.m...@gmail.com

unread,
Feb 9, 2026, 7:53:53 AMFeb 9
to Window Maker Development
Sure, will do. 

david.m...@gmail.com

unread,
Feb 9, 2026, 6:19:22 PMFeb 9
to Window Maker Development
Enclosed the updated patch.
Here the commit details:

This patch is adding the app icon in between the flags icon and
the window name from the window list.
Feature request from https://github.com/window-maker/wmaker/issues/19
It is disabled by default, it needs WindowListAppIcons to be
set to YES manually in the conf file or "Show app icons in window list."
enabled from WPrefs expert panel.
---
 WPrefs.app/Expert.c                 |  3 ++
 WindowMaker/Defaults/WindowMaker.in |  1 +
 src/WindowMaker.h                   |  1 +
 src/defaults.c                      |  2 ++
 src/menu.c                          | 31 ++++++++++++++++++

 src/menu.h                          |  1 +
 src/switchmenu.c                    | 50 +++++++++++++++++++++++++++++
 7 files changed, 89 insertions(+)

diff --git a/WPrefs.app/Expert.c b/WPrefs.app/Expert.c
index f7b7c7ce..6ef6307d 100644
--- a/WPrefs.app/Expert.c
+++ b/WPrefs.app/Expert.c
@@ -78,6 +78,9 @@ static struct expert_option {
  { N_("Ignore minimized windows when cycling."),
    /* default: */ False, OPTION_WMAKER, "CycleIgnoreMinimized" },
 
+ { N_("Show app icons in window list."),
+   /* default: */ False, OPTION_WMAKER, "WindowListAppIcons" },
+
  { N_("Show switch panel when cycling windows."),
    /* default: */ True, OPTION_WMAKER_ARRAY, "SwitchPanelImages" },
 
diff --git a/WindowMaker/Defaults/WindowMaker.in b/WindowMaker/Defaults/WindowMaker.in
index 0cc0f654..49cd181f 100644
--- a/WindowMaker/Defaults/WindowMaker.in
+++ b/WindowMaker/Defaults/WindowMaker.in
@@ -254,4 +254,5 @@
  CycleActiveHeadOnly = NO;
  CycleAllWorkspaces = NO;
  CycleIgnoreMinimized = NO;
+ WindowListAppIcons = NO;
 }
diff --git a/src/WindowMaker.h b/src/WindowMaker.h
index 23ca8741..75464fdf 100644
--- a/src/WindowMaker.h
+++ b/src/WindowMaker.h
@@ -479,6 +479,7 @@ extern struct WPreferences {
  int hot_corner_delay;    /* Delay after which the hot corner is triggered */
  int hot_corner_edge;    /* Hot corner edge size */
  char *hot_corner_actions[4];    /* Action of each corner */
+ char window_list_app_icons;        /* Show app icons in window list */
 
  struct {
 #ifdef USE_ICCCM_WMREPLACE
diff --git a/src/defaults.c b/src/defaults.c
index 8720b4ac..43860599 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -534,6 +534,8 @@ WDefaultEntry optionList[] = {
      &wPreferences.hot_corner_edge, getInt, NULL, NULL, NULL},
  {"HotCornerActions", "(\"None\", \"None\", \"None\", \"None\")", &wPreferences,
      NULL, getPropList, setHotCornerActions, NULL, NULL},
+ {"WindowListAppIcons", "NO", NULL,
+ &wPreferences.window_list_app_icons, getBool, NULL, NULL, NULL},
 
  /* style options */
 
diff --git a/src/menu.c b/src/menu.c
index e84b1caa..7d3b69db 100644

--- a/src/menu.c
+++ b/src/menu.c
@@ -267,6 +267,7 @@ WMenuEntry *wMenuInsertCallback(WMenu *menu, int index, const char *text,
  entry = wmalloc(sizeof(WMenuEntry));
  entry->flags.enabled = 1;
  entry->text = wstrdup(text);
+ entry->icon = NULL;
  entry->cascade = -1;
  entry->clientdata = clientdata;
  entry->callback = callback;
@@ -369,6 +370,9 @@ void wMenuRemoveItem(WMenu * menu, int index)
  if (menu->entries[index]->text)
  wfree(menu->entries[index]->text);
 
+ if (menu->entries[index]->icon)
+ wPixmapDestroy(menu->entries[index]->icon);
+
  if (menu->entries[index]->rtext)
  wfree(menu->entries[index]->rtext);
 
@@ -499,6 +503,10 @@ void wMenuRealize(WMenu * menu)
  text = menu->entries[i]->text;
  width = WMWidthOfString(scr->menu_entry_font, text, strlen(text)) + 10;
 
+ if (wPreferences.window_list_app_icons && menu->entries[i]->icon) {

+ width += menu->entries[i]->icon->width + 4;
+ }
+
  if (menu->entries[i]->flags.indicator) {
  width += MENU_INDICATOR_SPACE;
  }
@@ -562,6 +570,9 @@ void wMenuDestroy(WMenu * menu, int recurse)
 
  wfree(menu->entries[i]->text);
 
+ if (menu->entries[i]->icon)
+ wPixmapDestroy(menu->entries[i]->icon);
+
  if (menu->entries[i]->rtext)
  wfree(menu->entries[i]->rtext);
 #ifdef USER_MENU
@@ -711,6 +722,26 @@ static void paintEntry(WMenu * menu, int index, int selected)
  if (entry->flags.indicator)
  x += MENU_INDICATOR_SPACE + 2;
 
+ if (wPreferences.window_list_app_icons && entry->icon && entry->icon->image != None) {
0001-wmaker-add-window-app-icon-to-the-window-list.patch
Reply all
Reply to author
Forward
0 new messages