diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..dfcd0045 --- /dev/null +++ b/.clangd @@ -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 diff --git a/format.sh b/format.sh old mode 100644 new mode 100755 diff --git a/src/animation/client.h b/src/animation/client.h index 79544cd2..b87bff91 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -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); diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61af421b..5f589420 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -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; diff --git a/src/layout/infinite.h b/src/layout/infinite.h new file mode 100644 index 00000000..b9726b96 --- /dev/null +++ b/src/layout/infinite.h @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/src/layout/layout.h b/src/layout/layout.h index 1f82eeaa..6f1cd801 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -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}, -}; \ No newline at end of file + {"I", infinite, "infinite", INFINITE}, + {"FI", free_infinite, "free_infinite", FREE_INFINITE}, +}; diff --git a/src/mango.c b/src/mango.c index 5549686c..49ac06ab 100644 --- a/src/mango.c +++ b/src/mango.c @@ -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; }