maomaowm/src/layout/overview.h

329 lines
7.4 KiB
C
Raw Normal View History

2026-05-21 10:57:22 +08:00
typedef struct {
float x, y, w, h;
} OvPlacedRect;
typedef struct {
float x, y;
} OvPoint;
typedef struct {
Client *c;
float orig_w;
float orig_h;
float area;
} OvLayoutItem;
static int compare_layout_items(const void *a, const void *b) {
float area_a = ((const OvLayoutItem *)a)->area;
float area_b = ((const OvLayoutItem *)b)->area;
if (area_a < area_b)
return 1;
if (area_a > area_b)
return -1;
return 0;
}
static bool try_place(OvPlacedRect *placed, int placed_cnt, float w, float h,
float gap, float avail_w, float avail_h,
OvPlacedRect *out, OvPoint *cands, OvPoint *feas) {
int cand_cnt = 0;
cands[cand_cnt++] = (OvPoint){0.0f, 0.0f};
for (int i = 0; i < placed_cnt; i++) {
OvPlacedRect p = placed[i];
cands[cand_cnt++] = (OvPoint){p.x + p.w + gap, p.y};
cands[cand_cnt++] = (OvPoint){p.x, p.y + p.h + gap};
cands[cand_cnt++] = (OvPoint){p.x + p.w + gap, p.y + p.h + gap};
}
int unique_cnt = 0;
for (int i = 0; i < cand_cnt; i++) {
bool dup = false;
for (int j = 0; j < unique_cnt; j++) {
if (fabs(cands[i].x - cands[j].x) < 0.5f &&
fabs(cands[i].y - cands[j].y) < 0.5f) {
dup = true;
break;
}
}
if (!dup)
cands[unique_cnt++] = cands[i];
}
cand_cnt = unique_cnt;
int feas_cnt = 0;
for (int i = 0; i < cand_cnt; i++) {
float cx = cands[i].x;
float cy = cands[i].y;
if (cx < 0 || cy < 0 || cx + w > avail_w || cy + h > avail_h)
continue;
bool overlap = false;
for (int j = 0; j < placed_cnt; j++) {
OvPlacedRect p = placed[j];
if (!(cx + w + gap <= p.x || cx >= p.x + p.w + gap ||
cy + h + gap <= p.y || cy >= p.y + p.h + gap)) {
overlap = true;
break;
}
}
if (!overlap) {
feas[feas_cnt++] = (OvPoint){cx, cy};
}
}
if (feas_cnt == 0)
return false;
int best = 0;
for (int i = 1; i < feas_cnt; i++) {
if (feas[i].y < feas[best].y ||
(fabs(feas[i].y - feas[best].y) < 0.5f &&
feas[i].x < feas[best].x)) {
best = i;
}
}
out->x = feas[best].x;
out->y = feas[best].y;
out->w = w;
out->h = h;
return true;
}
void overview_scale(Monitor *m) {
int32_t target_gappo = config.overviewgappo;
int32_t target_gappi = config.overviewgappi;
int orig_n = m->visible_clients;
if (orig_n == 0)
return;
2026-05-21 10:57:22 +08:00
OvLayoutItem *items = calloc(orig_n, sizeof(OvLayoutItem));
if (!items)
return;
2026-05-21 10:57:22 +08:00
int n = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal && !client_is_x11_popup(c)) {
2026-05-21 10:57:22 +08:00
items[n].c = c;
float w = c->overview_backup_geom.width;
float h = c->overview_backup_geom.height;
if (w <= 0 || h <= 0) {
w = 100.0f;
h = 100.0f;
}
2026-05-21 10:57:22 +08:00
items[n].orig_w = w;
items[n].orig_h = h;
items[n].area = w * h;
n++;
}
}
if (n == 0) {
2026-05-21 10:57:22 +08:00
free(items);
return;
}
2026-05-21 10:57:22 +08:00
qsort(items, n, sizeof(OvLayoutItem), compare_layout_items);
float max_avail_w = fmaxf(1.0f, m->w.width - 2 * target_gappo);
float max_avail_h = fmaxf(1.0f, m->w.height - 2 * target_gappo);
int max_points = 1 + 3 * n;
OvPlacedRect *placed = calloc(n, sizeof(OvPlacedRect));
OvPoint *cands = calloc(max_points, sizeof(OvPoint));
OvPoint *feas = calloc(max_points, sizeof(OvPoint));
if (!placed || !cands || !feas) {
free(items);
free(placed);
free(cands);
free(feas);
return;
}
2026-05-21 10:57:22 +08:00
float low = 0.0f, high = 1.0f, best_s = 0.0f;
for (int iter = 0; iter < 50; iter++) {
float mid = (low + high) / 2.0f;
bool ok = true;
int placed_cnt = 0;
2026-05-21 10:57:22 +08:00
for (int k = 0; k < n; k++) {
float w = items[k].orig_w * mid;
float h = items[k].orig_h * mid;
OvPlacedRect out;
if (!try_place(placed, placed_cnt, w, h, (float)target_gappi,
max_avail_w, max_avail_h, &out, cands, feas)) {
ok = false;
break;
}
2026-05-21 10:57:22 +08:00
placed[placed_cnt++] = out;
}
2026-05-21 10:57:22 +08:00
if (ok) {
best_s = mid;
low = mid;
} else {
high = mid;
}
}
2026-05-21 10:57:22 +08:00
if (best_s > 0.0f) {
float box_w = 0, box_h = 0;
int placed_cnt = 0;
2026-05-21 10:57:22 +08:00
for (int k = 0; k < n; k++) {
float w = items[k].orig_w * best_s;
float h = items[k].orig_h * best_s;
OvPlacedRect out;
try_place(placed, placed_cnt, w, h, (float)target_gappi,
max_avail_w, max_avail_h, &out, cands, feas);
placed[placed_cnt++] = out;
2026-05-21 10:57:22 +08:00
float r = out.x + w;
float b = out.y + h;
if (r > box_w)
box_w = r;
if (b > box_h)
box_h = b;
}
float dx = (max_avail_w - box_w) / 2.0f;
float dy = (max_avail_h - box_h) / 2.0f;
float base_x = m->w.x + target_gappo + dx;
float base_y = m->w.y + target_gappo + dy;
for (int k = 0; k < n; k++) {
Client *cl = items[k].c;
struct wlr_box geom;
geom.x = (int)(base_x + placed[k].x + 0.5f);
geom.y = (int)(base_y + placed[k].y + 0.5f);
float w = items[k].orig_w * best_s;
float h = items[k].orig_h * best_s;
geom.width = (int)(geom.x + w + 0.5f) - geom.x;
geom.height = (int)(geom.y + h + 0.5f) - geom.y;
resize(cl, geom, 0);
}
}
2026-05-21 10:57:22 +08:00
free(items);
free(placed);
free(cands);
free(feas);
}
void overview_resize(Monitor *m) {
int32_t target_gappo = config.overviewgappo;
int32_t target_gappi = config.overviewgappi;
2026-05-21 10:57:22 +08:00
float single_width_ratio = 0.7f;
float single_height_ratio = 0.8f;
2026-05-21 10:57:22 +08:00
int orig_n = m->visible_clients;
if (orig_n == 0)
return;
2026-05-21 10:57:22 +08:00
Client **c_arr = malloc(orig_n * sizeof(Client *));
if (!c_arr)
return;
int n = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
if (VISIBLEON(c, m) && !c->isunglobal && !client_is_x11_popup(c)) {
c_arr[n++] = c;
}
}
2026-05-21 10:57:22 +08:00
if (n == 0) {
free(c_arr);
return;
}
if (n == 1) {
int32_t cw = (m->w.width - 2 * target_gappo) * single_width_ratio;
int32_t ch = (m->w.height - 2 * target_gappo) * single_height_ratio;
c_arr[0]->geom.x = m->w.x + (m->w.width - cw) / 2;
c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2;
c_arr[0]->geom.width = cw;
c_arr[0]->geom.height = ch;
resize(c_arr[0], c_arr[0]->geom, 0);
free(c_arr);
return;
}
if (n == 2) {
2026-05-21 10:57:22 +08:00
int32_t cw = (m->w.width - 2 * target_gappo - target_gappi) / 2;
int32_t ch = (m->w.height - 2 * target_gappo) * 0.65f;
c_arr[0]->geom.x = m->w.x + target_gappo;
c_arr[0]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
c_arr[0]->geom.width = cw;
c_arr[0]->geom.height = ch;
resize(c_arr[0], c_arr[0]->geom, 0);
c_arr[1]->geom.x = m->w.x + cw + target_gappo + target_gappi;
c_arr[1]->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
c_arr[1]->geom.width = cw;
c_arr[1]->geom.height = ch;
resize(c_arr[1], c_arr[1]->geom, 0);
free(c_arr);
return;
}
2026-05-21 10:57:22 +08:00
int32_t cols = 1;
while (cols * cols < n) {
cols++;
}
2026-05-21 10:57:22 +08:00
int32_t rows = (n + cols - 1) / cols;
2026-05-21 10:57:22 +08:00
int32_t ch =
(m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows;
int32_t cw =
(m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols;
2026-05-21 10:57:22 +08:00
if (ch < 1)
ch = 1;
if (cw < 1)
cw = 1;
int32_t overcols = n % cols;
int32_t dx = 0;
if (overcols) {
dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / 2 -
target_gappo;
}
2026-05-21 10:57:22 +08:00
for (int i = 0; i < n; i++) {
int32_t cx = m->w.x + (i % cols) * (cw + target_gappi);
int32_t cy = m->w.y + (i / cols) * (ch + target_gappi);
if (overcols && i >= n - overcols) {
cx += dx;
}
2026-05-21 10:57:22 +08:00
c_arr[i]->geom.x = cx + target_gappo;
c_arr[i]->geom.y = cy + target_gappo;
c_arr[i]->geom.width = cw;
c_arr[i]->geom.height = ch;
resize(c_arr[i], c_arr[i]->geom, 0);
}
2026-05-21 10:57:22 +08:00
free(c_arr);
}
void overview(Monitor *m) {
if (config.ov_no_resize) {
overview_scale(m);
} else {
overview_resize(m);
}
}