refactor: change fair/fairv to use a 2 level tree

the idea is that we can step once through the tree to get the layout
(dfs) and since this makes it declarative resize can simply mutate the
tree and the arranger will handle the rest.

the tree looks a bit like:

FairState (root)
├── cols[0]  { ratio: col_width_weight }
│   ├── row_ratios[0]  row_height_weight
│   ├── row_ratios[1]  row_height_weight
│   └── row_ratios[2]  row_height_weight
├── cols[1]  { ratio: col_width_weight }
│   ├── row_ratios[0]  row_height_weight
│   └── row_ratios[1]  row_height_weight
└── cols[2]  { ratio: col_width_weight }
    └── row_ratios[0]  row_height_weight
This commit is contained in:
DuckTapeMan35 2026-05-16 19:50:52 +01:00
parent 65c9ac6dd2
commit b56cab98da
4 changed files with 396 additions and 480 deletions

View file

@ -536,300 +536,233 @@ void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx,
}
}
void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
Monitor *m = grabc->mon;
if (m->isoverview)
return;
static struct FairState *ensure_fair_state(Monitor *m, uint32_t tag) {
if (!m->pertag->fair_state[tag]) {
m->pertag->fair_state[tag] = calloc(1, sizeof(struct FairState));
}
return m->pertag->fair_state[tag];
}
if (m->visible_tiling_clients <= 1)
return;
static void fair_state_adapt(struct FairState *fs, int cols, int base_rows, int remainder) {
for (int c = 0; c < cols; c++) {
if (fs->cols[c].ratio <= 0.0f) fs->cols[c].ratio = 1.0f;
int rows = (c < cols - remainder) ? base_rows : base_rows + 1;
for (int r = 0; r < rows; r++)
if (fs->cols[c].row_ratios[r] <= 0.0f)
fs->cols[c].row_ratios[r] = 1.0f;
}
fs->n_cols = cols;
}
// 获取当前布局 ID
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
Monitor *m = grabc->mon;
if (m->isoverview)
return;
if (m->visible_tiling_clients <= 1)
return;
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
bool is_vfair = current_layout->id == VERTICAL_FAIR;
Client *c;
wl_list_for_each(c, &clients, link) {
c->old_grid_col_per =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
c->old_grid_row_per =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
}
uint32_t tag = m->pertag->curtag;
struct FairState *fs = ensure_fair_state(m, tag);
grabc->old_grid_col_per = grabc->grid_col_per;
grabc->old_grid_row_per = grabc->grid_row_per;
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->drag_begin_geom = grabc->geom;
} else {
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->drag_begin_geom = grabc->geom;
Client *c;
wl_list_for_each(c, &clients, link) {
c->old_grid_col_per =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
c->old_grid_row_per =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
}
grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false;
}
Client *c;
wl_list_for_each(c, &clients, link) {
c->old_grid_col_per = (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
c->old_grid_row_per = (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
}
// 以屏幕分辨率为基准算出缩放比变化的量
float delta_x = (float)offsetx * grabc->old_grid_col_per /
grabc->drag_begin_geom.width;
float delta_y = (float)offsety * grabc->old_grid_row_per /
grabc->drag_begin_geom.height;
grabc->old_grid_col_per = grabc->grid_col_per;
grabc->old_grid_row_per = grabc->grid_row_per;
grabc->cursor_in_left_half = cursor->x < grabc->geom.x + grabc->geom.width / 2;
grabc->cursor_in_upper_half = cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->drag_begin_geom = grabc->geom;
} else {
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->drag_begin_geom = grabc->geom;
Client *c;
wl_list_for_each(c, &clients, link) {
c->old_grid_col_per = (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
c->old_grid_row_per = (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
}
grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false;
}
int adj_c_idx = grabc->grid_col_idx;
int adj_r_idx = grabc->grid_row_idx;
float sign_x = 1.0f, sign_y = 1.0f;
float delta_x = (float)offsetx * grabc->old_grid_col_per / grabc->drag_begin_geom.width;
float delta_y = (float)offsety * grabc->old_grid_row_per / grabc->drag_begin_geom.height;
if (isdrag) {
if (grabc->cursor_in_left_half) {
adj_c_idx -= 1;
sign_x = -1.0f;
} else {
adj_c_idx += 1;
sign_x = 1.0f;
}
int adj_c_idx = grabc->grid_col_idx + 1;
int adj_r_idx = grabc->grid_row_idx + 1;
float sign_x = 1.0f, sign_y = 1.0f;
if (grabc->cursor_in_upper_half) {
adj_r_idx -= 1;
sign_y = -1.0f;
} else {
adj_r_idx += 1;
sign_y = 1.0f;
}
}
// 键盘热键逻辑不变
int max_col = -1, max_row = -1, min_col = INT32_MAX,
min_row = INT32_MAX;
Client *tmp;
wl_list_for_each(tmp, &clients, link) {
if (tmp->mon != m || !VISIBLEON(tmp, m) || !ISTILED(tmp))
continue;
if (tmp->grid_col_idx > max_col)
max_col = tmp->grid_col_idx;
if (tmp->grid_row_idx > max_row)
max_row = tmp->grid_row_idx;
if (tmp->grid_col_idx < min_col)
min_col = tmp->grid_col_idx;
if (tmp->grid_row_idx < min_row)
min_row = tmp->grid_row_idx;
}
int max_col = -1, max_row = -1, min_col = INT32_MAX, min_row = INT32_MAX;
Client *tmp;
wl_list_for_each(tmp, &clients, link) {
if (tmp->mon != m || !VISIBLEON(tmp, m) || !ISTILED(tmp)) continue;
if (tmp->grid_col_idx > max_col) max_col = tmp->grid_col_idx;
if (tmp->grid_row_idx > max_row) max_row = tmp->grid_row_idx;
if (tmp->grid_col_idx < min_col) min_col = tmp->grid_col_idx;
if (tmp->grid_row_idx < min_row) min_row = tmp->grid_row_idx;
}
adj_c_idx = grabc->grid_col_idx + 1;
adj_r_idx = grabc->grid_row_idx + 1;
sign_x = 1.0f;
sign_y = 1.0f;
adj_c_idx = grabc->grid_col_idx + 1; sign_x = 1.0f;
adj_r_idx = grabc->grid_row_idx + 1; sign_y = 1.0f;
if (grabc->grid_col_idx == max_col) {
adj_c_idx = grabc->grid_col_idx - 1;
sign_x = -1.0f;
}
if (grabc->grid_row_idx == max_row) {
adj_r_idx = grabc->grid_row_idx - 1;
sign_y = -1.0f;
}
if (grabc->grid_col_idx == min_col) {
adj_c_idx = grabc->grid_col_idx + 1;
sign_x = 1.0f;
}
if (grabc->grid_row_idx == min_row) {
adj_r_idx = grabc->grid_row_idx + 1;
sign_y = 1.0f;
}
if (grabc->grid_col_idx == max_col) { adj_c_idx = grabc->grid_col_idx - 1; sign_x = -1.0f; }
if (grabc->grid_row_idx == max_row) { adj_r_idx = grabc->grid_row_idx - 1; sign_y = -1.0f; }
if (grabc->grid_col_idx == min_col) { adj_c_idx = grabc->grid_col_idx + 1; sign_x = 1.0f; }
if (grabc->grid_row_idx == min_row) { adj_r_idx = grabc->grid_row_idx + 1; sign_y = 1.0f; }
float dx = delta_x * sign_x;
float dy = delta_y * sign_y;
float dx = delta_x * sign_x;
float dy = delta_y * sign_y;
float my_old_col = grabc->old_grid_col_per;
float my_old_row = grabc->old_grid_row_per;
float adj_old_col = -1.0f, adj_old_row = -1.0f;
float my_old_col = grabc->old_grid_col_per;
float my_old_row = grabc->old_grid_row_per;
float adj_old_col = -1.0f, adj_old_row = -1.0f;
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c))
continue;
if (c->grid_col_idx == adj_c_idx && adj_old_col < 0)
adj_old_col = c->old_grid_col_per;
if (c->grid_row_idx == adj_r_idx && adj_old_row < 0)
adj_old_row = c->old_grid_row_per;
}
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) continue;
if (c->grid_col_idx == adj_c_idx && adj_old_col < 0) adj_old_col = c->old_grid_col_per;
if (c->grid_row_idx == adj_r_idx && adj_old_row < 0) adj_old_row = c->old_grid_row_per;
}
// 应用列宽调节
if (adj_old_col > 0.0f) {
float dx_clamped = dx;
if (my_old_col + dx_clamped < 0.1f)
dx_clamped = 0.1f - my_old_col;
if (adj_old_col - dx_clamped < 0.1f)
dx_clamped = adj_old_col - 0.1f;
int col = grabc->grid_col_idx;
int row = grabc->grid_row_idx;
float new_my_col = my_old_col + dx_clamped;
float new_adj_col = adj_old_col - dx_clamped;
// Apply column width adjustment
if (adj_old_col > 0.0f) {
float dx_clamped = dx;
if (my_old_col + dx_clamped < 0.1f) dx_clamped = 0.1f - my_old_col;
if (adj_old_col - dx_clamped < 0.1f) dx_clamped = adj_old_col - 0.1f;
// 处理被强行锁死在 1.0f 的列边界,头部是个错位窗口
if (current_layout && current_layout->id == VERTICAL_FAIR) {
int32_t n_tiling = m->visible_tiling_clients;
int32_t l_rows;
for (l_rows = 0; l_rows <= n_tiling; l_rows++) {
if (l_rows * l_rows >= n_tiling)
break;
}
int32_t base_cols = n_tiling / l_rows;
// 当调节边界恰好处于非对称的锁死列(如 3 窗口下的 col 0 与 col
// 1 之间)
if ((grabc->grid_col_idx == base_cols - 1 &&
adj_c_idx == base_cols) ||
(grabc->grid_col_idx == base_cols &&
adj_c_idx == base_cols - 1)) {
float new_my_col = my_old_col + dx_clamped;
float new_adj_col = adj_old_col - dx_clamped;
float p_col =
(grabc->grid_col_idx == base_cols - 1)
? (my_old_col + dx) / (my_old_col + adj_old_col)
: (adj_old_col - dx) / (my_old_col + adj_old_col);
if (p_col < 0.01f)
p_col = 0.01f;
if (p_col > 0.99f)
p_col = 0.99f;
// Handle asymmetric boundary
int32_t n_tiling = m->visible_tiling_clients;
int32_t l_cols;
for (l_cols = 0; l_cols <= n_tiling; l_cols++)
if (l_cols * l_cols >= n_tiling) break;
int32_t first_group_cols = l_cols - (n_tiling % l_cols);
// 反推非线性真实权重值
float new_r_var_per = p_col / (1.0f - p_col);
if (new_r_var_per < 0.1f)
new_r_var_per = 0.1f;
if (new_r_var_per > 10.0f)
new_r_var_per = 10.0f;
if (is_vfair) {
// in vfair, col boundary asymmetry is within a row
// base_cols = n/rows, first_group_rows = rows - remainder
int32_t l_rows;
for (l_rows = 0; l_rows <= n_tiling; l_rows++)
if (l_rows * l_rows >= n_tiling) break;
int32_t vbase_cols = n_tiling / l_rows;
int32_t vfirst_group_rows = l_rows - (n_tiling % l_rows);
// cols_in_this_row depends on which row we're in
int32_t cols_in_row = (row < vfirst_group_rows) ? vbase_cols : vbase_cols + 1;
if ((col == cols_in_row - 2 && adj_c_idx == cols_in_row - 1) ||
(col == cols_in_row - 1 && adj_c_idx == cols_in_row - 2)) {
// last col boundary — no asymmetry issue in vfair cols
}
} else {
if ((col == first_group_cols - 1 && adj_c_idx == first_group_cols) ||
(col == first_group_cols && adj_c_idx == first_group_cols - 1)) {
float p_col = (col == first_group_cols - 1)
? (my_old_col + dx) / (my_old_col + adj_old_col)
: (adj_old_col - dx) / (my_old_col + adj_old_col);
p_col = fmaxf(0.01f, fminf(0.99f, p_col));
float new_r = p_col / (1.0f - p_col);
new_r = fmaxf(0.1f, fminf(10.0f, new_r));
if (col == first_group_cols - 1) { new_my_col = new_r; new_adj_col = 1.0f; }
else { new_my_col = 1.0f; new_adj_col = new_r; }
}
}
if (grabc->grid_col_idx == base_cols - 1) {
new_my_col = new_r_var_per;
new_adj_col = 1.0f;
} else {
new_my_col = 1.0f;
new_adj_col = new_r_var_per;
}
}
}
// Mutate FairState
if (is_vfair) {
fs->cols[row].row_ratios[col] = new_my_col;
fs->cols[row].row_ratios[adj_c_idx] = new_adj_col;
} else {
fs->cols[col].ratio = new_my_col;
fs->cols[adj_c_idx].ratio = new_adj_col;
}
}
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c))
continue;
if (c->grid_col_idx == grabc->grid_col_idx)
c->grid_col_per = new_my_col;
if (c->grid_col_idx == adj_c_idx)
c->grid_col_per = new_adj_col;
}
// Apply row height adjustment
if (adj_old_row > 0.0f) {
float dy_clamped = dy;
if (my_old_row + dy_clamped < 0.1f) dy_clamped = 0.1f - my_old_row;
if (adj_old_row - dy_clamped < 0.1f) dy_clamped = adj_old_row - 0.1f;
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c))
continue;
if (c->grid_row_idx == 0) {
if (c->grid_col_idx == grabc->grid_col_idx)
c->grid_col_per = new_my_col;
else if (c->grid_col_idx == adj_c_idx)
c->grid_col_per = new_adj_col;
}
}
}
float new_my_row = my_old_row + dy_clamped;
float new_adj_row = adj_old_row - dy_clamped;
// 应用行高调节
if (adj_old_row > 0.0f) {
float dy_clamped = dy;
if (my_old_row + dy_clamped < 0.1f)
dy_clamped = 0.1f - my_old_row;
if (adj_old_row - dy_clamped < 0.1f)
dy_clamped = adj_old_row - 0.1f;
// Handle asymmetric boundary
int32_t n_tiling = m->visible_tiling_clients;
int32_t l_cols;
for (l_cols = 0; l_cols <= n_tiling; l_cols++)
if (l_cols * l_cols >= n_tiling) break;
int32_t base_rows = n_tiling / l_cols;
float new_my_row = my_old_row + dy_clamped;
float new_adj_row = adj_old_row - dy_clamped;
if (is_vfair) {
int32_t l_rows;
for (l_rows = 0; l_rows <= n_tiling; l_rows++)
if (l_rows * l_rows >= n_tiling) break;
int32_t vfirst_group_rows = l_rows - (n_tiling % l_rows);
if ((row == vfirst_group_rows - 1 && adj_r_idx == vfirst_group_rows) ||
(row == vfirst_group_rows && adj_r_idx == vfirst_group_rows - 1)) {
float p_row = (row == vfirst_group_rows - 1)
? (my_old_row + dy) / (my_old_row + adj_old_row)
: (adj_old_row - dy) / (my_old_row + adj_old_row);
p_row = fmaxf(0.01f, fminf(0.99f, p_row));
float new_r = p_row / (1.0f - p_row);
new_r = fmaxf(0.1f, fminf(10.0f, new_r));
if (row == vfirst_group_rows - 1) { new_my_row = new_r; new_adj_row = 1.0f; }
else { new_my_row = 1.0f; new_adj_row = new_r; }
}
} else {
if ((row == base_rows - 1 && adj_r_idx == base_rows) ||
(row == base_rows && adj_r_idx == base_rows - 1)) {
float p_row = (row == base_rows - 1)
? (my_old_row + dy) / (my_old_row + adj_old_row)
: (adj_old_row - dy) / (my_old_row + adj_old_row);
p_row = fmaxf(0.01f, fminf(0.99f, p_row));
float new_r = p_row / (1.0f - p_row);
new_r = fmaxf(0.1f, fminf(10.0f, new_r));
if (row == base_rows - 1) { new_my_row = new_r; new_adj_row = 1.0f; }
else { new_my_row = 1.0f; new_adj_row = new_r; }
}
}
// 处理被强行锁死在 1.0f 的行边界,头部是个错位窗口
if (current_layout && current_layout->id == FAIR) {
int32_t n_tiling = m->visible_tiling_clients;
int32_t l_cols;
for (l_cols = 0; l_cols <= n_tiling; l_cols++) {
if (l_cols * l_cols >= n_tiling)
break;
}
int32_t base_rows = n_tiling / l_cols;
// 当调节边界恰好处于非对称的锁死行(如 3 窗口下的 row 0 与 row
// 1 之间)
if ((grabc->grid_row_idx == base_rows - 1 &&
adj_r_idx == base_rows) ||
(grabc->grid_row_idx == base_rows &&
adj_r_idx == base_rows - 1)) {
// Mutate FairState
if (is_vfair) {
fs->cols[row].ratio = new_my_row;
fs->cols[adj_r_idx].ratio = new_adj_row;
} else {
fs->cols[col].row_ratios[row] = new_my_row;
fs->cols[col].row_ratios[adj_r_idx] = new_adj_row;
}
}
float p_row =
(grabc->grid_row_idx == base_rows - 1)
? (my_old_row + dy) / (my_old_row + adj_old_row)
: (adj_old_row - dy) / (my_old_row + adj_old_row);
if (p_row < 0.01f)
p_row = 0.01f;
if (p_row > 0.99f)
p_row = 0.99f;
if (!isdrag) {
arrange(m, false, false);
return;
}
// 反推非线性真实权重值
float new_r_var_per = p_row / (1.0f - p_row);
if (new_r_var_per < 0.1f)
new_r_var_per = 0.1f;
if (new_r_var_per > 10.0f)
new_r_var_per = 10.0f;
if (grabc->grid_row_idx == base_rows - 1) {
new_my_row = new_r_var_per;
new_adj_row = 1.0f;
} else {
new_my_row = 1.0f;
new_adj_row = new_r_var_per;
}
}
}
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c))
continue;
if (c->grid_row_idx == grabc->grid_row_idx)
c->grid_row_per = new_my_row;
if (c->grid_row_idx == adj_r_idx)
c->grid_row_per = new_adj_row;
}
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c))
continue;
if (c->grid_col_idx == 0) {
if (c->grid_row_idx == grabc->grid_row_idx)
c->grid_row_per = new_my_row;
else if (c->grid_row_idx == adj_r_idx)
c->grid_row_per = new_adj_row;
}
}
}
if (!isdrag) {
arrange(m, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(m, false, false);
last_apply_drap_time = time;
}
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(m, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,

View file

@ -866,119 +866,100 @@ void grid(Monitor *m) {
}
}
void fair(Monitor *m) {
int32_t i, n = 0;
Client *c = NULL;
n = m->visible_tiling_clients;
if (n == 0)
return;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t cols;
for (cols = 0; cols <= n; cols++) {
if (cols * cols >= n)
break;
}
int32_t base_rows = n / cols;
int32_t remainder = n % cols;
int32_t first_group_cols = cols - remainder;
int32_t first_group_count = first_group_cols * base_rows;
int32_t max_rows = base_rows + (remainder > 0 ? 1 : 0);
float col_pers[cols];
float row_pers[max_rows];
for (i = 0; i < cols; i++)
col_pers[i] = 1.0f;
for (i = 0; i < max_rows; i++)
row_pers[i] = 1.0f;
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
int32_t col_idx, row_idx;
if (i < first_group_count) {
col_idx = i / base_rows;
row_idx = i % base_rows;
} else {
int32_t offset = i - first_group_count;
col_idx = first_group_cols + (offset / (base_rows + 1));
row_idx = offset % (base_rows + 1);
}
if (row_idx == 0)
col_pers[col_idx] =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
if (col_idx == 0)
row_pers[row_idx] =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
i++;
}
float sum_col = 0.0f;
for (i = 0; i < cols; i++)
sum_col += col_pers[i];
float avail_w = m->w.width - 2 * cur_gappoh - (cols - 1) * cur_gappih;
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
int32_t col_idx, row_idx, rows_in_this_col;
if (i < first_group_count) {
col_idx = i / base_rows;
row_idx = i % base_rows;
rows_in_this_col = base_rows;
} else {
int32_t offset = i - first_group_count;
col_idx = first_group_cols + (offset / (base_rows + 1));
row_idx = offset % (base_rows + 1);
rows_in_this_col = base_rows + 1;
}
c->grid_col_per = col_pers[col_idx];
c->grid_row_per = row_pers[row_idx];
c->grid_col_idx = col_idx;
c->grid_row_idx = row_idx;
float fl_cx = m->w.x + cur_gappoh;
for (int j = 0; j < col_idx; j++)
fl_cx += avail_w * (col_pers[j] / sum_col) + cur_gappih;
float fl_cw = (col_idx == cols - 1)
? (m->w.x + m->w.width - cur_gappoh - fl_cx)
: avail_w * (col_pers[col_idx] / sum_col);
float sum_row_this_col = 0.0f;
for (int j = 0; j < rows_in_this_col; j++)
sum_row_this_col += row_pers[j];
float avail_h =
m->w.height - 2 * cur_gappov - (rows_in_this_col - 1) * cur_gappiv;
float fl_cy = m->w.y + cur_gappov;
for (int j = 0; j < row_idx; j++)
fl_cy += avail_h * (row_pers[j] / sum_row_this_col) + cur_gappiv;
float fl_ch = (row_idx == rows_in_this_col - 1)
? (m->w.y + m->w.height - cur_gappov - fl_cy)
: avail_h * (row_pers[row_idx] / sum_row_this_col);
resize(c,
(struct wlr_box){.x = (int32_t)fl_cx,
.y = (int32_t)fl_cy,
.width = (int32_t)fl_cw,
.height = (int32_t)fl_ch},
0);
i++;
}
void cleanup_monitor_fair(Monitor *m) {
for (int i = 0; i <= LENGTH(tags); i++) {
free(m->pertag->fair_state[i]);
m->pertag->fair_state[i] = NULL;
}
}
void fair(Monitor *m) {
int32_t n = m->visible_tiling_clients;
if (n == 0) return;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t cols;
for (cols = 0; cols <= n; cols++)
if (cols * cols >= n) break;
int32_t base_rows = n / cols;
int32_t remainder = n % cols;
int32_t first_group_cols = cols - remainder;
int32_t first_group_count = first_group_cols * base_rows;
uint32_t tag = m->pertag->curtag;
struct FairState *fs = ensure_fair_state(m, tag);
fair_state_adapt(fs, cols, base_rows, remainder);
float avail_w = m->w.width - 2*cur_gappoh - (cols-1)*cur_gappih;
float sum_col = 0.0f;
for (int c = 0; c < cols; c++) sum_col += fs->cols[c].ratio;
int i = 0;
int prev_col = -1;
float col_x = 0, row_y = 0, avail_h = 0, sum_row = 0;
int rows_in_col = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) continue;
int col, row;
if (i < first_group_count) {
col = i / base_rows;
row = i % base_rows;
rows_in_col = base_rows;
} else {
int off = i - first_group_count;
col = first_group_cols + off / (base_rows + 1);
row = off % (base_rows + 1);
rows_in_col = base_rows + 1;
}
if (col != prev_col) {
col_x = m->w.x + cur_gappoh;
for (int pc = 0; pc < col; pc++)
col_x += avail_w * (fs->cols[pc].ratio / sum_col) + cur_gappih;
struct FairColNode *cn = &fs->cols[col];
sum_row = 0.0f;
avail_h = m->w.height - 2*cur_gappov - (rows_in_col-1)*cur_gappiv;
for (int r = 0; r < rows_in_col; r++) sum_row += cn->row_ratios[r];
row_y = m->w.y + cur_gappov;
prev_col = col;
}
struct FairColNode *cn = &fs->cols[col];
float col_w = (col == cols - 1)
? (m->w.x + m->w.width - cur_gappoh - col_x)
: avail_w * (cn->ratio / sum_col);
float row_h = (row == rows_in_col - 1)
? (m->w.y + m->w.height - cur_gappov - row_y)
: avail_h * (cn->row_ratios[row] / sum_row);
c->grid_col_idx = col;
c->grid_row_idx = row;
c->grid_col_per = cn->ratio;
c->grid_row_per = cn->row_ratios[row];
resize(c, (struct wlr_box){
.x = (int32_t)col_x,
.y = (int32_t)row_y,
.width = (int32_t)col_w,
.height = (int32_t)row_h,
}, 0);
row_y += row_h + cur_gappiv;
i++;
}
}

View file

@ -355,118 +355,105 @@ void vertical_grid(Monitor *m) {
}
void vertical_fair(Monitor *m) {
int32_t i, n = 0;
Client *c = NULL;
int32_t n = m->visible_tiling_clients;
if (n == 0) return;
n = m->visible_tiling_clients;
if (n == 0)
return;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t rows;
for (rows = 0; rows <= n; rows++)
if (rows * rows >= n) break;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t base_cols = n / rows;
int32_t remainder = n % rows;
int32_t first_group_rows = rows - remainder;
int32_t first_group_count = first_group_rows * base_cols;
int32_t rows;
for (rows = 0; rows <= n; rows++) {
if (rows * rows >= n)
break;
}
uint32_t tag = m->pertag->curtag;
struct FairState *fs = ensure_fair_state(m, tag);
// adapt with transposed semantics: rows are top-level
if (fs->n_cols != rows) {
for (int r = fs->n_cols; r < rows; r++) {
if (fs->cols[r].ratio <= 0.0f) fs->cols[r].ratio = 1.0f;
int cols_in_row = (r < first_group_rows) ? base_cols : base_cols + 1;
for (int c = 0; c < cols_in_row; c++)
if (fs->cols[r].row_ratios[c] <= 0.0f)
fs->cols[r].row_ratios[c] = 1.0f;
}
fs->n_cols = rows;
}
int32_t base_cols = n / rows;
int32_t remainder = n % rows;
int32_t first_group_rows = rows - remainder;
int32_t first_group_count = first_group_rows * base_cols;
int32_t max_cols = base_cols + (remainder > 0 ? 1 : 0);
float avail_h = m->w.height - 2*cur_gappov - (rows-1)*cur_gappiv;
float sum_row = 0.0f;
for (int r = 0; r < rows; r++) sum_row += fs->cols[r].ratio;
float row_pers[rows];
float col_pers[max_cols];
for (i = 0; i < rows; i++)
row_pers[i] = 1.0f;
for (i = 0; i < max_cols; i++)
col_pers[i] = 1.0f;
int i = 0;
int prev_row = -1;
float row_y = 0, col_x = 0, avail_w = 0, sum_col = 0;
int cols_in_row = 0;
Client *c;
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
int32_t row_idx, col_idx;
if (i < first_group_count) {
row_idx = i / base_cols;
col_idx = i % base_cols;
} else {
int32_t offset = i - first_group_count;
row_idx = first_group_rows + (offset / (base_cols + 1));
col_idx = offset % (base_cols + 1);
}
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) continue;
if (col_idx == 0)
row_pers[row_idx] =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
if (row_idx == 0)
col_pers[col_idx] =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
i++;
}
int row, col;
if (i < first_group_count) {
row = i / base_cols;
col = i % base_cols;
cols_in_row = base_cols;
} else {
int off = i - first_group_count;
row = first_group_rows + off / (base_cols + 1);
col = off % (base_cols + 1);
cols_in_row = base_cols + 1;
}
float sum_row = 0.0f;
for (i = 0; i < rows; i++)
sum_row += row_pers[i];
float avail_h = m->w.height - 2 * cur_gappov - (rows - 1) * cur_gappiv;
if (row != prev_row) {
row_y = m->w.y + cur_gappov;
for (int pr = 0; pr < row; pr++)
row_y += avail_h * (fs->cols[pr].ratio / sum_row) + cur_gappiv;
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
struct FairColNode *rn = &fs->cols[row];
sum_col = 0.0f;
avail_w = m->w.width - 2*cur_gappoh - (cols_in_row-1)*cur_gappih;
for (int ci = 0; ci < cols_in_row; ci++) {
if (rn->row_ratios[ci] <= 0.0f) rn->row_ratios[ci] = 1.0f;
sum_col += rn->row_ratios[ci];
}
col_x = m->w.x + cur_gappoh;
prev_row = row;
}
int32_t row_idx, col_idx, cols_in_this_row;
if (i < first_group_count) {
row_idx = i / base_cols;
col_idx = i % base_cols;
cols_in_this_row = base_cols;
} else {
int32_t offset = i - first_group_count;
row_idx = first_group_rows + (offset / (base_cols + 1));
col_idx = offset % (base_cols + 1);
cols_in_this_row = base_cols + 1;
}
struct FairColNode *rn = &fs->cols[row];
c->grid_row_per = row_pers[row_idx];
c->grid_col_per = col_pers[col_idx];
c->grid_row_idx = row_idx;
c->grid_col_idx = col_idx;
float row_h = (row == rows - 1)
? (m->w.y + m->w.height - cur_gappov - row_y)
: avail_h * (rn->ratio / sum_row);
float fl_cy = m->w.y + cur_gappov;
for (int j = 0; j < row_idx; j++)
fl_cy += avail_h * (row_pers[j] / sum_row) + cur_gappiv;
float fl_ch = (row_idx == rows - 1)
? (m->w.y + m->w.height - cur_gappov - fl_cy)
: avail_h * (row_pers[row_idx] / sum_row);
float col_w = (col == cols_in_row - 1)
? (m->w.x + m->w.width - cur_gappoh - col_x)
: avail_w * (rn->row_ratios[col] / sum_col);
float sum_col_this_row = 0.0f;
for (int j = 0; j < cols_in_this_row; j++)
sum_col_this_row += col_pers[j];
c->grid_row_idx = row;
c->grid_col_idx = col;
c->grid_row_per = rn->ratio;
c->grid_col_per = rn->row_ratios[col];
float avail_w =
m->w.width - 2 * cur_gappoh - (cols_in_this_row - 1) * cur_gappih;
float fl_cx = m->w.x + cur_gappoh;
for (int j = 0; j < col_idx; j++)
fl_cx += avail_w * (col_pers[j] / sum_col_this_row) + cur_gappih;
float fl_cw = (col_idx == cols_in_this_row - 1)
? (m->w.x + m->w.width - cur_gappoh - fl_cx)
: avail_w * (col_pers[col_idx] / sum_col_this_row);
resize(c, (struct wlr_box){
.x = (int32_t)col_x,
.y = (int32_t)row_y,
.width = (int32_t)col_w,
.height = (int32_t)row_h,
}, 0);
resize(c,
(struct wlr_box){.x = (int32_t)fl_cx,
.y = (int32_t)fl_cy,
.width = (int32_t)fl_cw,
.height = (int32_t)fl_ch},
0);
i++;
}
}
col_x += col_w + cur_gappih;
i++;
}
}

View file

@ -585,6 +585,19 @@ struct DwindleNode {
Client *client;
};
typedef struct FairColNode FairColNode;
struct FairColNode {
float ratio;
int n_rows;
float row_ratios[32];
};
typedef struct FairState FairState;
struct FairState {
int n_cols;
struct FairColNode cols[32];
};
struct ScrollerStackNode {
Client *client;
float scroller_proportion;
@ -1004,6 +1017,7 @@ struct Pertag {
int32_t no_render_border[LENGTH(tags) + 1];
int32_t open_as_floating[LENGTH(tags) + 1];
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
struct FairState *fair_state[LENGTH(tags) + 1];
const Layout *ltidxs[LENGTH(tags) + 1];
struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
};
@ -2539,6 +2553,7 @@ void cleanupmon(struct wl_listener *listener, void *data) {
cleanup_monitor_dwindle(m);
cleanup_monitor_scroller(m);
cleanup_monitor_fair(m);
free(m->pertag);
free(m);