From d1c02153202030c29c497636736483e0e2425bee Mon Sep 17 00:00:00 2001 From: uint23 Date: Wed, 16 Apr 2025 18:38:01 +0100 Subject: tiling complete --- src/defs.h | 14 ++-- src/sxwm.c | 260 ++++++++++++++++++++++++++++++++++++---------------------- src/usercfg.h | 3 +- 3 files changed, 171 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/defs.h b/src/defs.h index 2046bc3..cf911cf 100644 --- a/src/defs.h +++ b/src/defs.h @@ -1,6 +1,7 @@ #ifndef DEFS_H #define DEFS_H +#include #include #define SXWM_VERSION "sxwm ver. 0.1.0" @@ -11,14 +12,13 @@ #define SUPER Mod4Mask #define SHIFT ShiftMask +#define LENGTH(X) (sizeof X / sizeof X[0]) #define BIND(mod, key, cmdstr) { (mod), XK_##key, { cmdstr }, 0 } #define CALL(mod, key, fnptr) { (mod), XK_##key, { .fn = fnptr }, 1 } #define MAXCLIENTS 64 -typedef void -(*EventHandler)(XEvent *); - +typedef void (*EventHandler)(XEvent *); typedef union { const char **cmd; void (*fn)(void); @@ -32,12 +32,8 @@ typedef struct { } Binding; typedef struct Client{ - Window id; - int x, y; - unsigned int w, h; - unsigned int bw; - Bool isfocused; - Bool isfloating; + Window win; + uint height, width; struct Client *next; } Client; diff --git a/src/sxwm.c b/src/sxwm.c index 86e4407..16ae451 100644 --- a/src/sxwm.c +++ b/src/sxwm.c @@ -1,15 +1,15 @@ /* See LICENSE for more info - - sxwm is a user-friendly, easily configurable yet powerful - tiling window manager inspired by window managers such as - DWM and i3 - - The userconfig is designed to be as user-friendly as - possible, and I hope it is easy to configure even without - knowledge of C or programming, although most people who - will use this will probably be programmers :) - -*/// (C) Abhinav Prasai 2025 + * + * sxwm is a user-friendly, easily configurable yet powerful + * tiling window manager inspired by window managers such as + * DWM and i3 + * + * The userconfig is designed to be as user-friendly as + * possible, and I hope it is easy to configure even without + * knowledge of C or programming, although most people who + * will use this will probably be programmers :) + * (C) Abhinav Prasai 2025 +*/ #include #include @@ -22,91 +22,114 @@ #include "defs.h" -typedef void (*EventHandler)(XEvent *); - static void add_client(Window w); -static unsigned int clean_mask(unsigned int mask); +static uint clean_mask(uint mask); +static void grab_keys(void); static void hdl_dummy(XEvent *xev); -static void hdl_config_req(XEvent *xev); static void hdl_destroy_ntf(XEvent *xev); static void hdl_keypress(XEvent *xev); static void hdl_map_req(XEvent *xev); -static void hdl_unmap_ntf(XEvent *xev); static void other_wm(void); static int other_wm_err(Display *dpy, XErrorEvent *ee); -static unsigned long parse_col(const char *hex); +static uint64_t parse_col(const char *hex); static void quit(void); +static void remove_client(Window w); static void run(void); static void setup(void); static void spawn(const char **cmd); +static void tile(void); static int xerr(Display *dpy, XErrorEvent *ee); static void xev_case(XEvent *xev); static Client *clients = NULL; static EventHandler evtable[LASTEvent]; -static Display *dpy; -static Window root; +static Display *dpy; +static Window root; -static unsigned long border_foc_col; -static unsigned long border_ufoc_col; -static unsigned int scr_width; -static unsigned int scr_height; +static uint64_t border_foc_col; +static uint64_t border_ufoc_col; +static uint scr_width; +static uint scr_height; +static uint open_windows = 0; #include "usercfg.h" static void add_client(Window w) { + Client *c = malloc(sizeof(Client)); + if (!c) { + fprintf(stderr, "sxwm: could not alloc memory for client\n"); + return; + } + c->win = w; + c->next = clients; + clients = c; + ++open_windows; + + XSelectInput(dpy, w, EnterWindowMask | LeaveWindowMask | FocusChangeMask | + PropertyChangeMask | StructureNotifyMask); + + XRaiseWindow(dpy, w); + } -static unsigned int -clean_mask(unsigned int mask) +static uint +clean_mask(uint mask) { return mask & ~(LockMask | Mod2Mask | Mod3Mask); } static void -hdl_dummy(XEvent *xev) -{} +grab_keys(void) +{ + KeyCode keycode; + uint modifiers[] = { 0, LockMask, Mod2Mask, LockMask|Mod2Mask }; + + // ungrab all keys + XUngrabKey(dpy, AnyKey, AnyModifier, root); + + for (uint i = 0; i < LENGTH(binds); ++i) { + if ((keycode = XKeysymToKeycode(dpy, binds[i].keysym))) { + for (uint j = 0; j < LENGTH(modifiers); ++j) { + XGrabKey(dpy, keycode, + binds[i].mods | modifiers[j], + root, True, GrabModeAsync, GrabModeAsync); + } + } + } +} static void -hdl_config_req(XEvent *xev) +hdl_dummy(XEvent *xev) { - XConfigureRequestEvent ev = xev->xconfigurerequest; - XWindowChanges wc; - wc.y = ev.y; - wc.width = ev.width; - wc.height = ev.height; - wc.border_width = ev.border_width; - wc.sibling = ev.above; - wc.stack_mode = ev.detail; - - XConfigureWindow(dpy, ev.window, ev.value_mask, &wc); - printf("sxwm: window configured: %ld\n", ev.window); + (void) xev; } - static void hdl_destroy_ntf(XEvent *xev) { - XDestroyWindowEvent ev = xev->xdestroywindow; - XDestroyWindow(dpy, ev.window); - printf("sxwm: window destroyed: %ld\n", ev.window); + remove_client(xev->xdestroywindow.window); + tile(); } static void -hdl_keypress(XEvent *xev) +hdl_map_req(XEvent *xev) { - KeySym keysym; - XKeyEvent *ev = &xev->xkey; - unsigned int modifiers; + XConfigureRequestEvent *config_req = &xev->xconfigurerequest; + add_client(config_req->window); + tile(); + XMapWindow(dpy, config_req->window); +} - keysym = XkbKeycodeToKeysym(dpy, ev->keycode, 0, 0); - modifiers = clean_mask(ev->state); +static void +hdl_keypress(XEvent *xev) +{ + KeySym keysym = XLookupKeysym(&xev->xkey, 0); + uint mods = clean_mask(xev->xkey.state); - int lenbindings = sizeof(binds) / sizeof(binds[0]); - for (int i = 0; i < lenbindings; ++i) { - if (keysym == binds[i].keysym && modifiers == clean_mask(binds[i].mods)) { + for (uint i = 0; i < LENGTH(binds); ++i) { + if (keysym == binds[i].keysym && mods == clean_mask(binds[i].mods)) { if (binds[i].is_func) binds[i].action.fn(); else @@ -116,29 +139,6 @@ hdl_keypress(XEvent *xev) } } -static void -hdl_map_req(XEvent *xev) -{ - XMapRequestEvent ev = xev->xmaprequest; - XSetWindowBorder(dpy, ev.window, border_foc_col); - XSetWindowBorderWidth(dpy, ev.window, BORDER_WIDTH); - XMapWindow(dpy, ev.window); - - XWindowAttributes wa; - if (!XGetWindowAttributes(dpy, ev.window, &wa)) - return; - - printf("sxwm: window mapped: %ld\n", ev.window); -} - -static void -hdl_unmap_ntf(XEvent *xev) -{ - XUnmapEvent ev = xev->xunmap; - XUnmapWindow(dpy, ev.window); - printf("sxwm: window unmapped: %ld\n", ev.window); -} - static void other_wm(void) { @@ -147,18 +147,20 @@ other_wm(void) &(XSetWindowAttributes){.event_mask = SubstructureRedirectMask}); XSync(dpy, False); XSetErrorHandler(xerr); + XChangeWindowAttributes(dpy, root, CWEventMask, + &(XSetWindowAttributes){.event_mask = 0}); XSync(dpy, False); } static int other_wm_err(Display *dpy, XErrorEvent *ee) { - errx(0, "sxwm: can't start because another window manager is already running"); + errx(0, "can't start because another window manager is already running"); return 0; if (dpy && ee) return 0; } -static unsigned long +static uint64_t parse_col(const char *hex) { XColor col; @@ -180,7 +182,23 @@ parse_col(const char *hex) static void quit(void) { - errx(0, "sxwm: quitting..."); + errx(0, "quitting..."); +} + +static void +remove_client(Window w) +{ + Client **curr = &clients; // Current window + while (*curr) { + if ((*curr)->win == w) { + Client *tmp = *curr; + *curr = (*curr)->next; + free(tmp); + open_windows--; + break; + } + curr = &(*curr)->next; + } } static void @@ -196,47 +214,97 @@ run(void) static void setup(void) { - dpy = XOpenDisplay(NULL); - if (dpy == 0) - errx(0, "sxwm: can't open display."); - + if ((dpy = XOpenDisplay(NULL)) == 0) + errx(0, "can't open display. quitting..."); root = XDefaultRootWindow(dpy); other_wm(); - XSelectInput(dpy, root, - SubstructureRedirectMask | SubstructureNotifyMask | KeyPressMask); + grab_keys(); + + scr_width = XDisplayWidth(dpy, DefaultScreen(dpy)); + scr_height = XDisplayHeight(dpy, DefaultScreen(dpy)); + XSelectInput(dpy, root, SubstructureRedirectMask | SubstructureNotifyMask | KeyPressMask); for (int i = 0; i < LASTEvent; ++i) evtable[i] = hdl_dummy; - evtable[ConfigureRequest] = hdl_config_req; evtable[DestroyNotify] = hdl_destroy_ntf; evtable[KeyPress] = hdl_keypress; evtable[MapRequest] = hdl_map_req; - evtable[UnmapNotify] = hdl_unmap_ntf; border_foc_col = parse_col(BORDER_FOC_COL); border_ufoc_col = parse_col(BORDER_UFOC_COL); - scr_width = XDisplayWidth(dpy, DefaultScreen(dpy)); - scr_height = XDisplayHeight(dpy, DefaultScreen(dpy)); } +} static void spawn(const char **cmd) { - if (!cmd) return; - printf("sxwm: attempting to spawn: %s\n", cmd[0]); + if (!cmd) + return; - pid_t pid = fork(); - if (pid == 0) { - if (dpy) - close(ConnectionNumber(dpy)); + if (fork() == 0) { setsid(); execvp(cmd[0], (char *const*)cmd); errx(1, "sxwm: execvp '%s' failed\n", cmd[0]); - } else if (pid < 0) { - fprintf(stderr, "sxwm: failed to fork process\n"); + } else { + fprintf(stderr, "sxwm: falied to fork proc %s", cmd[0]); } } +static void +tile(void) +{ + if (!open_windows) + return; + + int masterX = GAPS + BORDER_WIDTH, + masterY = GAPS + BORDER_WIDTH, + availableH = scr_height - (GAPS * 2), + masterW, masterH, stackW = 0, stackWinH = 0, + stackCount = open_windows - 1; + + if (open_windows == 1) { + masterW = scr_width - (GAPS * 2 + BORDER_WIDTH * 2); + masterH = availableH - (BORDER_WIDTH * 2); + } else { + int totalGapsW = GAPS * 4, totalBordersW = BORDER_WIDTH * 4; + masterW = (scr_width - totalGapsW - totalBordersW) / 2; + stackW = masterW; + masterH = availableH - (BORDER_WIDTH * 2); + + int totalGapsH = (stackCount > 0 ? GAPS * (stackCount - 1) : 0), + totalBordersH = BORDER_WIDTH * 2 * stackCount, + totalStackH = availableH - totalGapsH - totalBordersH; + stackWinH = (stackCount > 0 ? totalStackH / stackCount : 0); + } + + int stackX = masterX + masterW + GAPS + (BORDER_WIDTH * 2), + stackY = GAPS; + Client *c = clients; + uint i = 0; + + for (; c; c = c->next, ++i) { + XWindowChanges changes = { .border_width = BORDER_WIDTH }; + if (i == 0) { + changes.x = masterX; + changes.y = masterY; + changes.width = masterW; + changes.height = masterH; + } else { + changes.x = stackX; + changes.y = stackY + BORDER_WIDTH; // adjust for border + changes.width = stackW; + changes.height = stackWinH; + if (i == open_windows - 1) { + int used = stackY - GAPS + stackWinH + (BORDER_WIDTH * 2); + changes.height += (availableH - used); + } + stackY += stackWinH + (BORDER_WIDTH * 2) + GAPS; + } + XSetWindowBorder(dpy, c->win, border_foc_col); + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes); + } +} + static int xerr(Display *dpy, XErrorEvent *ee) { diff --git a/src/usercfg.h b/src/usercfg.h index 457bc17..69c613c 100644 --- a/src/usercfg.h +++ b/src/usercfg.h @@ -4,7 +4,8 @@ #include #include "defs.h" -#define BORDER_WIDTH 10 +#define GAPS 10 +#define BORDER_WIDTH 1 #define BORDER_FOC_COL "#00FF00" #define BORDER_UFOC_COL "#FF0000" -- cgit v1.2.3