maomaowm/src/layout/horizontal.h
2026-05-20 09:42:43 +08:00

927 lines
No EOL
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

void deck(Monitor *m) {
int32_t mw, my;
int32_t i, n = 0;
Client *c = NULL;
Client *fc = NULL;
float mfact;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
cur_gappih =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
n = m->visible_tiling_clients;
if (n == 0)
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
// Calculate master width using mfact from pertag
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
// Calculate master width including outer gaps
if (n > nmasters)
mw = nmasters ? round((m->w.width - 2 * cur_gappoh) * mfact) : 0;
else
mw = m->w.width - 2 * cur_gappoh;
i = my = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < nmasters) {
c->master_mfact_per = mfact;
// Master area clients
resize(
c,
(struct wlr_box){.x = m->w.x + cur_gappoh,
.y = m->w.y + cur_gappov + my,
.width = mw,
.height = (m->w.height - 2 * cur_gappov - my) /
(MIN(n, nmasters) - i)},
0);
my += c->geom.height;
} else {
// Stack area clients
c->master_mfact_per = mfact;
resize(c,
(struct wlr_box){.x = m->w.x + mw + cur_gappoh + cur_gappih,
.y = m->w.y + cur_gappov,
.width = m->w.width - mw - 2 * cur_gappoh -
cur_gappih,
.height = m->w.height - 2 * cur_gappov},
0);
if (c == focustop(m))
wlr_scene_node_raise_to_top(&c->scene->node);
}
i++;
}
}
void center_tile(Monitor *m) {
int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw;
Client *c = NULL;
Client *fc = NULL;
double mfact = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag];
master_num = n > master_num ? master_num : n;
stack_num = n - master_num;
if (n == 0)
return;
// 获取第一个可见的平铺客户端用于主区域宽度百分比
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
// 间隙参数处理
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 && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappih =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
int32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
// 初始化区域
mw = m->w.width;
mx = cur_gappoh;
my = cur_gappov;
tw = mw;
// 判断是否需要主区域铺满
int32_t should_overspread =
config.center_master_overspread && (n <= nmasters);
int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0;
int32_t slave_left_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num / 2 - 1));
float slave_left_surplus_ratio = 1.0;
int32_t slave_right_surplus_height =
(m->w.height - 2 * cur_gappov -
cur_gappiv * ie * ((stack_num + 1) / 2 - 1));
float slave_right_surplus_ratio = 1.0;
if (n > nmasters || !should_overspread) {
// 计算主区域宽度(居中模式)
mw = nmasters ? (m->w.width - 2 * cur_gappoh - cur_gappih * ie) * mfact
: 0;
if (n - nmasters > 1) {
// 多个堆叠窗口:主区域居中,左右两侧各有一个堆叠区域
tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie;
mx = cur_gappoh + tw + cur_gappih * ie;
} else if (n - nmasters == 1) {
// 单个堆叠窗口的处理
if (config.center_when_single_stack) {
// stack在右边master居中左边空着
tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie;
mx = cur_gappoh + tw + cur_gappih * ie; // master居中
} else {
// stack在右边master在左边
tw = m->w.width - mw - 2 * cur_gappoh - cur_gappih * ie;
mx = cur_gappoh; // master在左边
}
} else {
// 只有主区域窗口:居中显示
tw = (m->w.width - mw) / 2 - cur_gappoh - cur_gappih * ie;
mx = cur_gappoh + tw + cur_gappih * ie;
}
} else {
// 主区域铺满模式(只有主区域窗口时)
mw = m->w.width - 2 * cur_gappoh;
mx = cur_gappoh;
tw = 0; // 堆叠区域宽度为0
}
oty = cur_gappov;
ety = cur_gappov;
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < nmasters) {
// 主区域窗口
r = MIN(n, nmasters) - i;
if (c->master_inner_per > 0.0f) {
h = master_surplus_height * c->master_inner_per /
master_surplus_ratio;
master_surplus_height = master_surplus_height - h;
master_surplus_ratio =
master_surplus_ratio - c->master_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->master_inner_per = h / (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
resize(c,
(struct wlr_box){.x = m->w.x + mx,
.y = m->w.y + my,
.width = mw,
.height = h},
0);
my += c->geom.height + cur_gappiv * ie;
} else {
// 堆叠区域窗口
int32_t stack_index = i - nmasters;
if (n - nmasters == 1) {
// 单个堆叠窗口
r = n - i;
if (c->stack_inner_per > 0.0f) {
h = (m->w.height - 2 * cur_gappov -
cur_gappiv * ie * (stack_num - 1)) *
c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - ety - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->stack_inner_per = h / (m->w.height - ety - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
int32_t stack_x;
if (config.center_when_single_stack) {
// 放在右侧master居中时stack在右边
stack_x = m->w.x + mx + mw + cur_gappih * ie;
} else {
// 放在右侧master在左边时stack在右边
stack_x = m->w.x + mx + mw + cur_gappih * ie;
}
resize(c,
(struct wlr_box){.x = stack_x,
.y = m->w.y + ety,
.width = tw,
.height = h},
0);
ety += c->geom.height + cur_gappiv * ie;
} else {
// 多个堆叠窗口:交替放在左右两侧
r = (n - i + 1) / 2;
if ((stack_index % 2) ^ (n % 2 == 0)) {
// 右侧堆叠窗口
if (c->stack_inner_per > 0.0f) {
h = slave_right_surplus_height * c->stack_inner_per /
slave_right_surplus_ratio;
slave_right_surplus_height =
slave_right_surplus_height - h;
slave_right_surplus_ratio =
slave_right_surplus_ratio - c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - ety - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->stack_inner_per =
h / (m->w.height - ety - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
int32_t stack_x = m->w.x + mx + mw + cur_gappih * ie;
resize(c,
(struct wlr_box){.x = stack_x,
.y = m->w.y + ety,
.width = tw,
.height = h},
0);
ety += c->geom.height + cur_gappiv * ie;
} else {
// 左侧堆叠窗口
if (c->stack_inner_per > 0.0f) {
h = slave_left_surplus_height * c->stack_inner_per /
slave_left_surplus_ratio;
slave_left_surplus_height =
slave_left_surplus_height - h;
slave_left_surplus_ratio =
slave_left_surplus_ratio - c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - oty - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->stack_inner_per =
h / (m->w.height - oty - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
int32_t stack_x = m->w.x + cur_gappoh;
resize(c,
(struct wlr_box){.x = stack_x,
.y = m->w.y + oty,
.width = tw,
.height = h},
0);
oty += c->geom.height + cur_gappiv * ie;
}
}
}
i++;
}
}
void tile(Monitor *m) {
int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty;
Client *c = NULL;
Client *fc = NULL;
double mfact = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag];
master_num = n > master_num ? master_num : n;
stack_num = n - master_num;
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 && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappih =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
if (n > m->pertag->nmasters[m->pertag->curtag])
mw = m->pertag->nmasters[m->pertag->curtag]
? (m->w.width + cur_gappih * ie) * mfact
: 0;
else
mw = m->w.width - 2 * cur_gappoh + cur_gappih * ie;
i = 0;
my = ty = cur_gappov;
int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0;
int32_t slave_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < m->pertag->nmasters[m->pertag->curtag]) {
r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i;
if (c->master_inner_per > 0.0f) {
h = master_surplus_height * c->master_inner_per /
master_surplus_ratio;
master_surplus_height = master_surplus_height - h;
master_surplus_ratio =
master_surplus_ratio - c->master_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->master_inner_per = h / (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
resize(c,
(struct wlr_box){.x = m->w.x + cur_gappoh,
.y = m->w.y + my,
.width = mw - cur_gappih * ie,
.height = h},
0);
my += c->geom.height + cur_gappiv * ie;
} else {
r = n - i;
if (c->stack_inner_per > 0.0f) {
h = slave_surplus_height * c->stack_inner_per /
slave_surplus_ratio;
slave_surplus_height = slave_surplus_height - h;
slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - ty - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->stack_inner_per = h / (m->w.height - ty - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
// wlr_log(WLR_ERROR, "stack_inner_per: %f", c->stack_inner_per);
resize(c,
(struct wlr_box){.x = m->w.x + mw + cur_gappoh,
.y = m->w.y + ty,
.width = m->w.width - mw - 2 * cur_gappoh,
.height = h},
0);
ty += c->geom.height + cur_gappiv * ie;
}
i++;
}
}
void right_tile(Monitor *m) {
int32_t i, n = 0, h, r, ie = enablegaps, mw, my, ty;
Client *c = NULL;
Client *fc = NULL;
double mfact = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
n = m->visible_tiling_clients;
master_num = m->pertag->nmasters[m->pertag->curtag];
master_num = n > master_num ? master_num : n;
stack_num = n - master_num;
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 && m->visible_tiling_clients == 1 ? 0 : cur_gappiv;
cur_gappih =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappih;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc))
break;
}
mfact = fc->master_mfact_per > 0.0f ? fc->master_mfact_per
: m->pertag->mfacts[m->pertag->curtag];
if (n > m->pertag->nmasters[m->pertag->curtag])
mw = m->pertag->nmasters[m->pertag->curtag]
? (m->w.width + cur_gappih * ie) * mfact
: 0;
else
mw = m->w.width - 2 * cur_gappoh + cur_gappih * ie;
i = 0;
my = ty = cur_gappov;
int32_t master_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (master_num - 1));
float master_surplus_ratio = 1.0;
int32_t slave_surplus_height =
(m->w.height - 2 * cur_gappov - cur_gappiv * ie * (stack_num - 1));
float slave_surplus_ratio = 1.0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
if (i < m->pertag->nmasters[m->pertag->curtag]) {
r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i;
if (c->master_inner_per > 0.0f) {
h = master_surplus_height * c->master_inner_per /
master_surplus_ratio;
master_surplus_height = master_surplus_height - h;
master_surplus_ratio =
master_surplus_ratio - c->master_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->master_inner_per = h / (m->w.height - my - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
resize(c,
(struct wlr_box){.x = m->w.x + m->w.width - mw - cur_gappoh +
cur_gappih * ie,
.y = m->w.y + my,
.width = mw - cur_gappih * ie,
.height = h},
0);
my += c->geom.height + cur_gappiv * ie;
} else {
r = n - i;
if (c->stack_inner_per > 0.0f) {
h = slave_surplus_height * c->stack_inner_per /
slave_surplus_ratio;
slave_surplus_height = slave_surplus_height - h;
slave_surplus_ratio = slave_surplus_ratio - c->stack_inner_per;
c->master_mfact_per = mfact;
} else {
h = (m->w.height - ty - cur_gappov -
cur_gappiv * ie * (r - 1)) /
r;
c->stack_inner_per = h / (m->w.height - ty - cur_gappov -
cur_gappiv * ie * (r - 1));
c->master_mfact_per = mfact;
}
// wlr_log(WLR_ERROR, "stack_inner_per: %f", c->stack_inner_per);
resize(c,
(struct wlr_box){.x = m->w.x + cur_gappoh,
.y = m->w.y + ty,
.width = m->w.width - mw - 2 * cur_gappoh,
.height = h},
0);
ty += c->geom.height + cur_gappiv * ie;
}
i++;
}
}
void // 17
monocle(Monitor *m) {
Client *c = NULL;
struct wlr_box geom;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappoh =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappoh;
cur_gappov =
config.smartgaps && m->visible_tiling_clients == 1 ? 0 : cur_gappov;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c))
continue;
geom.x = m->w.x + cur_gappoh;
geom.y = m->w.y + cur_gappov;
geom.width = m->w.width - 2 * cur_gappoh;
geom.height = m->w.height - 2 * cur_gappov;
resize(c, geom, 0);
}
if ((c = focustop(m)))
wlr_scene_node_raise_to_top(&c->scene->node);
}
// 网格布局窗口大小和位置计算
void grid(Monitor *m) {
int32_t i, n;
int32_t cw, ch;
int32_t cols, rows, overcols;
Client *c = NULL;
n = 0;
int32_t target_gappo =
enablegaps ? m->isoverview ? config.overviewgappo : config.gappoh : 0;
int32_t target_gappi =
enablegaps ? m->isoverview ? config.overviewgappi : config.gappih : 0;
float single_width_ratio = m->isoverview ? 0.7 : 0.9;
float single_height_ratio = m->isoverview ? 0.8 : 0.9;
n = m->isoverview ? m->visible_clients : m->visible_tiling_clients;
if (n == 0)
return;
if (n == 1) {
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
cw = (m->w.width - 2 * target_gappo) * single_width_ratio;
ch = (m->w.height - 2 * target_gappo) * single_height_ratio;
c->geom.x = m->w.x + (m->w.width - cw) / 2;
c->geom.y = m->w.y + (m->w.height - ch) / 2;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
return;
}
}
}
if (n == 2) {
float col_pers[2] = {1.0f, 1.0f};
// 先提取这两个窗口现有的列比例
i = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
if (i < 2)
col_pers[i] =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
i++;
}
}
float sum_col = col_pers[0] + col_pers[1];
float avail_w = m->w.width - 2 * target_gappo - target_gappi;
ch =
(m->w.height - 2 * target_gappo) * 0.65; // 依然保持 0.65 的美观高度
i = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
c->grid_col_idx = i;
c->grid_row_idx = 0;
c->grid_col_per = col_pers[i];
c->grid_row_per = 1.0f;
// 根据分配的权重动态计算当前窗口的宽度
cw = avail_w * (col_pers[i] / sum_col);
if (i == 0) {
c->geom.x = m->w.x + target_gappo;
} else if (i == 1) {
// 第二个窗口的 X 坐标紧跟第一个窗口后面
float cw0 = avail_w * (col_pers[0] / sum_col);
c->geom.x = m->w.x + target_gappo + cw0 + target_gappi;
}
c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
c->geom.width = cw;
c->geom.height = ch;
resize(c, c->geom, 0);
i++;
}
}
return;
}
// 计算列数和行数
for (cols = 0; cols <= n / 2; cols++) {
if (cols * cols >= n)
break;
}
rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols;
overcols = n % cols;
float col_pers[cols];
float row_pers[rows];
for (i = 0; i < cols; i++)
col_pers[i] = 1.0f;
for (i = 0; i < rows; i++)
row_pers[i] = 1.0f;
// 提取首个窗口比例
i = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
int32_t c_idx = i % cols;
int32_t r_idx = i / cols;
if (r_idx == 0)
col_pers[c_idx] =
(c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
if (c_idx == 0)
row_pers[r_idx] =
(c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f;
i++;
}
}
float sum_col = 0.0f, sum_row = 0.0f;
for (i = 0; i < cols; i++)
sum_col += col_pers[i];
for (i = 0; i < rows; i++)
sum_row += row_pers[i];
float avail_w = m->w.width - 2 * target_gappo - (cols - 1) * target_gappi;
float avail_h = m->w.height - 2 * target_gappo - (rows - 1) * target_gappi;
// 分配位置与尺寸
i = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
int32_t c_idx = i % cols;
int32_t r_idx = i / cols;
// 矫正属性及标记索引
c->grid_col_per = col_pers[c_idx];
c->grid_row_per = row_pers[r_idx];
c->grid_col_idx = c_idx;
c->grid_row_idx = r_idx;
// X 坐标及宽度计算
float fl_cx = m->w.x + target_gappo;
float fl_cw = 0.0f;
if (overcols && i >= n - overcols) {
float over_w = 0.0f;
for (int j = 0; j < overcols; j++)
over_w += avail_w * (col_pers[j] / sum_col);
over_w += (overcols - 1) * target_gappi;
float dx = (m->w.width - over_w) / 2.0f - target_gappo;
fl_cx += dx;
for (int j = 0; j < c_idx; j++)
fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi;
fl_cw = avail_w * (col_pers[c_idx] / sum_col);
} else {
for (int j = 0; j < c_idx; j++)
fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi;
fl_cw = (c_idx == cols - 1)
? (m->w.x + m->w.width - target_gappo - fl_cx)
: avail_w * (col_pers[c_idx] / sum_col);
}
// Y 坐标及高度计算
float fl_cy = m->w.y + target_gappo;
for (int j = 0; j < r_idx; j++)
fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi;
float fl_ch = (r_idx == rows - 1)
? (m->w.y + m->w.height - target_gappo - fl_cy)
: avail_h * (row_pers[r_idx] / sum_row);
c->geom.x = (int32_t)fl_cx;
c->geom.y = (int32_t)fl_cy;
c->geom.width = (int32_t)fl_cw;
c->geom.height = (int32_t)fl_ch;
resize(c, c->geom, 0);
i++;
}
}
}
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;
if (config.smartgaps && n == 1) {
cur_gappiv = cur_gappih = cur_gappov = cur_gappoh = 0;
}
// 计算网格行列数
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);
// 将有效客户端存入数组
Client *arr[n];
int32_t arr_idx = 0;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
arr[arr_idx++] = c;
if (arr_idx >= n)
break; // 安全边界
}
}
// 初始化比例数组
float col_pers[cols];
float row_pers[max_rows];
for (i = 0; i < cols; i++)
col_pers[i] = 0.0f;
for (i = 0; i < max_rows; i++)
row_pers[i] = 0.0f;
// 直接基于数组进行两遍比例锁定
for (i = 0; i < n; i++) {
c = arr[i];
int32_t col_idx =
(i < first_group_count)
? (i / base_rows)
: (first_group_cols + (i - first_group_count) / max_rows);
int32_t row_idx = (i < first_group_count)
? (i % base_rows)
: ((i - first_group_count) % max_rows);
if (c->grid_col_idx == col_idx && c->grid_col_per > 0.0f)
col_pers[col_idx] = c->grid_col_per;
if (c->grid_row_idx == row_idx && c->grid_row_per > 0.0f)
row_pers[row_idx] = c->grid_row_per;
}
for (i = 0; i < n; i++) {
c = arr[i];
int32_t col_idx =
(i < first_group_count)
? (i / base_rows)
: (first_group_cols + (i - first_group_count) / max_rows);
int32_t row_idx = (i < first_group_count)
? (i % base_rows)
: ((i - first_group_count) % max_rows);
if (col_pers[col_idx] == 0.0f && c->grid_col_per > 0.0f)
col_pers[col_idx] = c->grid_col_per;
if (row_pers[row_idx] == 0.0f && c->grid_row_per > 0.0f)
row_pers[row_idx] = c->grid_row_per;
}
// 兜底策略与总权重计算
float sum_col = 0.0f;
for (i = 0; i < cols; i++) {
if (col_pers[i] == 0.0f)
col_pers[i] = 1.0f;
sum_col += col_pers[i];
}
for (i = 0; i < max_rows; i++) {
if (row_pers[i] == 0.0f)
row_pers[i] = 1.0f;
}
// 预计算所有列的 X 坐标和宽度
float col_x[cols], col_w[cols];
float avail_w = m->w.width - 2 * cur_gappoh - (cols - 1) * cur_gappih;
float next_x = m->w.x + cur_gappoh;
for (i = 0; i < cols; i++) {
col_x[i] = next_x;
col_w[i] = (i == cols - 1) ? (m->w.x + m->w.width - cur_gappoh - next_x)
: (avail_w * (col_pers[i] / sum_col));
next_x += col_w[i] + cur_gappih;
}
// 预计算两组不同的行几何参数(解决不同列行数不一致的问题)
float row_y_base[base_rows], row_h_base[base_rows];
float sum_row_base = 0.0f;
for (i = 0; i < base_rows; i++)
sum_row_base += row_pers[i];
float avail_h_base =
m->w.height - 2 * cur_gappov - (base_rows - 1) * cur_gappiv;
float next_y = m->w.y + cur_gappov;
for (i = 0; i < base_rows; i++) {
row_y_base[i] = next_y;
row_h_base[i] = (i == base_rows - 1)
? (m->w.y + m->w.height - cur_gappov - next_y)
: (avail_h_base * (row_pers[i] / sum_row_base));
next_y += row_h_base[i] + cur_gappiv;
}
float row_y_max[max_rows], row_h_max[max_rows];
if (remainder > 0) {
float sum_row_max = 0.0f;
for (i = 0; i < max_rows; i++)
sum_row_max += row_pers[i];
float avail_h_max =
m->w.height - 2 * cur_gappov - (max_rows - 1) * cur_gappiv;
next_y = m->w.y + cur_gappov;
for (i = 0; i < max_rows; i++) {
row_y_max[i] = next_y;
row_h_max[i] = (i == max_rows - 1)
? (m->w.y + m->w.height - cur_gappov - next_y)
: (avail_h_max * (row_pers[i] / sum_row_max));
next_y += row_h_max[i] + cur_gappiv;
}
}
// 最终渲染布局
for (i = 0; i < n; i++) {
c = arr[i];
int32_t col_idx, row_idx;
float fl_cx, fl_cy, fl_cw, fl_ch;
if (i < first_group_count) {
col_idx = i / base_rows;
row_idx = i % base_rows;
fl_cy = row_y_base[row_idx];
fl_ch = row_h_base[row_idx];
} else {
int32_t offset = i - first_group_count;
col_idx = first_group_cols + (offset / max_rows);
row_idx = offset % max_rows;
fl_cy = row_y_max[row_idx];
fl_ch = row_h_max[row_idx];
}
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;
fl_cx = col_x[col_idx];
fl_cw = col_w[col_idx];
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);
}
}