Basic varcol layout working

This commit is contained in:
Micah Gorrell 2023-05-14 16:00:52 -06:00
parent 7fee7bac29
commit ff0343000e
6 changed files with 608 additions and 2 deletions

1
.gitignore vendored
View File

@ -3,7 +3,6 @@ dwl
*-protocol.c *-protocol.c
*-protocol.h *-protocol.h
.ccls-cache .ccls-cache
config.h
.cache/ .cache/
compile_commands.json compile_commands.json
TODO.md TODO.md

View File

@ -11,7 +11,7 @@ DWLDEVCFLAGS = -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused
# CFLAGS / LDFLAGS # CFLAGS / LDFLAGS
PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS) PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) -lm
all: dwl all: dwl
dwl: dwl.o util.o dwl: dwl.o util.o

232
config.h Normal file
View File

@ -0,0 +1,232 @@
/* appearance */
static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
static const int smartgaps = 1; /* 1 means no outer gap when there is only one window */
static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int gappih = 10; /* horiz inner gap between windows */
static const unsigned int gappiv = 10; /* vert inner gap between windows */
static const unsigned int gappoh = 0; /* horiz outer gap between windows and screen edge */
static const unsigned int gappov = 0; /* vert outer gap between windows and screen edge */
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
static const float focuscolor[] = {0.658, 0.105, 0.277, 1.0};
/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
/* tagging - tagcount must be no greater than 31 */
static const int tagcount = 9;
static const Rule rules[] = {
/* app_id title tags mask isfloating isterm noswallow monitor isLeft */
{ "kitty", NULL, 0, 0, 1, 0, -1, 0 },
{ "firefox", NULL, 1 << 4, 0, 0, 0, -1, 0 },
{ "Slack", NULL, 1 << 5, 0, 0, 0, -1, 1 },
{ NULL, "- Slack", 1 << 5, 0, 0, 0, -1, 1 },
{ "googlemessages", NULL, 1 << 5, 0, 0, 0, -1, 1 },
{ "Spotify", NULL, 1 << 7, 0, 0, 0, -1, 0 },
{ "Steam", NULL, 1 << 8, 0, 0, 0, -1, 0 },
};
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
{ "=O=", varcol }, /* first entry is default */
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* monitors */
static const MonitorRule monrules[] = {
/* name mfact nmaster scale layout rotate/reflect x y */
/* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
*/
/* defaults */
{ NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
};
/* keyboard */
static const struct xkb_rule_names xkb_rules = {
/* can specify fields: rules, model, layout, variant, options */
/* example:
.options = "ctrl:nocaps",
*/
.options = NULL,
};
static const int repeat_rate = 25;
static const int repeat_delay = 600;
/* Trackpad */
static const int tap_to_click = 0;
static const int tap_and_drag = 0;
static const int drag_lock = 1;
static const int natural_scrolling = 0;
static const int disable_while_typing = 1;
static const int left_handed = 0;
static const int middle_button_emulation = 0;
/* You can choose between:
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
LIBINPUT_CONFIG_SCROLL_2FG
LIBINPUT_CONFIG_SCROLL_EDGE
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
*/
static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
/* You can choose between:
LIBINPUT_CONFIG_CLICK_METHOD_NONE
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
*/
static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
/* You can choose between:
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
*/
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
/* You can choose between:
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
*/
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
static const double accel_speed = 0.0;
static const int cursor_timeout = 5;
/* You can choose between:
LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
*/
static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
#define TAGKEYS(KEY,SKEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static const char *termcmd[] = { "kitty", NULL };
static const char *menucmd[] = { "bemenu-run", NULL };
static const char *medianextcmd[] = { "sh", "-c", "mediacontrol", "next", NULL };
static const char *mediaprevcmd[] = { "sh", "-c", "mediacontrol", "prev", NULL };
static const char *mediatogglecmd[] = { "sh", "-c", "mediacontrol", "toggle", NULL };
static const char *mediaupcmd[] = { "sh", "-c", "mediacontrol", "up", NULL };
static const char *mediadowncmd[] = { "sh", "-c", "mediacontrol", "down", NULL };
static const char *dunstclosecmd[] = { "dunstctl", "close", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY, XKB_KEY_slash, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_h, spawn, {.v = mediaprevcmd } },
{ WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_l, spawn, {.v = medianextcmd } },
{ WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_j, spawn, {.v = mediadowncmd } },
{ WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_k, spawn, {.v = mediaupcmd } },
{ WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_space, spawn, {.v = mediatogglecmd } },
{ WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, spawn, {.v = mediaprevcmd } },
{ WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, spawn, {.v = medianextcmd } },
{ WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_j, spawn, {.v = mediadowncmd } },
{ WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_k, spawn, {.v = mediaupcmd } },
{ WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_space, spawn, {.v = mediatogglecmd } },
{ MODKEY, XKB_KEY_space, spawn, {.v = dunstclosecmd } },
{ WLR_MODIFIER_LOGO, XKB_KEY_space, spawn, {.v = dunstclosecmd } },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_J, movestack, {.i = +1} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_K, movestack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
{ MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
{ MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
{ MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
{ MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
{ MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
{ MODKEY, XKB_KEY_w, killclient, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
#ifdef MNG_LAYOUT_VARCOL
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Tab, pushleft, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_ISO_Left_Tab, \
pushleft, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_I, incncols, {.i = +1 } },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_D, incncols, {.i = -1 } },
{ MODKEY, XKB_KEY_h, setcolfact, {.f = -0.05} },
{ MODKEY, XKB_KEY_l, setcolfact, {.f = +0.05} },
#endif
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
};
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, moveresize, {.ui = Curmfact} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
};

View File

@ -12,3 +12,5 @@ MANDIR = $(PREFIX)/share/man
# Uncomment to build XWayland support # Uncomment to build XWayland support
XWAYLAND = -DXWAYLAND XWAYLAND = -DXWAYLAND
XLIBS = xcb xcb-icccm XLIBS = xcb xcb-icccm
CFLAGS += -DMNG_LAYOUT_VARCOL=1

42
dwl.c
View File

@ -131,6 +131,11 @@ struct Client {
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
pid_t pid; pid_t pid;
Client *swallowing, *swallowedby; Client *swallowing, *swallowedby;
#if MNG_LAYOUT_VARCOL
int isLeft;
float cfact;
#endif // MNG_LAYOUT_VARCOL
}; };
typedef struct { typedef struct {
@ -200,6 +205,12 @@ struct Monitor {
double mfact; double mfact;
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
#if MNG_LAYOUT_VARCOL
float colfact[3]; /* Relative sizes of the different column types */
int nmastercols; /* The number of master columns to use */
int nrightcols; /* The number of right "stack" columns to use */
#endif // MNG_LAYOUT_VARCOL
}; };
typedef struct { typedef struct {
@ -220,6 +231,10 @@ typedef struct {
int isterm; int isterm;
int noswallow; int noswallow;
int monitor; int monitor;
#if MNG_LAYOUT_VARCOL
int isLeft;
#endif // MNG_VARCOL
} Rule; } Rule;
typedef struct { typedef struct {
@ -326,6 +341,12 @@ static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
static void tagmon(const Arg *arg); static void tagmon(const Arg *arg);
static void tile(Monitor *m); static void tile(Monitor *m);
#ifdef MNG_LAYOUT_VARCOL
static void varcol(Monitor *m);
static void pushleft(const Arg *arg);
void incncols(const Arg *arg);
void setcolfact(const Arg *arg);
#endif
static void togglefloating(const Arg *arg); static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg); static void togglefullscreen(const Arg *arg);
static void togglegaps(const Arg *arg); static void togglegaps(const Arg *arg);
@ -447,6 +468,10 @@ static Atom netatom[NetLast];
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
#ifdef MNG_LAYOUT_VARCOL
#include "mng-varcol.c"
#endif
/* function implementations */ /* function implementations */
void void
applybounds(Client *c, struct wlr_box *bbox) applybounds(Client *c, struct wlr_box *bbox)
@ -490,12 +515,19 @@ applyrules(Client *c)
if (!(title = client_get_title(c))) if (!(title = client_get_title(c)))
title = broken; title = broken;
#if MNG_LAYOUT_VARCOL
c->isLeft = 0;
#endif // MNG_LAYOUT_VARCOL
for (r = rules; r < END(rules); r++) { for (r = rules; r < END(rules); r++) {
if ((!r->title || strstr(title, r->title)) if ((!r->title || strstr(title, r->title))
&& (!r->id || strstr(appid, r->id))) { && (!r->id || strstr(appid, r->id))) {
c->isfloating = r->isfloating; c->isfloating = r->isfloating;
c->isterm = r->isterm; c->isterm = r->isterm;
c->noswallow = r->noswallow; c->noswallow = r->noswallow;
#if MNG_LAYOUT_VARCOL
c->isLeft = r->isLeft;
#endif // MNG_LAYOUT_VARCOL
newtags |= r->tags; newtags |= r->tags;
i = 0; i = 0;
wl_list_for_each(m, &mons, link) wl_list_for_each(m, &mons, link)
@ -958,6 +990,12 @@ createmon(struct wl_listener *listener, void *data)
m->gappoh = gappoh; m->gappoh = gappoh;
m->gappov = gappov; m->gappov = gappov;
m->tagset[0] = m->tagset[1] = 1; m->tagset[0] = m->tagset[1] = 1;
#if MNG_LAYOUT_VARCOL
m->colfact[0] = colfact[0];
m->colfact[1] = colfact[1];
m->colfact[2] = colfact[2];
#endif // MNG_VARCOL
for (r = monrules; r < END(monrules); r++) { for (r = monrules; r < END(monrules); r++) {
if (!r->name || strstr(wlr_output->name, r->name)) { if (!r->name || strstr(wlr_output->name, r->name)) {
m->mfact = r->mfact; m->mfact = r->mfact;
@ -1582,6 +1620,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
*/ */
int handled = 0; int handled = 0;
const Key *k; const Key *k;
// printf("%08X %08X\n", mods, sym);
for (k = keys; k < END(keys); k++) { for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod) && if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
sym == k->keysym && k->func) { sym == k->keysym && k->func) {
@ -1760,6 +1800,8 @@ mapnotify(struct wl_listener *listener, void *data)
c->geom.width += 2 * c->bw; c->geom.width += 2 * c->bw;
c->geom.height += 2 * c->bw; c->geom.height += 2 * c->bw;
c->cfact = 1.0;
/* Insert this client into client lists. */ /* Insert this client into client lists. */
wl_list_insert(&clients, &c->link); wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);

331
mng-varcol.c Normal file
View File

@ -0,0 +1,331 @@
#include <math.h>
static int isleft(Client *c);
/*
Variable Column Layout
- Special 'left' column, with helper to move clients in or out of the left
column. This is useful for things like chat, or status monitor windows that
you want to keep visible.
- Variable number of master columns
- Variable number of right columns
- Variable number of master windows
// TODO Calculate remainders
*/
/* The relative factors for the size of each column */
static const float colfact[3] = { 0.1, 0.6, 0.3 };
/*
Move a client within a column
w The width of the column
x The left position of the column
offset The offset of this client within the column
count The number of clients in the column
*/
static void placeClientInColumn(Monitor *m, Client *c, int w, int x, int offset, int count)
{
struct wlr_box geom = {0};
geom.width = w;
geom.height = floor(m->w.height / count);
geom.x = x;
geom.y = m->w.y + (offset * geom.height);
resize(c, geom, False);
}
/*
variable column layout
This layout has a variable number of columns, in 3 categories.
0-1 small left columns, containing clients that have been "pushed" left
1-n master columns
0-n right columns
*/
void varcol(Monitor *m)
{
int masterw, leftw, rightw, x;
unsigned int i, leftn, rightn, mastern, coln, offset;
float colfacts;
Client *c, *tmp;
int nmastercols = m->nmastercols;
int nrightcols = m->nrightcols;
struct wl_list left_clients;
/*
Remove each of window that belongs in the left column, so they can be
reattached at the end of the list below.
*/
wl_list_init(&left_clients);
i = 0;
wl_list_for_each_safe(c, tmp, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) {
continue;
}
if (i < m->nmaster) {
/* Master */
;
} else if (isleft(c)) {
/* Left; Detach and put in the left list */
wl_list_remove(&c->link);
wl_list_insert(&left_clients, &c->link);
}
i++;
}
/* Reattach th eleft clients to the main list */
wl_list_for_each_safe(c, tmp, &left_clients, link) {
wl_list_remove(&c->link);
wl_list_insert(clients.prev, &c->link);
}
/* Count the windows for each column type */
leftn = rightn = mastern = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) {
continue;
}
if (mastern < m->nmaster) {
mastern++;
} else if (isleft(c)) {
leftn++;
} else {
rightn++;
}
}
nmastercols = MAX(MIN(mastern, nmastercols), 1);
nrightcols = MAX(MIN(rightn, nrightcols), 1);
if (mastern == 0) {
return;
}
/* Calculate the total colfacts value */
colfacts = 0;
/* Left column */
if (leftn > 0) {
colfacts += m->colfact[0];
}
/* Center column(s) */
for (i = 0; i < nmastercols; i++) {
colfacts += m->colfact[1];
}
/* Right column(s) */
if (rightn > 0) {
for (i = 0; i < nrightcols; i++) {
colfacts += m->colfact[2];
}
}
/* Calculate the width for each column type */
leftw = (m->w.width / colfacts) * m->colfact[0];
masterw = (m->w.width / colfacts) * m->colfact[1];
rightw = (m->w.width / colfacts) * m->colfact[2];
/* Place each client */
i = 0;
x = m->w.x;
if (leftn > 0) {
x += leftw;
}
wl_list_for_each(c, &clients, link) {
struct wlr_box min = {0}, max = {0};
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) {
continue;
}
/* Get client size hints */
client_get_size_hints(c, &max, &min);
if (i < mastern) {
/* Master columns */
/* Offset within the section */
offset = i;
/* Max number of items in each master column */
coln = ceil((float) mastern / nmastercols);
placeClientInColumn(m, c, masterw, x, offset % coln, coln);
/* Only increment x if this is the last client in this column */
if ((++offset % coln) == 0) {
x += masterw;
}
} else if (!isleft(c)) {
/* Right columns */
/* Offset within the section */
offset = (i - mastern);
/* Max number of items in each right column */
coln = ceil((float) rightn / nrightcols);
placeClientInColumn(m, c, rightw, x, offset % coln, coln);
/* Only increment x if this is the last client in this column */
if ((++offset % coln) == 0) {
x += rightw;
}
} else if (leftn > 0) {
/* left column */
x = m->w.x;
/* Offset within the section */
offset = i - (mastern + rightn);
/* There is only one left column */
coln = leftn;
placeClientInColumn(m, c, leftw, x, offset, leftn);
}
i++;
}
}
static int isleft(Client *c)
{
if (c == NULL) {
return 0;
}
if (c->mon != NULL && c->mon->m.width <= 2000) {
/* The left column is not worth using on a small monitor */
return 0;
}
return c->isLeft;
}
/* Return non-zero if the currently selected client is in a master column */
static int ismaster(void)
{
Client *c, *sel = focustop(selmon);
int i;
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) {
return 0;
}
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen) {
continue;
}
if (sel == c) {
/* c is the selected client, and is index i */
if (i < selmon->nmaster) {
return 1;
} else {
return 0;
}
}
i++;
}
return 0;
}
/* A value >= 1.0 sets that colfact to that value - 1.0 */
void setcolfact(const Arg *arg)
{
Client *sel = focustop(selmon);
int index = 1;
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) {
return;
}
if (ismaster()) {
index = 0;
/* master */
index = 0;
} else if (isleft(sel)) {
/* left */
index = -1;
} else {
/* right */
index = 1;
}
index++;
if (arg->f >= 1.0) {
selmon->colfact[index] = arg->f - 1.0;
} else {
/* Adjust the argument based on the selected column */
selmon->colfact[index] += arg->f;
}
if (selmon->colfact[index] < 0.1) {
selmon->colfact[index] = 0.1;
} else if (selmon->colfact[index] > 0.9) {
selmon->colfact[index] = 0.9;
}
arrange(selmon);
}
static void pushleft(const Arg *arg)
{
Client *sel = focustop(selmon);
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) {
return;
}
sel->isLeft = !sel->isLeft;
focusclient(sel, 1);
arrange(selmon);
}
/*
Modify either the right or master column count
*/
void incncols(const Arg *arg)
{
Client *sel = focustop(selmon);
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) {
return;
}
if (selmon->nmastercols < 1) selmon->nmastercols = 1;
if (selmon->nrightcols < 1) selmon->nrightcols = 1;
if (ismaster()) {
/* master */
selmon->nmastercols += arg->i;
/* Auto adjust nmaster as well */
selmon->nmaster = selmon->nmastercols;
} else if (isleft(sel)) {
/* left */
;
} else {
/* right */
selmon->nrightcols += arg->i;
}
if (selmon->nmastercols < 1) selmon->nmastercols = 1;
if (selmon->nrightcols < 1) selmon->nrightcols = 1;
arrange(selmon);
}