commit - 9c8821a84e92c4ceb948bd14259d6519f7c2a307
commit + 08f4348e34813e6491f12260ef98c315649f58e5
blob - c6dd9c0f940cc061ecf8614a248f3ef9765d2f68
blob + 70264fcc1cdba5e22c8beebc33b759d304e062ef
--- bxwm.c
+++ bxwm.c
#define SOCKET_SUFFIX "/.local/tmp/bxwm.sock"
-/* Layout types */
enum { FLOAT, CENTER, LEFT_HALF, RIGHT_HALF, SMALL, MAXIMIZE };
-/* Types */
typedef struct {
Window win;
int focused;
int ws;
- int is_dock; /* dock/bar window: no border, no positioning, no focus */
- int layout; /* Remember window geometry type */
+ int is_dock;
+ int layout;
} Client;
-/* Globals */
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 unsigned long wa_x, wa_y, wa_w, wa_h;
static Client *clients = NULL;
static unsigned int num_clients = 0;
static Client *focused_client = NULL;
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 ws_focus_idx[NUM_WORKSPACES] = {-1};
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;
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;
-/* Forward function declarations */
+/* Forward Declarations */
+/* Utilities */
static void logmsg(const char *fmt, ...);
+static void spawn(const char *cmd);
+static int xerror(Display *dpy, XErrorEvent *ee);
+/* Lifecycle */
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);
+/* State Updates */
+static void update_client_list(void);
static void update_workarea(void);
+/* Window Management */
static void manage(Window w);
-static void spawn(const char *cmd);
+static void unmanage(Window w);
static void focus_client(Client *c);
static void center_window(Client *c);
static void left_half_window(Client *c);
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);
+/* Inter-Process Communication */
+static void handle_socket_command(int fd);
int
main(int argc, char *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
setup();
running = 1;
- // 4. Main Loop
+ /* Event loop: poll(2) on both X connection and IPC socket */
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);
}
}
- // Handle socket commands
if (fds[1].revents & POLLIN) {
handle_socket_command(server_fd);
}
}
- // 5. Final Cleanup
cleanup(0);
close(server_fd);
XCloseDisplay(dpy);
static void logmsg(const char *fmt, ...) { (void)fmt; }
#endif
-int socket_init(void) {
+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");
}
snprintf(socket_path, sizeof(socket_path), "%s%s", home, SOCKET_SUFFIX);
- // Remove stale socket from previous session
- unlink(socket_path);
+ unlink(socket_path); /* clear stale socket from prior run */
- // 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);
exit(1);
}
- // Listen for incoming connections
- if (listen(server_fd, 5) < 0) {
+ if (listen(server_fd, SOMAXCONN) < 0) {
perror("bxwm: listen");
close(server_fd);
unlink(socket_path);
cleanup(1);
}
- /* Allocate border colors */
if (!XParseColor(dpy, cmap, BORDER_FOCUSED, &color) ||
!XAllocColor(dpy, cmap, &color)) {
fprintf(stderr, "bxwm: cannot allocate focus color '%s'\n", BORDER_FOCUSED);
}
unfocus_pixel = color.pixel;
- /* Root window event mask */
wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
ButtonPressMask | PointerMotionMask | EnterWindowMask |
LeaveWindowMask | KeyPressMask | KeyReleaseMask |
PropertyChangeMask;
XChangeWindowAttributes(dpy, root, CWEventMask, &wa);
- /* Set root cursor */
Cursor cursor = XCreateFontCursor(dpy, XC_left_ptr);
XDefineCursor(dpy, root, cursor);
XFreeCursor(dpy, cursor);
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,
if (!XGetWindowAttributes(dpy, w, &wa))
return;
- /* Check if window is a dock/bar */
int is_dock = 0;
Atom actual_type;
int actual_format;
XFree(data);
}
- /* Events only (border pixel set via Configure later) */
attrs.event_mask = StructureNotifyMask | EnterWindowMask | PropertyChangeMask;
XChangeWindowAttributes(dpy, w, CWEventMask, &attrs);
if (!c) return;
c->layout = SMALL;
- /* Step 1: Calculate height first (50% of screen, minus borders) */
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 > (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 = 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 = wa_y + (wa_h - (win_h + 2 * BORDER_WIDTH)) / 2;
{
if (num_clients == 0)
return;
- /* Find current index */
if (focused_client) {
for (i = 0; i < num_clients; i++) {
if (&clients[i] == focused_client) {
}
}
if (!found)
- start = num_clients - 1; /* So first check is index 0 */
+ start = num_clients - 1;
- /* Search for next visible window */
for (i = 1; i <= num_clients; i++) {
idx = (start + i) % num_clients;
if (clients[idx].ws == curws) {
}
}
- /* No visible windows on this workspace - clear focus */
if (focused_client) {
XSetWindowBorder(dpy, focused_client->win, unfocus_pixel);
focused_client->focused = 0;
if (!found)
start = 0;
- /* Search backward for visible window */
for (i = 1; i <= num_clients; i++) {
idx = (start + num_clients - i) % num_clients;
if (clients[idx].ws == curws) {
}
}
- /* No visible windows */
if (focused_client) {
XSetWindowBorder(dpy, focused_client->win, unfocus_pixel);
focused_client->focused = 0;
if (ws < 0 || ws >= NUM_WORKSPACES || ws == curws)
return;
- /* Save current focus index */
ws_focus_idx[curws] = (focused_client ? (focused_client - clients) : -1);
- /* Unmap current workspace windows */
for (i = 0; i < num_clients; i++) {
if (clients[i].ws == curws)
XUnmapWindow(dpy, clients[i].win);
PropModeReplace, (unsigned char *)¤t_desktop, 1);
- /* Map new workspace windows */
for (i = 0; i < num_clients; i++) {
if (clients[i].ws == curws)
XMapWindow(dpy, clients[i].win);
}
- /* Restore focus or find first visible */
int idx = ws_focus_idx[curws];
if (idx >= 0 && idx < (int)num_clients && clients[idx].ws == curws)
focus_client(&clients[idx]);
PropModeReplace, (unsigned char *)&new_ws, 1);
if (old_ws == curws) {
- /* Moving away from visible workspace */
XUnmapWindow(dpy, c->win);
int c_idx = (c - clients);
if (ws_focus_idx[old_ws] == c_idx)
ws_focus_idx[old_ws] = -1;
- focus_next(); /* Find new focus on current workspace */
+ focus_next();
}
if (ws == curws) {
- /* Moving to visible workspace */
XMapWindow(dpy, c->win);
focus_client(c);
}
int destroyed_idx = i;
- /* Update workspace focus indices */
for (int ws = 0; ws < NUM_WORKSPACES; ws++) {
if (ws_focus_idx[ws] == destroyed_idx) {
- ws_focus_idx[ws] = -1; /* Focused window destroyed */
+ ws_focus_idx[ws] = -1;
} else if (ws_focus_idx[ws] > destroyed_idx) {
- ws_focus_idx[ws]--; /* Array shifted down by one */
+ ws_focus_idx[ws]--;
}
}
- /* Shift and realloc */
for (; i < num_clients - 1; i++)
clients[i] = clients[i + 1];
num_clients--;
xerror(Display *dpy, XErrorEvent *ee) {
char buf[1024];
XGetErrorText(dpy, ee->error_code, buf, sizeof(buf));
- fprintf(stderr, "bxwm: X error: %s\n", buf);
+ logmsg("X error: %s", buf);
return 0;
}
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) {
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) {
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;
if (read(client_fd, buffer, sizeof(buffer) - 1) > 0) {
buffer[strcspn(buffer, "\n")] = '\0';
- fprintf(stderr, "bxwm: socket received '%s'\n", buffer);
+ logmsg("socket received '%s'", buffer);
- // Map command strings to functions
if (strcmp(buffer, "center_window") == 0) {
center_window(focused_client);
} else if (strcmp(buffer, "left_half") == 0) {
}
unlink(socket_path);
close(server_fd);
- fprintf(stderr, "bxwm: exit\n");
exit(status);
}