Add 6 desktop features: workspace shortcuts, Exposé, deskbar icons, notifications, screen lock, move-to-desktop
- Super+1-4: switch virtual desktop; Super+Shift+1-4: move focused window to desktop N - Super+Space: Exposé window overview (tiled grid), click to select, Escape to cancel - wm/src/expose.c: expose_show/expose_hide using ceil(sqrt) grid layout with saved/restored geometry - client_move_to_desktop(): moves window between desktops, fires _NET_WM_DESKTOP update - on_client_message: handles _NET_WM_DESKTOP to move windows from external requests - deskbar: optional imlib2 app icon rendering (Papirus-Dark → Papirus → hicolor → pixmaps fallback) - deskbar: XGetClassHint populates cls field for better icon lookup - deskbar: context menu extended with separator + "→ Desktop 1-4" items via _NET_WM_DESKTOP - orionnotify/: freedesktop.org notification daemon (libdbus-1), stacks up to 5 popups top-right - orionlock/: PAM-authenticated fullscreen screen lock with clock, date, password dots - Lock Screen menu item wired to orionlock - Root Makefile includes orionnotify and orionlock targets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHiBQKPfUoaJgCKuA9op1H
This commit is contained in:
14 files changed
+977
-31
No files matched your search
+2
-2
@@ -12,9 +12,9 @@ ifeq ($(PKG_XFT_C),)
|
||||
endif
|
||||
|
||||
CFLAGS = -Wall -Wextra -O2 -Iinclude $(PKG_XFT_C)
|
||||
LDFLAGS = $(PKG_XFT_L)
|
||||
LDFLAGS = $(PKG_XFT_L) -lm
|
||||
|
||||
SRC = src/wm.c src/client.c src/events.c src/draw.c src/color.c src/menu.c src/switcher.c
|
||||
SRC = src/wm.c src/client.c src/events.c src/draw.c src/color.c src/menu.c src/switcher.c src/expose.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
BIN = orionwm
|
||||
|
||||
|
||||
@@ -82,6 +82,9 @@ typedef struct Client {
|
||||
int min_w, min_h; /* PMinSize */
|
||||
int inc_w, inc_h; /* PResizeInc — 0 means no constraint */
|
||||
int base_w, base_h; /* PBaseSize — baseline for increment calc */
|
||||
/* Exposé saved geometry */
|
||||
int exp_x, exp_y;
|
||||
unsigned exp_w, exp_h;
|
||||
struct Client *next;
|
||||
} Client;
|
||||
|
||||
@@ -141,6 +144,11 @@ typedef struct WM {
|
||||
Window swl_wins[64];
|
||||
char swl_titles[64][128];
|
||||
int swl_mini[64]; /* 1 if corresponding entry is minimized */
|
||||
|
||||
/* Exposé window overview */
|
||||
int expose_active;
|
||||
int expose_count;
|
||||
Window expose_wins[64]; /* frames shown in overview */
|
||||
} WM;
|
||||
|
||||
/* core */
|
||||
@@ -163,6 +171,7 @@ void client_toggle_maximize(WM *wm, Client *c);
|
||||
void client_snap_left(WM *wm, Client *c);
|
||||
void client_snap_right(WM *wm, Client *c);
|
||||
void client_set_wm_state(WM *wm, Client *c, long state);
|
||||
void client_move_to_desktop(WM *wm, Client *c, int new_desk);
|
||||
|
||||
/* drawing */
|
||||
void frame_draw(WM *wm, Client *c);
|
||||
@@ -190,6 +199,10 @@ void switcher_prev(WM *wm);
|
||||
void switcher_hide(WM *wm, int commit);
|
||||
void switcher_draw(WM *wm);
|
||||
|
||||
/* exposé */
|
||||
void expose_show(WM *wm);
|
||||
void expose_hide(WM *wm, Window selected_frame);
|
||||
|
||||
/* events */
|
||||
void on_map_request(WM *wm, XMapRequestEvent *e);
|
||||
void on_unmap_notify(WM *wm, XUnmapEvent *e);
|
||||
|
||||
@@ -328,6 +328,34 @@ void client_snap_left(WM *wm, Client *c) {
|
||||
frame_draw(wm, c);
|
||||
}
|
||||
|
||||
void client_move_to_desktop(WM *wm, Client *c, int new_desk) {
|
||||
if (new_desk < 0 || new_desk >= 4 || c->desktop == new_desk) return;
|
||||
|
||||
/* hide frame if it's on the current (now-active) desktop */
|
||||
if (c->desktop == wm->cur_desktop && !c->minimized)
|
||||
XUnmapWindow(wm->dpy, c->frame);
|
||||
|
||||
c->desktop = new_desk;
|
||||
|
||||
/* update _NET_WM_DESKTOP */
|
||||
long desk = new_desk;
|
||||
XChangeProperty(wm->dpy, c->win, wm->net_wm_desktop, XA_CARDINAL, 32,
|
||||
PropModeReplace, (unsigned char *)&desk, 1);
|
||||
|
||||
/* if we moved the focused window away, re-focus another */
|
||||
if (wm->focused == c) {
|
||||
wm->focused->focused = 0;
|
||||
frame_draw(wm, wm->focused);
|
||||
wm->focused = NULL;
|
||||
for (Client *n = wm->clients; n; n = n->next) {
|
||||
if (!n->minimized && n->desktop == wm->cur_desktop) {
|
||||
client_focus(wm, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void client_snap_right(WM *wm, Client *c) {
|
||||
if (c->maximized) client_unmaximize(wm, c);
|
||||
int sw = DisplayWidth(wm->dpy, wm->screen);
|
||||
|
||||
+56
-3
@@ -74,6 +74,7 @@ void on_expose(WM *wm, XExposeEvent *e) {
|
||||
if (c && !c->minimized) frame_draw(wm, c);
|
||||
}
|
||||
|
||||
|
||||
void on_configure_request(WM *wm, XConfigureRequestEvent *e) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
XWindowChanges wc;
|
||||
@@ -136,6 +137,7 @@ 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);
|
||||
Atom net_wm_desktop = XInternAtom(wm->dpy, "_NET_WM_DESKTOP", False);
|
||||
|
||||
/* _NET_CURRENT_DESKTOP → switch virtual desktop */
|
||||
if (e->message_type == wm->net_current_desktop && e->window == wm->root) {
|
||||
@@ -143,6 +145,13 @@ void on_client_message(WM *wm, XClientMessageEvent *e) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* _NET_WM_DESKTOP → move window to desktop */
|
||||
if (e->message_type == net_wm_desktop) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
if (c) client_move_to_desktop(wm, c, (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);
|
||||
@@ -208,6 +217,14 @@ void on_property_notify(WM *wm, XPropertyEvent *e) {
|
||||
}
|
||||
|
||||
void on_button_press(WM *wm, XButtonEvent *e) {
|
||||
/* Exposé — click on any frame to select and exit */
|
||||
if (wm->expose_active) {
|
||||
Client *c = client_find_by_frame(wm, e->window);
|
||||
if (c) expose_hide(wm, c->frame);
|
||||
else expose_hide(wm, None);
|
||||
return;
|
||||
}
|
||||
|
||||
/* menu window */
|
||||
if (wm->menu_visible && e->window == wm->menu_win) {
|
||||
if (e->button == Button1) menu_click(wm, e->x, e->y);
|
||||
@@ -401,9 +418,45 @@ void on_key_press(WM *wm, XKeyEvent *e) {
|
||||
if ((e->state & Mod4Mask) && sym == XK_Down && wm->focused)
|
||||
client_unmaximize(wm, wm->focused);
|
||||
|
||||
/* Escape — hide menu */
|
||||
if (sym == XK_Escape && wm->menu_visible)
|
||||
menu_hide(wm);
|
||||
/* Super+Space — Exposé window overview */
|
||||
if ((e->state & Mod4Mask) && sym == XK_space) {
|
||||
if (wm->expose_active) expose_hide(wm, None);
|
||||
else expose_show(wm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Super+1-4 — switch to desktop N */
|
||||
if ((e->state & Mod4Mask) && !(e->state & ShiftMask)) {
|
||||
int desk = -1;
|
||||
if (sym == XK_1) desk = 0;
|
||||
else if (sym == XK_2) desk = 1;
|
||||
else if (sym == XK_3) desk = 2;
|
||||
else if (sym == XK_4) desk = 3;
|
||||
if (desk >= 0) {
|
||||
if (wm->expose_active) expose_hide(wm, None);
|
||||
wm_switch_desktop(wm, desk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Super+Shift+1-4 — move focused window to desktop N */
|
||||
if ((e->state & Mod4Mask) && (e->state & ShiftMask) && wm->focused) {
|
||||
int desk = -1;
|
||||
if (sym == XK_1) desk = 0;
|
||||
else if (sym == XK_2) desk = 1;
|
||||
else if (sym == XK_3) desk = 2;
|
||||
else if (sym == XK_4) desk = 3;
|
||||
if (desk >= 0) {
|
||||
client_move_to_desktop(wm, wm->focused, desk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Escape — hide menu or exit Exposé */
|
||||
if (sym == XK_Escape) {
|
||||
if (wm->expose_active) expose_hide(wm, None);
|
||||
else if (wm->menu_visible) menu_hide(wm);
|
||||
}
|
||||
}
|
||||
|
||||
void on_key_release(WM *wm, XKeyEvent *e) {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "wm.h"
|
||||
#include <math.h>
|
||||
|
||||
void expose_show(WM *wm) {
|
||||
if (wm->expose_active) return;
|
||||
|
||||
/* collect non-minimized windows on the current desktop */
|
||||
int n = 0;
|
||||
for (Client *c = wm->clients; c; c = c->next) {
|
||||
if (!c->minimized && c->desktop == wm->cur_desktop && n < 64) {
|
||||
/* save original geometry */
|
||||
c->exp_x = c->x;
|
||||
c->exp_y = c->y;
|
||||
c->exp_w = c->w;
|
||||
c->exp_h = c->h;
|
||||
wm->expose_wins[n++] = c->frame;
|
||||
}
|
||||
}
|
||||
if (n == 0) return;
|
||||
|
||||
wm->expose_active = 1;
|
||||
wm->expose_count = n;
|
||||
|
||||
int sw = DisplayWidth(wm->dpy, wm->screen) - DESKBAR_W;
|
||||
int sh = DisplayHeight(wm->dpy, wm->screen);
|
||||
|
||||
int cols = (int)ceil(sqrt((double)n));
|
||||
int rows = (n + cols - 1) / cols;
|
||||
int pad = 16;
|
||||
int cell_w = (sw - pad * (cols + 1)) / cols;
|
||||
int cell_h = (sh - pad * (rows + 1)) / rows;
|
||||
|
||||
int idx = 0;
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols && idx < n; col++, idx++) {
|
||||
Client *c = client_find_by_frame(wm, wm->expose_wins[idx]);
|
||||
if (!c) continue;
|
||||
|
||||
/* un-maximize so we can freely resize */
|
||||
if (c->maximized) {
|
||||
c->maximized = 0;
|
||||
XDeleteProperty(wm->dpy, c->win, wm->net_wm_state);
|
||||
}
|
||||
|
||||
int tx = pad + col * (cell_w + pad);
|
||||
int ty = pad + row * (cell_h + pad);
|
||||
int tw = cell_w;
|
||||
int th = cell_h;
|
||||
|
||||
c->x = tx; c->y = ty;
|
||||
c->w = (unsigned)(tw - BORDER_W * 2);
|
||||
c->h = (unsigned)(th - TITLE_HEIGHT - BORDER_W);
|
||||
|
||||
int fw = (int)c->w + BORDER_W * 2;
|
||||
int fh = (int)c->h + TITLE_HEIGHT + BORDER_W;
|
||||
XMoveResizeWindow(wm->dpy, c->frame, c->x, c->y, fw, fh);
|
||||
XResizeWindow(wm->dpy, c->win, c->w, c->h);
|
||||
frame_draw(wm, c);
|
||||
}
|
||||
}
|
||||
XSync(wm->dpy, False);
|
||||
}
|
||||
|
||||
void expose_hide(WM *wm, Window selected_frame) {
|
||||
if (!wm->expose_active) return;
|
||||
wm->expose_active = 0;
|
||||
|
||||
Client *to_focus = NULL;
|
||||
|
||||
for (int i = 0; i < wm->expose_count; i++) {
|
||||
Client *c = client_find_by_frame(wm, wm->expose_wins[i]);
|
||||
if (!c) continue;
|
||||
|
||||
c->x = c->exp_x;
|
||||
c->y = c->exp_y;
|
||||
c->w = c->exp_w;
|
||||
c->h = c->exp_h;
|
||||
|
||||
int fw = (int)c->w + BORDER_W * 2;
|
||||
int fh = (int)c->h + TITLE_HEIGHT + BORDER_W;
|
||||
XMoveResizeWindow(wm->dpy, c->frame, c->x, c->y, fw, fh);
|
||||
XResizeWindow(wm->dpy, c->win, c->w, c->h);
|
||||
frame_draw(wm, c);
|
||||
|
||||
if (c->frame == selected_frame) to_focus = c;
|
||||
}
|
||||
|
||||
wm->expose_count = 0;
|
||||
XSync(wm->dpy, False);
|
||||
|
||||
if (to_focus) client_focus(wm, to_focus);
|
||||
else if (wm->focused) client_focus(wm, wm->focused);
|
||||
}
|
||||
+11
@@ -103,6 +103,17 @@ void wm_init(WM *wm) {
|
||||
{ XK_Right, Mod4Mask }, /* snap right */
|
||||
{ XK_Up, Mod4Mask }, /* maximize */
|
||||
{ XK_Down, Mod4Mask }, /* restore */
|
||||
{ XK_space, Mod4Mask }, /* Exposé overview */
|
||||
/* Super+1-4: switch desktop */
|
||||
{ XK_1, Mod4Mask },
|
||||
{ XK_2, Mod4Mask },
|
||||
{ XK_3, Mod4Mask },
|
||||
{ XK_4, Mod4Mask },
|
||||
/* Super+Shift+1-4: move window to desktop */
|
||||
{ XK_1, Mod4Mask | ShiftMask },
|
||||
{ XK_2, Mod4Mask | ShiftMask },
|
||||
{ XK_3, Mod4Mask | ShiftMask },
|
||||
{ XK_4, Mod4Mask | ShiftMask },
|
||||
{ XK_Escape, 0 },
|
||||
};
|
||||
for (int i = 0; i < (int)(sizeof(grabs)/sizeof(grabs[0])); i++) {
|
||||
|
||||
Reference in new issue
Block a user