Commit Diff


commit - ac5b7bb7f2b6a4fa8bb0dc85e94f181feeac1886
commit + 66bbd5426a0538e0a9b8bb43bb50204ef13fdc41
blob - 3ba07a044fb7c4f06008b9e9195aabc1a5fa1b74
blob + 600e835e7dbe1e8824b3cca52d858609b5fa586e
--- bxwm.c
+++ bxwm.c
@@ -13,6 +13,7 @@
 #include <X11/Xutil.h>
 #include <X11/cursorfont.h>
 #include <X11/keysym.h>
+#include <X11/Xatom.h> 
 
 #include "config.h"
 
@@ -36,6 +37,13 @@ static unsigned long unfocus_pixel;
 static int curws = INITIAL_WORKSPACE;
 static int ws_focus_idx[NUM_WORKSPACES] = {-1}; /* Index of last focused per WS */
 
+/* EWMH atoms */
+static Atom net_supported, net_number_of_desktops, net_current_desktop;
+static Atom net_client_list, net_active_window, net_wm_desktop;
+
+/* ICCCM atoms */
+static Atom wm_protocols, wm_delete_window;
+
 /* Function declarations */
 static void run(void);
 static void cleanup(int status);
@@ -55,6 +63,7 @@ static void focus_prev(void);
 static void view(int ws);
 static void movetows(int ws);
 static void unmanage(Window w);
+static void update_client_list(void);
 
 int main(void) {
     signal(SIGTERM, cleanup);
@@ -153,6 +162,40 @@ static void setup(void) {
                  root, True, GrabModeAsync, GrabModeAsync); /* Super+Shift+N: move */
     }
 
+    /* Intern EWMH/ICCCM atoms */
+    net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
+    net_number_of_desktops = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False);
+    net_current_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
+    net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+    net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+    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);
+
+    /* 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
+    };
+    XChangeProperty(dpy, root, net_supported, XA_ATOM, 32,
+                    PropModeReplace, (unsigned char *)supported,
+                    sizeof(supported) / sizeof(Atom));
+
+    long ndesktops = NUM_WORKSPACES;
+    XChangeProperty(dpy, root, net_number_of_desktops, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char *)&ndesktops, 1);
+
+    long current_desktop = INITIAL_WORKSPACE;
+    XChangeProperty(dpy, root, net_current_desktop, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char *)&current_desktop, 1);
+
+    XChangeProperty(dpy, root, net_client_list, XA_WINDOW, 32,
+                    PropModeReplace, NULL, 0);
+
+    long active = None;
+    XChangeProperty(dpy, root, net_active_window, XA_WINDOW, 32,
+                    PropModeReplace, (unsigned char *)&active, 1);
+
     #ifdef DEBUG
         fprintf(stderr, "bxwm: setup complete\n");
     #endif
@@ -212,6 +255,11 @@ static void manage(Window w) {
     XMapWindow(dpy, w);  /* Now map (configure already applied) */
 
     focus_client(c);  /* Border color + XRaise redundant (already Above), but keeps focus/input */
+
+    long ws = curws;
+    XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char *)&ws, 1);
+    update_client_list();
 }
 
 static void focus_client(Client *c) {
@@ -228,6 +276,9 @@ static void focus_client(Client *c) {
         XSetWindowBorder(dpy, c->win, focus_pixel);
         XSetInputFocus(dpy, c->win, RevertToParent, CurrentTime);
         XRaiseWindow(dpy, c->win);
+        long active = c->win;
+        XChangeProperty(dpy, root, net_active_window, XA_WINDOW, 32,
+                        PropModeReplace, (unsigned char *)&active, 1);
         #ifdef DEBUG
             fprintf(stderr, "bxwm: focused 0x%lx\n", c->win);
         #endif
@@ -388,10 +439,25 @@ static void maximize_window(Client *c) {
 static void close_window(Client *c) {
     if (!c) return;
 
-    #ifdef DEBUG
-        fprintf(stderr, "bxwm: close 0x%lx\n", c->win);
-    #endif
-
+    int n;
+    Atom *protocols;
+    if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
+        for (int i = 0; i < n; i++) {
+            if (protocols[i] == wm_delete_window) {
+                XEvent ev;
+                ev.type = ClientMessage;
+                ev.xclient.window = c->win;
+                ev.xclient.message_type = wm_protocols;
+                ev.xclient.format = 32;
+                ev.xclient.data.l[0] = wm_delete_window;
+                ev.xclient.data.l[1] = CurrentTime;
+                XSendEvent(dpy, c->win, False, NoEventMask, &ev);
+                XFree(protocols);
+                return;
+            }
+        }
+        XFree(protocols);
+    }
     XKillClient(dpy, c->win);
 }
 
@@ -489,6 +555,11 @@ static void view(int ws) {
 
     curws = ws;
 
+    long current_desktop = curws;
+    XChangeProperty(dpy, root, net_current_desktop, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char *)&current_desktop, 1);
+
+
     /* Map new workspace windows */
     for (i = 0; i < num_clients; i++) {
         if (clients[i].ws == curws)
@@ -512,7 +583,11 @@ static void movetows(int ws) {
     
     old_ws = c->ws;
     c->ws = ws;
-    
+
+    long new_ws = ws;
+    XChangeProperty(dpy, c->win, net_wm_desktop, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char *)&new_ws, 1);
+
     if (old_ws == curws) {
         /* Moving away from visible workspace */
         XUnmapWindow(dpy, c->win);
@@ -574,6 +649,14 @@ static void unmanage(Window w) {
 
     if (!focused_client && num_clients)
         focus_client(&clients[num_clients - 1]);
+
+    update_client_list();
+
+    if (!focused_client) {
+        long active = None;
+        XChangeProperty(dpy, root, net_active_window, XA_WINDOW, 32,
+                        PropModeReplace, (unsigned char *)&active, 1);
+    }
 }
 
 static void spawn(const char *cmd) {
@@ -695,6 +778,21 @@ static void run(void) {
     }
 }
 
+static void update_client_list(void) {
+    Window *wins = NULL;
+    unsigned int i;
+
+    if (num_clients > 0) {
+        wins = malloc(num_clients * sizeof(Window));
+        for (i = 0; i < num_clients; i++)
+            wins[i] = clients[i].win;
+    }
+    XChangeProperty(dpy, root, net_client_list, XA_WINDOW, 32,
+                    PropModeReplace, (unsigned char *)wins, num_clients);
+    if (wins)
+        free(wins);
+}
+
 static void cleanup(int status) {
     if (dpy) {
         if (clients)