Commit Diff


commit - 66bbd5426a0538e0a9b8bb43bb50204ef13fdc41
commit + a2df4b2f53b80bce268b88f369f1c4fda6f06ca0
blob - 600e835e7dbe1e8824b3cca52d858609b5fa586e
blob + f40494588161c557dbdd73e9ce09016bcd9beb81
--- bxwm.c
+++ bxwm.c
@@ -1,7 +1,5 @@
-/* bxwm.c
- *
+/*
  * A very basic X window manager, a.k.a. Brett's X window manager.
- *
  * See LICENSE.md and README.md for details.
  */
 
@@ -22,6 +20,7 @@ typedef struct {
     Window win;
     int focused;
     int ws;
+    int is_dock; /* dock/bar window: no border, no positioning,  no focus */
 } Client;
 
 /* Globals */
@@ -29,6 +28,7 @@ static Display *dpy;
 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;
@@ -40,6 +40,8 @@ static int ws_focus_idx[NUM_WORKSPACES] = {-1}; /* Ind
 /* 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;
@@ -49,6 +51,7 @@ static void run(void);
 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);
@@ -109,6 +112,11 @@ static void setup(void) {
     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))) {
@@ -134,7 +142,8 @@ static void setup(void) {
     /* 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 */
@@ -171,12 +180,20 @@ static void setup(void) {
     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));
@@ -196,6 +213,8 @@ static void setup(void) {
     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
@@ -215,6 +234,25 @@ static void manage(Window w) {
     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");
@@ -222,24 +260,20 @@ static void manage(Window w) {
     }
     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,
@@ -252,13 +286,16 @@ static void manage(Window w) {
         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();
 }
 
@@ -286,9 +323,9 @@ static void focus_client(Client *c) {
 }
 
 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;
 
@@ -301,7 +338,7 @@ static void center_window(Client *c) {
             .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
         };
@@ -312,9 +349,9 @@ static void center_window(Client *c) {
 }
 
 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;
 
@@ -327,7 +364,7 @@ static void left_half_window(Client *c) {
             .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
         };
@@ -338,9 +375,9 @@ static void left_half_window(Client *c) {
 }
 
 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;
 
@@ -353,7 +390,7 @@ static void right_half_window(Client *c) {
             .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
         };
@@ -370,24 +407,24 @@ static void small_window(Client *c) {
     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
@@ -399,21 +436,21 @@ static void small_window(Client *c) {
             .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;
 
@@ -650,6 +687,8 @@ static void unmanage(Window w) {
     if (!focused_client && num_clients)
         focus_client(&clients[num_clients - 1]);
 
+    update_workarea();
+
     update_client_list();
 
     if (!focused_client) {
@@ -774,6 +813,10 @@ static void run(void) {
             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();
+            }
         }
     }
 }
@@ -793,6 +836,65 @@ static void update_client_list(void) {
         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)