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:
Claude committed 2026-08-30 20:00:10 +00:00
1 parent 36641e77e2
commit b0f64d9b2e
7 files changed
+173 -16

No files matched your search

+56 -2
View File
@@ -94,11 +94,55 @@ void on_enter_notify(WM *wm, XCrossingEvent *e) {
if (c && !c->minimized) set_cursor(wm, c, e->x, e->y);
}
static void wm_switch_desktop(WM *wm, int new_desk) {
if (new_desk < 0 || new_desk >= 4 || new_desk == wm->cur_desktop) return;
/* hide windows on old desktop */
for (Client *c = wm->clients; c; c = c->next) {
if (!c->minimized && c->desktop == wm->cur_desktop)
XUnmapWindow(wm->dpy, c->frame);
}
wm->cur_desktop = new_desk;
/* show windows on new desktop */
for (Client *c = wm->clients; c; c = c->next) {
if (!c->minimized && c->desktop == new_desk)
XMapWindow(wm->dpy, c->frame);
}
/* update focus */
if (wm->focused && wm->focused->desktop != new_desk) {
wm->focused->focused = 0;
frame_draw(wm, wm->focused);
wm->focused = NULL;
}
if (!wm->focused) {
for (Client *c = wm->clients; c; c = c->next) {
if (!c->minimized && c->desktop == new_desk) {
client_focus(wm, c);
break;
}
}
}
/* advertise new desktop */
long nd = new_desk;
XChangeProperty(wm->dpy, wm->root, wm->net_current_desktop, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&nd, 1);
}
void on_client_message(WM *wm, XClientMessageEvent *e) {
Atom net_active = XInternAtom(wm->dpy, "_NET_ACTIVE_WINDOW", False);
Atom net_close = XInternAtom(wm->dpy, "_NET_CLOSE_WINDOW", False);
Atom wm_change_state = XInternAtom(wm->dpy, "WM_CHANGE_STATE", False);
/* _NET_CURRENT_DESKTOP → switch virtual desktop */
if (e->message_type == wm->net_current_desktop && e->window == wm->root) {
wm_switch_desktop(wm, (int)e->data.l[0]);
return;
}
if (e->message_type == net_active) {
Client *c = client_find_by_win(wm, e->window);
if (c) client_restore(wm, c);
@@ -271,8 +315,18 @@ void on_motion_notify(WM *wm, XMotionEvent *e) {
} else {
int nw = (int)wm->drag_win_w + dx;
int nh = (int)wm->drag_win_h + dy;
if (nw < MIN_WIN_W) nw = MIN_WIN_W;
if (nh < MIN_WIN_H) nh = MIN_WIN_H;
/* clamp to minimum */
if (nw < c->min_w) nw = c->min_w;
if (nh < c->min_h) nh = c->min_h;
/* snap to resize-increment grid (e.g. terminal character cells) */
if (c->inc_w > 1) {
nw = c->base_w + ((nw - c->base_w) / c->inc_w) * c->inc_w;
if (nw < c->min_w) nw += c->inc_w;
}
if (c->inc_h > 1) {
nh = c->base_h + ((nh - c->base_h) / c->inc_h) * c->inc_h;
if (nh < c->min_h) nh += c->inc_h;
}
c->w = (unsigned)nw; c->h = (unsigned)nh;
XResizeWindow(wm->dpy, c->frame, c->w+BORDER_W*2, c->h+TITLE_HEIGHT+BORDER_W);
XResizeWindow(wm->dpy, c->win, c->w, c->h);