Author: pbadeer Date: 2025-07-20 Description: Add directional gap support with runtime adjustment Commit Version: 539a8ff (fix bottom bar struts taking half screen && windows killed on exit) This patch extends the existing gap system to support individual gaps for each side: gap_top, gap_bottom, gap_left, and gap_right. The original 'gaps' config option is preserved for backward compatibility and sets all four directional gaps when used. Key features: - Individual gap configuration per side (gap_top, gap_bottom, gap_left, gap_right) - Runtime adjustment functions for each gap direction - Parser commands: increase_gap_[side], decrease_gap_[side] - Backward compatibility with existing 'gaps' option - Fixed stack window positioning to avoid internal gaps - Transparent border fix for picom compatibility - Added linux-headers dependency for Alpine Linux builds This allows users to configure asymmetric gaps for specific layouts while maintaining the simplicity of the original gap system for basic use cases. --- diff --git a/README.md b/README.md index 11c0314..262b6de 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ sudo zypper install libX11-devel libXinerama-devel gcc make
Alpine Linux
doas apk update
-doas apk add libx11-dev libxinerama-dev gcc make musl-dev
+doas apk add libx11-dev libxinerama-dev gcc make musl-dev linux-headers
diff --git a/src/defs.h b/src/defs.h index 1652fbe..496dc61 100644 --- a/src/defs.h +++ b/src/defs.h @@ -89,6 +89,10 @@ typedef struct Client { typedef struct { int modkey; int gaps; + int gap_top; + int gap_bottom; + int gap_left; + int gap_right; int border_width; long border_foc_col; long border_ufoc_col; @@ -131,6 +135,14 @@ typedef struct { extern void centre_window(); extern void close_focused(void); extern void dec_gaps(void); +extern void inc_gap_top(void); +extern void dec_gap_top(void); +extern void inc_gap_bottom(void); +extern void dec_gap_bottom(void); +extern void inc_gap_left(void); +extern void dec_gap_left(void); +extern void inc_gap_right(void); +extern void dec_gap_right(void); extern void focus_next(void); extern void focus_prev(void); extern void focus_next_mon(void); diff --git a/src/parser.c b/src/parser.c index 9e82c08..f347762 100644 --- a/src/parser.c +++ b/src/parser.c @@ -21,6 +21,14 @@ static const CommandEntry call_table[] = {{"close_window", close_focused}, {"focus_next_mon", focus_next_mon}, {"focus_prev_mon", focus_prev_mon}, {"increase_gaps", inc_gaps}, + {"increase_gap_top", inc_gap_top}, + {"decrease_gap_top", dec_gap_top}, + {"increase_gap_bottom", inc_gap_bottom}, + {"decrease_gap_bottom", dec_gap_bottom}, + {"increase_gap_left", inc_gap_left}, + {"decrease_gap_left", dec_gap_left}, + {"increase_gap_right", inc_gap_right}, + {"decrease_gap_right", dec_gap_right}, {"master_next", move_master_next}, {"master_previous", move_master_prev}, {"move_next_mon", move_next_mon}, @@ -225,7 +233,24 @@ found: } } else if (!strcmp(key, "gaps")) { - cfg->gaps = atoi(rest); + int gap_val = atoi(rest); + cfg->gaps = gap_val; + cfg->gap_top = gap_val; + cfg->gap_bottom = gap_val; + cfg->gap_left = gap_val; + cfg->gap_right = gap_val; + } + else if (!strcmp(key, "gap_top")) { + cfg->gap_top = atoi(rest); + } + else if (!strcmp(key, "gap_bottom")) { + cfg->gap_bottom = atoi(rest); + } + else if (!strcmp(key, "gap_left")) { + cfg->gap_left = atoi(rest); + } + else if (!strcmp(key, "gap_right")) { + cfg->gap_right = atoi(rest); } else if (!strcmp(key, "border_width")) { cfg->border_width = atoi(rest); diff --git a/src/sxwm.c b/src/sxwm.c index 90ef09d..c6598fb 100644 --- a/src/sxwm.c +++ b/src/sxwm.c @@ -112,7 +112,6 @@ Atom atom_net_active_window; Atom atom_net_current_desktop; Atom atom_net_supported; Atom atom_net_wm_state; -Atom atom_net_wm_state_fullscreen; Atom atom_wm_window_type; Atom atom_net_wm_window_type_dock; Atom atom_net_workarea; @@ -367,6 +366,19 @@ void dec_gaps(void) { if (user_config.gaps > 0) { user_config.gaps--; + user_config.gap_top--; + user_config.gap_bottom--; + user_config.gap_left--; + user_config.gap_right--; + tile(); + update_borders(); + } +} + +void dec_gap_top(void) +{ + if (user_config.gap_top > 0) { + user_config.gap_top--; tile(); update_borders(); } @@ -854,19 +866,6 @@ void hdl_client_msg(XEvent *xev) change_workspace(ws); return; } - if (xev->xclient.message_type == atom_net_wm_state) { - long action = xev->xclient.data.l[0]; - Atom target = xev->xclient.data.l[1]; - if (target == atom_net_wm_state_fullscreen) { - if (action == 1 || action == 2) { - toggle_fullscreen(); - } - else if (action == 0 && focused && focused->fullscreen) { - toggle_fullscreen(); - } - } - return; - } } void hdl_config_ntf(XEvent *xev) @@ -1519,14 +1518,77 @@ void update_workarea(void) void inc_gaps(void) { user_config.gaps++; + user_config.gap_top++; + user_config.gap_bottom++; + user_config.gap_left++; + user_config.gap_right++; tile(); update_borders(); } +void inc_gap_top(void) +{ + user_config.gap_top++; + tile(); + update_borders(); +} + +void inc_gap_bottom(void) +{ + user_config.gap_bottom++; + tile(); + update_borders(); +} + +void dec_gap_bottom(void) +{ + if (user_config.gap_bottom > 0) { + user_config.gap_bottom--; + tile(); + update_borders(); + } +} + +void inc_gap_left(void) +{ + user_config.gap_left++; + tile(); + update_borders(); +} + +void dec_gap_left(void) +{ + if (user_config.gap_left > 0) { + user_config.gap_left--; + tile(); + update_borders(); + } +} + +void inc_gap_right(void) +{ + user_config.gap_right++; + tile(); + update_borders(); +} + +void dec_gap_right(void) +{ + if (user_config.gap_right > 0) { + user_config.gap_right--; + tile(); + update_borders(); + } +} + void init_defaults(void) { default_config.modkey = Mod4Mask; default_config.gaps = 10; + default_config.gap_top = 10; + default_config.gap_bottom = 10; + default_config.gap_left = 10; + default_config.gap_right = 10; default_config.border_width = 1; default_config.border_foc_col = parse_col("#c0cbff"); default_config.border_ufoc_col = parse_col("#555555"); @@ -1730,9 +1792,8 @@ long parse_col(const char *hex) return WhitePixel(dpy, DefaultScreen(dpy)); } - /* return col.pixel |= 0xff << 24; */ - /* This is a fix for picom making the borders transparent. DANGEROUS */ - return col.pixel; + /* possibly unsafe BUT i dont think it can cause any problems */ + return col.pixel |= 0xff << 24; } void quit(void) @@ -2062,9 +2123,7 @@ void setup_atoms(void) atom_net_wm_window_type_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); atom_net_workarea = XInternAtom(dpy, "_NET_WORKAREA", False); atom_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); - atom_net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); atom_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); - atom_net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); atom_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); atom_net_supporting_wm_check = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); atom_net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", False); @@ -2078,7 +2137,6 @@ void setup_atoms(void) atom_net_active_window, atom_net_supported, atom_net_wm_state, - atom_net_wm_state_fullscreen, atom_wm_window_type, atom_net_wm_window_type_dock, atom_net_workarea, @@ -2305,13 +2363,14 @@ void tile(void) continue; } - int gx = user_config.gaps, gy = user_config.gaps; - int tile_x = mon_x + gx, tile_y = mon_y + gy; - int tile_w = MAX(1, mon_w - 2 * gx); - int tile_h = MAX(1, mon_h - 2 * gy); + int gap_left = user_config.gap_left, gap_right = user_config.gap_right; + int gap_top = user_config.gap_top, gap_bottom = user_config.gap_bottom; + int tile_x = mon_x + gap_left, tile_y = mon_y + gap_top; + int tile_w = MAX(1, mon_w - gap_left - gap_right); + int tile_h = MAX(1, mon_h - gap_top - gap_bottom); float mf = CLAMP(user_config.master_width[m], MF_MIN, MF_MAX); int master_w = (N > 1) ? (int)(tile_w * mf) : tile_w; - int stack_w = (N > 1) ? (tile_w - master_w - gx) : 0; + int stack_w = (N > 1) ? (tile_w - master_w) : 0; { Client *c = stackers[0]; @@ -2338,7 +2397,6 @@ void tile(void) } int bw2 = 2 * user_config.border_width; - int num_stack = N - 1; int min_raw = bw2 + 1; int total_fixed_heights = 0, auto_count = 0; int heights_final[MAXCLIENTS] = {0}; @@ -2354,7 +2412,7 @@ void tile(void) } } - int total_vgaps = (num_stack - 1) * gy; + int total_vgaps = 0; int remaining = tile_h - total_fixed_heights - total_vgaps; if (auto_count > 0 && remaining >= auto_count * min_raw) { @@ -2408,7 +2466,7 @@ void tile(void) int sy = tile_y; for (int i = 1; i < N; i++) { Client *c = stackers[i]; - XWindowChanges wc = {.x = tile_x + master_w + gx, + XWindowChanges wc = {.x = tile_x + master_w, .y = sy, .width = MAX(1, stack_w - (2 * user_config.border_width)), .height = MAX(1, heights_final[i] - (2 * user_config.border_width)), @@ -2423,7 +2481,7 @@ void tile(void) c->w = wc.width; c->h = wc.height; - sy += heights_final[i] + gy; + sy += heights_final[i]; } update_borders();