X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=tree.c;h=a6041a92546b11bad8603093d7ad3c33353c64e2;hb=d5d8805ad21cac67044b68b2747003e60e5695f6;hp=a350784c4705da94ce59f98156671c6ae7af124d;hpb=a009f70ecd88e32cc77312ac39b01cf8ed41e432;p=bspwm.git diff --git a/tree.c b/tree.c index a350784..a6041a9 100644 --- a/tree.c +++ b/tree.c @@ -1,7 +1,10 @@ #include #include #include +#include +#include #include +#include #include #include #include "settings.h" @@ -21,7 +24,7 @@ bool is_tiled(client_t *c) { if (c == NULL) return false; - return (!c->floating && !c->transient && !c->fullscreen); + return (!c->floating && !c->fullscreen); } bool is_floating(client_t *c) @@ -41,10 +44,19 @@ bool is_second_child(node_t *n) return (n != NULL && n->parent != NULL && n->parent->second_child == n); } -void change_split_ratio(node_t *n, value_change_t chg) { +void change_split_ratio(node_t *n, value_change_t chg) +{ n->split_ratio = pow(n->split_ratio, (chg == CHANGE_INCREASE ? INC_EXP : DEC_EXP)); } +void change_layout(monitor_t *m, desktop_t *d, layout_t l) +{ + d->layout = l; + arrange(m, d); + if (d == mon->desk) + put_status(); +} + node_t *first_extrema(node_t *n) { if (n == NULL) @@ -65,30 +77,43 @@ node_t *second_extrema(node_t *n) return second_extrema(n->second_child); } -node_t *next_leaf(node_t *n) +node_t *next_leaf(node_t *n, node_t *r) { if (n == NULL) return NULL; node_t *p = n; - while (is_second_child(p)) + while (is_second_child(p) && p != r) p = p->parent; - if (p->parent == NULL) + if (p == r) return NULL; return first_extrema(p->parent->second_child); } -node_t *prev_leaf(node_t *n) +node_t *prev_leaf(node_t *n, node_t *r) { if (n == NULL) return NULL; node_t *p = n; - while (is_first_child(p)) + while (is_first_child(p) && p != r) p = p->parent; - if (p->parent == NULL) + if (p == r) return NULL; return second_extrema(p->parent->first_child); } +bool is_adjacent(node_t *a, node_t *r) +{ + node_t *f = r->parent; + node_t *p = a; + bool first_child = is_first_child(r); + while (p != r) { + if (p->parent->split_type == f->split_type && is_first_child(p) == first_child) + return false; + p = p->parent; + } + return true; +} + node_t *find_fence(node_t *n, direction_t dir) { node_t *p; @@ -112,17 +137,142 @@ node_t *find_fence(node_t *n, direction_t dir) node_t *find_neighbor(node_t *n, direction_t dir) { + if (n == NULL) + return NULL; + node_t *fence = find_fence(n, dir); if (fence == NULL) return NULL; + node_t *nearest = NULL; + if (dir == DIR_UP || dir == DIR_LEFT) - return second_extrema(fence->first_child); + nearest = second_extrema(fence->first_child); else if (dir == DIR_DOWN || dir == DIR_RIGHT) - return first_extrema(fence->second_child); + nearest = first_extrema(fence->second_child); - return NULL; + return nearest; +} + +node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir) +{ + if (n == NULL || !is_tiled(n->client)) + return NULL; + + node_t *target = find_fence(n, dir); + if (target == NULL) + return NULL; + if (dir == DIR_UP || dir == DIR_LEFT) + target = target->first_child; + else if (dir == DIR_DOWN || dir == DIR_RIGHT) + target = target->second_child; + + node_t *nearest = NULL; + int min_rank = INT_MAX; + + for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) { + if (!is_tiled(a->client) || !is_adjacent(a, target) || a == n) + continue; + int rank = history_rank(f, a); + if (rank >= 0 && rank < min_rank) { + nearest = a; + min_rank = rank; + } + } + + return nearest; +} + +node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir) +{ + if (n == NULL) + return NULL; + + node_t *target = NULL; + + if (is_tiled(n->client)) { + target = find_fence(n, dir); + if (target == NULL) + return NULL; + if (dir == DIR_UP || dir == DIR_LEFT) + target = target->first_child; + else if (dir == DIR_DOWN || dir == DIR_RIGHT) + target = target->second_child; + } else { + target = d->root; + } + + node_t *nearest = NULL; + direction_t dir2; + xcb_point_t pt; + xcb_point_t pt2; + get_side_handle(n->client, dir, &pt); + get_opposite(dir, &dir2); + double ds = DBL_MAX; + + for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) { + if (is_tiled(a->client) != is_tiled(n->client) + || (is_tiled(a->client) && !is_adjacent(a, target)) + || a == n) + continue; + get_side_handle(a->client, dir2, &pt2); + double ds2 = distance(pt, pt2); + if (ds2 < ds) { + ds = ds2; + nearest = a; + } + } + + return nearest; +} + +void get_opposite(direction_t src, direction_t *dst) +{ + switch (src) { + case DIR_RIGHT: + *dst = DIR_LEFT; + break; + case DIR_DOWN: + *dst = DIR_UP; + break; + case DIR_LEFT: + *dst = DIR_RIGHT; + break; + case DIR_UP: + *dst = DIR_DOWN; + break; + } +} + +int tiled_area(node_t *n) +{ + if (n == NULL) + return -1; + xcb_rectangle_t rect = n->client->tiled_rectangle; + return rect.width * rect.height; +} + +node_t *find_biggest(desktop_t *d) +{ + if (d == NULL) + return NULL; + + node_t *r = NULL; + int r_area = tiled_area(r); + + for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) { + int f_area = tiled_area(f); + if (r == NULL) { + r = f; + r_area = f_area; + } else if (f_area > r_area) { + r = f; + r_area = f_area; + } + } + + return r; } void move_fence(node_t *n, direction_t dir, fence_move_t mov) @@ -141,7 +291,7 @@ void move_fence(node_t *n, direction_t dir, fence_move_t mov) void rotate_tree(node_t *n, rotate_t rot) { - if (n == NULL || is_leaf(n)) + if (n == NULL || is_leaf(n) || rot == ROTATE_IDENTITY) return; node_t *tmp; @@ -166,85 +316,95 @@ void rotate_tree(node_t *n, rotate_t rot) rotate_tree(n->second_child, rot); } -void dump_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth) +void rotate_brother(node_t *n) { - if (n == NULL) + if (n == NULL || n->parent == NULL) return; - - char line[MAXLEN]; - - for (unsigned int i = 0; i < depth; i++) - strncat(rsp, " ", REMLEN(rsp)); - - if (is_leaf(n)) - snprintf(line, sizeof(line), "%s %X %s%s%s%s%s", n->client->class_name, n->client->window, (n->client->floating ? "f" : "-"), (n->client->transient ? "t" : "-"), (n->client->fullscreen ? "F" : "-"), (n->client->urgent ? "u" : "-"), (n->client->locked ? "l" : "-")); + if (is_first_child(n)) + rotate_tree(n->parent->second_child, n->birth_rotation); else - snprintf(line, sizeof(line), "%s %.2f", (n->split_type == TYPE_HORIZONTAL ? "H" : "V"), n->split_ratio); + rotate_tree(n->parent->first_child, n->birth_rotation); +} - strncat(rsp, line, REMLEN(rsp)); +void unrotate_tree(node_t *n, rotate_t rot) +{ + switch(rot) { + case ROTATE_CLOCKWISE: + rotate_tree(n, ROTATE_COUNTER_CLOCKWISE); + break; + case ROTATE_COUNTER_CLOCKWISE: + rotate_tree(n, ROTATE_CLOCKWISE); + break; + case ROTATE_IDENTITY: + case ROTATE_FULL_CYCLE: + break; + } +} - if (n == d->focus) - strncat(rsp, " *\n", REMLEN(rsp)); +void unrotate_brother(node_t *n) +{ + if (n == NULL || n->parent == NULL) + return; + if (is_first_child(n)) + unrotate_tree(n->parent->second_child, n->birth_rotation); else - strncat(rsp, "\n", REMLEN(rsp)); - - dump_tree(d, n->first_child, rsp, depth + 1); - dump_tree(d, n->second_child, rsp, depth + 1); + unrotate_tree(n->parent->first_child, n->birth_rotation); } -void list_monitors(list_option_t opt, char *rsp) +void flip_tree(node_t *n, flip_t flp) { - monitor_t *m = mon_head; + if (n == NULL || is_leaf(n)) + return; - while (m != NULL) { - strncat(rsp, m->name, REMLEN(rsp)); - if (mon == m) - strncat(rsp, " #\n", REMLEN(rsp)); - else - strncat(rsp, "\n", REMLEN(rsp)); - if (opt == LIST_OPTION_VERBOSE) - list_desktops(m, opt, 1, rsp); - m = m->next; + node_t *tmp; + + if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) + || (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) { + tmp = n->first_child; + n->first_child = n->second_child; + n->second_child = tmp; + n->split_ratio = 1.0 - n->split_ratio; } + + flip_tree(n->first_child, flp); + flip_tree(n->second_child, flp); } -void list_desktops(monitor_t *m, list_option_t opt, unsigned int depth, char *rsp) +void list_history(char *rsp) { - desktop_t *d = m->desk_head; - - while (d != NULL) { - for (unsigned int i = 0; i < depth; i++) - strncat(rsp, " ", REMLEN(rsp)); - strncat(rsp, d->name, REMLEN(rsp)); - if (m->desk == d) - strncat(rsp, " @\n", REMLEN(rsp)); - else - strncat(rsp, "\n", REMLEN(rsp)); - if (opt == LIST_OPTION_VERBOSE) - dump_tree(d, d->root, rsp, depth + 1); - d = d->next; - } + char line[MAXLEN]; + for (monitor_t *m = mon_head; m != NULL; m = m->next) + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { + snprintf(line, sizeof(line), "%s\n", d->name); + strncat(rsp, line, REMLEN(rsp)); + for (node_list_t *a = d->history->tail; a != NULL; a = a->prev) { + snprintf(line, sizeof(line), " %X\n", a->node->client->window); + strncat(rsp, line, REMLEN(rsp)); + } + } } -void put_status(void) +int balance_tree(node_t *n) { - if (status_fifo == NULL) - return; - bool urgent = false; - for (monitor_t *m = mon_head; m != NULL; m = m->next) { - fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name); - for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) { - for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n)) - urgent |= n->client->urgent; - fprintf(status_fifo, "%c%c%s:", (m->desk == d ? 'D' : (d->root != NULL ? 'd' : '_')), (urgent ? '!' : '_'), d->name); - } + if (n == NULL || n->vacant) { + return 0; + } else if (is_leaf(n)) { + return 1; + } else { + int b1 = balance_tree(n->first_child); + int b2 = balance_tree(n->second_child); + int b = b1 + b2; + if (b1 > 0 && b2 > 0) + n->split_ratio = (double) b1 / b; + return b; } - fprintf(status_fifo, "L%s:W%X\n", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"), (mon->desk->focus == NULL ? 0 : mon->desk->focus->client->window)); - fflush(status_fifo); } void arrange(monitor_t *m, desktop_t *d) { + if (d->root == NULL) + return; + PRINTF("arrange %s%s%s\n", (num_monitors > 1 ? m->name : ""), (num_monitors > 1 ? " " : ""), d->name); xcb_rectangle_t rect = m->rectangle; @@ -253,8 +413,6 @@ void arrange(monitor_t *m, desktop_t *d) rect.y += m->top_padding + wg; rect.width -= m->left_padding + m->right_padding + wg; rect.height -= m->top_padding + m->bottom_padding + wg; - if (focus_follows_mouse) - get_pointer_position(&pointer_position); apply_layout(m, d, d->root, rect, rect); } @@ -266,8 +424,6 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x n->rectangle = rect; if (is_leaf(n)) { - if (n->client->fullscreen) - return; if (is_floating(n->client) && n->client->border_width != border_width) { int ds = 2 * (border_width - n->client->border_width); @@ -275,24 +431,32 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x n->client->floating_rectangle.height += ds; } - if (borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE) + if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE) || + n->client->fullscreen) n->client->border_width = 0; else n->client->border_width = border_width; xcb_rectangle_t r; - if (is_tiled(n->client)) { - if (d->layout == LAYOUT_TILED) - r = rect; - else if (d->layout == LAYOUT_MONOCLE) - r = root_rect; - int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap); - int bleed = wg + 2 * n->client->border_width; - r.width = (bleed < r.width ? r.width - bleed : 1); - r.height = (bleed < r.height ? r.height - bleed : 1); - n->client->tiled_rectangle = r; + if (!n->client->fullscreen) { + if (!n->client->floating) { + /* tiled clients */ + if (d->layout == LAYOUT_TILED) + r = rect; + else if (d->layout == LAYOUT_MONOCLE) + r = root_rect; + int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : window_gap); + int bleed = wg + 2 * n->client->border_width; + r.width = (bleed < r.width ? r.width - bleed : 1); + r.height = (bleed < r.height ? r.height - bleed : 1); + n->client->tiled_rectangle = r; + } else { + /* floating clients */ + r = n->client->floating_rectangle; + } } else { - r = n->client->floating_rectangle; + /* fullscreen clients */ + r = m->rectangle; } window_move_resize(n->client->window, r.x, r.y, r.width, r.height); @@ -339,7 +503,7 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n) node_t *dad = make_node(); node_t *fopar = focus->parent; n->parent = dad; - n->born_as = split_mode; + dad->birth_rotation = focus->birth_rotation; switch (split_mode) { case MODE_AUTOMATIC: if (fopar == NULL) { @@ -365,15 +529,19 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n) dad->split_type = fopar->split_type; dad->split_ratio = fopar->split_ratio; fopar->parent = dad; + rotate_t rot; if (is_first_child(focus)) { dad->first_child = n; dad->second_child = fopar; - rotate_tree(fopar, ROTATE_CLOCKWISE); + rot = ROTATE_CLOCKWISE; } else { dad->first_child = fopar; dad->second_child = n; - rotate_tree(fopar, ROTATE_COUNTER_CLOCKWISE); + rot = ROTATE_COUNTER_CLOCKWISE; } + if (!is_floating(n->client)) + rotate_tree(fopar, rot); + n->birth_rotation = rot; } break; case MODE_MANUAL: @@ -386,6 +554,7 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n) dad->split_ratio = focus->split_ratio; dad->parent = fopar; focus->parent = dad; + focus->birth_rotation = ROTATE_IDENTITY; switch (split_dir) { case DIR_LEFT: dad->split_type = TYPE_VERTICAL; @@ -416,51 +585,73 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n) if (focus->vacant) update_vacant_state(fopar); } + put_status(); } -void focus_node(monitor_t *m, desktop_t *d, node_t *n, bool is_mapped) +void pseudo_focus(desktop_t *d, node_t *n) { - if (n == NULL) + if (d->focus == n) return; + d->focus = n; + history_add(d->history, n); +} - PRINTF("focus node %X\n", n->client->window); +void focus_node(monitor_t *m, desktop_t *d, node_t *n) +{ + if (n == NULL && d->root != NULL) + return; split_mode = MODE_AUTOMATIC; - n->client->urgent = false; - if (is_mapped) { - if (mon != m || d->focus != n) { - window_draw_border(d->focus, m != mon, m == mon); + if (mon->desk != d) + clear_input_focus(); + + if (mon != m) { + for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next) + window_draw_border(cd->focus, true, false); + for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next) + if (cd != d) + window_draw_border(cd->focus, true, true); + if (d->focus == n) window_draw_border(n, true, true); - } - if (focus_follows_mouse) - get_pointer_position(&pointer_position); - xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME); } - if (!is_tiled(n->client)) { - if (!adaptative_raise || !might_cover(d, n)) - window_raise(n->client->window); - } else { - window_pseudo_raise(d, n->client->window); + if (d->focus != n) { + window_draw_border(d->focus, false, true); + window_draw_border(n, true, true); } - if (d->focus != n) { - d->last_focus = d->focus; - d->focus = n; + select_desktop(m, d); + + if (n == NULL) { + ewmh_update_active_window(); + return; + } + + PRINTF("focus node %X\n", n->client->window); + + n->client->urgent = false; + + pseudo_focus(d, n); + stack(d, n); + + xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME); + + if (focus_follows_pointer) { + xcb_window_t win = XCB_NONE; + query_pointer(&win, NULL); + if (win != n->client->window) + enable_motion_recorder(); + else + disable_motion_recorder(); } ewmh_update_active_window(); - put_status(); } void update_current(void) { - if (mon->desk->focus == NULL) - ewmh_update_active_window(); - else - focus_node(mon, mon->desk, mon->desk->focus, true); - put_status(); + focus_node(mon, mon->desk, mon->desk->focus); } void unlink_node(desktop_t *d, node_t *n) @@ -475,19 +666,17 @@ void unlink_node(desktop_t *d, node_t *n) if (p == NULL) { d->root = NULL; d->focus = NULL; - d->last_focus = NULL; } else { node_t *b; node_t *g = p->parent; - bool n_first_child = is_first_child(n); - if (n_first_child) { + if (is_first_child(n)) { b = p->second_child; - if (n->born_as == MODE_AUTOMATIC) - rotate_tree(b, ROTATE_COUNTER_CLOCKWISE); + if (!n->vacant) + unrotate_tree(b, n->birth_rotation); } else { b = p->first_child; - if (n->born_as == MODE_AUTOMATIC) - rotate_tree(b, ROTATE_CLOCKWISE); + if (!n->vacant) + unrotate_tree(b, n->birth_rotation); } b->parent = g; if (g != NULL) { @@ -499,26 +688,22 @@ void unlink_node(desktop_t *d, node_t *n) d->root = b; } + b->birth_rotation = p->birth_rotation; n->parent = NULL; free(p); - if (n == d->last_focus) { - d->last_focus = NULL; - } else if (n == d->focus) { - if (d->last_focus != NULL) - d->focus = d->last_focus; - else - d->focus = (n_first_child ? first_extrema(b) : second_extrema(b)); - d->last_focus = NULL; - } + if (n == d->focus) + d->focus = history_get(d->history, 1); update_vacant_state(b->parent); } + history_remove(d->history, n); + put_status(); } void remove_node(desktop_t *d, node_t *n) { - if (d == NULL || n == NULL) + if (n == NULL) return; PRINTF("remove node %X\n", n->client->window); @@ -534,6 +719,19 @@ void remove_node(desktop_t *d, node_t *n) update_current(); } +void destroy_tree(node_t *n) +{ + if (n == NULL) + return; + node_t *first_tree = n->first_child; + node_t *second_tree = n->second_child; + if (n->client != NULL) + free(n->client); + free(n); + destroy_tree(first_tree); + destroy_tree(second_tree); +} + void swap_nodes(node_t *n1, node_t *n2) { if (n1 == NULL || n2 == NULL || n1 == n2) @@ -546,6 +744,8 @@ void swap_nodes(node_t *n1, node_t *n2) node_t *pn2 = n2->parent; bool n1_first_child = is_first_child(n1); bool n2_first_child = is_first_child(n2); + rotate_t br1 = n1->birth_rotation; + rotate_t br2 = n2->birth_rotation; if (pn1 != NULL) { if (n1_first_child) @@ -563,31 +763,34 @@ void swap_nodes(node_t *n1, node_t *n2) n1->parent = pn2; n2->parent = pn1; + n1->birth_rotation = br2; + n2->birth_rotation = br1; if (n1->vacant != n2->vacant) { update_vacant_state(n1->parent); update_vacant_state(n2->parent); } -} -void fit_monitor(monitor_t *m, client_t *c) -{ - xcb_rectangle_t crect = c->floating_rectangle; - xcb_rectangle_t mrect = m->rectangle; - while (crect.x < mrect.x) - crect.x += mrect.width; - while (crect.x > (mrect.x + mrect.width - 1)) - crect.x -= mrect.width; - while (crect.y < mrect.y) - crect.y += mrect.height; - while (crect.y > (mrect.y + mrect.height - 1)) - crect.y -= mrect.height; - c->floating_rectangle = crect; + /* If we ever need to generalize: */ + /* if (d1 != d2) { */ + /* if (d1->root == n1) */ + /* d1->root = n2; */ + /* if (d1->focus == n1) */ + /* d1->focus = n2; */ + /* if (d1->last_focus == n1) */ + /* d1->last_focus = n2; */ + /* if (d2->root == n2) */ + /* d2->root = n1; */ + /* if (d2->focus == n2) */ + /* d2->focus = n1; */ + /* if (d2->last_focus == n2) */ + /* d2->last_focus = n1; */ + /* } */ } void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n) { - if (n == NULL || ds == NULL || dd == NULL || ms == NULL || md == NULL || (ms == md && dd == ds)) + if (n == NULL || dd == ds) return; PRINTF("transfer node %X\n", n->client->window); @@ -597,20 +800,23 @@ void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, n ewmh_set_wm_desktop(n, dd); if (ds == ms->desk && dd != md->desk) { + if (n == ds->focus) + clear_input_focus(); window_hide(n->client->window); } fit_monitor(md, n->client); - if (n->client->fullscreen) - window_move_resize(n->client->window, md->rectangle.x, md->rectangle.y, md->rectangle.width, md->rectangle.height); - - if (ds != ms->desk && dd == md->desk) { + if (ds != ms->desk && dd == md->desk) window_show(n->client->window); - focus_node(md, dd, n, true); - } else { - focus_node(md, dd, n, false); - } + + pseudo_focus(dd, n); + + if (md->desk == dd) + stack(dd, n); + + arrange(ms, ds); + arrange(md, dd); if (ds == ms->desk || dd == md->desk) update_current(); @@ -618,55 +824,48 @@ void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, n void select_monitor(monitor_t *m) { - if (m == NULL || mon == m) + if (mon == m) return; PRINTF("select monitor %s\n", m->name); - focus_node(m, m->desk, m->desk->focus, true); - last_mon = mon; mon = m; + if (pointer_follows_monitor) + center_pointer(m); + ewmh_update_current_desktop(); put_status(); } -void select_desktop(desktop_t *d) +void select_desktop(monitor_t *m, desktop_t *d) { - if (d == NULL || d == mon->desk) + select_monitor(m); + + if (d == mon->desk) return; PRINTF("select desktop %s\n", d->name); - node_t *n = first_extrema(d->root); - - while (n != NULL) { - window_show(n->client->window); - n = next_leaf(n); - } - - n = first_extrema(mon->desk->root); - - while (n != NULL) { - window_hide(n->client->window); - n = next_leaf(n); - } + desktop_show(d); + desktop_hide(mon->desk); mon->last_desk = mon->desk; mon->desk = d; - update_current(); ewmh_update_current_desktop(); put_status(); } void cycle_monitor(cycle_dir_t dir) { + monitor_t *m = NULL; if (dir == CYCLE_NEXT) - select_monitor((mon->next == NULL ? mon_head : mon->next)); + m = (mon->next == NULL ? mon_head : mon->next); else if (dir == CYCLE_PREV) - select_monitor((mon->prev == NULL ? mon_tail : mon->prev)); + m = (mon->prev == NULL ? mon_tail : mon->prev); + focus_node(m, m->desk, m->desk->focus); } void cycle_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, skip_desktop_t skip) @@ -676,10 +875,10 @@ void cycle_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, skip_desktop_t s f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); while (f != d) { - if (skip == DESKTOP_SKIP_NONE + if (skip == DESKTOP_SKIP_NONE || (skip == DESKTOP_SKIP_FREE && f->root != NULL) || (skip == DESKTOP_SKIP_OCCUPIED && f->root == NULL)) { - select_desktop(f); + focus_node(m, f, f->focus); return; } f = (dir == CYCLE_PREV ? f->prev : f->next); @@ -695,7 +894,7 @@ void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_cli PUTS("cycle leaf"); - node_t *f = (dir == CYCLE_PREV ? prev_leaf(n) : next_leaf(n)); + node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root)); if (f == NULL) f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); @@ -704,10 +903,10 @@ void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_cli if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !tiled) || (skip == CLIENT_SKIP_FLOATING && tiled) || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0) || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) { - focus_node(m, d, f, true); + focus_node(m, d, f); return; } - f = (dir == CYCLE_PREV ? prev_leaf(f) : next_leaf(f)); + f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root)); if (f == NULL) f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); } @@ -722,7 +921,7 @@ void nearest_leaf(monitor_t *m, desktop_t *d, node_t *n, nearest_arg_t dir, skip node_t *x = NULL; - for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f)) + for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !is_tiled(f->client)) || (skip == CLIENT_SKIP_FLOATING && is_tiled(f->client)) || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0) || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) @@ -734,24 +933,25 @@ void nearest_leaf(monitor_t *m, desktop_t *d, node_t *n, nearest_arg_t dir, skip && (x == NULL || f->client->uid < x->client->uid))) x = f; - focus_node(m, d, x, true); + focus_node(m, d, x); } -void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir) { +void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir) +{ if (d == NULL || d->root == NULL || is_leaf(d->root)) return; node_t *par = d->focus->parent; bool focus_first_child = is_first_child(d->focus); if (dir == CIRCULATE_FORWARD) - for (node_t *s = second_extrema(d->root), *f = prev_leaf(s); f != NULL; s = prev_leaf(f), f = prev_leaf(s)) + for (node_t *s = second_extrema(d->root), *f = prev_leaf(s, d->root); f != NULL; s = prev_leaf(f, d->root), f = prev_leaf(s, d->root)) swap_nodes(f, s); else - for (node_t *f = first_extrema(d->root), *s = next_leaf(f); s != NULL; f = next_leaf(s), s = next_leaf(f)) + for (node_t *f = first_extrema(d->root), *s = next_leaf(f, d->root); s != NULL; f = next_leaf(s, d->root), s = next_leaf(f, d->root)) swap_nodes(f, s); if (focus_first_child) - focus_node(m, d, par->first_child, true); + focus_node(m, d, par->first_child); else - focus_node(m, d, par->second_child, true); + focus_node(m, d, par->second_child); } void update_vacant_state(node_t *n) @@ -770,43 +970,278 @@ void update_vacant_state(node_t *n) } } -monitor_t *find_monitor(char *name) +void fit_monitor(monitor_t *m, client_t *c) { - for (monitor_t *m = mon_head; m != NULL; m = m->next) - if (strcmp(m->name, name) == 0) - return m; - return NULL; + xcb_rectangle_t crect = c->floating_rectangle; + xcb_rectangle_t mrect = m->rectangle; + while (crect.x < mrect.x) + crect.x += mrect.width; + while (crect.x > (mrect.x + mrect.width - 1)) + crect.x -= mrect.width; + while (crect.y < mrect.y) + crect.y += mrect.height; + while (crect.y > (mrect.y + mrect.height - 1)) + crect.y -= mrect.height; + c->floating_rectangle = crect; } -void add_desktop(monitor_t *m, char *name) +void put_status(void) { - desktop_t *d = make_desktop(name); - if (m->desk == NULL) { - m->desk = d; - m->desk_head = d; - m->desk_tail = d; - } else { - m->desk_tail->next = d; - d->prev = m->desk_tail; - m->desk_tail = d; + if (status_fifo == NULL) + return; + if (status_prefix != NULL) + fprintf(status_fifo, "%s", status_prefix); + bool urgent = false; + for (monitor_t *m = mon_head; m != NULL; m = m->next) { + fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name); + for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) { + for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root)) + urgent |= n->client->urgent; + fprintf(status_fifo, "%c%s:", m->desk == d ? (urgent ? 'U' : 'D') : (d->root == NULL ? 'E' : (urgent ? 'u' : 'd')), d->name); + } + } + if (mon != NULL && mon->desk != NULL) + fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle")); + fprintf(status_fifo, "\n"); + fflush(status_fifo); +} + +void list_monitors(list_option_t opt, char *rsp) +{ + char line[MAXLEN]; + for (monitor_t *m = mon_head; m != NULL; m = m->next) { + snprintf(line, sizeof(line), "%s %ux%u%+i%+i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y); + strncat(rsp, line, REMLEN(rsp)); + if (m == mon) + strncat(rsp, " #\n", REMLEN(rsp)); + else if (m == last_mon) + strncat(rsp, " ~\n", REMLEN(rsp)); + else + strncat(rsp, "\n", REMLEN(rsp)); + if (opt == LIST_OPTION_VERBOSE) + list_desktops(m, opt, 1, rsp); } - num_desktops++; - ewmh_update_number_of_desktops(); - ewmh_update_desktop_names(); - put_status(); } -void add_monitor(xcb_rectangle_t *rect) +void list_desktops(monitor_t *m, list_option_t opt, unsigned int depth, char *rsp) { - monitor_t *m = make_monitor(rect); - if (mon == NULL) { - mon = m; - mon_head = m; - mon_tail = m; + char line[MAXLEN]; + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { + for (unsigned int i = 0; i < depth; i++) + strncat(rsp, " ", REMLEN(rsp)); + snprintf(line, sizeof(line), "%s %c", d->name, (d->layout == LAYOUT_TILED ? 'T' : 'M')); + strncat(rsp, line, REMLEN(rsp)); + if (d == m->desk) + strncat(rsp, " @\n", REMLEN(rsp)); + else if (d == m->last_desk) + strncat(rsp, " ~\n", REMLEN(rsp)); + else + strncat(rsp, "\n", REMLEN(rsp)); + if (opt == LIST_OPTION_VERBOSE) + list(d, d->root, rsp, depth + 1); + } +} + +void list(desktop_t *d, node_t *n, char *rsp, unsigned int depth) +{ + if (n == NULL) + return; + + char line[MAXLEN]; + + for (unsigned int i = 0; i < depth; i++) + strncat(rsp, " ", REMLEN(rsp)); + + if (is_leaf(n)) { + client_t *c = n->client; + snprintf(line, sizeof(line), "%c %s %X %u %u %ux%u%+i%+i %c%c%c%c%c", (n->birth_rotation == ROTATE_CLOCKWISE ? 'a' : (n->birth_rotation == ROTATE_COUNTER_CLOCKWISE ? 'c' : 'm')), c->class_name, c->window, c->uid, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-')); } else { - mon_tail->next = m; - m->prev = mon_tail; - mon_tail = m; + snprintf(line, sizeof(line), "%c %c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == ROTATE_CLOCKWISE ? 'a' : (n->birth_rotation == ROTATE_COUNTER_CLOCKWISE ? 'c' : 'm')), n->split_ratio); + } + + strncat(rsp, line, REMLEN(rsp)); + + if (n == d->focus) + strncat(rsp, " *\n", REMLEN(rsp)); + else + strncat(rsp, "\n", REMLEN(rsp)); + + list(d, n->first_child, rsp, depth + 1); + list(d, n->second_child, rsp, depth + 1); +} + +void restore_layout(char *file_path) +{ + if (file_path == NULL) + return; + + FILE *snapshot = fopen(file_path, "r"); + if (snapshot == NULL) { + warn("restore: can't open file\n"); + return; } - num_monitors++; + + PUTS("restore layout"); + + char line[MAXLEN]; + monitor_t *m = NULL; + desktop_t *d = NULL; + node_t *n = NULL; + num_clients = 0; + unsigned int level, last_level = 0, max_uid = 0; + bool aborted = false; + + while (!aborted && fgets(line, sizeof(line), snapshot) != NULL) { + unsigned int len = strlen(line); + level = 0; + while (level < strlen(line) && isspace(line[level])) + level++; + if (level == 0) { + if (m == NULL) + m = mon_head; + else + m = m->next; + if (len >= 2) + switch (line[len - 2]) { + case '#': + mon = m; + break; + case '~': + last_mon = m; + break; + } + } else if (level == 2) { + if (d == NULL) + d = m->desk_head; + else + d = d->next; + int i = len - 1; + while (i > 0 && !isupper(line[i])) + i--; + if (line[i] == 'M') + d->layout = LAYOUT_MONOCLE; + else if (line[i] == 'T') + d->layout = LAYOUT_TILED; + if (len >= 2) + switch (line[len - 2]) { + case '@': + m->desk = d; + break; + case '~': + m->last_desk = d; + break; + } + } else { + node_t *birth = make_node(); + if (level == 4) { + empty_desktop(d); + d->root = birth; + } else { + if (level > last_level) { + n->first_child = birth; + } else { + do { + n = n->parent; + } while (n != NULL && n->second_child != NULL); + if (n == NULL) { + warn("restore: file is malformed\n"); + aborted = true; + } + n->second_child = birth; + } + birth->parent = n; + } + n = birth; + + char br; + if (isupper(line[level])) { + char st; + sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio); + if (st == 'H') + n->split_type = TYPE_HORIZONTAL; + else if (st == 'V') + n->split_type = TYPE_VERTICAL; + } else { + client_t *c = make_client(XCB_NONE); + num_clients++; + char floating, transient, fullscreen, urgent, locked; + sscanf(line + level, "%c %s %X %u %u %hux%hu%hi%hi %c%c%c%c%c", &br, c->class_name, &c->window, &c->uid, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &floating, &transient, &fullscreen, &urgent, &locked); + c->floating = (floating == '-' ? false : true); + c->transient = (transient == '-' ? false : true); + c->fullscreen = (fullscreen == '-' ? false : true); + c->urgent = (urgent == '-' ? false : true); + c->locked = (locked == '-' ? false : true); + if (c->uid > max_uid) + max_uid = c->uid; + n->client = c; + if (len >= 2 && line[len - 2] == '*') + d->focus = n; + } + if (br == 'a') + n->birth_rotation = ROTATE_CLOCKWISE; + else if (br == 'c') + n->birth_rotation = ROTATE_COUNTER_CLOCKWISE; + else if (br == 'm') + n->birth_rotation = ROTATE_IDENTITY; + } + last_level = level; + } + + fclose(snapshot); + + if (!aborted) { + client_uid = max_uid + 1; + for (monitor_t *m = mon_head; m != NULL; m = m->next) + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) + for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { + uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)}; + xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values); + if (n->client->floating) { + n->vacant = true; + update_vacant_state(n->parent); + } + } + ewmh_update_current_desktop(); + } +} + +void restore_history(char *file_path) +{ + if (file_path == NULL) + return; + + FILE *snapshot = fopen(file_path, "r"); + if (snapshot == NULL) { + warn("restore history: can't open file\n"); + return; + } + + PUTS("restore history"); + + char line[MAXLEN]; + desktop_t *d = NULL; + unsigned int level; + + while (fgets(line, sizeof(line), snapshot) != NULL) { + unsigned int i = strlen(line) - 1; + while (i > 0 && isspace(line[i])) + line[i--] = '\0'; + level = 0; + while (level < strlen(line) && isspace(line[level])) + level++; + if (level == 0) { + desktop_location_t loc; + if (locate_desktop(line + level, &loc)) + d = loc.desktop; + } else if (d != NULL) { + xcb_window_t win; + if (sscanf(line + level, "%X", &win) == 1) { + window_location_t loc; + if (locate_window(win, &loc)) + history_add(d->history, loc.node); + } + } + } + + fclose(snapshot); }