feat: smart spawning/tiling for easier keyboard usage

This commit is contained in:
ernestoCruz05 2026-03-30 18:19:30 +01:00
parent 64e29e7f1d
commit b509004f4a
2 changed files with 153 additions and 3 deletions

View file

@ -314,6 +314,8 @@ typedef struct {
uint32_t gappoh; uint32_t gappoh;
uint32_t gappov; uint32_t gappov;
uint32_t borderpx; uint32_t borderpx;
int32_t canvas_tiling;
int32_t canvas_tiling_gap;
float scratchpad_width_ratio; float scratchpad_width_ratio;
float scratchpad_height_ratio; float scratchpad_height_ratio;
float rootcolor[4]; float rootcolor[4];
@ -1713,6 +1715,10 @@ bool parse_option(Config *config, char *key, char *value) {
config->scratchpad_height_ratio = atof(value); config->scratchpad_height_ratio = atof(value);
} else if (strcmp(key, "borderpx") == 0) { } else if (strcmp(key, "borderpx") == 0) {
config->borderpx = atoi(value); config->borderpx = atoi(value);
} else if (strcmp(key, "canvas_tiling") == 0) {
config->canvas_tiling = atoi(value);
} else if (strcmp(key, "canvas_tiling_gap") == 0) {
config->canvas_tiling_gap = atoi(value);
} else if (strcmp(key, "rootcolor") == 0) { } else if (strcmp(key, "rootcolor") == 0) {
int64_t color = parse_color(value); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
@ -3352,6 +3358,8 @@ void set_value_default() {
config.idleinhibit_ignore_visible = 0; config.idleinhibit_ignore_visible = 0;
config.borderpx = 4; config.borderpx = 4;
config.canvas_tiling = 0;
config.canvas_tiling_gap = 10;
config.overviewgappi = 5; config.overviewgappi = 5;
config.overviewgappo = 30; config.overviewgappo = 30;
config.cursor_hide_timeout = 0; config.cursor_hide_timeout = 0;

View file

@ -1,9 +1,144 @@
static void canvas_pan_to_client(Monitor *m, Client *c);
// dir: 1=left, 2=right, 3=up, 4=down
static Client *canvas_chain_end(Monitor *m, uint32_t tag, Client *ref, int dir,
int gap) {
Client *c;
int tolerance = 60;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c == ref || c->isunglobal)
continue;
if (c->canvas_geom[tag].width == 0 || c->canvas_geom[tag].height == 0)
continue;
int cx1 = c->canvas_geom[tag].x;
int cy1 = c->canvas_geom[tag].y;
int cx2 = cx1 + c->canvas_geom[tag].width;
int cy2 = cy1 + c->canvas_geom[tag].height;
int rx1 = ref->canvas_geom[tag].x;
int ry1 = ref->canvas_geom[tag].y;
int rx2 = rx1 + ref->canvas_geom[tag].width;
int ry2 = ry1 + ref->canvas_geom[tag].height;
bool overlaps_h = cx1 < rx2 && cx2 > rx1;
bool overlaps_v = cy1 < ry2 && cy2 > ry1;
bool adjacent = false;
switch (dir) {
case 1:
adjacent = overlaps_v && abs(cx2 - (rx1 - gap)) < tolerance;
break;
case 2:
adjacent = overlaps_v && abs(cx1 - (rx2 + gap)) < tolerance;
break;
case 3:
adjacent = overlaps_h && abs(cy2 - (ry1 - gap)) < tolerance;
break;
case 4:
adjacent = overlaps_h && abs(cy1 - (ry2 + gap)) < tolerance;
break;
}
if (adjacent)
return canvas_chain_end(m, tag, c, dir, gap);
}
return ref;
}
static void canvas_geom_init(Client *c, Monitor *m, uint32_t tag, float pan_x, static void canvas_geom_init(Client *c, Monitor *m, uint32_t tag, float pan_x,
float pan_y, int *cascade_idx) { float pan_y, int *cascade_idx) {
int w = c->geom.width > 0 ? c->geom.width : 640; int w = c->geom.width > 100 ? c->geom.width : 640;
int h = c->geom.height > 0 ? c->geom.height : 480; int h = c->geom.height > 100 ? c->geom.height : 480;
float zoom = m->pertag->canvas_zoom[tag]; float zoom = m->pertag->canvas_zoom[tag];
int tiling = config.canvas_tiling;
if (tiling > 0 && !client_is_float_type(c) && !client_get_parent(c)) {
Client *focused = NULL, *tmp;
wl_list_for_each(tmp, &fstack, flink) {
if (tmp == c || tmp->iskilling || tmp->isunglobal)
continue;
if (VISIBLEON(tmp, m)) {
focused = tmp;
break;
}
}
float vp_w = m->w.width / zoom;
float vp_h = m->w.height / zoom;
bool focused_in_viewport =
focused && focused->canvas_geom[tag].width > 0 &&
focused->canvas_geom[tag].height > 0 &&
focused->canvas_geom[tag].x + focused->canvas_geom[tag].width >
pan_x &&
focused->canvas_geom[tag].y + focused->canvas_geom[tag].height >
pan_y &&
focused->canvas_geom[tag].x < pan_x + vp_w &&
focused->canvas_geom[tag].y < pan_y + vp_h;
if (focused_in_viewport) {
int dir = tiling;
int gap = config.canvas_tiling_gap;
if (tiling == 5) {
float fx = focused->canvas_geom[tag].x;
float fy = focused->canvas_geom[tag].y;
float fw = focused->canvas_geom[tag].width;
float fh = focused->canvas_geom[tag].height;
float mouse_cx = pan_x + (cursor->x - m->w.x) / zoom;
float mouse_cy = pan_y + (cursor->y - m->w.y) / zoom;
float dl = fabsf(mouse_cx - fx);
float dr = fabsf(mouse_cx - (fx + fw));
float du = fabsf(mouse_cy - fy);
float dd = fabsf(mouse_cy - (fy + fh));
float min = dl;
dir = 1;
if (dr < min) {
min = dr;
dir = 2;
}
if (du < min) {
min = du;
dir = 3;
}
if (dd < min) {
dir = 4;
}
}
Client *end = canvas_chain_end(m, tag, focused, dir, gap);
switch (dir) {
case 1:
c->canvas_geom[tag].x = end->canvas_geom[tag].x - w - gap;
c->canvas_geom[tag].y = end->canvas_geom[tag].y;
break;
case 2:
c->canvas_geom[tag].x =
end->canvas_geom[tag].x + end->canvas_geom[tag].width + gap;
c->canvas_geom[tag].y = end->canvas_geom[tag].y;
break;
case 3:
c->canvas_geom[tag].x = end->canvas_geom[tag].x;
c->canvas_geom[tag].y = end->canvas_geom[tag].y - h - gap;
break;
case 4:
c->canvas_geom[tag].x = end->canvas_geom[tag].x;
c->canvas_geom[tag].y = end->canvas_geom[tag].y +
end->canvas_geom[tag].height + gap;
break;
}
c->canvas_geom[tag].width = w;
c->canvas_geom[tag].height = h;
return;
}
}
float cx = pan_x + (m->w.width / 2.0f) / zoom; float cx = pan_x + (m->w.width / 2.0f) / zoom;
float cy = pan_y + (m->w.height / 2.0f) / zoom; float cy = pan_y + (m->w.height / 2.0f) / zoom;
@ -76,6 +211,7 @@ static void canvas(Monitor *m) {
float zoom = m->pertag->canvas_zoom[tag]; float zoom = m->pertag->canvas_zoom[tag];
int cascade_idx = 0; int cascade_idx = 0;
Client *newly_tiled = NULL;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isunglobal) if (!VISIBLEON(c, m) || c->isunglobal)
@ -84,8 +220,11 @@ static void canvas(Monitor *m) {
if (c->isfullscreen || c->ismaximizescreen) if (c->isfullscreen || c->ismaximizescreen)
continue; continue;
if (c->canvas_geom[tag].width == 0 && c->canvas_geom[tag].height == 0) if (c->canvas_geom[tag].width == 0 && c->canvas_geom[tag].height == 0) {
canvas_geom_init(c, m, tag, pan_x, pan_y, &cascade_idx); canvas_geom_init(c, m, tag, pan_x, pan_y, &cascade_idx);
if (config.canvas_tiling > 0 && !newly_tiled)
newly_tiled = c;
}
int screen_x = int screen_x =
m->w.x + (int32_t)roundf((c->canvas_geom[tag].x - pan_x) * zoom); m->w.x + (int32_t)roundf((c->canvas_geom[tag].x - pan_x) * zoom);
@ -115,6 +254,9 @@ static void canvas(Monitor *m) {
else else
apply_visual_zoom(c, effective_zoom); apply_visual_zoom(c, effective_zoom);
} }
if (newly_tiled)
canvas_pan_to_client(m, newly_tiled);
} }
static void canvas_pan_to_client(Monitor *m, Client *c) { static void canvas_pan_to_client(Monitor *m, Client *c) {