maomaowm/src/layout/arrange.h
DreamMaoMao 58d989d218
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run
feat: fair layout
2026-05-16 08:34:51 +08:00

1006 lines
29 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 save_old_size_per(Monitor *m) {
Client *c = NULL;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
}
}
}
void restore_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
if (!m || !c)
return;
if (!m->wlr_output->enabled)
return;
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc)) {
fc->old_ismaster = fc->ismaster;
}
}
c->old_master_inner_per = c->master_inner_per;
c->old_stack_inner_per = c->stack_inner_per;
pre_caculate_before_arrange(m, false, false, true);
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
if (current_layout->id == SCROLLER ||
current_layout->id == VERTICAL_SCROLLER || current_layout->id == GRID ||
current_layout->id == FAIR || current_layout->id == VERTICAL_FAIR ||
current_layout->id == VERTICAL_GRID || current_layout->id == DECK ||
current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) {
return;
}
if (current_layout->id == CENTER_TILE) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
// it is possible that the current floating window is moved to another tag,
// but the tag has not executed save_old_size_per
// so it must be judged whether their old size values are initial values
if (!c->ismaster && c->old_stack_inner_per < 1.0 &&
c->old_stack_inner_per > 0.0f && c->stack_inner_per < 1.0 &&
c->stack_inner_per > 0.0f) {
c->stack_inner_per = (1.0 - c->stack_inner_per) *
c->old_stack_inner_per /
(1.0 - c->old_stack_inner_per);
}
if (c->ismaster && c->old_master_inner_per < 1.0 &&
c->old_master_inner_per > 0.0f && c->master_inner_per < 1.0 &&
c->master_inner_per > 0.0f) {
c->master_inner_per = (1.0 - c->master_inner_per) *
c->old_master_inner_per /
(1.0 - c->old_master_inner_per);
}
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c && !fc->ismaster &&
fc->old_ismaster && fc->old_stack_inner_per < 1.0 &&
fc->old_stack_inner_per > 0.0f && fc->stack_inner_per < 1.0 &&
fc->stack_inner_per > 0.0f) {
fc->stack_inner_per = (1.0 - fc->stack_inner_per) *
fc->old_stack_inner_per /
(1.0 - fc->old_stack_inner_per);
fc->old_ismaster = false;
}
}
}
void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
bool found = false;
if (!m || !c)
return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
if (current_layout->id == CENTER_TILE &&
(fc->isleftstack ^ c->isleftstack))
continue;
c->master_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_inner_per;
found = true;
break;
}
}
if (!found) {
c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag];
c->master_inner_per = 1.0f;
c->stack_inner_per = 1.0f;
}
}
void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time,
int32_t type) {
Client *tc = NULL;
float delta_x, delta_y;
Client *next = NULL;
Client *prev = NULL;
Client *nextnext = NULL;
Client *prevprev = NULL;
struct wl_list *node;
bool begin_find_nextnext = false;
bool begin_find_prevprev = false;
/* 寻找 next / nextnext */
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
if (begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
nextnext = tc;
break;
}
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
begin_find_nextnext = true;
continue;
}
}
/* 寻找 prev / prevprev */
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
if (begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prevprev = tc;
break;
}
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
begin_find_prevprev = true;
continue;
}
}
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
grabc->drag_begin_geom = grabc->geom;
} else {
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
grabc->cursor_in_upper_half = true;
grabc->cursor_in_left_half = false;
}
if (grabc->ismaster) {
delta_x = (float)(offsetx) * (grabc->old_master_mfact_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_master_inner_per) /
grabc->drag_begin_geom.height;
} else {
delta_x = (float)(offsetx) * (1 - grabc->old_master_mfact_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_inner_per) /
grabc->drag_begin_geom.height;
}
bool moving_up, moving_down;
if (!isdrag) {
moving_up = offsety < 0;
moving_down = offsety > 0;
} else {
moving_up = cursor->y < drag_begin_cursory;
moving_down = cursor->y > drag_begin_cursory;
}
if (grabc->ismaster && !prev) {
if (moving_up)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_up)
delta_y = fabsf(delta_y);
else
delta_y = -fabsf(delta_y);
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_up)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
} else if (!grabc->ismaster && !next) {
if (moving_up)
delta_y = fabsf(delta_y);
else
delta_y = -fabsf(delta_y);
} else if (type == CENTER_TILE && !grabc->ismaster && !nextnext) {
if (moving_up)
delta_y = fabsf(delta_y);
else
delta_y = -fabsf(delta_y);
} else if (type == CENTER_TILE && !grabc->ismaster && prevprev &&
prevprev->ismaster) {
if (moving_up)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
} else if ((grabc->cursor_in_upper_half && moving_up) ||
(!grabc->cursor_in_upper_half && moving_down)) {
delta_y = fabsf(delta_y) * 2;
} else {
delta_y = -fabsf(delta_y) * 2;
}
if (!grabc->ismaster && grabc->isleftstack && type == CENTER_TILE)
delta_x = delta_x * -1.0f;
if (grabc->ismaster && type == CENTER_TILE &&
grabc->cursor_in_left_half)
delta_x = delta_x * -1.0f;
if (grabc->ismaster && type == CENTER_TILE)
delta_x = delta_x * 2;
if (type == RIGHT_TILE)
delta_x = delta_x * -1.0f;
float new_master_mfact_per = grabc->old_master_mfact_per + delta_x;
float new_master_inner_per = grabc->old_master_inner_per + delta_y;
float new_stack_inner_per = grabc->old_stack_inner_per + delta_y;
new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per));
new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per));
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
// 实时缩放同组其他窗口的比例,保持组内总和为 1,
// 不然增加的比例并不是排布后的比例
if (isdrag) {
if (grabc->ismaster) {
/* 主窗口组:调整所有主窗口的 master_inner_per */
float cur_other_sum = 1.0f - grabc->master_inner_per;
float new_other_sum = 1.0f - new_master_inner_per;
if (cur_other_sum > 0.001f) {
float scale = new_other_sum / cur_other_sum;
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc) &&
tc->ismaster && tc != grabc)
tc->master_inner_per *= scale;
}
}
} else {
/* 栈窗口组:根据布局类型分开处理 */
if (type == CENTER_TILE) {
/* 仅缩放同侧栈窗口的 stack_inner_per */
float cur_other_sum = 1.0f - grabc->stack_inner_per;
float new_other_sum = 1.0f - new_stack_inner_per;
if (cur_other_sum > 0.001f) {
float scale = new_other_sum / cur_other_sum;
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc) &&
!tc->ismaster && tc != grabc &&
tc->isleftstack == grabc->isleftstack)
tc->stack_inner_per *= scale;
}
}
} else {
/* TILE / RIGHT_TILE / DECK所有栈窗口共用一个比例组 */
float cur_other_sum = 1.0f - grabc->stack_inner_per;
float new_other_sum = 1.0f - new_stack_inner_per;
if (cur_other_sum > 0.001f) {
float scale = new_other_sum / cur_other_sum;
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc) &&
!tc->ismaster && tc != grabc)
tc->stack_inner_per *= scale;
}
}
}
}
} else {
/* 键盘步进 */
wl_list_for_each(tc, &clients, link) {
if (!VISIBLEON(tc, grabc->mon) || !ISTILED(tc))
continue;
if (tc != grabc) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f &&
(type != CENTER_TILE ||
!(grabc->isleftstack ^ tc->isleftstack)))
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
}
}
/* 将新比例应用到抓取窗口本身 */
grabc->master_inner_per = new_master_inner_per;
grabc->stack_inner_per = new_stack_inner_per;
/* 广播 master_mfact_per 到所有平铺窗口 */
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc))
tc->master_mfact_per = new_master_mfact_per;
}
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(grabc->mon, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, int32_t type) {
Client *tc = NULL;
float delta_x, delta_y;
Client *next = NULL;
Client *prev = NULL;
struct wl_list *node;
/* 寻找 next */
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
next = tc;
break;
}
}
/* 寻找 prev */
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
prev = tc;
break;
}
}
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->cursor_in_upper_half =
cursor->y < grabc->geom.y + grabc->geom.height / 2;
grabc->cursor_in_left_half =
cursor->x < grabc->geom.x + grabc->geom.width / 2;
grabc->drag_begin_geom = grabc->geom;
} else {
if (isdrag) {
offsetx = cursor->x - drag_begin_cursorx;
offsety = cursor->y - drag_begin_cursory;
} else {
grabc->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
grabc->cursor_in_upper_half = true;
grabc->cursor_in_left_half = false;
}
if (grabc->ismaster) {
delta_x = (float)(offsetx) * (grabc->old_master_inner_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_master_mfact_per) /
grabc->drag_begin_geom.height;
} else {
delta_x = (float)(offsetx) * (grabc->old_stack_inner_per) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (1 - grabc->old_master_mfact_per) /
grabc->drag_begin_geom.height;
}
bool moving_left, moving_right;
if (!isdrag) {
moving_left = offsetx < 0;
moving_right = offsetx > 0;
} else {
moving_left = cursor->x < drag_begin_cursorx;
moving_right = cursor->x > drag_begin_cursorx;
}
if (grabc->ismaster && !prev) {
if (moving_left)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_left)
delta_x = fabsf(delta_x);
else
delta_x = -fabsf(delta_x);
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_left)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
} else if (!grabc->ismaster && !next) {
if (moving_left)
delta_x = fabsf(delta_x);
else
delta_x = -fabsf(delta_x);
} else if ((grabc->cursor_in_left_half && moving_left) ||
(!grabc->cursor_in_left_half && moving_right)) {
delta_x = fabsf(delta_x) * 2;
} else {
delta_x = -fabsf(delta_x) * 2;
}
float new_master_mfact_per = grabc->old_master_mfact_per + delta_y;
float new_master_inner_per = grabc->old_master_inner_per + delta_x;
float new_stack_inner_per = grabc->old_stack_inner_per + delta_x;
new_master_mfact_per = fmaxf(0.1f, fminf(0.9f, new_master_mfact_per));
new_master_inner_per = fmaxf(0.1f, fminf(0.9f, new_master_inner_per));
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
// 实时缩放同组其他窗口的比例,保持组内总和为 1,
// 不然增加的比例并不是排布后的比例
if (isdrag) {
if (grabc->ismaster) {
float cur_other_sum = 1.0f - grabc->master_inner_per;
float new_other_sum = 1.0f - new_master_inner_per;
if (cur_other_sum > 0.001f) {
float scale = new_other_sum / cur_other_sum;
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc) &&
tc->ismaster && tc != grabc)
tc->master_inner_per *= scale;
}
}
} else {
/* 所有栈窗口(垂直布局没有左侧/右侧区分) */
float cur_other_sum = 1.0f - grabc->stack_inner_per;
float new_other_sum = 1.0f - new_stack_inner_per;
if (cur_other_sum > 0.001f) {
float scale = new_other_sum / cur_other_sum;
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc) &&
!tc->ismaster && tc != grabc)
tc->stack_inner_per *= scale;
}
}
}
} else {
/* 键盘步进 */
wl_list_for_each(tc, &clients, link) {
if (!VISIBLEON(tc, grabc->mon) || !ISTILED(tc))
continue;
if (tc != grabc) {
if (!tc->ismaster && new_stack_inner_per != 1.0f &&
grabc->old_stack_inner_per != 1.0f)
tc->stack_inner_per = (1 - new_stack_inner_per) /
(1 - grabc->old_stack_inner_per) *
tc->stack_inner_per;
if (tc->ismaster && new_master_inner_per != 1.0f &&
grabc->old_master_inner_per != 1.0f)
tc->master_inner_per =
(1.0f - new_master_inner_per) /
(1.0f - grabc->old_master_inner_per) *
tc->master_inner_per;
}
}
}
grabc->master_inner_per = new_master_inner_per;
grabc->stack_inner_per = new_stack_inner_per;
/* 广播 master_mfact_per */
wl_list_for_each(tc, &clients, link) {
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc))
tc->master_mfact_per = new_master_mfact_per;
}
if (!isdrag) {
arrange(grabc->mon, false, false);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
arrange(grabc->mon, false, false);
last_apply_drap_time = time;
}
}
}
void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
if (!isdrag) {
dwindle_resize_client_step(grabc->mon, grabc, offsetx, offsety);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
dwindle_resize_client(grabc->mon, grabc);
last_apply_drap_time = time;
}
}
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
if (grabc->mon->isoverview)
return;
Monitor *m = grabc->mon;
uint32_t tag = m->pertag->curtag;
struct TagScrollerState *st = m->pertag->scroller_state[tag];
if (!st)
return;
struct ScrollerStackNode *curnode = find_scroller_node(st, grabc);
if (!curnode)
return;
struct ScrollerStackNode *headnode = curnode;
while (headnode->prev_in_stack)
headnode = headnode->prev_in_stack;
Client *stack_head_client = headnode->client;
if (m->visible_tiling_clients == 1 &&
!config.scroller_ignore_proportion_single)
return;
float delta_x, delta_y;
float new_scroller_proportion;
float new_stack_proportion;
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
headnode->client->old_scroller_pproportion =
headnode->scroller_proportion;
grabc->old_stack_proportion = curnode->stack_proportion;
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->old_master_mfact_per = grabc->master_mfact_per;
grabc->old_master_inner_per = grabc->master_inner_per;
grabc->old_stack_inner_per = grabc->stack_inner_per;
grabc->drag_begin_geom = grabc->geom;
stack_head_client->old_scroller_pproportion =
headnode->scroller_proportion;
grabc->old_stack_proportion = curnode->stack_proportion;
grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false;
}
if (isvertical) {
delta_y = (float)(offsety) *
(headnode->client->old_scroller_pproportion) /
grabc->drag_begin_geom.height;
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.width;
} else {
delta_x = (float)(offsetx) *
(headnode->client->old_scroller_pproportion) /
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.height;
}
bool moving_up, moving_down, moving_left, moving_right;
if (!isdrag) {
moving_up = offsety < 0;
moving_down = offsety > 0;
moving_left = offsetx < 0;
moving_right = offsetx > 0;
} else {
moving_up = cursor->y < drag_begin_cursory;
moving_down = cursor->y > drag_begin_cursory;
moving_left = cursor->x < drag_begin_cursorx;
moving_right = cursor->x > drag_begin_cursorx;
}
if ((grabc->cursor_in_upper_half && moving_up) ||
(!grabc->cursor_in_upper_half && moving_down)) {
delta_y = fabsf(delta_y);
} else {
delta_y = -fabsf(delta_y);
}
if ((grabc->cursor_in_left_half && moving_left) ||
(!grabc->cursor_in_left_half && moving_right)) {
delta_x = fabsf(delta_x);
} else {
delta_x = -fabsf(delta_x);
}
if (isvertical) {
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
delta_x = delta_x * -1.0f;
}
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
if (moving_right)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
}
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
if (moving_left)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
}
if (isdrag) {
if (moving_up)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
}
} else {
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
delta_y = delta_y * -1.0f;
}
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
if (moving_down)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
}
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
if (moving_up)
delta_y = -fabsf(delta_y);
else
delta_y = fabsf(delta_y);
}
if (isdrag) {
if (moving_left)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
}
}
if (isvertical) {
new_scroller_proportion =
headnode->client->old_scroller_pproportion + delta_y;
new_stack_proportion = grabc->old_stack_proportion + delta_x;
} else {
new_scroller_proportion =
headnode->client->old_scroller_pproportion + delta_x;
new_stack_proportion = grabc->old_stack_proportion + delta_y;
}
new_scroller_proportion =
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
// 保持总和为 1避免后续 arrange 归一化吞掉位移
if (isdrag) {
float current_other_sum = 1.0f - curnode->stack_proportion;
float new_other_sum = 1.0f - new_stack_proportion;
if (current_other_sum > 0.001f) {
float scale = new_other_sum / current_other_sum;
for (struct ScrollerStackNode *tc = headnode; tc;
tc = tc->next_in_stack) {
if (tc != curnode) {
tc->stack_proportion *= scale;
}
}
}
} else {
// 键盘步进
if (grabc->old_stack_proportion != 1.0f) {
for (struct ScrollerStackNode *tc = headnode; tc;
tc = tc->next_in_stack) {
if (tc != curnode) {
tc->stack_proportion =
(1.0f - new_stack_proportion) /
(1.0f - grabc->old_stack_proportion) *
tc->stack_proportion;
}
}
}
}
curnode->stack_proportion = new_stack_proportion;
headnode->scroller_proportion = new_scroller_proportion;
/* 同步回全局字段 */
sync_scroller_state_to_clients(m, tag);
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;
}
}
}
void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return;
if (grabc->mon->isoverview)
return;
const Layout *current_layout =
grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag];
if (current_layout->id == TILE || current_layout->id == DECK ||
current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE
) {
resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time,
current_layout->id);
} else if (current_layout->id == VERTICAL_TILE ||
current_layout->id == VERTICAL_DECK) {
resize_tile_master_vertical(grabc, isdrag, offsetx, offsety, time,
current_layout->id);
} else if (current_layout->id == SCROLLER) {
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, false);
} else if (current_layout->id == VERTICAL_SCROLLER) {
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true);
} else if (current_layout->id == DWINDLE) {
resize_tile_dwindle(grabc, isdrag, offsetx, offsety, time, true);
}
}
/* If there are no calculation omissions,
these two functions will never be triggered.
Just in case to facilitate the final investigation*/
void check_size_per_valid(Client *c) {
if (c->ismaster) {
assert(c->master_inner_per > 0.0f && c->master_inner_per <= 1.0f);
} else {
assert(c->stack_inner_per > 0.0f && c->stack_inner_per <= 1.0f);
}
}
void reset_size_per_mon(Monitor *m, int32_t tile_cilent_num,
double total_left_stack_hight_percent,
double total_right_stack_hight_percent,
double total_stack_hight_percent,
double total_master_inner_percent, int32_t master_num,
int32_t stack_num) {
Client *c = NULL;
int32_t i = 0;
uint32_t stack_index = 0;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
if (m->pertag->ltidxs[m->pertag->curtag]->id != CENTER_TILE) {
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true;
c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f;
c->master_inner_per =
c->master_inner_per / total_master_inner_percent;
} else {
c->ismaster = false;
c->master_inner_per =
master_num > 0 ? 1.0f / master_num : 1.0f;
c->stack_inner_per =
total_stack_hight_percent
? c->stack_inner_per / total_stack_hight_percent
: 1.0f;
}
i++;
check_size_per_valid(c);
}
}
} else {
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) {
c->ismaster = true;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per =
stack_num > 1 ? 1.0f / ((stack_num - 1) / 2.0f)
: 1.0f;
} else {
c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f;
}
c->master_inner_per =
c->master_inner_per / total_master_inner_percent;
} else {
stack_index = i - nmasters;
c->ismaster = false;
c->master_inner_per =
master_num > 0 ? 1.0f / master_num : 1.0f;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
c->stack_inner_per =
total_right_stack_hight_percent
? c->stack_inner_per /
total_right_stack_hight_percent
: 1.0f;
} else {
c->stack_inner_per =
total_left_stack_hight_percent
? c->stack_inner_per /
total_left_stack_hight_percent
: 1.0f;
}
}
i++;
check_size_per_valid(c);
}
}
}
}
void pre_caculate_before_arrange(Monitor *m, bool want_animation,
bool from_view, bool only_caculate) {
Client *c = NULL;
double total_stack_inner_percent = 0;
double total_master_inner_percent = 0;
double total_right_stack_hight_percent = 0;
double total_left_stack_hight_percent = 0;
int32_t i = 0;
int32_t nmasters = 0;
int32_t stack_index = 0;
int32_t master_num = 0;
int32_t stack_num = 0;
m->visible_clients = 0;
m->visible_tiling_clients = 0;
m->visible_scroll_tiling_clients = 0;
uint32_t tag = m->pertag->curtag;
struct TagScrollerState *st = m->pertag->scroller_state[tag];
const Layout *cur_layout = m->pertag->ltidxs[m->pertag->curtag];
if (cur_layout->id == SCROLLER || cur_layout->id == VERTICAL_SCROLLER) {
update_scroller_state(m);
}
wl_list_for_each(c, &clients, link) {
if (from_view && (c->isglobal || c->isunglobal)) {
set_size_per(m, c);
}
if (c->mon == m && (c->isglobal || c->isunglobal)) {
c->tags = m->tagset[m->seltags];
}
if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) {
focusclient(c, 1);
}
if (VISIBLEON(c, m)) {
if (from_view && !client_only_in_one_tag(c)) {
set_size_per(m, c);
}
if (!c->isunglobal)
m->visible_clients++;
if (ISTILED(c)) {
m->visible_tiling_clients++;
/* 更新可见滚动客户端计数 */
if (st) {
struct ScrollerStackNode *n = find_scroller_node(st, c);
if (n && !n->prev_in_stack) /* 是堆叠头部 */
m->visible_scroll_tiling_clients++;
} else if (ISSCROLLTILED(c)) {
m->visible_scroll_tiling_clients++;
}
}
}
}
nmasters = m->pertag->nmasters[m->pertag->curtag];
wl_list_for_each(c, &clients, link) {
if (c->iskilling)
continue;
if (c->mon == m) {
if (VISIBLEON(c, m)) {
if (ISTILED(c)) {
if (i < nmasters) {
master_num++;
total_master_inner_percent += c->master_inner_per;
} else {
stack_num++;
total_stack_inner_percent += c->stack_inner_per;
stack_index = i - nmasters;
if ((stack_index % 2) ^
(m->visible_tiling_clients % 2 == 0)) {
c->isleftstack = false;
total_right_stack_hight_percent +=
c->stack_inner_per;
} else {
c->isleftstack = true;
total_left_stack_hight_percent +=
c->stack_inner_per;
}
}
i++;
}
if (!only_caculate)
set_arrange_visible(m, c, want_animation);
} else {
if (!only_caculate)
set_arrange_hidden(m, c, want_animation);
}
}
if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c);
}
}
reset_size_per_mon(
m, m->visible_tiling_clients, total_left_stack_hight_percent,
total_right_stack_hight_percent, total_stack_inner_percent,
total_master_inner_percent, master_num, stack_num);
}
void // 17
arrange(Monitor *m, bool want_animation, bool from_view) {
if (!m)
return;
if (!m->wlr_output->enabled)
return;
pre_caculate_before_arrange(m, want_animation, from_view, false);
if (m->isoverview) {
overviewlayout.arrange(m);
} else {
m->pertag->ltidxs[m->pertag->curtag]->arrange(m);
}
if (!start_drag_window) {
motionnotify(0, NULL, 0, 0, 0, 0);
checkidleinhibitor(NULL);
}
printstatus();
}