Adds support for infinite and free_infinite layouts and some related binds and parameters

This commit is contained in:
horta-arch 2026-07-04 23:03:13 +02:00
parent 8216cacb3b
commit 673d193c16
7 changed files with 420 additions and 2 deletions

22
.clangd Normal file
View file

@ -0,0 +1,22 @@
CompileFlags:
CompilationDatabase: build
Add:
- "-DWLR_USE_UNSTABLE"
- "-D_POSIX_C_SOURCE=200809L"
- -g
- "-DVERSION=\"0.14.4\""
- "-DSYSCONFDIR=\"/etc\""
- "-DXWAYLAND"
Diagnostics:
UnusedIncludes: None
Suppress:
- unused-function
- unused-variable
- unused-parameter
Hover:
ShowAKA: false
Index:
Background: Build

0
format.sh Normal file → Executable file
View file

View file

@ -1299,7 +1299,10 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
// float_geom = c->geom;
bbox = (interact || c->isfloating || c->isfullscreen) ? &sgeom : &c->mon->w;
if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) {
if ((is_scroller_layout(c->mon) ||
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == INFINITE ||
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == FREE_INFINITE) &&
(!c->isfloating || c == grabc)) {
c->geom = geo;
c->geom.width = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.width);
c->geom.height = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.height);

View file

@ -218,6 +218,7 @@ typedef struct {
int32_t scroller_focus_center;
int32_t scroller_prefer_center;
int32_t scroller_prefer_overspread;
float new_window_scale;
int32_t edge_scroller_pointer_focus;
double edge_scroller_focus_allow_speed;
int32_t focus_cross_monitor;
@ -1288,6 +1289,18 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
func = dwindle_split_horizontal;
} else if (strcmp(func_name, "dwindle_split_vertical") == 0) {
func = dwindle_split_vertical;
} else if (strcmp(func_name, "scroll_left") == 0) {
func = scroll_left;
} else if (strcmp(func_name, "scroll_right") == 0) {
func = scroll_right;
} else if (strcmp(func_name, "scroll_up") == 0) {
func = scroll_up;
} else if (strcmp(func_name, "scroll_down") == 0) {
func = scroll_down;
} else if (strcmp(func_name, "home_canvas_wrapper") == 0) {
func = home_canvas_wrapper;
} else if (strcmp(func_name, "center_focused_wrapper") == 0) {
func = center_focused_wrapper;
} else {
return NULL;
}
@ -1436,6 +1449,8 @@ bool parse_option(Config *config, char *key, char *value) {
}
} else if (strcmp(key, "scroller_structs") == 0) {
config->scroller_structs = atoi(value);
} else if (strcmp(key, "new_window_scale") == 0) {
config->new_window_scale = atof(value);
} else if (strcmp(key, "scroller_default_proportion") == 0) {
config->scroller_default_proportion = atof(value);
} else if (strcmp(key, "scroller_default_proportion_single") == 0) {
@ -3449,6 +3464,7 @@ void override_config(void) {
CLAMP_FLOAT(config.scroller_default_proportion, 0.1f, 1.0f);
config.scroller_default_proportion_single =
CLAMP_FLOAT(config.scroller_default_proportion_single, 0.1f, 1.0f);
config.new_window_scale = CLAMP_FLOAT(config.new_window_scale, 0.1f, 5.0f);
config.scroller_ignore_proportion_single =
CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1);
config.scroller_focus_center =
@ -3666,6 +3682,7 @@ void set_value_default() {
config.scratchpad_height_ratio = 0.9f;
config.scroller_structs = 20;
config.new_window_scale = 1.0f;
config.scroller_default_proportion = 0.9f;
config.scroller_default_proportion_single = 1.0f;
config.scroller_ignore_proportion_single = 1;

353
src/layout/infinite.h Normal file
View file

@ -0,0 +1,353 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <wayland-util.h>
#include <wlr/util/box.h>
typedef struct Monitor Monitor;
typedef struct Client Client;
extern int32_t enablegaps;
extern struct wl_list clients;
extern void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact);
extern void resize(Client *c, struct wlr_box geo, int32_t interact);
extern Config config;
extern struct wlr_cursor *cursor;
struct Infinite_layout_data {
int32_t center_x, center_y;
struct wl_list nodes;
bool isRigid;
} static infinite_data;
struct Infinite_node {
struct wl_list link;
Client *client;
// here maybe lx and ly
};
static struct Infinite_node *find_node(Client *c) {
struct Infinite_node *n;
wl_list_for_each(n, &infinite_data.nodes, link) {
if (n->client == c)
return n;
};
return NULL;
}
static void init_infinite_layout_data(Monitor *m) {
infinite_data.center_x = m->w.x + m->w.width / 2;
infinite_data.center_y = m->w.y + m->w.height / 2;
wl_list_init(&infinite_data.nodes);
}
static bool check_colision(float nx, float ny, Client *self, int32_t gap,
Monitor *m) {
Client *other;
wl_list_for_each(other, &clients, link) {
if (other == self || !VISIBLEON(other, m) || !ISFAKETILED(other))
continue;
int32_t g2 = gap / 2;
int32_t ax = (int32_t)roundf(nx);
int32_t ay = (int32_t)roundf(ny);
int32_t aw = self->geom.width;
int32_t ah = self->geom.height;
int32_t bx = other->geom.x;
int32_t by = other->geom.y;
int32_t bw = other->geom.width;
int32_t bh = other->geom.height;
if (!(ax + aw + g2 <= bx - g2 || bx + bw + g2 <= ax - g2 ||
ay + ah + g2 <= by - g2 || by + bh + g2 <= ay - g2))
return true;
}
return false;
}
static struct wlr_box calculate_position(Monitor *m, Client *c, int32_t gap) {
// Start from the window's current position
float px = c->geom.x;
float py = c->geom.y;
// If current position already overlaps, push outward (away from center)
// until clear
if (check_colision(px, py, c, gap, m)) {
float ox = px - infinite_data.center_x;
float oy = py - infinite_data.center_y;
float olen = sqrtf(ox * ox + oy * oy);
if (olen > 0.5f) {
ox /= olen;
oy /= olen;
for (int s = 0; s < 2000; s++) {
px += ox;
py += oy;
if (!check_colision(px, py, c, gap, m))
break;
}
}
}
// Now slide toward center
float dx = infinite_data.center_x - (px + c->geom.width / 2.0f);
float dy = infinite_data.center_y - (py + c->geom.height / 2.0f);
float len = sqrtf(dx * dx + dy * dy);
if (len < 1.0f) {
return (struct wlr_box){
.x = (int32_t)roundf(px),
.y = (int32_t)roundf(py),
.width = c->geom.width,
.height = c->geom.height,
};
}
dx /= len;
dy /= len; // unit vector toward center
while (len > 0.0f) {
float nx = px + dx;
float ny = py + dy;
if (!check_colision(nx, ny, c, gap, m)) {
px = nx;
py = ny;
len -= 1.0f;
} else {
break;
}
}
return (struct wlr_box){
.x = (int32_t)roundf(px),
.y = (int32_t)roundf(py),
.width = c->geom.width,
.height = c->geom.height,
};
}
static void set_initial_positon(Monitor *m, Client *c) {
int32_t gap = enablegaps ? m->gappiv : 0;
struct wlr_box pos = calculate_position(m, c, gap);
c->geom.x = pos.x;
c->geom.y = pos.y;
}
static void move_canvas(Monitor *m, int32_t dx, int32_t dy) {
Client *c;
wl_list_for_each(c, &clients, link) {
if (infinite_data.isRigid) {
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
continue;
} else { // continue on base of isRigid
if (!VISIBLEON(c, m))
continue;
}
c->geom.x += dx;
c->geom.y += dy; // move the windows in the oposite direction of the
// scroll in the canvas
resize(c, c->geom, 0);
}
}
void center_focused(Monitor *m, Client *c) {
int32_t cx = m->w.x + m->w.width / 2;
int32_t cy = m->w.y + m->w.height / 2;
int32_t dx = cx - (c->geom.x + c->geom.width / 2);
int32_t dy = cy - (c->geom.y + c->geom.height / 2);
move_canvas(m, dx, dy);
}
void home_canvas(Monitor *m) {
Client *c;
int32_t cx = 0, cy = 0, i = 0;
wl_list_for_each(c, &clients, link) {
if (infinite_data.isRigid) {
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
continue;
} else { // continue on base of isRigid
if (!VISIBLEON(c, m))
continue;
}
cx += c->geom.x + c->geom.width / 2;
cy += c->geom.y + c->geom.height / 2;
i++;
}
if (!i)
return;
int32_t dx = infinite_data.center_x - cx / i;
int32_t dy = infinite_data.center_y - cy / i;
move_canvas(m, dx, dy);
}
int32_t home_canvas_wrapper(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m)
home_canvas(m);
return 0;
}
int32_t center_focused_wrapper(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m && m->sel)
center_focused(m, m->sel);
return 0;
}
int32_t scroll_left(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m)
move_canvas(m, -120, 0);
return 0;
}
int32_t scroll_right(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m)
move_canvas(m, 120, 0);
return 0;
}
int32_t scroll_up(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m)
move_canvas(m, 0, -120);
return 0;
}
int32_t scroll_down(const Arg *arg) {
Monitor *m = xytomon(cursor->x, cursor->y);
if (m)
move_canvas(m, 0, 120);
return 0;
}
void main_infinite(Monitor *m) {
int32_t i, n = 0;
Client *c = NULL;
init_infinite_layout_data(m);
// --- Map all the windows to infinite_node ---
wl_list_for_each(c, &clients, link) {
struct Infinite_node *node = find_node(c);
if (!node) {
node = calloc(1, sizeof(*node));
node->client = c;
wl_list_insert(&infinite_data.nodes, &node->link);
// Scale new window geometry by user-configurable factor
float s = config.new_window_scale;
if (fabsf(s - 1.0f) > 0.001f) {
c->geom.width = (int32_t)roundf(c->geom.width * s);
c->geom.height = (int32_t)roundf(c->geom.height * s);
if (c->geom.width < 1)
c->geom.width = 1;
if (c->geom.height < 1)
c->geom.height = 1;
}
}
};
if (infinite_data.isRigid) {
n = m->visible_fake_tiling_clients;
if (n == 0)
return;
int32_t gap = enablegaps ? m->gappiv : 0;
// --- Gaps ---
if (config.smartgaps && n == 1) {
gap = 0;
}
i = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
continue;
if (c->geom.width == 0) {
set_initial_positon(m, c);
}
i++;
}
// Build array of visible clients sorted by distance to center (closest
// first) so collision checks see already-placed windows' final
// positions
Client **order = calloc(i, sizeof(Client *));
int32_t idx = 0;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISFAKETILED(c))
continue;
order[idx++] = c;
}
for (int32_t a = 0; a < i - 1; a++) {
for (int32_t b = a + 1; b < i; b++) {
float da =
hypotf(order[a]->geom.x + order[a]->geom.width / 2.0f -
infinite_data.center_x,
order[a]->geom.y + order[a]->geom.height / 2.0f -
infinite_data.center_y);
float db =
hypotf(order[b]->geom.x + order[b]->geom.width / 2.0f -
infinite_data.center_x,
order[b]->geom.y + order[b]->geom.height / 2.0f -
infinite_data.center_y);
if (db < da) {
Client *t = order[a];
order[a] = order[b];
order[b] = t;
}
}
}
// Poll until stable (no window moved) or max passes
bool moved;
int pass = 0;
do {
moved = false;
for (int32_t a = 0; a < i; a++) {
struct wlr_box pos = calculate_position(m, order[a], gap);
if (pos.x != order[a]->geom.x || pos.y != order[a]->geom.y)
moved = true;
client_tile_resize(order[a], pos, 0);
}
pass++;
} while (moved && pass < 100);
free(order);
} else {
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m)) // even if the client is floating
continue;
resize(
c, c->geom,
0); // the geometry has been aplied at the start of the function
}
}
// --- free the nodes ---
struct Infinite_node *_n, *tmp;
wl_list_for_each_safe(_n, tmp, &infinite_data.nodes, link) {
bool alive = false;
wl_list_for_each(c, &clients, link) {
if (c == _n->client && VISIBLEON(c, m) /* && ISFAKETILED(c) */)
alive = true;
}
if (!alive) {
wl_list_remove(&_n->link);
free(_n);
}
};
}
void infinite(Monitor *m) {
infinite_data.isRigid = true;
main_infinite(m);
}
void free_infinite(Monitor *m) {
infinite_data.isRigid = false;
main_infinite(m);
}

View file

@ -14,6 +14,8 @@ static void vertical_deck(Monitor *mon);
static void dwindle(Monitor *m);
static void fair(Monitor *m);
static void vertical_fair(Monitor *m);
static void infinite(Monitor *m);
static void free_infinite(Monitor *m);
/* layout(s) */
Layout overviewlayout = {"󰃇", overview, "overview"};
@ -33,6 +35,8 @@ enum {
DWINDLE,
FAIR,
VERTICAL_FAIR,
INFINITE,
FREE_INFINITE,
};
Layout layouts[] = {
@ -53,4 +57,6 @@ Layout layouts[] = {
{"DW", dwindle, "dwindle", DWINDLE},
{"F", fair, "fair", FAIR},
{"VF", vertical_fair, "vertical_fair", VERTICAL_FAIR},
};
{"I", infinite, "infinite", INFINITE},
{"FI", free_infinite, "free_infinite", FREE_INFINITE},
};

View file

@ -1056,6 +1056,16 @@ struct Pertag {
const Layout *ltidxs[LENGTH(tags) + 1];
struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
};
extern int32_t scroll_left(const Arg *arg);
extern int32_t scroll_right(const Arg *arg);
extern int32_t scroll_up(const Arg *arg);
extern int32_t scroll_down(const Arg *arg);
extern int32_t home_canvas_wrapper(const Arg *arg);
extern int32_t center_focused_wrapper(const Arg *arg);
extern void center_focused(Monitor *m, Client *c);
extern void home_canvas(Monitor *m);
#include "config/parse_config.h"
static struct wl_signal mango_print_status;
@ -1128,6 +1138,7 @@ static struct wl_event_source *sync_keymap;
#include "layout/arrange.h"
#include "layout/dwindle.h"
#include "layout/horizontal.h"
#include "layout/infinite.h"
#include "layout/overview.h"
#include "layout/scroll.h"
#include "layout/vertical.h"
@ -3899,6 +3910,12 @@ void focusclient(Client *c, int32_t lift) {
arrange(selmon, false, false);
}
if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == INFINITE ||
selmon->pertag->ltidxs[selmon->pertag->curtag]->id ==
FREE_INFINITE) {
center_focused(selmon, c);
}
// change border color
c->isurgent = 0;
}