commit - 66bbd5426a0538e0a9b8bb43bb50204ef13fdc41
commit + a2df4b2f53b80bce268b88f369f1c4fda6f06ca0
blob - 600e835e7dbe1e8824b3cca52d858609b5fa586e
blob + f40494588161c557dbdd73e9ce09016bcd9beb81
--- bxwm.c
+++ bxwm.c
-/* bxwm.c
- *
+/*
* A very basic X window manager, a.k.a. Brett's X window manager.
- *
* See LICENSE.md and README.md for details.
*/
Window win;
int focused;
int ws;
+ int is_dock; /* dock/bar window: no border, no positioning, no focus */
} Client;
/* Globals */
static Window root;
static int screen;
static unsigned long screen_w, screen_h;
+static unsigned long wa_x, wa_y, wa_w, wa_h; /* work area (screen minus struts) */
static Client *clients = NULL;
static unsigned int num_clients = 0;
static Client *focused_client = NULL;
/* EWMH atoms */
static Atom net_supported, net_number_of_desktops, net_current_desktop;
static Atom net_client_list, net_active_window, net_wm_desktop;
+static Atom net_wm_strut_partial, net_wm_strut, net_workarea;
+static Atom net_wm_window_type, net_wm_window_type_dock;
/* ICCCM atoms */
static Atom wm_protocols, wm_delete_window;
static void cleanup(int status);
static void setup(void);
static int xerror(Display *dpy, XErrorEvent *ee);
+static void update_workarea(void);
static void manage(Window w);
static void spawn(const char *cmd);
static void focus_client(Client *c);
screen_w = DisplayWidth(dpy, screen);
screen_h = DisplayHeight(dpy, screen);
+ wa_x = 0;
+ wa_y = 0;
+ wa_w = screen_w;
+ wa_h = screen_h;
+
XSetErrorHandler(xerror);
if (XGetSelectionOwner(dpy, XInternAtom(dpy, "WM_S0", False))) {
/* Root window event mask */
wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
ButtonPressMask | PointerMotionMask | EnterWindowMask |
- LeaveWindowMask | KeyPressMask | KeyReleaseMask;
+ LeaveWindowMask | KeyPressMask | KeyReleaseMask |
+ PropertyChangeMask;
XChangeWindowAttributes(dpy, root, CWEventMask, &wa);
/* Set root cursor */
net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ net_wm_strut_partial = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False);
+ net_wm_strut = XInternAtom(dpy, "_NET_WM_STRUT", False);
+ net_workarea = XInternAtom(dpy, "_NET_WORKAREA", False);
+ net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ net_wm_window_type_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
/* Set EWMH properties on root window */
Atom supported[] = {
net_supported, net_number_of_desktops, net_current_desktop,
- net_client_list, net_active_window, net_wm_desktop
+ net_client_list, net_active_window, net_wm_desktop,
+ net_wm_strut_partial, net_wm_strut, net_workarea,
+ net_wm_window_type, net_wm_window_type_dock
};
+
XChangeProperty(dpy, root, net_supported, XA_ATOM, 32,
PropModeReplace, (unsigned char *)supported,
sizeof(supported) / sizeof(Atom));
XChangeProperty(dpy, root, net_active_window, XA_WINDOW, 32,
PropModeReplace, (unsigned char *)&active, 1);
+ update_workarea();
+
#ifdef DEBUG
fprintf(stderr, "bxwm: setup complete\n");
#endif
if (!XGetWindowAttributes(dpy, w, &wa))
return;
+ /* Check if window is a dock/bar */
+ int is_dock = 0;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = NULL;
+
+ if (XGetWindowProperty(dpy, w, net_wm_window_type, 0, 1, False,
+ XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after,
+ &data) == Success && data) {
+ if (nitems >= 1 && ((Atom *)data)[0] == net_wm_window_type_dock)
+ is_dock = 1;
+ XFree(data);
+ }
+
+ /* Events only (border pixel set via Configure later) */
+ attrs.event_mask = StructureNotifyMask | EnterWindowMask | PropertyChangeMask;
+ XChangeWindowAttributes(dpy, w, CWEventMask, &attrs);
+
clients = realloc(clients, ++num_clients * sizeof(Client));
if (!clients) {
fprintf(stderr, "bxwm: realloc failed\n");
}
c = &clients[num_clients - 1];
c->win = w;
- c->ws = curws; /* Assign to current workspace */
+ c->ws = curws;
+ c->is_dock = is_dock;
- /* Events only (border pixel set via Configure later) */
- attrs.event_mask = StructureNotifyMask | EnterWindowMask | PropertyChangeMask;
- XChangeWindowAttributes(dpy, w, CWEventMask, &attrs);
+ if (!is_dock) {
+ /* Golden ratio initial placement, centered */
+ win_h = (wa_h * INITIAL_HEIGHT) - (2 * BORDER_WIDTH);
+ win_w = (win_h * PHI) - (2 * BORDER_WIDTH);
+ if (win_w > (wa_w * 0.8) - (2 * BORDER_WIDTH)) {
+ win_w = (wa_w * 0.8) - (2 * BORDER_WIDTH);
+ win_h = (win_w / PHI) - (2 * BORDER_WIDTH);
+ }
+ win_x = wa_x + (wa_w - (win_w + 2 * BORDER_WIDTH)) / 2;
+ win_y = wa_y + (wa_h - (win_h + 2 * BORDER_WIDTH)) / 2;
- /* Golden ratio initial placement, centered */
- win_h = (screen_h * INITIAL_HEIGHT) - (2 * BORDER_WIDTH);
- win_w = (win_h * PHI) - (2 * BORDER_WIDTH);
- if (win_w > (screen_w * 0.8) - (2 * BORDER_WIDTH)) {
- win_w = (screen_w * 0.8) - (2 * BORDER_WIDTH);
- win_h = (win_w / PHI) - (2 * BORDER_WIDTH);
- }
- win_x = (screen_w - (win_w + 2 * BORDER_WIDTH)) / 2;
- win_y = (screen_h - (win_h + 2 * BORDER_WIDTH)) / 2;
-
- /* Batch: pos + size + border_width + stack Above */
- {
XWindowChanges wc = {
.x = win_x,
.y = win_y,
XConfigureWindow(dpy, w, mask, &wc);
}
- XMapWindow(dpy, w); /* Now map (configure already applied) */
+ XMapWindow(dpy, w);
- focus_client(c); /* Border color + XRaise redundant (already Above), but keeps focus/input */
+ if (!is_dock) {
+ focus_client(c);
+ long ws = curws;
+ XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&ws, 1);
+ }
- long ws = curws;
- XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char *)&ws, 1);
+ update_workarea();
update_client_list();
}
}
static void center_window(Client *c) {
- unsigned int win_w = (screen_w / 2) - (2 * BORDER_WIDTH);
- int win_x = (screen_w - (win_w + 2 * BORDER_WIDTH)) / 2;
- int win_y = 0;
+ unsigned int win_w = (wa_w / 2) - (2 * BORDER_WIDTH);
+ int win_x = wa_x + (wa_w - (win_w + 2 * BORDER_WIDTH)) / 2;
+ int win_y = wa_y;
if (!c) return;
.x = win_x,
.y = win_y,
.width = win_w,
- .height = screen_h - (2 * BORDER_WIDTH),
+ .height = wa_h - (2 * BORDER_WIDTH),
.border_width = BORDER_WIDTH,
.stack_mode = Above
};
}
static void left_half_window(Client *c) {
- unsigned int win_w = (screen_w / 2) - (2 * BORDER_WIDTH);
- int win_x = 0;
- int win_y = 0;
+ unsigned int win_w = (wa_w / 2) - (2 * BORDER_WIDTH);
+ int win_x = wa_x;
+ int win_y = wa_y;
if (!c) return;
.x = win_x,
.y = win_y,
.width = win_w,
- .height = screen_h - (2 * BORDER_WIDTH),
+ .height = wa_h - (2 * BORDER_WIDTH),
.border_width = BORDER_WIDTH,
.stack_mode = Above
};
}
static void right_half_window(Client *c) {
- unsigned int win_w = (screen_w / 2) - (2 * BORDER_WIDTH);
- int win_x = screen_w - win_w - (2 * BORDER_WIDTH);
- int win_y = 0;
+ unsigned int win_w = (wa_w / 2) - (2 * BORDER_WIDTH);
+ int win_x = wa_x + wa_w - win_w - (2 * BORDER_WIDTH);
+ int win_y = wa_y;
if (!c) return;
.x = win_x,
.y = win_y,
.width = win_w,
- .height = screen_h - (2 * BORDER_WIDTH),
+ .height = wa_h - (2 * BORDER_WIDTH),
.border_width = BORDER_WIDTH,
.stack_mode = Above
};
if (!c) return;
/* Step 1: Calculate height first (50% of screen, minus borders) */
- win_h = (screen_h * INITIAL_HEIGHT) - (2 * BORDER_WIDTH);
+ win_h = (wa_h * INITIAL_HEIGHT) - (2 * BORDER_WIDTH);
/* Step 2: Calculate width using golden ratio (width = height * PHI) */
win_w = (win_h * PHI) - (2 * BORDER_WIDTH);
/* Step 3: Safety check - if too wide (>80% of screen), cap it */
- if (win_w > (screen_w * 0.8) - (2 * BORDER_WIDTH)) {
- win_w = (screen_w * 0.8) - (2 * BORDER_WIDTH);
+ if (win_w > (wa_w * 0.8) - (2 * BORDER_WIDTH)) {
+ win_w = (wa_w * 0.8) - (2 * BORDER_WIDTH);
win_h = (win_w / PHI) - (2 * BORDER_WIDTH);
}
/* Step 4: Center the window horizontally
(screen width - total window width including borders) / 2 */
- win_x = (screen_w - (win_w + 2 * BORDER_WIDTH)) / 2;
+ win_x = wa_x + (wa_w - (win_w + 2 * BORDER_WIDTH)) / 2;
/* Step 5: Center the window vertically
(screen height - total window height including borders) / 2 */
- win_y = (screen_h - (win_h + 2 * BORDER_WIDTH)) / 2;
+ win_y = wa_y + (wa_h - (win_h + 2 * BORDER_WIDTH)) / 2;
#ifdef DEBUG
.x = win_x,
.y = win_y,
.width = win_w,
- .height = win_h, /* You had screen_h here - that's maximize! */
+ .height = win_h,
.border_width = BORDER_WIDTH,
.stack_mode = Above
};
- unsigned int mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth | CWStackMode; /* Fix line break */
+ unsigned int mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth | CWStackMode;
XConfigureWindow(dpy, c->win, mask, &wc);
}
focus_client(c);
}
static void maximize_window(Client *c) {
- unsigned int win_w = screen_w - (2 * BORDER_WIDTH);
- unsigned int win_h = screen_h - (2 * BORDER_WIDTH);
- int win_x = 0;
- int win_y = 0;
+ unsigned int win_w = wa_w - (2 * BORDER_WIDTH);
+ unsigned int win_h = wa_h - (2 * BORDER_WIDTH);
+ int win_x = wa_x;
+ int win_y = wa_y;
if (!c) return;
if (!focused_client && num_clients)
focus_client(&clients[num_clients - 1]);
+ update_workarea();
+
update_client_list();
if (!focused_client) {
manage(ev.xmaprequest.window);
} else if (ev.type == DestroyNotify) {
unmanage(ev.xdestroywindow.window);
+ } else if (ev.type == PropertyNotify) {
+ if (ev.xproperty.atom == net_wm_strut_partial) {
+ update_workarea();
+ }
}
}
}
free(wins);
}
+static void update_workarea(void) {
+ unsigned long left = 0, right = 0, top = 0, bottom = 0;
+ unsigned int i;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = NULL;
+
+ for (i = 0; i < num_clients; i++) {
+ /* Try _NET_WM_STRUT_PARTIAL (12 CARDINALs) */
+ if (XGetWindowProperty(dpy, clients[i].win, net_wm_strut_partial,
+ 0, 12, False, XA_CARDINAL, &actual_type, &actual_format,
+ &nitems, &bytes_after, &data) == Success && data) {
+ if (actual_type == XA_CARDINAL && nitems >= 4) {
+ unsigned long *s = (unsigned long *)data;
+ if (s[0] > left) left = s[0];
+ if (s[1] > right) right = s[1];
+ if (s[2] > top) top = s[2];
+ if (s[3] > bottom) bottom = s[3];
+ }
+ XFree(data);
+ data = NULL;
+ continue;
+ }
+ /* Fall back to _NET_WM_STRUT (4 CARDINALs) */
+ if (XGetWindowProperty(dpy, clients[i].win, net_wm_strut,
+ 0, 4, False, XA_CARDINAL, &actual_type, &actual_format,
+ &nitems, &bytes_after, &data) == Success && data) {
+ if (actual_type == XA_CARDINAL && nitems >= 4) {
+ unsigned long *s = (unsigned long *)data;
+ if (s[0] > left) left = s[0];
+ if (s[1] > right) right = s[1];
+ if (s[2] > top) top = s[2];
+ if (s[3] > bottom) bottom = s[3];
+ }
+ XFree(data);
+ }
+ }
+
+ wa_x = left;
+ wa_y = top;
+ wa_w = screen_w - left - right;
+ wa_h = screen_h - top - bottom;
+
+ /* Set _NET_WORKAREA (4 values per desktop) */
+ long *workareas = calloc(NUM_WORKSPACES * 4, sizeof(long));
+ for (i = 0; i < NUM_WORKSPACES; i++) {
+ workareas[i * 4 + 0] = wa_x;
+ workareas[i * 4 + 1] = wa_y;
+ workareas[i * 4 + 2] = wa_w;
+ workareas[i * 4 + 3] = wa_h;
+ }
+ XChangeProperty(dpy, root, net_workarea, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)workareas,
+ NUM_WORKSPACES * 4);
+ free(workareas);
+}
+
+
static void cleanup(int status) {
if (dpy) {
if (clients)