Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cycle monitors #36

Merged
merged 12 commits into from
Jan 3, 2025
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ rule = wm_class("firefox"), state(tiled), desktop(-1)
- **change_state**: Set window state (FLAOTING, TILED).
- **grow_floating_window**: Grow floating window (horizontally or vertically).
- **shrink_floating_window**: Shrink floating window (horizontally or vertically).
- **cycle_monitors**: Cycle between monitors (left or right, relative to the linked-list order not the physical positioning).

- Default keys

Expand Down Expand Up @@ -320,14 +321,16 @@ bind = shift + up -> func(shift_window:up)
bind = shift + right -> func(shift_window:right)
bind = shift + left -> func(shift_window:left)
bind = shift + down -> func(shift_window:down)
bind = shift + f -> func(change_state:float)
bind = shift + t -> func(change_state:tile)
bind = super|shift + t -> func(shrink_floating_window:horizontal)
bind = super|shift + g -> func(shrink_floating_window:vertical)
bind = super|shift + y -> func(grow_floating_window:horizontal)
bind = super|shift + h -> func(grow_floating_window:vertical)
bind = shift + t -> func(change_state:tile)
bind = shift + f -> func(change_state:float)
bind = super|shift + left -> func(cycle_desktop:left)
bind = super|shift + right -> func(cycle_desktop:right)
bind = super|ctrl + right -> func(cycle_monitors:next)
bind = super|ctrl + left -> func(cycle_monitors:prev)
bind = super|shift + 1 -> func(transfer_node:1)
bind = super|shift + 2 -> func(transfer_node:2)
bind = super|shift + 3 -> func(transfer_node:3)
Expand Down Expand Up @@ -369,6 +372,8 @@ More options will be added in the future as development progresses.
| `super + shift + h` | grow floating windows vertically |
| `super + shift + t` | shrink floating windows horizontally |
| `super + shift + g` | shrink floating windows vertically |
| `super + ctrl + →` | focus/change monitor right |
| `super + ctrl + ←` | focus/change monitor left |
| `super + s` | swap window's orientation |
| `super + ←` | focus window on the left |
| `super + ↑` | focus window above |
Expand Down
11 changes: 11 additions & 0 deletions src/config_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static const conf_mapper_t _cmapper_[] = {
DEFINE_MAPPING("cycle_window", cycle_win_wrapper),
DEFINE_MAPPING("reload_config", reload_config_wrapper),
DEFINE_MAPPING("cycle_desktop", cycle_desktop_wrapper),
DEFINE_MAPPING("cycle_monitors", cycle_monitors),
DEFINE_MAPPING("shift_window", shift_floating_window),
DEFINE_MAPPING("grow_floating_window", grow_floating_window),
DEFINE_MAPPING("shrink_floating_window", shrink_floating_window),
Expand Down Expand Up @@ -344,6 +345,10 @@ write_default_config(const char *filename, config_t *c)
"bind = super + right -> func(cycle_window:right)\n"
"bind = super + left -> func(cycle_window:left)\n"
"bind = super + down -> func(cycle_window:down)\n"
"\n"
"; cycle focus between monitors\n"
"bind = super|ctrl + right -> func(cycle_monitors:next)\n"
"bind = super|ctrl + left -> func(cycle_monitors:prev)\n"
"\n"
"; shift floating window position to the specified direction\n"
"bind = shift + up -> func(shift_window:up)\n"
Expand Down Expand Up @@ -776,6 +781,12 @@ set_key_args(conf_key_t *key, char *func, char *arg)
} else if (strcmp(arg, "vertical") == 0) {
key->arg->rd = VERTICAL_DIR;
}
} else if (strcmp(func, "cycle_monitors") == 0) {
if (strcmp(arg, "next") == 0) {
key->arg->tr = NEXT;
} else if (strcmp(arg, "prev") == 0) {
key->arg->tr = PREV;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

#define DEFINE_KEY(mask, keysym, handler, arg) {mask, keysym, handler, arg}
#define DEFINE_MAPPING(name, value) {name, value}
#define _KEY(k) XK_##k

/* spent way too many hours hunting double-free bugs. This should handle it. */
#define _FREE_(ptr) \
Expand Down
219 changes: 88 additions & 131 deletions src/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,54 +189,64 @@ insert_floating_node(node_t *node, desktop_t *d)
}
node->node_type = EXTERNAL_NODE;
}
static void
split_rect(node_t *n, split_type_t s)
{
const int16_t gap = conf.window_gap - conf.border_width;
const int16_t half_width = (n->rectangle.width - gap) / 2;
const int16_t half_height = (n->rectangle.height - gap) / 2;
node_t *n1 = n->first_child;
node_t *n2 = n->second_child;
rectangle_t *fr = &n1->rectangle;
rectangle_t *sr = &n2->rectangle;
if (s == HORIZONTAL_TYPE) {
fr->x = n->rectangle.x;
fr->y = n->rectangle.y;
fr->width = half_width;
fr->height = n->rectangle.height;
if (!IS_FLOATING(n1->client)) {
sr->x = n->rectangle.x + fr->width + conf.window_gap +
conf.border_width;
sr->y = n->rectangle.y;
sr->width = n->rectangle.width - fr->width - conf.window_gap -
conf.border_width;
sr->height = n->rectangle.height;
} else {
*sr = n->rectangle;
}
} else {
fr->x = n->rectangle.x;
fr->y = n->rectangle.y;
fr->width = n->rectangle.width;
fr->height = half_height;
if (!IS_FLOATING(n2->client)) {
sr->x = n->rectangle.x;
sr->y = n->rectangle.y + fr->height + conf.window_gap +
conf.border_width;
sr->width = n->rectangle.width;
sr->height = n->rectangle.height - fr->height - conf.window_gap -
conf.border_width;
} else {
*sr = n->rectangle;
}
}
}

/* split_node - splits a node's rectangle in half, the split could be vertical
* or horizontal depending on (width > height)? */
/* split_node - splits a node's rectangle in half, the split could be
* vertical or horizontal depending on (width > height)? */
static void
split_node(node_t *n, node_t *nd)
{
rectangle_t *first_rect = &n->first_child->rectangle;
rectangle_t *second_rect = &n->second_child->rectangle;
if (IS_FLOATING(nd->client)) {
*first_rect = n->floating_rectangle = n->rectangle;
n->first_child->rectangle = n->floating_rectangle = n->rectangle;
return;
}
if (n->rectangle.width >= n->rectangle.height) {
/* horizontal split */
const int16_t gap = conf.window_gap - conf.border_width;
const int16_t half_width = (n->rectangle.width - gap) / 2;
first_rect->x = n->rectangle.x;
first_rect->y = n->rectangle.y;
first_rect->width = half_width;
first_rect->height = n->rectangle.height;
if (!IS_FLOATING(n->first_child->client)) {
second_rect->x = n->rectangle.x + first_rect->width +
conf.window_gap + conf.border_width;
second_rect->y = n->rectangle.y;
second_rect->width = n->rectangle.width - first_rect->width -
conf.window_gap - conf.border_width;
second_rect->height = n->rectangle.height;
} else {
*second_rect = n->rectangle;
}
split_rect(n, HORIZONTAL_TYPE);
} else {
/* verticall split */
const int16_t gap = conf.window_gap - conf.border_width;
const int16_t half_height = (n->rectangle.height - gap) / 2;
first_rect->x = n->rectangle.x;
first_rect->y = n->rectangle.y;
first_rect->width = n->rectangle.width;
first_rect->height = half_height;
if (!IS_FLOATING(n->first_child->client)) {
second_rect->x = n->rectangle.x;
second_rect->y = n->rectangle.y + first_rect->height +
conf.window_gap + conf.border_width;
second_rect->width = n->rectangle.width;
second_rect->height = n->rectangle.height - first_rect->height -
conf.window_gap - conf.border_width;
} else {
*second_rect = n->rectangle;
}
split_rect(n, VERTICAL_TYPE);
}
}

Expand Down Expand Up @@ -382,45 +392,18 @@ resize_subtree(node_t *parent)
if (parent == NULL)
return;

rectangle_t r, r2 = {0};

if (parent->rectangle.width >= parent->rectangle.height) {
r.x = parent->rectangle.x;
r.y = parent->rectangle.y;
r.width =
(parent->rectangle.width - (conf.window_gap - conf.border_width)) /
2;
r.height = parent->rectangle.height;
r2.x = (int16_t)(parent->rectangle.x + r.width + conf.window_gap +
conf.border_width);
r2.y = parent->rectangle.y;
r2.width = parent->rectangle.width - r.width - conf.window_gap -
conf.border_width;
r2.height = parent->rectangle.height;
split_rect(parent, HORIZONTAL_TYPE);
} else {
r.x = parent->rectangle.x;
r.y = parent->rectangle.y;
r.width = parent->rectangle.width;
r.height =
(parent->rectangle.height - (conf.window_gap - conf.border_width)) /
2;
r2.x = parent->rectangle.x;
r2.y = (int16_t)(parent->rectangle.y + r.height + conf.window_gap +
conf.border_width);
r2.width = parent->rectangle.width;
r2.height = parent->rectangle.height - r.height - conf.window_gap -
conf.border_width;
split_rect(parent, VERTICAL_TYPE);
}

if (parent->first_child) {
parent->first_child->rectangle = r;
if (IS_INTERNAL(parent->first_child)) {
resize_subtree(parent->first_child);
}
}

if (parent->second_child) {
parent->second_child->rectangle = r2;
if (IS_INTERNAL(parent->second_child)) {
resize_subtree(parent->second_child);
}
Expand Down Expand Up @@ -556,11 +539,7 @@ sort(node_t **s, int n)
void
restack(void)
{
int idx = get_focused_desktop_idx();
if (idx == -1)
return;

node_t *root = curr_monitor->desktops[idx]->tree;
node_t *root = curr_monitor->desk->tree;
if (root == NULL)
return;

Expand All @@ -571,11 +550,8 @@ restack(void)
_LOG_(ERROR, "cannot allocate stack");
return;
}
stack_and_lower(root,
stack,
&top,
stack_size,
curr_monitor->desktops[idx]->layout == STACK);
stack_and_lower(
root, stack, &top, stack_size, curr_monitor->desk->layout == STACK);
if (top == 0) {
if (stack[0]->client)
raise_window(stack[0]->client->window);
Expand Down Expand Up @@ -891,6 +867,27 @@ apply_default_layout(node_t *root)
}
}

static void
calculate_base_rect(rectangle_t *r, monitor_t *m)
{
uint16_t w = m->rectangle.width;
uint16_t h = m->rectangle.height;
uint16_t x = m->rectangle.x;
uint16_t y = m->rectangle.y;
if (wm->bar && m == prim_monitor) {
r->x = (int16_t)(x + conf.window_gap);
r->y = (int16_t)(y + wm->bar->rectangle.height + conf.window_gap);
r->width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r->height = (uint16_t)(h - wm->bar->rectangle.height -
2 * conf.window_gap - 2 * conf.border_width);
} else {
r->x = (int16_t)(x + conf.window_gap);
r->y = (int16_t)(y + conf.window_gap);
r->width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r->height = (uint16_t)(h - 2 * conf.window_gap - 2 * conf.border_width);
}
}

/* default_layout - applies the default layout to the tree.
*
* initializes the default layout for the entire screen or
Expand All @@ -902,24 +899,8 @@ default_layout(node_t *root)
{
if (root == NULL)
return;

rectangle_t r = {0};
uint16_t w = curr_monitor->rectangle.width;
uint16_t h = curr_monitor->rectangle.height;
uint16_t x = curr_monitor->rectangle.x;
uint16_t y = curr_monitor->rectangle.y;
if (wm->bar && curr_monitor == prim_monitor) {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + wm->bar->rectangle.height + conf.window_gap);
r.width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height = (uint16_t)(h - wm->bar->rectangle.height -
2 * conf.window_gap - 2 * conf.border_width);
} else {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + conf.window_gap);
r.width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height = (uint16_t)(h - 2 * conf.window_gap - 2 * conf.border_width);
}
calculate_base_rect(&r, curr_monitor);
root->rectangle = r;
apply_default_layout(root);
}
Expand Down Expand Up @@ -1084,23 +1065,8 @@ apply_stack_layout(node_t *root)
static void
stack_layout(node_t *root)
{
rectangle_t r = {0};
uint16_t w = curr_monitor->rectangle.width;
uint16_t h = curr_monitor->rectangle.height;
const uint16_t x = curr_monitor->rectangle.x;
const uint16_t y = curr_monitor->rectangle.y;
if (wm->bar && curr_monitor == prim_monitor) {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + wm->bar->rectangle.height + conf.window_gap);
r.width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height = (uint16_t)(h - wm->bar->rectangle.height -
2 * conf.window_gap - 2 * conf.border_width);
} else {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + conf.window_gap);
r.width = (uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height = (uint16_t)(h - 2 * conf.window_gap - 2 * conf.border_width);
}
rectangle_t r = {0};
calculate_base_rect(&r, curr_monitor);
root->rectangle = r;
apply_stack_layout(root);
}
Expand Down Expand Up @@ -1363,7 +1329,7 @@ delete_node(node_t *node, desktop_t *d)
_LOG_(ERROR, "node to be deleted is null");
return;
}

/*bool swap = node == d->node;*/
if (IS_INTERNAL(node)) {
_LOG_(ERROR,
"node to be deleted is not an external node type: %d",
Expand All @@ -1381,19 +1347,28 @@ delete_node(node_t *node, desktop_t *d)
check = true;
}

/*node_t *n = prev_node(node);
if (!n) {
n = next_node(node);
}*/

if (!unlink_node(node, d)) {
_LOG_(ERROR, "could not unlink node.. abort");
return;
}

if (check) {
assert(!d->tree);
/*d->node = NULL;*/
}

_FREE_(node->client);
_FREE_(node);

out:
/*if (!check) {
d->node = n;
}*/

d->n_count -= 1;
if (!is_tree_empty(d->tree)) {
arrange_tree(d->tree, d->layout);
Expand Down Expand Up @@ -1833,25 +1808,7 @@ transfer_node(node_t *node, desktop_t *d)

if (is_tree_empty(d->tree)) {
rectangle_t r = {0};
uint16_t w = curr_monitor->rectangle.width;
uint16_t h = curr_monitor->rectangle.height;
uint16_t x = curr_monitor->rectangle.x;
uint16_t y = curr_monitor->rectangle.y;
if (wm->bar && curr_monitor == prim_monitor) {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + wm->bar->rectangle.height + conf.window_gap);
r.width =
(uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height = (uint16_t)(h - wm->bar->rectangle.height -
2 * conf.window_gap - 2 * conf.border_width);
} else {
r.x = (int16_t)(x + conf.window_gap);
r.y = (int16_t)(y + conf.window_gap);
r.width =
(uint16_t)(w - 2 * conf.window_gap - 2 * conf.border_width);
r.height =
(uint16_t)(h - 2 * conf.window_gap - 2 * conf.border_width);
}
calculate_base_rect(&r, curr_monitor);
node->node_type = ROOT_NODE;
d->tree = node;
d->tree->rectangle = r;
Expand Down
Loading