Commit Diff


commit - 78a11878fa94c94014b7a0364319fb5025821916
commit + 3b54925cbf35d15232908b7ff996ef354d5b4fab
blob - 1b19669b48acd0d65bfc6423ef3f8ff04efbe10b
blob + 0812932392a17514093d9fe4d36951657c2a5705
--- bxwm.c
+++ bxwm.c
@@ -8,8 +8,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
-#include <unistd.h>
+#include <errno.h>
 #include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <unistd.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/cursorfont.h>
@@ -18,6 +23,8 @@
 
 #include "config.h"
 
+#define SOCKET_SUFFIX "/.local/tmp/bxwm.sock"
+
 /* Layout types */
 enum { FLOAT, CENTER, LEFT_HALF, RIGHT_HALF, SMALL, MAXIMIZE };
 
@@ -43,6 +50,9 @@ static unsigned long focus_pixel;
 static unsigned long unfocus_pixel;
 static int curws = INITIAL_WORKSPACE;
 static int ws_focus_idx[NUM_WORKSPACES] = {-1}; /* Index of last focused per WS */
+static int server_fd;
+static char socket_path[256];
+static int running;
 
 /* EWMH atoms */
 static Atom net_supported, net_number_of_desktops, net_current_desktop;
@@ -55,9 +65,10 @@ static Atom wm_protocols, wm_delete_window;
 
 /* Forward function declarations */
 static void logmsg(const char *fmt, ...);
-static void run(void);
-static void cleanup(int status);
+int socket_init(void);
 static void setup(void);
+static void handle_socket_command(int fd);
+static void cleanup(int status);
 static int xerror(Display *dpy, XErrorEvent *ee);
 static void update_workarea(void);
 static void manage(Window w);
@@ -76,18 +87,161 @@ static void movetows(int ws);
 static void unmanage(Window w);
 static void update_client_list(void);
 
-int 
-main(void) {
-    signal(SIGTERM, cleanup);
-    dpy = XOpenDisplay(NULL);
-    if (!dpy) {
-        fprintf(stderr, "bxwm: cannot open display '%s'\n", XDisplayName(NULL));
-        return 1;
-    }
+int
+main(int argc, char *argv[])
+{
+    (void)argc;
+    (void)argv;
+
+    XEvent ev;
+
+    // 1. Initial setup
+    if (!(dpy = XOpenDisplay(NULL))) exit(1);
     root = DefaultRootWindow(dpy);
+    
+    // 2. Initialize Socket and Poll
+    server_fd = socket_init();
+    int x_fd = ConnectionNumber(dpy);
+    
+    struct pollfd fds[2];
+    fds[0].fd = x_fd;
+    fds[0].events = POLLIN;
+    fds[1].fd = server_fd;
+    fds[1].events = POLLIN;
+
+    // 3. User configuration/setup (key grabs, etc)
     setup();
-    run();
+
+    /* Cache keycodes */
+    KeyCode kcodes[11];
+    KeyCode ws_codes[10];
+    int i, w;
+
+    for (i = 0; i < 11; i++)
+        kcodes[i] = XKeysymToKeycode(dpy, XStringToKeysym(
+            i == 0 ? "Return" :
+            i == 1 ? "Q" :
+            i == 2 ? "C" :
+            i == 3 ? "H" :
+            i == 4 ? "L" :
+            i == 5 ? "S" :
+            i == 6 ? "M" :
+            i == 7 ? "J" :
+            i == 8 ? "K" :
+            i == 9 ? "X" : "P"));
+
+    for (w = 0; w < 10; w++) {
+        KeySym ks = (w == 9) ? XK_0 : (XK_1 + w);
+        ws_codes[w] = XKeysymToKeycode(dpy, ks);
+    }
+
+    running = 1;
+
+    // 4. Main Loop
+    while (running) {
+        if (poll(fds, 2, -1) < 0) {
+            if (errno == EINTR) continue;
+            break;
+        }
+
+        // Handle X11 events
+        if (fds[0].revents & POLLIN) {
+            while (XPending(dpy)) {
+                XNextEvent(dpy, &ev);
+                if (ev.type == KeyPress) {
+                    /* Workspace view keys */
+                    for (w = 0; w < 10; w++) {
+                        if (ev.xkey.keycode == ws_codes[w] &&
+                            (ev.xkey.state & Mod4Mask) &&
+                            !(ev.xkey.state & ShiftMask)) {
+                            view(w);
+                            goto processed;
+                        }
+                    }
+                    /* Workspace move keys */
+                    for (w = 0; w < 10; w++) {
+                        if (ev.xkey.keycode == ws_codes[w] &&
+                            (ev.xkey.state & (Mod4Mask | ShiftMask)) ==
+                            (Mod4Mask | ShiftMask)) {
+                            movetows(w);
+                            goto processed;
+                        }
+                    }
+                    /* Action keys */
+                    for (i = 0; i < 11; i++) {
+                        if (ev.xkey.keycode == kcodes[i]) {
+                            switch (i) {
+                            case 0:
+                                if ((ev.xkey.state & (ShiftMask | Mod1Mask)) == (ShiftMask | Mod1Mask))
+                                    spawn(TERMINAL);
+                                break;
+                            case 1:
+                                if ((ev.xkey.state & (ControlMask | ShiftMask)) == (ControlMask | ShiftMask))
+                                    cleanup(0);
+                                break;
+                            case 2:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    center_window(focused_client);
+                                break;
+                            case 3:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    left_half_window(focused_client);
+                                break;
+                            case 4:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    right_half_window(focused_client);
+                                break;
+                            case 5:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    small_window(focused_client);
+                                break;
+                            case 6:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    maximize_window(focused_client);
+                                break;
+                            case 7:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    focus_next();
+                                break;
+                            case 8:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    focus_prev();
+                                break;
+                            case 9:
+                                if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
+                                    close_window(focused_client);
+                                break;
+                            case 10:
+                                if ((ev.xkey.state & (ShiftMask | Mod1Mask)) == (ShiftMask | Mod1Mask))
+                                    spawn(LAUNCHPROGRAM);
+                                break;
+                            }
+                            goto processed;
+                        }
+                    }
+                processed:;
+                } else if (ev.type == MapRequest) {
+                    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();
+                    }
+                }
+            }
+        }
+
+        // Handle socket commands
+        if (fds[1].revents & POLLIN) {
+            handle_socket_command(server_fd);
+        }
+    }
+
+    // 5. Final Cleanup
     cleanup(0);
+    close(server_fd);
+    XCloseDisplay(dpy);
     return 0;
 }
 
@@ -108,6 +262,51 @@ logmsg(const char *fmt, ...) {
 static void logmsg(const char *fmt, ...) { (void)fmt; }
 #endif
 
+int socket_init(void) {
+    struct sockaddr_un addr;
+    int server_fd;
+    char socket_path[256];
+
+    // Build the socket path
+    char *home = getenv("HOME");
+    if (home == NULL) {
+        fprintf(stderr, "bxwm: HOME not set\n");
+        exit(1);
+    }
+    snprintf(socket_path, sizeof(socket_path), "%s%s", home, SOCKET_SUFFIX);
+
+    // Remove stale socket from previous session
+    unlink(socket_path);
+
+    // Create the socket
+    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (server_fd < 0) {
+        perror("bxwm: socket");
+        exit(1);
+    }
+
+    // Bind the socket to the path
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
+
+    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        perror("bxwm: bind");
+        close(server_fd);
+        exit(1);
+    }
+
+    // Listen for incoming connections
+    if (listen(server_fd, 5) < 0) {
+        perror("bxwm: listen");
+        close(server_fd);
+        unlink(socket_path);
+        exit(1);
+    }
+
+    return server_fd;
+}
+
 static void 
 setup(void) {
     XSetWindowAttributes wa;
@@ -727,118 +926,6 @@ xerror(Display *dpy, XErrorEvent *ee) {
 }
 
 static void 
-run(void) {
-    XEvent ev;
-    KeyCode kcodes[11];
-    KeyCode ws_codes[10];
-    int i, w;
-
-    /* Cache keycodes */
-    for (i = 0; i < 11; i++)
-        kcodes[i] = XKeysymToKeycode(dpy, XStringToKeysym(
-            i == 0 ? "Return" :
-            i == 1 ? "Q" :
-            i == 2 ? "C" :
-            i == 3 ? "H" :
-            i == 4 ? "L" :
-            i == 5 ? "S" :
-            i == 6 ? "M" :
-            i == 7 ? "J" : 
-			i == 8 ? "K" :
-            i == 9 ? "X" : "P"));
-
-    for (w = 0; w < 10; w++) {
-        KeySym ks = (w == 9) ? XK_0 : (XK_1 + w);
-        ws_codes[w] = XKeysymToKeycode(dpy, ks);
-    }
-
-    for (;;) {
-        XNextEvent(dpy, &ev);
-        if (ev.type == KeyPress) {
-            /* Workspace view keys (Super+Shift+1..0) */
-            for (w = 0; w < 10; w++) {
-                if (ev.xkey.keycode == ws_codes[w] && 
-                    (ev.xkey.state & Mod4Mask) &&
-                    !(ev.xkey.state & ShiftMask)) {
-                    view(w);
-                    goto processed;
-                }
-            }
-            /* Workspace move keys (Super+Shift+1..0) */
-            for (w = 0; w < 10; w++) {
-                if (ev.xkey.keycode == ws_codes[w] && 
-                    (ev.xkey.state & (Mod4Mask | ShiftMask)) == 
-                    (Mod4Mask | ShiftMask)) {
-                    movetows(w);
-                    goto processed;
-                }
-            }
-            /* Existing key handling */
-            for (i = 0; i < 11; i++) {
-                if (ev.xkey.keycode == kcodes[i]) {
-                    switch (i) {
-                    case 0:
-                        if ((ev.xkey.state & (ShiftMask | Mod1Mask)) == (ShiftMask | Mod1Mask))
-                            spawn(TERMINAL);
-                        break;
-                    case 1:
-                        if ((ev.xkey.state & (ControlMask | ShiftMask)) == (ControlMask | ShiftMask))
-                            cleanup(0);
-                        break;
-                    case 2:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            center_window(focused_client);
-                        break;
-                    case 3:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            left_half_window(focused_client);
-                        break;
-                    case 4:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            right_half_window(focused_client);
-                        break;
-                    case 5:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            small_window(focused_client);
-                        break;
-                    case 6:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            maximize_window(focused_client);
-                        break;
-                    case 7:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            focus_next();
-                        break;
-                    case 8:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            focus_prev();
-                        break;
-                    case 9:
-                        if ((ev.xkey.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
-                            close_window(focused_client);
-                        break;
-                    case 10:
-                        if ((ev.xkey.state & (ShiftMask | Mod1Mask)) == (ShiftMask | Mod1Mask))
-                            spawn(LAUNCHPROGRAM);
-                        break;
-                    }
-                    goto processed;
-                }
-            }
-        processed:;
-        } else if (ev.type == MapRequest) {
-            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();
-            }
-        }
-    }
-}
-
-static void 
 update_client_list(void) {
     Window *wins = NULL;
     unsigned int i;
@@ -931,14 +1018,65 @@ update_workarea(void) {
     arrange();
 }
 
+static void
+handle_socket_command(int server_fd)
+{
+    int client_fd = accept(server_fd, NULL, NULL);
+    if (client_fd < 0) return;
 
-static void 
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    
+    if (read(client_fd, buffer, sizeof(buffer) - 1) > 0) {
+        buffer[strcspn(buffer, "\n")] = '\0';
+        fprintf(stderr, "bxwm: socket received '%s'\n", buffer);
+
+        // Map command strings to functions
+        if (strcmp(buffer, "center_window") == 0) {
+            center_window(focused_client);
+        } else if (strcmp(buffer, "left_half") == 0) {
+            left_half_window(focused_client);
+        } else if (strcmp(buffer, "right_half") == 0) {
+            right_half_window(focused_client);
+        } else if (strcmp(buffer, "small_window") == 0) {
+            small_window(focused_client);
+        } else if (strcmp(buffer, "maximize_window") == 0) {
+            maximize_window(focused_client);
+        } else if (strcmp(buffer, "focus_next") == 0) {
+            focus_next();
+        } else if (strcmp(buffer, "focus_prev") == 0) {
+            focus_prev();
+        } else if (strcmp(buffer, "close_window") == 0) {
+            close_window(focused_client);
+        } else if (strcmp(buffer, "spawn_terminal") == 0) {
+            spawn(TERMINAL);
+        } else if (strcmp(buffer, "spawn_launcher") == 0) {
+            spawn(LAUNCHPROGRAM);
+        } else if (strcmp(buffer, "quit") == 0) {
+            running = 0;
+        } else if (strncmp(buffer, "view ", 5) == 0) {
+            int ws = atoi(buffer + 5);
+            if (ws >= 0 && ws <= 9)
+                view(ws);
+        } else if (strncmp(buffer, "movetows ", 9) == 0) {
+            int ws = atoi(buffer + 9);
+            if (ws >= 0 && ws <= 9)
+                movetows(ws);
+        }
+    }
+    XSync(dpy, False);
+    close(client_fd);
+}
+
+static void
 cleanup(int status) {
     if (dpy) {
         if (clients)
             free(clients);
         XCloseDisplay(dpy);
     }
+    unlink(socket_path);
+    close(server_fd);
     fprintf(stderr, "bxwm: exit\n");
     exit(status);
 }