Fixes #7–#11: launcher Other tab, ICCCM size hints, icon loading, multi-desktop
Fix #7: Add "Other" catch-all category to the launcher. map_category() now returns "Other" instead of "" for unrecognized .desktop Categories, and an "Other" tab is added to the CATS list so those apps are discoverable instead of silently hidden. Fix #8: Respect ICCCM WM_NORMAL_HINTS. client_add() reads PMinSize, PResizeInc, and PBaseSize from the window's size hints. The resize drag handler in events.c clamps to min_w/min_h and snaps to the width_inc/height_inc grid, fixing jank in character-cell terminal emulators (xterm, Alacritty). Fix #10: Optional freedesktop icon loading via imlib2. The launcher now parses the Icon= field from .desktop files and attempts to render a PNG icon from Papirus-Dark (or hicolor fallback). When imlib2 is not present the existing letter-square is shown unchanged. launcher/Makefile auto-detects imlib2 via pkg-config and sets -DHAVE_IMLIB2. Fix #11: True 4-desktop support in the WM. Each Client carries a desktop field (assigned at map time from wm->cur_desktop). Clicking a workspace button in the deskbar sends _NET_CURRENT_DESKTOP; the WM now handles it via wm_switch_desktop() which unmaps frames on the leaving desktop, maps frames on the arriving desktop, and re-focuses. _NET_WM_DESKTOP is set per window so the deskbar can filter db_update_apps() to show only current-desktop windows (sticky windows with desktop 0xFFFFFFFF are always shown).
This commit is contained in:
7 files changed
+173
-16
No files matched your search
@@ -7,6 +7,14 @@ ifeq ($(PKG_XFT_C),)
|
||||
PKG_XFT_L := -L/opt/homebrew/lib -lXft -lfontconfig
|
||||
endif
|
||||
|
||||
# Optional: imlib2 for freedesktop icon loading
|
||||
PKG_IL_C := $(shell pkg-config --cflags imlib2 2>/dev/null)
|
||||
PKG_IL_L := $(shell pkg-config --libs imlib2 2>/dev/null)
|
||||
ifneq ($(PKG_IL_C),)
|
||||
PKG_XFT_C += $(PKG_IL_C) -DHAVE_IMLIB2
|
||||
PKG_XFT_L += $(PKG_IL_L)
|
||||
endif
|
||||
|
||||
CFLAGS = -Wall -Wextra -O2 $(PKG_XFT_C)
|
||||
LDFLAGS = $(PKG_XFT_L)
|
||||
|
||||
|
||||
+51
-10
@@ -58,13 +58,13 @@ static const unsigned long ICON_BG[] = {
|
||||
#define FONT_ICON "Sans:size=13:weight=bold"
|
||||
#define MAX_FLT 64
|
||||
|
||||
typedef struct { char name[64]; char cmd[256]; char cat[32]; } App;
|
||||
typedef struct { char name[64]; char cmd[256]; char cat[32]; char icon[64]; } App;
|
||||
|
||||
static App APPS[MAX_APPS];
|
||||
static int NAPP = 0;
|
||||
|
||||
static const char *CATS[] = {
|
||||
"All", "Internet", "Media", "Office", "System", "Settings"
|
||||
"All", "Internet", "Media", "Office", "System", "Settings", "Other"
|
||||
};
|
||||
#define NCAT ((int)(sizeof(CATS)/sizeof(CATS[0])))
|
||||
|
||||
@@ -89,7 +89,7 @@ static const char *map_category(const char *cats) {
|
||||
strstr(cats, "FileManager") || strstr(cats, "Utility") ||
|
||||
strstr(cats, "Monitor") || strstr(cats, "Security"))
|
||||
return "System";
|
||||
return "";
|
||||
return "Other";
|
||||
}
|
||||
|
||||
static void clean_exec(char *dst, const char *src, size_t dstsz) {
|
||||
@@ -108,7 +108,7 @@ static void load_desktop_file(const char *path) {
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return;
|
||||
|
||||
char name[64] = {0}, exec[256] = {0}, cats[128] = {0};
|
||||
char name[64] = {0}, exec[256] = {0}, cats[128] = {0}, icon[64] = {0};
|
||||
int in_entry = 0, nodisplay = 0, is_app = 0;
|
||||
char line[512];
|
||||
|
||||
@@ -126,6 +126,8 @@ static void load_desktop_file(const char *path) {
|
||||
snprintf(exec, sizeof(exec), "%s", line + 5);
|
||||
else if (!cats[0] && strncmp(line, "Categories=", 11) == 0)
|
||||
snprintf(cats, sizeof(cats), "%s", line + 11);
|
||||
else if (!icon[0] && strncmp(line, "Icon=", 5) == 0)
|
||||
snprintf(icon, sizeof(icon), "%s", line + 5);
|
||||
else if (strncmp(line, "NoDisplay=true", 14) == 0)
|
||||
nodisplay = 1;
|
||||
else if (strncmp(line, "Type=Application", 16) == 0)
|
||||
@@ -137,6 +139,7 @@ static void load_desktop_file(const char *path) {
|
||||
snprintf(APPS[NAPP].name, 64, "%s", name);
|
||||
clean_exec(APPS[NAPP].cmd, exec, 256);
|
||||
snprintf(APPS[NAPP].cat, 32, "%s", map_category(cats));
|
||||
snprintf(APPS[NAPP].icon, 64, "%s", icon);
|
||||
NAPP++;
|
||||
}
|
||||
|
||||
@@ -292,6 +295,39 @@ static int card_at_vis(int x, int y) {
|
||||
return row * COLS + col;
|
||||
}
|
||||
|
||||
/* ── icon loading (imlib2, optional) ─────────────────────────────────── */
|
||||
#ifdef HAVE_IMLIB2
|
||||
#include <Imlib2.h>
|
||||
static const char *ICON_DIRS[] = {
|
||||
"/usr/share/icons/Papirus-Dark/48x48/apps",
|
||||
"/usr/share/icons/Papirus/48x48/apps",
|
||||
"/usr/share/icons/hicolor/48x48/apps",
|
||||
"/usr/share/pixmaps",
|
||||
NULL
|
||||
};
|
||||
static int render_icon(L *l, const char *icon_name, int ix, int iy, int sz) {
|
||||
if (!icon_name || !icon_name[0]) return 0;
|
||||
char path[512];
|
||||
for (int i = 0; ICON_DIRS[i]; i++) {
|
||||
snprintf(path, sizeof(path), "%s/%s.png", ICON_DIRS[i], icon_name);
|
||||
if (access(path, R_OK) == 0) goto found;
|
||||
}
|
||||
return 0;
|
||||
found:;
|
||||
imlib_context_set_display(l->dpy);
|
||||
imlib_context_set_visual(l->visual);
|
||||
imlib_context_set_colormap(l->cmap);
|
||||
imlib_context_set_drawable(l->win);
|
||||
imlib_context_set_blend(1);
|
||||
Imlib_Image img = imlib_load_image_immediately(path);
|
||||
if (!img) return 0;
|
||||
imlib_context_set_image(img);
|
||||
imlib_render_image_on_drawable_at_size(ix, iy, sz, sz);
|
||||
imlib_free_image();
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ── draw ────────────────────────────────────────────────────────────── */
|
||||
static void draw(L *l) {
|
||||
XftDraw *xd = XftDrawCreate(l->dpy, l->win, l->visual, l->cmap);
|
||||
@@ -382,12 +418,17 @@ static void draw(L *l) {
|
||||
for (const char *p = APPS[app_i].name; *p; p++) h = h * 31 + (unsigned char)*p;
|
||||
int ix = cx + (CARD_W - ICON_SZ) / 2;
|
||||
int iy = cy + 10;
|
||||
XSetForeground(l->dpy, l->gc, mkc(l, ICON_BG[h % NICON]));
|
||||
XFillRectangle(l->dpy, l->win, l->gc, ix, iy, ICON_SZ, ICON_SZ);
|
||||
|
||||
char letter[2] = { APPS[app_i].name[0], '\0' };
|
||||
if (letter[0] >= 'a' && letter[0] <= 'z') letter[0] -= 32;
|
||||
ds_ctr(l, xd, l->font_icon, 0xEAF0FF, ix, iy, ICON_SZ, ICON_SZ, letter);
|
||||
int icon_rendered = 0;
|
||||
#ifdef HAVE_IMLIB2
|
||||
icon_rendered = render_icon(l, APPS[app_i].icon, ix, iy, ICON_SZ);
|
||||
#endif
|
||||
if (!icon_rendered) {
|
||||
XSetForeground(l->dpy, l->gc, mkc(l, ICON_BG[h % NICON]));
|
||||
XFillRectangle(l->dpy, l->win, l->gc, ix, iy, ICON_SZ, ICON_SZ);
|
||||
char letter[2] = { APPS[app_i].name[0], '\0' };
|
||||
if (letter[0] >= 'a' && letter[0] <= 'z') letter[0] -= 32;
|
||||
ds_ctr(l, xd, l->font_icon, 0xEAF0FF, ix, iy, ICON_SZ, ICON_SZ, letter);
|
||||
}
|
||||
|
||||
char lbl[14];
|
||||
snprintf(lbl, sizeof(lbl), "%.12s", APPS[app_i].name);
|
||||
|
||||
Reference in new issue
Block a user