maomaowm/src/layout/arrange.h

1007 lines
29 KiB
C
Raw Normal View History

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 ||
2026-05-16 08:23:48 +08:00
current_layout->id == FAIR || current_layout->id == VERTICAL_FAIR ||
current_layout->id == VERTICAL_GRID || current_layout->id == DECK ||
2026-02-14 08:35:30 +08:00
current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) {
return;
}
if (current_layout->id == CENTER_TILE) {
2026-02-14 08:35:30 +08:00
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
2026-03-07 13:24:46 +08:00
// 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;
}
}
}
2025-10-09 13:09:40 +08:00
void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
bool found = false;
if (!m || !c)
return;
2026-02-14 08:35:30 +08:00
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
2025-10-09 13:09:40 +08:00
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
2026-02-14 08:35:30 +08:00
if (current_layout->id == CENTER_TILE &&
2026-03-07 23:14:17 +08:00
(fc->isleftstack ^ c->isleftstack))
2026-02-14 08:35:30 +08:00
continue;
2025-10-09 13:09:40 +08:00
c->master_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per;
2025-12-11 15:44:08 +08:00
c->stack_inner_per = fc->stack_inner_per;
2025-10-09 13:09:40 +08:00
found = true;
break;
}
}
if (!found) {
c->master_mfact_per = m->pertag->mfacts[m->pertag->curtag];
2025-10-09 13:09:40 +08:00
c->master_inner_per = 1.0f;
2025-12-11 15:44:08 +08:00
c->stack_inner_per = 1.0f;
2025-10-09 13:09:40 +08:00
}
}
void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time,
int32_t type) {
2025-10-09 13:09:40 +08:00
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 */
2025-10-09 13:09:40 +08:00
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;
}
2026-03-05 23:03:01 +08:00
if (!begin_find_nextnext && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
2025-10-09 13:09:40 +08:00
next = tc;
begin_find_nextnext = true;
continue;
}
}
/* 寻找 prev / prevprev */
2025-10-09 13:09:40 +08:00
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;
}
2026-03-05 23:03:01 +08:00
if (!begin_find_prevprev && VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
grabc->old_stack_inner_per = grabc->stack_inner_per;
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
grabc->old_stack_inner_per = grabc->stack_inner_per;
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
delta_y = (float)(offsety) * (grabc->old_stack_inner_per) /
2025-10-09 13:09:40 +08:00
grabc->drag_begin_geom.height;
}
bool moving_up, moving_down;
2025-10-09 13:09:40 +08:00
if (!isdrag) {
moving_up = offsety < 0;
moving_down = offsety > 0;
2025-10-09 13:09:40 +08:00
} else {
moving_up = cursor->y < drag_begin_cursory;
moving_down = cursor->y > drag_begin_cursory;
}
if (grabc->ismaster && !prev) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
delta_y = fabsf(delta_y);
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
delta_y = fabsf(delta_y);
} else if (!grabc->ismaster && !next) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
} else if (type == CENTER_TILE && !grabc->ismaster && !nextnext) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
} else if (type == CENTER_TILE && !grabc->ismaster && prevprev &&
prevprev->ismaster) {
if (moving_up)
2025-10-09 13:09:40 +08:00
delta_y = -fabsf(delta_y);
else
2025-10-09 13:09:40 +08:00
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;
2025-10-09 13:09:40 +08:00
} else {
delta_y = -fabsf(delta_y) * 2;
2025-10-09 13:09:40 +08:00
}
if (!grabc->ismaster && grabc->isleftstack && type == CENTER_TILE)
2025-10-09 13:09:40 +08:00
delta_x = delta_x * -1.0f;
if (grabc->ismaster && type == CENTER_TILE &&
grabc->cursor_in_left_half)
2025-10-09 13:09:40 +08:00
delta_x = delta_x * -1.0f;
if (grabc->ismaster && type == CENTER_TILE)
2025-10-09 13:09:40 +08:00
delta_x = delta_x * 2;
if (type == RIGHT_TILE)
2025-10-15 13:22:41 +08:00
delta_x = delta_x * -1.0f;
2025-10-09 13:09:40 +08:00
float new_master_mfact_per = grabc->old_master_mfact_per + delta_x;
float new_master_inner_per = grabc->old_master_inner_per + delta_y;
2025-12-11 15:44:08 +08:00
float new_stack_inner_per = grabc->old_stack_inner_per + delta_y;
2025-10-09 13:09:40 +08:00
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));
2025-12-11 15:44:08 +08:00
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
2025-10-09 13:09:40 +08:00
// 实时缩放同组其他窗口的比例,保持组内总和为 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;
}
2025-10-09 13:09:40 +08:00
}
}
/* 将新比例应用到抓取窗口本身 */
2025-10-09 13:09:40 +08:00
grabc->master_inner_per = new_master_inner_per;
2025-12-11 15:44:08 +08:00
grabc->stack_inner_per = new_stack_inner_per;
2025-10-09 13:09:40 +08:00
/* 广播 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;
}
2025-10-09 13:09:40 +08:00
if (!isdrag) {
2026-01-01 12:26:19 +08:00
arrange(grabc->mon, false, false);
2025-10-09 13:09:40 +08:00
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
2026-01-01 12:26:19 +08:00
arrange(grabc->mon, false, false);
2025-10-09 13:09:40 +08:00
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) {
2025-10-09 13:09:40 +08:00
Client *tc = NULL;
float delta_x, delta_y;
Client *next = NULL;
Client *prev = NULL;
struct wl_list *node;
/* 寻找 next */
2025-10-09 13:09:40 +08:00
for (node = grabc->link.next; node != &clients; node = node->next) {
tc = wl_container_of(node, tc, link);
2026-03-05 23:03:01 +08:00
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
2025-10-09 13:09:40 +08:00
next = tc;
break;
}
}
/* 寻找 prev */
2025-10-09 13:09:40 +08:00
for (node = grabc->link.prev; node != &clients; node = node->prev) {
tc = wl_container_of(node, tc, link);
2026-03-05 23:03:01 +08:00
if (VISIBLEON(tc, grabc->mon) && ISTILED(tc)) {
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
grabc->old_stack_inner_per = grabc->stack_inner_per;
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
grabc->old_stack_inner_per = grabc->stack_inner_per;
2025-10-09 13:09:40 +08:00
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 {
2025-12-11 15:44:08 +08:00
delta_x = (float)(offsetx) * (grabc->old_stack_inner_per) /
2025-10-09 13:09:40 +08:00
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;
2025-10-09 13:09:40 +08:00
if (!isdrag) {
moving_left = offsetx < 0;
moving_right = offsetx > 0;
2025-10-09 13:09:40 +08:00
} 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);
2025-10-09 13:09:40 +08:00
} else if (grabc->ismaster && next && !next->ismaster) {
if (moving_left)
delta_x = fabsf(delta_x);
else
delta_x = -fabsf(delta_x);
2025-10-09 13:09:40 +08:00
} else if (!grabc->ismaster && prev && prev->ismaster) {
if (moving_left)
delta_x = -fabsf(delta_x);
else
delta_x = fabsf(delta_x);
2025-10-09 13:09:40 +08:00
} else if (!grabc->ismaster && !next) {
if (moving_left)
delta_x = fabsf(delta_x);
else
delta_x = -fabsf(delta_x);
2025-10-09 13:09:40 +08:00
} else if ((grabc->cursor_in_left_half && moving_left) ||
(!grabc->cursor_in_left_half && moving_right)) {
delta_x = fabsf(delta_x) * 2;
2025-10-09 13:09:40 +08:00
} else {
delta_x = -fabsf(delta_x) * 2;
2025-10-09 13:09:40 +08:00
}
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;
2025-10-09 13:09:40 +08:00
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));
2025-12-11 15:44:08 +08:00
new_stack_inner_per = fmaxf(0.1f, fminf(0.9f, new_stack_inner_per));
2025-10-09 13:09:40 +08:00
// 实时缩放同组其他窗口的比例,保持组内总和为 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;
}
2025-10-09 13:09:40 +08:00
}
}
grabc->master_inner_per = new_master_inner_per;
2025-12-11 15:44:08 +08:00
grabc->stack_inner_per = new_stack_inner_per;
2025-10-09 13:09:40 +08:00
/* 广播 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;
}
2025-10-09 13:09:40 +08:00
if (!isdrag) {
2026-01-01 12:26:19 +08:00
arrange(grabc->mon, false, false);
2025-10-09 13:09:40 +08:00
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
2026-01-01 12:26:19 +08:00
arrange(grabc->mon, false, false);
2025-10-09 13:09:40 +08:00
last_apply_drap_time = time;
}
}
}
2026-05-09 11:55:59 +08:00
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) {
2026-05-10 19:07:10 +08:00
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;
2025-10-09 13:09:40 +08:00
float delta_x, delta_y;
float new_scroller_proportion;
2026-01-16 18:49:35 +01:00
float new_stack_proportion;
2025-10-09 13:09:40 +08:00
if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
start_drag_window = true;
2026-05-10 19:07:10 +08:00
headnode->client->old_scroller_pproportion =
headnode->scroller_proportion;
grabc->old_stack_proportion = curnode->stack_proportion;
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
grabc->old_stack_inner_per = grabc->stack_inner_per;
2025-10-09 13:09:40 +08:00
grabc->drag_begin_geom = grabc->geom;
2026-05-10 19:07:10 +08:00
stack_head_client->old_scroller_pproportion =
headnode->scroller_proportion;
grabc->old_stack_proportion = curnode->stack_proportion;
2025-10-09 13:09:40 +08:00
grabc->cursor_in_upper_half = false;
grabc->cursor_in_left_half = false;
}
2026-01-16 18:49:35 +01:00
if (isvertical) {
delta_y = (float)(offsety) *
2026-05-10 19:07:10 +08:00
(headnode->client->old_scroller_pproportion) /
2026-01-16 18:49:35 +01:00
grabc->drag_begin_geom.height;
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.width;
} else {
delta_x = (float)(offsetx) *
2026-05-10 19:07:10 +08:00
(headnode->client->old_scroller_pproportion) /
2026-01-16 18:49:35 +01:00
grabc->drag_begin_geom.width;
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
grabc->drag_begin_geom.height;
}
2025-10-09 13:09:40 +08:00
2026-05-10 19:07:10 +08:00
bool moving_up, moving_down, moving_left, moving_right;
2025-10-09 13:09:40 +08:00
if (!isdrag) {
2026-05-10 19:07:10 +08:00
moving_up = offsety < 0;
moving_down = offsety > 0;
moving_left = offsetx < 0;
moving_right = offsetx > 0;
2025-10-09 13:09:40 +08:00
} 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);
}
2026-01-16 18:49:35 +01:00
if (isvertical) {
2026-05-10 19:07:10 +08:00
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
2026-01-16 18:49:35 +01:00
delta_x = delta_x * -1.0f;
}
2026-05-10 19:07:10 +08:00
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
if (moving_right)
2026-01-19 11:50:25 +08:00
delta_x = -fabsf(delta_x);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_x = fabsf(delta_x);
}
2026-05-10 19:07:10 +08:00
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
if (moving_left)
2026-01-19 11:50:25 +08:00
delta_x = -fabsf(delta_x);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_x = fabsf(delta_x);
}
if (isdrag) {
2026-05-10 19:07:10 +08:00
if (moving_up)
2026-01-19 11:50:25 +08:00
delta_y = -fabsf(delta_y);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_y = fabsf(delta_y);
}
2026-01-16 18:49:35 +01:00
} else {
2026-05-10 19:07:10 +08:00
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
2026-01-16 18:49:35 +01:00
delta_y = delta_y * -1.0f;
}
2026-05-10 19:07:10 +08:00
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
if (moving_down)
2026-01-19 11:50:25 +08:00
delta_y = -fabsf(delta_y);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_y = fabsf(delta_y);
}
2026-05-10 19:07:10 +08:00
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
if (moving_up)
2026-01-19 11:50:25 +08:00
delta_y = -fabsf(delta_y);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_y = fabsf(delta_y);
}
if (isdrag) {
2026-05-10 19:07:10 +08:00
if (moving_left)
2026-01-19 11:50:25 +08:00
delta_x = -fabsf(delta_x);
2026-05-10 19:07:10 +08:00
else
2026-01-19 11:50:25 +08:00
delta_x = fabsf(delta_x);
}
2026-01-16 18:49:35 +01:00
}
2025-10-09 13:09:40 +08:00
if (isvertical) {
2026-01-16 18:49:35 +01:00
new_scroller_proportion =
2026-05-10 19:07:10 +08:00
headnode->client->old_scroller_pproportion + delta_y;
2026-01-16 18:49:35 +01:00
new_stack_proportion = grabc->old_stack_proportion + delta_x;
2025-10-09 13:09:40 +08:00
} else {
2026-01-16 18:49:35 +01:00
new_scroller_proportion =
2026-05-10 19:07:10 +08:00
headnode->client->old_scroller_pproportion + delta_x;
2026-01-16 18:49:35 +01:00
new_stack_proportion = grabc->old_stack_proportion + delta_y;
2025-10-09 13:09:40 +08:00
}
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;
}
2026-05-10 19:07:10 +08:00
}
}
}
curnode->stack_proportion = new_stack_proportion;
headnode->scroller_proportion = new_scroller_proportion;
/* 同步回全局字段 */
2026-05-10 19:07:10 +08:00
sync_scroller_state_to_clients(m, tag);
2025-10-09 13:09:40 +08:00
if (!isdrag) {
2026-05-10 19:07:10 +08:00
arrange(m, false, false);
2025-10-09 13:09:40 +08:00
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
2026-05-10 19:07:10 +08:00
arrange(m, false, false);
2025-10-09 13:09:40 +08:00
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;
2025-11-08 22:48:53 +08:00
if (grabc->mon->isoverview)
return;
2025-10-09 13:09:40 +08:00
const Layout *current_layout =
grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag];
if (current_layout->id == TILE || current_layout->id == DECK ||
2026-05-09 22:07:21 +08:00
current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE
2025-10-09 13:09:40 +08:00
) {
resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time,
current_layout->id);
2025-10-15 07:24:40 +08:00
} else if (current_layout->id == VERTICAL_TILE ||
current_layout->id == VERTICAL_DECK) {
2025-10-09 13:09:40 +08:00
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);
2026-05-09 11:55:59 +08:00
} else if (current_layout->id == DWINDLE) {
resize_tile_dwindle(grabc, isdrag, offsetx, offsety, time, true);
2025-10-09 13:09:40 +08:00
}
}
/* 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,
2025-10-09 13:09:40 +08:00
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) {
2025-10-09 13:09:40 +08:00
Client *c = NULL;
int32_t i = 0;
2025-12-02 16:57:24 +08:00
uint32_t stack_index = 0;
uint32_t nmasters = m->pertag->nmasters[m->pertag->curtag];
2025-10-09 13:09:40 +08:00
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) {
2025-10-09 13:09:40 +08:00
c->ismaster = true;
2025-12-11 15:44:08 +08:00
c->stack_inner_per = stack_num ? 1.0f / stack_num : 1.0f;
2025-10-09 13:09:40 +08:00
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;
2025-12-11 15:44:08 +08:00
c->stack_inner_per =
2025-10-09 13:09:40 +08:00
total_stack_hight_percent
2025-12-11 15:44:08 +08:00
? c->stack_inner_per / total_stack_hight_percent
2025-10-09 13:09:40 +08:00
: 1.0f;
}
i++;
check_size_per_valid(c);
2025-10-09 13:09:40 +08:00
}
}
} else {
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c)) {
if (total_master_inner_percent > 0.0 && i < nmasters) {
2025-10-09 13:09:40 +08:00
c->ismaster = true;
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
2025-12-11 15:44:08 +08:00
c->stack_inner_per =
stack_num > 1 ? 1.0f / ((stack_num - 1) / 2.0f)
: 1.0f;
} else {
2025-12-11 15:44:08 +08:00
c->stack_inner_per =
stack_num > 1 ? 2.0f / stack_num : 1.0f;
}
2025-10-09 13:09:40 +08:00
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;
2025-10-09 13:09:40 +08:00
if ((stack_index % 2) ^ (tile_cilent_num % 2 == 0)) {
2025-12-11 15:44:08 +08:00
c->stack_inner_per =
2025-10-09 13:09:40 +08:00
total_right_stack_hight_percent
2025-12-11 15:44:08 +08:00
? c->stack_inner_per /
2025-10-09 13:09:40 +08:00
total_right_stack_hight_percent
: 1.0f;
} else {
2025-12-11 15:44:08 +08:00
c->stack_inner_per =
2025-10-09 13:09:40 +08:00
total_left_stack_hight_percent
2025-12-11 15:44:08 +08:00
? c->stack_inner_per /
2025-10-09 13:09:40 +08:00
total_left_stack_hight_percent
: 1.0f;
}
}
i++;
check_size_per_valid(c);
2025-10-09 13:09:40 +08:00
}
}
}
}
void pre_caculate_before_arrange(Monitor *m, bool want_animation,
bool from_view, bool only_caculate) {
2025-10-09 13:09:40 +08:00
Client *c = NULL;
2025-12-11 15:44:08 +08:00
double total_stack_inner_percent = 0;
2025-10-09 13:09:40 +08:00
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;
2025-10-09 13:09:40 +08:00
m->visible_clients = 0;
m->visible_tiling_clients = 0;
m->visible_scroll_tiling_clients = 0;
2025-10-09 13:09:40 +08:00
2026-05-10 19:07:10 +08:00
uint32_t tag = m->pertag->curtag;
struct TagScrollerState *st = m->pertag->scroller_state[tag];
2026-05-10 19:07:10 +08:00
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) {
2026-01-16 18:49:35 +01:00
2026-01-01 12:26:19 +08:00
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);
}
2025-10-09 13:09:40 +08:00
if (VISIBLEON(c, m)) {
2026-01-01 12:26:19 +08:00
if (from_view && !client_only_in_one_tag(c)) {
set_size_per(m, c);
}
if (!c->isunglobal)
m->visible_clients++;
2025-10-09 13:09:40 +08:00
if (ISTILED(c)) {
m->visible_tiling_clients++;
2026-05-10 19:07:10 +08:00
/* 更新可见滚动客户端计数 */
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++;
}
}
2025-10-09 13:09:40 +08:00
}
}
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) {
2025-10-09 13:09:40 +08:00
master_num++;
total_master_inner_percent += c->master_inner_per;
} else {
stack_num++;
2025-12-11 15:44:08 +08:00
total_stack_inner_percent += c->stack_inner_per;
2025-10-09 13:09:40 +08:00
stack_index = i - nmasters;
if ((stack_index % 2) ^
(m->visible_tiling_clients % 2 == 0)) {
c->isleftstack = false;
total_right_stack_hight_percent +=
2025-12-11 15:44:08 +08:00
c->stack_inner_per;
2025-10-09 13:09:40 +08:00
} else {
c->isleftstack = true;
total_left_stack_hight_percent +=
2025-12-11 15:44:08 +08:00
c->stack_inner_per;
2025-10-09 13:09:40 +08:00
}
}
i++;
}
if (!only_caculate)
set_arrange_visible(m, c, want_animation);
2025-10-09 13:09:40 +08:00
} else {
if (!only_caculate)
set_arrange_hidden(m, c, want_animation);
2025-10-09 13:09:40 +08:00
}
}
if (!only_caculate && c->mon == m && c->ismaximizescreen &&
!c->animation.tagouted && !c->animation.tagouting &&
VISIBLEON(c, m)) {
reset_maximizescreen_size(c);
2025-10-09 13:09:40 +08:00
}
}
reset_size_per_mon(
m, m->visible_tiling_clients, total_left_stack_hight_percent,
2025-12-11 15:44:08 +08:00
total_right_stack_hight_percent, total_stack_inner_percent,
2025-10-09 13:09:40 +08:00
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);
2025-10-09 13:09:40 +08:00
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);
}
2025-12-10 22:35:45 +08:00
printstatus();
2025-10-09 13:09:40 +08:00
}