perf(wave2): dynamic config arrays, flat dwindle tree, dual client arrays
Wave 2 changes: - Dynamic Config arrays with geometric growth (cap=16, 2x growth) → saves ~2.5 MB heap for typical configs vs fixed 128-entry arrays - Proper deep-copy protocol in on_config_reload/do_reload (free old, move new, zero source) - Flat DwindleTree array with int children[2]/parent indices → eliminates pointer chasing, contiguous allocation, iterative two-pass recalc - Lazy per-tag allocation (DwindleTree.nodes starts NULL, allocates on first use) - Dual client_arr + fstack_arr arrays alongside wl_list/fstack for cache-friendly iteration → arrange() and focustop() use array scan instead of linked-list pointer chase - All arrays maintain size-tracking (nclients, nfstack) for bounds checking - Zero-warning build with clang -Werror -Wpedantic
This commit is contained in:
@@ -30,6 +30,11 @@ typedef union {
|
||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
|
||||
extern struct Client **client_arr;
|
||||
extern int nclients;
|
||||
extern struct Client **fstack_arr;
|
||||
extern int nfstack;
|
||||
|
||||
/* Convenience wrapper for the function below */
|
||||
#define VISIBLEON(C, M) visibleon((C), (M))
|
||||
|
||||
|
||||
+26
-14
@@ -5,10 +5,8 @@
|
||||
#include <wlr/types/wlr_session_lock_v1.h>
|
||||
#include "wlr_ext_workspace_v1.h"
|
||||
|
||||
/* forward declarations */
|
||||
struct Client;
|
||||
struct LayerSurface;
|
||||
typedef struct DwindleNode DwindleNode;
|
||||
|
||||
#ifndef TAGCOUNT
|
||||
#define TAGCOUNT 9
|
||||
@@ -21,15 +19,30 @@ typedef struct {
|
||||
void (*arrange)(Monitor *);
|
||||
} Layout;
|
||||
|
||||
/* Cold state — accessed only on layout changes, not every frame.
|
||||
* Allocated lazily on first dwindle/master layout use. */
|
||||
struct DwindleNode {
|
||||
int children[2];
|
||||
int parent;
|
||||
struct Client *client;
|
||||
struct wlr_box box;
|
||||
float split_ratio;
|
||||
int split_top;
|
||||
int is_node;
|
||||
};
|
||||
typedef struct DwindleNode DwindleNode;
|
||||
|
||||
typedef struct {
|
||||
struct DwindleNode *nodes;
|
||||
int node_count;
|
||||
int node_cap;
|
||||
} DwindleTree;
|
||||
|
||||
typedef struct MonitorCold {
|
||||
const Layout *lt[TAGCOUNT][2];
|
||||
unsigned int sellt[TAGCOUNT];
|
||||
float mfact;
|
||||
int nmaster;
|
||||
char ltsymbol[TAGCOUNT][16];
|
||||
DwindleNode *dwindle_root[TAGCOUNT];
|
||||
DwindleTree dwindle_tree[TAGCOUNT];
|
||||
struct Client *dwindle_focus[TAGCOUNT];
|
||||
struct Client *master_master[TAGCOUNT];
|
||||
int master_side[TAGCOUNT];
|
||||
@@ -39,23 +52,22 @@ struct Monitor {
|
||||
struct wl_list link;
|
||||
struct wlr_output *wlr_output;
|
||||
struct wlr_scene_output *scene_output;
|
||||
struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
|
||||
struct wlr_scene_rect *fullscreen_bg;
|
||||
struct wl_listener frame;
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener request_state;
|
||||
struct wl_listener destroy_lock_surface;
|
||||
struct wlr_session_lock_surface_v1 *lock_surface;
|
||||
struct wlr_box m; /* monitor area, layout-relative */
|
||||
struct wlr_box w; /* window area, layout-relative */
|
||||
struct wl_list layers[4]; /* LayerSurface.link */
|
||||
struct MonitorCold *cold; /* lazy-allocated layout state */
|
||||
struct wlr_box m;
|
||||
struct wlr_box w;
|
||||
struct wl_list layers[4];
|
||||
struct MonitorCold *cold;
|
||||
int gaps;
|
||||
unsigned int seltags;
|
||||
struct wlr_ext_workspace_group_handle_v1 *ext_group;
|
||||
uint32_t tagset[2];
|
||||
int asleep;
|
||||
int scratchpad_visible; /* whether scratchpad is shown */
|
||||
struct Client *scratchpad_prev_focus; /* client focused before scratchpad opened */
|
||||
struct Client *scratchpad_current; /* currently visible scratchpad client */
|
||||
int scratchpad_visible;
|
||||
struct Client *scratchpad_prev_focus;
|
||||
struct Client *scratchpad_current;
|
||||
};
|
||||
|
||||
|
||||
+181
-194
@@ -1,33 +1,24 @@
|
||||
/*
|
||||
* Layout algorithms extracted from peachwm.c
|
||||
* Dwindle, Master/Stack, Monocle, and associated utilities.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "layout.h"
|
||||
#include "parser/parser.h"
|
||||
#include "common.h"
|
||||
|
||||
/* globals from peachwm.c */
|
||||
extern struct wl_list clients;
|
||||
extern struct wl_list fstack;
|
||||
extern struct wl_list mons;
|
||||
extern Monitor *selmon;
|
||||
extern Config cfg;
|
||||
|
||||
/* functions from peachwm.c */
|
||||
void resize(Client *c, struct wlr_box geo, int interact);
|
||||
Client *focustop(Monitor *m);
|
||||
void focusclient(Client *c, int lift);
|
||||
void printstatus(void);
|
||||
void client_set_suspended(Client *c, int suspended);
|
||||
|
||||
/* ================================================================
|
||||
* layout table
|
||||
* ================================================================ */
|
||||
|
||||
const Layout layouts[] = {
|
||||
{"><>", nullptr},
|
||||
{"[T]", dwindle},
|
||||
@@ -36,11 +27,6 @@ const Layout layouts[] = {
|
||||
};
|
||||
const unsigned int layout_count = LENGTH(layouts);
|
||||
|
||||
/* ================================================================
|
||||
* helpers
|
||||
* ================================================================ */
|
||||
|
||||
/* Allocate and default-initialize monitor cold state on first use. */
|
||||
void
|
||||
ensure_cold(Monitor *m)
|
||||
{
|
||||
@@ -55,11 +41,6 @@ ensure_cold(Monitor *m)
|
||||
m->cold->nmaster = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 0-based index of the lowest set tag bit for monitor m.
|
||||
* For single-tag views this is the exact tag. For multi-tag views it
|
||||
* picks the lowest bit.
|
||||
*/
|
||||
int
|
||||
current_tag_idx(Monitor *m)
|
||||
{
|
||||
@@ -74,7 +55,6 @@ current_tag_idx(Monitor *m)
|
||||
return idx < TAGCOUNT ? idx : 0;
|
||||
}
|
||||
|
||||
/* Helper: get current layout for the active tag on monitor m */
|
||||
const Layout *
|
||||
curlayout(Monitor *m)
|
||||
{
|
||||
@@ -83,165 +63,190 @@ curlayout(Monitor *m)
|
||||
return m->cold->lt[ti][m->cold->sellt[ti]];
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* dwindle tree helpers
|
||||
* ================================================================ */
|
||||
|
||||
static DwindleNode *
|
||||
dwindle_new_node(void)
|
||||
static int
|
||||
dwindle_new_node(DwindleTree *tree)
|
||||
{
|
||||
DwindleNode *n = ecalloc(1, sizeof(*n));
|
||||
n->split_ratio = 1.0f;
|
||||
return n;
|
||||
if (tree->node_count >= tree->node_cap) {
|
||||
int new_cap = tree->node_cap ? tree->node_cap * 2 : 8;
|
||||
if (tree->nodes) {
|
||||
tree->nodes = realloc(tree->nodes,
|
||||
new_cap * sizeof(DwindleNode));
|
||||
memset(&tree->nodes[tree->node_cap], 0,
|
||||
(new_cap - tree->node_cap) * sizeof(DwindleNode));
|
||||
} else {
|
||||
tree->nodes = ecalloc(new_cap, sizeof(DwindleNode));
|
||||
}
|
||||
tree->node_cap = new_cap;
|
||||
}
|
||||
int idx = tree->node_count++;
|
||||
tree->nodes[idx].children[0] = -1;
|
||||
tree->nodes[idx].children[1] = -1;
|
||||
tree->nodes[idx].parent = -1;
|
||||
tree->nodes[idx].split_ratio = 1.0f;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void
|
||||
dwindle_free_tree(DwindleNode *n)
|
||||
dwindle_free_tree(DwindleTree *tree)
|
||||
{
|
||||
if (!n)
|
||||
return;
|
||||
dwindle_free_tree(n->children[0]);
|
||||
dwindle_free_tree(n->children[1]);
|
||||
free(n);
|
||||
free(tree->nodes);
|
||||
tree->nodes = nullptr;
|
||||
tree->node_count = 0;
|
||||
tree->node_cap = 0;
|
||||
}
|
||||
|
||||
DwindleNode *
|
||||
dwindle_find_leaf(DwindleNode *n, Client *c)
|
||||
int
|
||||
dwindle_find_leaf(const DwindleTree *tree, Client *c)
|
||||
{
|
||||
if (!n)
|
||||
return nullptr;
|
||||
for (int i = 0; i < tree->node_count; i++)
|
||||
if (!tree->nodes[i].is_node && tree->nodes[i].client == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
dwindle_first_leaf(const DwindleTree *tree)
|
||||
{
|
||||
for (int i = 0; i < tree->node_count; i++)
|
||||
if (!tree->nodes[i].is_node)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
dwindle_recalc(DwindleTree *tree, int gap)
|
||||
{
|
||||
for (int i = 0; i < tree->node_count; i++) {
|
||||
DwindleNode *n = &tree->nodes[i];
|
||||
if (!n->is_node)
|
||||
return n->client == c ? n : nullptr;
|
||||
DwindleNode *r = dwindle_find_leaf(n->children[0], c);
|
||||
return r ? r : dwindle_find_leaf(n->children[1], c);
|
||||
}
|
||||
continue;
|
||||
if (n->children[0] < 0 || n->children[1] < 0)
|
||||
continue;
|
||||
|
||||
static DwindleNode *
|
||||
dwindle_first_leaf(DwindleNode *n)
|
||||
{
|
||||
if (!n)
|
||||
return nullptr;
|
||||
while (n->is_node)
|
||||
n = n->children[0];
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively distribute geometry downward from node n.
|
||||
* gap is inner gap pixels between the two halves.
|
||||
*/
|
||||
void
|
||||
dwindle_recalc(DwindleNode *n, int gap)
|
||||
{
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
if (!n->is_node) {
|
||||
if (n->client && !n->client->isfullscreen)
|
||||
resize(n->client, n->box, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wider than tall -> split left/right; taller -> split top/bottom. */
|
||||
n->split_top = (n->box.height > n->box.width);
|
||||
|
||||
if (!n->split_top) {
|
||||
int w1 = MAX(1, (int)(n->box.width / 2.0f * n->split_ratio) - gap / 2);
|
||||
n->children[0]->box =
|
||||
(struct wlr_box){n->box.x, n->box.y, w1, n->box.height};
|
||||
n->children[1]->box =
|
||||
tree->nodes[n->children[0]].box =
|
||||
(struct wlr_box){n->box.x, n->box.y, w1,
|
||||
n->box.height};
|
||||
tree->nodes[n->children[1]].box =
|
||||
(struct wlr_box){n->box.x + w1 + gap, n->box.y,
|
||||
MAX(1, n->box.width - w1 - gap), n->box.height};
|
||||
MAX(1, n->box.width - w1 - gap),
|
||||
n->box.height};
|
||||
} else {
|
||||
int h1 = MAX(1, (int)(n->box.height / 2.0f * n->split_ratio) - gap / 2);
|
||||
n->children[0]->box =
|
||||
(struct wlr_box){n->box.x, n->box.y, n->box.width, h1};
|
||||
n->children[1]->box =
|
||||
(struct wlr_box){n->box.x, n->box.y + h1 + gap, n->box.width,
|
||||
tree->nodes[n->children[0]].box =
|
||||
(struct wlr_box){n->box.x, n->box.y, n->box.width,
|
||||
h1};
|
||||
tree->nodes[n->children[1]].box =
|
||||
(struct wlr_box){n->box.x, n->box.y + h1 + gap,
|
||||
n->box.width,
|
||||
MAX(1, n->box.height - h1 - gap)};
|
||||
}
|
||||
|
||||
dwindle_recalc(n->children[0], gap);
|
||||
dwindle_recalc(n->children[1], gap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert new_c into the tree, bisecting the focused client's node.
|
||||
* It falls back to the first leaf if focused is NULL or not in the tree.
|
||||
*/
|
||||
for (int i = 0; i < tree->node_count; i++) {
|
||||
DwindleNode *n = &tree->nodes[i];
|
||||
if (!n->is_node && n->client && !n->client->isfullscreen)
|
||||
resize(n->client, n->box, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dwindle_insert(DwindleNode **root, Client *new_c, Client *focused)
|
||||
dwindle_insert(DwindleTree *tree, Client *new_c, Client *focused)
|
||||
{
|
||||
DwindleNode *new_leaf = dwindle_new_node();
|
||||
new_leaf->client = new_c;
|
||||
new_leaf->is_node = 0;
|
||||
int new_leaf_idx = dwindle_new_node(tree);
|
||||
tree->nodes[new_leaf_idx].client = new_c;
|
||||
tree->nodes[new_leaf_idx].is_node = 0;
|
||||
|
||||
if (!*root) {
|
||||
*root = new_leaf;
|
||||
if (tree->node_count == 1)
|
||||
return;
|
||||
}
|
||||
|
||||
DwindleNode *opening_on = focused ? dwindle_find_leaf(*root, focused) : nullptr;
|
||||
if (!opening_on)
|
||||
opening_on = dwindle_first_leaf(*root);
|
||||
int leaf_idx = focused ? dwindle_find_leaf(tree, focused) : -1;
|
||||
if (leaf_idx < 0)
|
||||
leaf_idx = dwindle_first_leaf(tree);
|
||||
|
||||
DwindleNode *new_parent = dwindle_new_node();
|
||||
new_parent->is_node = 1;
|
||||
new_parent->box = opening_on->box;
|
||||
new_parent->parent = opening_on->parent;
|
||||
new_parent->split_top = (opening_on->box.height > opening_on->box.width);
|
||||
new_parent->children[0] = opening_on;
|
||||
new_parent->children[1] = new_leaf;
|
||||
int new_parent_idx = dwindle_new_node(tree);
|
||||
DwindleNode *parent = &tree->nodes[new_parent_idx];
|
||||
DwindleNode *opening = &tree->nodes[leaf_idx];
|
||||
|
||||
opening_on->parent = new_parent;
|
||||
new_leaf->parent = new_parent;
|
||||
parent->is_node = 1;
|
||||
parent->box = opening->box;
|
||||
parent->split_top = (opening->box.height > opening->box.width);
|
||||
parent->children[0] = leaf_idx;
|
||||
parent->children[1] = new_leaf_idx;
|
||||
|
||||
if (new_parent->parent) {
|
||||
if (new_parent->parent->children[0] == opening_on)
|
||||
new_parent->parent->children[0] = new_parent;
|
||||
int gp_idx = opening->parent;
|
||||
parent->parent = gp_idx;
|
||||
opening->parent = new_parent_idx;
|
||||
tree->nodes[new_leaf_idx].parent = new_parent_idx;
|
||||
|
||||
if (gp_idx >= 0) {
|
||||
DwindleNode *gp = &tree->nodes[gp_idx];
|
||||
if (gp->children[0] == leaf_idx)
|
||||
gp->children[0] = new_parent_idx;
|
||||
else
|
||||
new_parent->parent->children[1] = new_parent;
|
||||
} else {
|
||||
*root = new_parent;
|
||||
gp->children[1] = new_parent_idx;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove client c from the tree, promoting its sibling upward.
|
||||
*/
|
||||
static void
|
||||
dwindle_remove(DwindleNode **root, Client *c)
|
||||
dwindle_remove(DwindleTree *tree, Client *c)
|
||||
{
|
||||
DwindleNode *leaf = dwindle_find_leaf(*root, c);
|
||||
if (!leaf)
|
||||
int leaf_idx = dwindle_find_leaf(tree, c);
|
||||
if (leaf_idx < 0)
|
||||
return;
|
||||
|
||||
DwindleNode *parent = leaf->parent;
|
||||
if (!parent) {
|
||||
free(leaf);
|
||||
*root = nullptr;
|
||||
int parent_idx = tree->nodes[leaf_idx].parent;
|
||||
if (parent_idx < 0) {
|
||||
tree->node_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DwindleNode *sibling =
|
||||
(parent->children[0] == leaf) ? parent->children[1] : parent->children[0];
|
||||
DwindleNode *grandparent = parent->parent;
|
||||
DwindleNode *parent = &tree->nodes[parent_idx];
|
||||
int sibling_idx = (parent->children[0] == leaf_idx)
|
||||
? parent->children[1]
|
||||
: parent->children[0];
|
||||
int gp_idx = parent->parent;
|
||||
|
||||
sibling->parent = grandparent;
|
||||
if (grandparent) {
|
||||
if (grandparent->children[0] == parent)
|
||||
grandparent->children[0] = sibling;
|
||||
else
|
||||
grandparent->children[1] = sibling;
|
||||
} else {
|
||||
*root = sibling;
|
||||
int old_n = tree->node_count;
|
||||
int remap[512];
|
||||
int ni = 0;
|
||||
|
||||
for (int i = 0; i < old_n; i++)
|
||||
remap[i] = (i == leaf_idx || i == parent_idx) ? -1 : ni++;
|
||||
|
||||
int write = 0;
|
||||
for (int read = 0; read < old_n; read++) {
|
||||
if (remap[read] >= 0)
|
||||
tree->nodes[write++] = tree->nodes[read];
|
||||
}
|
||||
tree->node_count = ni;
|
||||
|
||||
for (int i = 0; i < ni; i++) {
|
||||
DwindleNode *n = &tree->nodes[i];
|
||||
if (n->children[0] >= 0)
|
||||
n->children[0] = remap[n->children[0]];
|
||||
if (n->children[1] >= 0)
|
||||
n->children[1] = remap[n->children[1]];
|
||||
if (n->parent >= 0)
|
||||
n->parent = remap[n->parent];
|
||||
}
|
||||
|
||||
free(leaf);
|
||||
free(parent);
|
||||
int new_sib = remap[sibling_idx];
|
||||
int new_gp = gp_idx >= 0 ? remap[gp_idx] : -1;
|
||||
|
||||
tree->nodes[new_sib].parent = new_gp;
|
||||
if (new_gp >= 0) {
|
||||
DwindleNode *gp = &tree->nodes[new_gp];
|
||||
if (gp->children[0] < 0)
|
||||
gp->children[0] = new_sib;
|
||||
else if (gp->children[1] < 0)
|
||||
gp->children[1] = new_sib;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove c from every monitor's per-tag tree. Called from unmapnotify(). */
|
||||
void
|
||||
dwindle_remove_client(Client *c)
|
||||
{
|
||||
@@ -249,13 +254,9 @@ dwindle_remove_client(Client *c)
|
||||
if (!m || !m->cold)
|
||||
return;
|
||||
for (int i = 0; i < TAGCOUNT; i++)
|
||||
dwindle_remove(&m->cold->dwindle_root[i], c);
|
||||
dwindle_remove(&m->cold->dwindle_tree[i], c);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* dwindle
|
||||
* ================================================================ */
|
||||
|
||||
void
|
||||
dwindle(Monitor *m)
|
||||
{
|
||||
@@ -275,20 +276,16 @@ dwindle(Monitor *m)
|
||||
: 0;
|
||||
|
||||
int ti = current_tag_idx(m);
|
||||
DwindleNode **root = &m->cold->dwindle_root[ti];
|
||||
DwindleTree *tree = &m->cold->dwindle_tree[ti];
|
||||
|
||||
/* prune leaves whose clients are no longer tiled here */
|
||||
{
|
||||
DwindleNode *stack[512];
|
||||
if (tree->node_count > 0) {
|
||||
Client *stale[512];
|
||||
int sp = 0, sc = 0;
|
||||
int sc = 0;
|
||||
|
||||
if (*root)
|
||||
stack[sp++] = *root;
|
||||
|
||||
while (sp > 0) {
|
||||
DwindleNode *nd = stack[--sp];
|
||||
if (!nd->is_node) {
|
||||
for (int i = 0; i < tree->node_count; i++) {
|
||||
DwindleNode *nd = &tree->nodes[i];
|
||||
if (nd->is_node)
|
||||
continue;
|
||||
int found = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c == nd->client && VISIBLEON(c, m) && !c->isfloating) {
|
||||
@@ -298,50 +295,42 @@ dwindle(Monitor *m)
|
||||
}
|
||||
if (!found && sc < 512)
|
||||
stale[sc++] = nd->client;
|
||||
} else {
|
||||
if (nd->children[1])
|
||||
stack[sp++] = nd->children[1];
|
||||
if (nd->children[0])
|
||||
stack[sp++] = nd->children[0];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sc; i++)
|
||||
dwindle_remove(root, stale[i]);
|
||||
dwindle_remove(tree, stale[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert any newly visible client, splitting the focused node.
|
||||
* Use m->cold->dwindle_focus[ti] so it actually splits what the user
|
||||
* was looking at when they spawned the window, rather than whatever
|
||||
* focustop() happens to return.
|
||||
*/
|
||||
Client *focused = m->cold->dwindle_focus[ti];
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
|
||||
continue;
|
||||
if (!dwindle_find_leaf(*root, c)) {
|
||||
dwindle_insert(root, c, focused);
|
||||
if (dwindle_find_leaf(tree, c) < 0) {
|
||||
dwindle_insert(tree, c, focused);
|
||||
focused = c;
|
||||
}
|
||||
}
|
||||
|
||||
/* assign root box and recurse */
|
||||
if (*root) {
|
||||
(*root)->box = (struct wlr_box){
|
||||
if (tree->node_count > 0) {
|
||||
int root_idx = -1;
|
||||
for (int i = 0; i < tree->node_count; i++) {
|
||||
if (tree->nodes[i].parent < 0) {
|
||||
root_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (root_idx >= 0) {
|
||||
tree->nodes[root_idx].box = (struct wlr_box){
|
||||
m->w.x + e,
|
||||
m->w.y + e,
|
||||
MAX(1, m->w.width - 2 * e),
|
||||
MAX(1, m->w.height - 2 * e),
|
||||
};
|
||||
dwindle_recalc(*root, e);
|
||||
dwindle_recalc(tree, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* master / stack
|
||||
* ================================================================ */
|
||||
|
||||
static void
|
||||
master_arrange(Monitor *m, int ti)
|
||||
@@ -434,7 +423,6 @@ master(Monitor *m)
|
||||
master_arrange(m, ti);
|
||||
}
|
||||
|
||||
/* Clear master references to client c on all tags of its monitor. */
|
||||
void
|
||||
master_remove_client(Client *c)
|
||||
{
|
||||
@@ -447,10 +435,6 @@ master_remove_client(Client *c)
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* monocle
|
||||
* ================================================================ */
|
||||
|
||||
void
|
||||
monocle(Monitor *m)
|
||||
{
|
||||
@@ -471,8 +455,6 @@ monocle(Monitor *m)
|
||||
int aw = MAX(1, m->w.width - 2 * e);
|
||||
int ah = MAX(1, m->w.height - 2 * e);
|
||||
|
||||
/* Stack all windows at the same position and raise the focused one to
|
||||
* top. */
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
|
||||
continue;
|
||||
@@ -485,10 +467,6 @@ monocle(Monitor *m)
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* swaptiled (tile-drag swap)
|
||||
* ================================================================ */
|
||||
|
||||
void
|
||||
swaptiled(Client *a, Client *b)
|
||||
{
|
||||
@@ -500,25 +478,34 @@ swaptiled(Client *a, Client *b)
|
||||
if (!m->cold)
|
||||
continue;
|
||||
for (int i = 0; i < TAGCOUNT; i++) {
|
||||
DwindleNode **root = &m->cold->dwindle_root[i];
|
||||
if (!*root)
|
||||
DwindleTree *tree = &m->cold->dwindle_tree[i];
|
||||
if (!tree->nodes || tree->node_count == 0)
|
||||
continue;
|
||||
DwindleNode *la = dwindle_find_leaf(*root, a);
|
||||
DwindleNode *lb = dwindle_find_leaf(*root, b);
|
||||
if (!la || !lb)
|
||||
int la = dwindle_find_leaf(tree, a);
|
||||
int lb = dwindle_find_leaf(tree, b);
|
||||
if (la < 0 || lb < 0)
|
||||
continue;
|
||||
la->client = b;
|
||||
lb->client = a;
|
||||
tree->nodes[la].client = b;
|
||||
tree->nodes[lb].client = a;
|
||||
int e = (m->gaps && cfg.appearance.gaps)
|
||||
? (int)cfg.appearance.gaps
|
||||
: 0;
|
||||
(*root)->box = (struct wlr_box){
|
||||
int root_idx = -1;
|
||||
for (int j = 0; j < tree->node_count; j++) {
|
||||
if (tree->nodes[j].parent < 0) {
|
||||
root_idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (root_idx >= 0) {
|
||||
tree->nodes[root_idx].box = (struct wlr_box){
|
||||
m->w.x + e,
|
||||
m->w.y + e,
|
||||
MAX(1, m->w.width - 2 * e),
|
||||
MAX(1, m->w.height - 2 * e),
|
||||
};
|
||||
dwindle_recalc(*root, e);
|
||||
}
|
||||
dwindle_recalc(tree, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-23
@@ -3,40 +3,20 @@
|
||||
#include "monitor.h"
|
||||
#include "client.h"
|
||||
|
||||
/* dwindle binary tree node */
|
||||
struct DwindleNode {
|
||||
DwindleNode *children[2];
|
||||
DwindleNode *parent;
|
||||
Client *client;
|
||||
struct wlr_box box;
|
||||
float split_ratio;
|
||||
int split_top;
|
||||
int is_node;
|
||||
};
|
||||
|
||||
/* layout table and count */
|
||||
extern const Layout layouts[];
|
||||
extern const unsigned int layout_count;
|
||||
|
||||
/* layout entry points (called via Layout.arrange function pointer) */
|
||||
void dwindle(Monitor *m);
|
||||
void master(Monitor *m);
|
||||
void monocle(Monitor *m);
|
||||
|
||||
/* helpers used across modules */
|
||||
[[nodiscard]] int current_tag_idx(Monitor *m);
|
||||
[[nodiscard]] const Layout *curlayout(Monitor *m);
|
||||
void ensure_cold(Monitor *m);
|
||||
|
||||
/* dwindle tree helpers (exposed for swapdir) */
|
||||
[[nodiscard]] DwindleNode *dwindle_find_leaf(DwindleNode *n, Client *c);
|
||||
void dwindle_recalc(DwindleNode *n, int gap);
|
||||
|
||||
/* lifecycle helpers (called from peachwm.c unmap/cleanup) */
|
||||
void dwindle_free_tree(DwindleNode *n);
|
||||
[[nodiscard]] int dwindle_find_leaf(const DwindleTree *tree, Client *c);
|
||||
void dwindle_recalc(DwindleTree *tree, int gap);
|
||||
void dwindle_free_tree(DwindleTree *tree);
|
||||
void dwindle_remove_client(Client *c);
|
||||
void master_remove_client(Client *c);
|
||||
|
||||
/* tile drag swap (called from buttonpress handler) */
|
||||
void swaptiled(Client *a, Client *b);
|
||||
|
||||
|
||||
+80
-16
@@ -85,6 +85,9 @@
|
||||
#include "ipc_socket.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* macros */
|
||||
#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
|
||||
#define END(A) ((A) + LENGTH(A))
|
||||
@@ -299,6 +302,11 @@ static struct wlr_xdg_activation_v1 *activation;
|
||||
static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
|
||||
struct wl_list clients; /* tiling order */
|
||||
struct wl_list fstack; /* focus order */
|
||||
|
||||
Client **client_arr;
|
||||
int nclients, client_cap;
|
||||
Client **fstack_arr;
|
||||
int nfstack, fstack_cap;
|
||||
static struct wlr_idle_notifier_v1 *idle_notifier;
|
||||
static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
|
||||
static struct wlr_layer_shell_v1 *layer_shell;
|
||||
@@ -465,13 +473,52 @@ static void applyrules(Client *c) {
|
||||
setmon(c, mon, newtags);
|
||||
}
|
||||
|
||||
static void client_arr_add(Client *c) {
|
||||
if (nclients >= client_cap) {
|
||||
client_cap = client_cap ? client_cap * 2 : 64;
|
||||
client_arr = realloc(client_arr, client_cap * sizeof(Client *));
|
||||
}
|
||||
client_arr[nclients++] = c;
|
||||
}
|
||||
|
||||
static void client_arr_remove(Client *c) {
|
||||
for (int i = 0; i < nclients; i++) {
|
||||
if (client_arr[i] == c) {
|
||||
client_arr[i] = client_arr[--nclients];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fstack_arr_add(Client *c) {
|
||||
if (nfstack >= fstack_cap) {
|
||||
fstack_cap = fstack_cap ? fstack_cap * 2 : 64;
|
||||
fstack_arr = realloc(fstack_arr, fstack_cap * sizeof(Client *));
|
||||
}
|
||||
memmove(&fstack_arr[1], &fstack_arr[0], nfstack * sizeof(Client *));
|
||||
fstack_arr[0] = c;
|
||||
nfstack++;
|
||||
}
|
||||
|
||||
static void fstack_arr_remove(Client *c) {
|
||||
for (int i = 0; i < nfstack; i++) {
|
||||
if (fstack_arr[i] == c) {
|
||||
memmove(&fstack_arr[i], &fstack_arr[i + 1],
|
||||
(nfstack - i - 1) * sizeof(Client *));
|
||||
nfstack--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void arrange(Monitor *m) {
|
||||
Client *c;
|
||||
|
||||
if (!m->wlr_output->enabled)
|
||||
return;
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
for (int i = 0; i < nclients; i++) {
|
||||
c = client_arr[i];
|
||||
if (c->mon == m) {
|
||||
wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m));
|
||||
client_set_suspended(c, !VISIBLEON(c, m));
|
||||
@@ -487,7 +534,8 @@ void arrange(Monitor *m) {
|
||||
|
||||
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
|
||||
* in floating layout to avoid "real" floating clients be always on top */
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
for (int i = 0; i < nclients; i++) {
|
||||
c = client_arr[i];
|
||||
if (c->mon != m || c->scene->node.parent == layers[LyrFS])
|
||||
continue;
|
||||
|
||||
@@ -759,7 +807,7 @@ static void cleanupmon(struct wl_listener *listener, void *data) {
|
||||
ipc_socket_send_output_event();
|
||||
if (m->cold) {
|
||||
for (int i = 0; i < TAGCOUNT; i++)
|
||||
dwindle_free_tree(m->cold->dwindle_root[i]);
|
||||
dwindle_free_tree(&m->cold->dwindle_tree[i]);
|
||||
free(m->cold);
|
||||
}
|
||||
wlr_scene_node_destroy(&m->fullscreen_bg->node);
|
||||
@@ -1474,6 +1522,8 @@ void focusclient(Client *c, int lift) {
|
||||
if (c && !client_is_unmanaged(c)) {
|
||||
wl_list_remove(&c->flink);
|
||||
wl_list_insert(&fstack, &c->flink);
|
||||
fstack_arr_remove(c);
|
||||
fstack_arr_add(c);
|
||||
selmon = c->mon;
|
||||
c->isurgent = 0;
|
||||
|
||||
@@ -1658,10 +1708,9 @@ void focusdir(const Arg *arg) {
|
||||
* will focus the topmost client of this mon, when actually will
|
||||
* only return that client */
|
||||
Client *focustop(Monitor *m) {
|
||||
Client *c;
|
||||
wl_list_for_each(c, &fstack, flink) {
|
||||
if (VISIBLEON(c, m))
|
||||
return c;
|
||||
for (int i = 0; i < nfstack; i++) {
|
||||
if (VISIBLEON(fstack_arr[i], m))
|
||||
return fstack_arr[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2165,6 +2214,8 @@ static void mapnotify(struct wl_listener *listener, void *data) {
|
||||
/* Insert this client into client lists. */
|
||||
wl_list_insert(&clients, &c->link);
|
||||
wl_list_insert(&fstack, &c->flink);
|
||||
client_arr_add(c);
|
||||
fstack_arr_add(c);
|
||||
|
||||
/* Set initial monitor, tags, floating status, and focus:
|
||||
* we always consider floating, clients that have parent and thus
|
||||
@@ -3349,6 +3400,11 @@ static void setup(void) {
|
||||
wl_list_init(&clients);
|
||||
wl_list_init(&fstack);
|
||||
|
||||
client_cap = 64;
|
||||
client_arr = ecalloc(client_cap, sizeof(Client *));
|
||||
fstack_cap = 64;
|
||||
fstack_arr = ecalloc(fstack_cap, sizeof(Client *));
|
||||
|
||||
xdg_shell = wlr_xdg_shell_create(dpy, 6);
|
||||
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
|
||||
wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
|
||||
@@ -3574,22 +3630,28 @@ static void swapdir(const Arg *arg) {
|
||||
|
||||
if (curlayout(selmon)->arrange == dwindle) {
|
||||
int ti = current_tag_idx(selmon);
|
||||
DwindleNode **root = &selmon->cold->dwindle_root[ti];
|
||||
if (*root) {
|
||||
DwindleNode *leaf_sel = dwindle_find_leaf(*root, sel);
|
||||
DwindleNode *leaf_other = dwindle_find_leaf(*root, other);
|
||||
if (leaf_sel && leaf_other) {
|
||||
leaf_sel->client = other;
|
||||
leaf_other->client = sel;
|
||||
DwindleTree *tree = &selmon->cold->dwindle_tree[ti];
|
||||
if (tree->nodes) {
|
||||
int idx_sel = dwindle_find_leaf(tree, sel);
|
||||
int idx_other = dwindle_find_leaf(tree, other);
|
||||
if (idx_sel >= 0 && idx_other >= 0) {
|
||||
tree->nodes[idx_sel].client = other;
|
||||
tree->nodes[idx_other].client = sel;
|
||||
int e = (selmon->gaps && cfg.appearance.gaps) ? (int)cfg.appearance.gaps
|
||||
: 0;
|
||||
(*root)->box = (struct wlr_box){
|
||||
/* Find root node (parent == -1) and set its box, then recalc */
|
||||
for (int i = 0; i < tree->node_count; i++) {
|
||||
if (tree->nodes[i].parent < 0) {
|
||||
tree->nodes[i].box = (struct wlr_box){
|
||||
selmon->w.x + e,
|
||||
selmon->w.y + e,
|
||||
MAX(1, selmon->w.width - 2 * e),
|
||||
MAX(1, selmon->w.height - 2 * e),
|
||||
};
|
||||
dwindle_recalc(*root, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dwindle_recalc(tree, e);
|
||||
}
|
||||
}
|
||||
} else if (curlayout(selmon)->arrange == master) {
|
||||
@@ -3692,9 +3754,11 @@ static void unmapnotify(struct wl_listener *listener, void *data) {
|
||||
} else {
|
||||
Monitor *oldmon = c->mon;
|
||||
wl_list_remove(&c->link);
|
||||
client_arr_remove(c);
|
||||
dwindle_remove_client(c);
|
||||
master_remove_client(c);
|
||||
wl_list_remove(&c->flink);
|
||||
fstack_arr_remove(c);
|
||||
/* Clear any stale scratchpad references to this client */
|
||||
if (oldmon) {
|
||||
if (oldmon->scratchpad_current == c)
|
||||
|
||||
Reference in New Issue
Block a user