Implement dock mode, update testsuite
Currently, dock clients are only possible at the top.
This commit is contained in:
parent
481ae6ccf2
commit
7f89c71689
|
@ -182,6 +182,8 @@ Rect con_border_style_rect(Con *con);
|
|||
* borderless and the only element in the tabbed container, the border is not
|
||||
* rendered.
|
||||
*
|
||||
* For children of a CT_DOCKAREA, the border style is always none.
|
||||
*
|
||||
*/
|
||||
int con_border_style(Con *con);
|
||||
|
||||
|
|
|
@ -263,23 +263,40 @@ struct Match {
|
|||
|
||||
enum { M_USER = 0, M_RESTART } source;
|
||||
|
||||
/* wo das fenster eingefügt werden soll. bei here wird es direkt
|
||||
* diesem Con zugewiesen, also layout saving. bei active ist es
|
||||
* ein assignment, welches an der momentan fokussierten stelle einfügt */
|
||||
enum { M_HERE = 0, M_ACTIVE } insert_where;
|
||||
/* Where the window looking for a match should be inserted:
|
||||
*
|
||||
* M_HERE = the matched container will be replaced by the window
|
||||
* (layout saving)
|
||||
* M_ACTIVE = the window will be inserted next to the currently focused
|
||||
* container below the matched container
|
||||
* (assignments)
|
||||
* M_BELOW = the window will be inserted as a child of the matched container
|
||||
* (dockareas)
|
||||
*
|
||||
*/
|
||||
enum { M_HERE = 0, M_ACTIVE, M_BELOW } insert_where;
|
||||
|
||||
TAILQ_ENTRY(Match) matches;
|
||||
};
|
||||
|
||||
struct Con {
|
||||
bool mapped;
|
||||
enum { CT_ROOT = 0, CT_OUTPUT = 1, CT_CON = 2, CT_FLOATING_CON = 3, CT_WORKSPACE = 4 } type;
|
||||
enum {
|
||||
CT_ROOT = 0,
|
||||
CT_OUTPUT = 1,
|
||||
CT_CON = 2,
|
||||
CT_FLOATING_CON = 3,
|
||||
CT_WORKSPACE = 4,
|
||||
CT_DOCKAREA = 5
|
||||
} type;
|
||||
orientation_t orientation;
|
||||
struct Con *parent;
|
||||
|
||||
struct Rect rect;
|
||||
struct Rect window_rect;
|
||||
struct Rect deco_rect;
|
||||
/** the geometry this window requested when getting mapped */
|
||||
struct Rect geometry;
|
||||
|
||||
char *name;
|
||||
|
||||
|
@ -332,7 +349,7 @@ struct Con {
|
|||
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
||||
|
||||
enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode;
|
||||
enum { L_DEFAULT = 0, L_STACKED = 1, L_TABBED = 2 } layout;
|
||||
enum { L_DEFAULT = 0, L_STACKED = 1, L_TABBED = 2, L_DOCKAREA = 3, L_OUTPUT = 4 } layout;
|
||||
border_style_t border_style;
|
||||
/** floating? (= not in tiling layout) This cannot be simply a bool
|
||||
* because we want to keep track of whether the status was set by the
|
||||
|
|
|
@ -24,7 +24,7 @@ void tree_init();
|
|||
* Opens an empty container in the current container
|
||||
*
|
||||
*/
|
||||
Con *tree_open_con(Con *con);
|
||||
Con *tree_open_con(Con *con, bool focus_it);
|
||||
|
||||
/**
|
||||
* Splits (horizontally or vertically) the given container by creating a new
|
||||
|
|
|
@ -403,7 +403,7 @@ open:
|
|||
TOK_OPEN
|
||||
{
|
||||
printf("opening new container\n");
|
||||
Con *con = tree_open_con(NULL);
|
||||
Con *con = tree_open_con(NULL, true);
|
||||
asprintf(&json_output, "{\"success\":true, \"id\":%ld}", (long int)con);
|
||||
}
|
||||
;
|
||||
|
|
14
src/con.c
14
src/con.c
|
@ -226,7 +226,6 @@ Con *con_get_workspace(Con *con) {
|
|||
Con *result = con;
|
||||
while (result != NULL && result->type != CT_WORKSPACE)
|
||||
result = result->parent;
|
||||
assert(result != NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -694,6 +693,8 @@ Rect con_border_style_rect(Con *con) {
|
|||
* borderless and the only element in the tabbed container, the border is not
|
||||
* rendered.
|
||||
*
|
||||
* For children of a CT_DOCKAREA, the border style is always none.
|
||||
*
|
||||
*/
|
||||
int con_border_style(Con *con) {
|
||||
Con *fs = con_get_fullscreen_con(con->parent);
|
||||
|
@ -708,6 +709,9 @@ int con_border_style(Con *con) {
|
|||
if (con->parent->layout == L_TABBED && con->border_style != BS_NORMAL)
|
||||
return (con_num_children(con->parent) == 1 ? con->border_style : BS_NORMAL);
|
||||
|
||||
if (con->parent->type == CT_DOCKAREA)
|
||||
return BS_NONE;
|
||||
|
||||
return con->border_style;
|
||||
}
|
||||
|
||||
|
@ -773,8 +777,12 @@ void con_set_layout(Con *con, int layout) {
|
|||
static void con_on_remove_child(Con *con) {
|
||||
DLOG("on_remove_child\n");
|
||||
|
||||
/* Nothing to do for workspaces */
|
||||
if (con->type == CT_WORKSPACE || con->type == CT_OUTPUT || con->type == CT_ROOT) {
|
||||
/* Every container 'above' (in the hierarchy) the workspace content should
|
||||
* not be closed when the last child was removed */
|
||||
if (con->type == CT_WORKSPACE ||
|
||||
con->type == CT_OUTPUT ||
|
||||
con->type == CT_ROOT ||
|
||||
con->type == CT_DOCKAREA) {
|
||||
DLOG("not handling, type = %d\n", con->type);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -288,8 +288,10 @@ IPC_HANDLER(get_workspaces) {
|
|||
|
||||
Con *output;
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(output->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output->nodes_head), nodes) {
|
||||
TAILQ_FOREACH(ws, &(child->nodes_head), nodes) {
|
||||
assert(ws->type == CT_WORKSPACE);
|
||||
y(map_open);
|
||||
|
||||
|
@ -328,6 +330,7 @@ IPC_HANDLER(get_workspaces) {
|
|||
y(map_close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
y(array_close);
|
||||
|
||||
|
|
14
src/manage.c
14
src/manage.c
|
@ -167,8 +167,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
LOG("using current container, focused = %p, focused->name = %s\n",
|
||||
focused, focused->name);
|
||||
nc = focused;
|
||||
} else nc = tree_open_con(NULL);
|
||||
} else nc = tree_open_con(NULL, true);
|
||||
} else {
|
||||
/* M_ACTIVE are assignments */
|
||||
if (match != NULL && match->insert_where == M_ACTIVE) {
|
||||
/* We need to go down the focus stack starting from nc */
|
||||
while (TAILQ_FIRST(&(nc->focus_head)) != TAILQ_END(&(nc->focus_head))) {
|
||||
|
@ -178,9 +179,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
/* We need to open a new con */
|
||||
/* TODO: make a difference between match-once containers (directly assign
|
||||
* cwindow) and match-multiple (tree_open_con first) */
|
||||
nc = tree_open_con(nc->parent);
|
||||
nc = tree_open_con(nc->parent, true);
|
||||
}
|
||||
|
||||
/* M_BELOW inserts the new window as a child of the one which was
|
||||
* matched (e.g. dock areas) */
|
||||
else if (match != NULL && match->insert_where == M_BELOW) {
|
||||
nc = tree_open_con(nc, !cwindow->dock);
|
||||
}
|
||||
}
|
||||
|
||||
DLOG("new container = %p\n", nc);
|
||||
nc->window = cwindow;
|
||||
x_reinit(nc);
|
||||
|
@ -208,6 +216,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
con_by_window_id(cwindow->leader) != NULL))
|
||||
want_floating = true;
|
||||
|
||||
nc->geometry = (Rect){ geom->x, geom->y, geom->width, geom->height };
|
||||
|
||||
if (want_floating) {
|
||||
nc->rect.x = geom->x;
|
||||
nc->rect.y = geom->y;
|
||||
|
|
36
src/randr.c
36
src/randr.c
|
@ -250,6 +250,7 @@ void output_init_con(Output *output) {
|
|||
FREE(con->name);
|
||||
con->name = sstrdup(output->name);
|
||||
con->type = CT_OUTPUT;
|
||||
con->layout = L_OUTPUT;
|
||||
}
|
||||
con->rect = output->rect;
|
||||
output->con = con;
|
||||
|
@ -257,12 +258,43 @@ void output_init_con(Output *output) {
|
|||
char *name;
|
||||
asprintf(&name, "[i3 con] output %s", con->name);
|
||||
x_set_name(con, name);
|
||||
free(name);
|
||||
FREE(name);
|
||||
|
||||
if (reused) {
|
||||
DLOG("Not adding workspace, this was a reused con\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG("Changing layout, adding top/bottom dockarea\n");
|
||||
Con *topdock = con_new(NULL);
|
||||
topdock->type = CT_DOCKAREA;
|
||||
topdock->layout = L_DOCKAREA;
|
||||
topdock->orientation = VERT;
|
||||
/* this container swallows dock clients */
|
||||
Match *match = scalloc(sizeof(Match));
|
||||
match_init(match);
|
||||
match->dock = true;
|
||||
match->insert_where = M_BELOW;
|
||||
TAILQ_INSERT_TAIL(&(topdock->swallow_head), match, matches);
|
||||
|
||||
topdock->name = sstrdup("topdock");
|
||||
|
||||
asprintf(&name, "[i3 con] top dockarea %s", con->name);
|
||||
x_set_name(topdock, name);
|
||||
FREE(name);
|
||||
DLOG("attaching\n");
|
||||
con_attach(topdock, con, false);
|
||||
|
||||
DLOG("adding main content container\n");
|
||||
Con *content = con_new(NULL);
|
||||
content->type = CT_CON;
|
||||
content->name = sstrdup("content");
|
||||
|
||||
asprintf(&name, "[i3 con] content %s", con->name);
|
||||
x_set_name(content, name);
|
||||
FREE(name);
|
||||
con_attach(content, con, false);
|
||||
|
||||
DLOG("Now adding a workspace\n");
|
||||
|
||||
/* add a workspace to this output */
|
||||
|
@ -295,7 +327,7 @@ void output_init_con(Output *output) {
|
|||
DLOG("result for ws %s / %d: exists = %d\n", ws->name, c, exists);
|
||||
}
|
||||
ws->num = c;
|
||||
con_attach(ws, con, false);
|
||||
con_attach(ws, content, false);
|
||||
|
||||
asprintf(&name, "[i3 con] workspace %s", ws->name);
|
||||
x_set_name(ws, name);
|
||||
|
|
91
src/render.c
91
src/render.c
|
@ -8,6 +8,73 @@
|
|||
* container (for debugging purposes) */
|
||||
static bool show_debug_borders = false;
|
||||
|
||||
/*
|
||||
* Renders a container with layout L_OUTPUT. In this layout, all CT_DOCKAREAs
|
||||
* get the height of their content and the remaining CT_CON gets the rest.
|
||||
*
|
||||
*/
|
||||
static void render_l_output(Con *con) {
|
||||
Con *child, *dockchild;
|
||||
|
||||
int x = con->rect.x;
|
||||
int y = con->rect.y;
|
||||
int height = con->rect.height;
|
||||
DLOG("Available height: %d\n", height);
|
||||
|
||||
/* First pass: determine the height of all CT_DOCKAREAs (the sum of their
|
||||
* children) and figure out how many pixels we have left for the rest */
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
if (child->type != CT_DOCKAREA)
|
||||
continue;
|
||||
|
||||
child->rect.height = 0;
|
||||
TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes)
|
||||
child->rect.height += dockchild->geometry.height;
|
||||
DLOG("This dockarea's height: %d\n", child->rect.height);
|
||||
|
||||
height -= child->rect.height;
|
||||
}
|
||||
|
||||
DLOG("Remaining: %d\n", height);
|
||||
|
||||
/* Second pass: Set the widths/heights */
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
if (child->type == CT_CON) {
|
||||
if (height == -1) {
|
||||
DLOG("More than one CT_CON on output container\n");
|
||||
assert(false);
|
||||
}
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = con->rect.width;
|
||||
child->rect.height = height;
|
||||
height = -1;
|
||||
}
|
||||
|
||||
else if (child->type != CT_DOCKAREA) {
|
||||
DLOG("Child %p of type %d is inside the OUTPUT con\n", child, child->type);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = con->rect.width;
|
||||
|
||||
child->deco_rect.x = 0;
|
||||
child->deco_rect.y = 0;
|
||||
child->deco_rect.width = 0;
|
||||
child->deco_rect.height = 0;
|
||||
|
||||
y += child->rect.height;
|
||||
|
||||
DLOG("child at (%d, %d) with (%d x %d)\n",
|
||||
child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||
DLOG("x now %d, y now %d\n", x, y);
|
||||
x_raise_con(child);
|
||||
render_con(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "Renders" the given container (and its children), meaning that all rects are
|
||||
* updated correctly. Note that this function does not call any xcb_*
|
||||
|
@ -95,7 +162,7 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
}
|
||||
|
||||
/* Check for fullscreen nodes */
|
||||
Con *fullscreen = con_get_fullscreen_con(con);
|
||||
Con *fullscreen = (con->type == CT_OUTPUT ? NULL : con_get_fullscreen_con(con));
|
||||
if (fullscreen) {
|
||||
DLOG("got fs node: %p\n", fullscreen);
|
||||
fullscreen->rect = rect;
|
||||
|
@ -130,6 +197,11 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
}
|
||||
}
|
||||
|
||||
if (con->layout == L_OUTPUT) {
|
||||
render_l_output(con);
|
||||
} else {
|
||||
|
||||
/* FIXME: refactor this into separate functions: */
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
|
||||
|
@ -202,6 +274,21 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
}
|
||||
}
|
||||
|
||||
/* dockarea layout */
|
||||
else if (con->layout == L_DOCKAREA) {
|
||||
DLOG("dockarea con\n");
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = rect.width;
|
||||
child->rect.height = child->geometry.height;
|
||||
|
||||
child->deco_rect.x = 0;
|
||||
child->deco_rect.y = 0;
|
||||
child->deco_rect.width = 0;
|
||||
child->deco_rect.height = 0;
|
||||
y += child->rect.height;
|
||||
}
|
||||
|
||||
DLOG("child at (%d, %d) with (%d x %d)\n",
|
||||
child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||
DLOG("x now %d, y now %d\n", x, y);
|
||||
|
@ -221,7 +308,9 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
render_con(foc, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
||||
DLOG("render floating:\n");
|
||||
DLOG("floating child at (%d,%d) with %d x %d\n", child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||
|
|
|
@ -55,18 +55,21 @@ void tree_init() {
|
|||
* Opens an empty container in the current container
|
||||
*
|
||||
*/
|
||||
Con *tree_open_con(Con *con) {
|
||||
Con *tree_open_con(Con *con, bool focus_it) {
|
||||
if (con == NULL) {
|
||||
/* every focusable Con has a parent (outputs have parent root) */
|
||||
con = focused->parent;
|
||||
/* If the parent is an output, we are on a workspace. In this case,
|
||||
* the new container needs to be opened as a leaf of the workspace. */
|
||||
if (con->type == CT_OUTPUT)
|
||||
if (con->parent->type == CT_OUTPUT && con->type != CT_DOCKAREA) {
|
||||
con = focused;
|
||||
}
|
||||
|
||||
/* If the currently focused container is a floating container, we
|
||||
* attach the new container to the workspace */
|
||||
if (con->type == CT_FLOATING_CON)
|
||||
con = con->parent;
|
||||
DLOG("con = %p\n", con);
|
||||
}
|
||||
|
||||
assert(con != NULL);
|
||||
|
@ -78,6 +81,7 @@ Con *tree_open_con(Con *con) {
|
|||
con_fix_percent(con);
|
||||
|
||||
/* 5: focus the new container */
|
||||
if (focus_it)
|
||||
con_focus(new);
|
||||
|
||||
return new;
|
||||
|
|
|
@ -18,26 +18,39 @@
|
|||
*
|
||||
*/
|
||||
Con *workspace_get(const char *num) {
|
||||
Con *output, *workspace = NULL, *current;
|
||||
Con *output, *workspace = NULL, *current, *child;
|
||||
|
||||
/* TODO: could that look like this in the future?
|
||||
GET_MATCHING_NODE(workspace, croot, strcasecmp(current->name, num) != 0);
|
||||
*/
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
TAILQ_FOREACH(current, &(output->nodes_head), nodes) {
|
||||
if (strcasecmp(current->name, num) != 0)
|
||||
if (current->type != CT_CON)
|
||||
continue;
|
||||
|
||||
workspace = current;
|
||||
TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
|
||||
if (strcasecmp(child->name, num) != 0)
|
||||
continue;
|
||||
|
||||
workspace = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG("getting ws %s\n", num);
|
||||
if (workspace == NULL) {
|
||||
LOG("need to create this one\n");
|
||||
output = con_get_output(focused);
|
||||
LOG("got output %p\n", output);
|
||||
Con *child, *content = NULL;
|
||||
TAILQ_FOREACH(child, &(output->nodes_head), nodes) {
|
||||
if (child->type == CT_CON) {
|
||||
content = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(content != NULL);
|
||||
LOG("got output %p with child %p\n", output, content);
|
||||
/* We need to attach this container after setting its type. con_attach
|
||||
* will handle CT_WORKSPACEs differently */
|
||||
workspace = con_new(NULL);
|
||||
|
@ -60,7 +73,7 @@ Con *workspace_get(const char *num) {
|
|||
else workspace->num = parsed_num;
|
||||
LOG("num = %d\n", workspace->num);
|
||||
workspace->orientation = HORIZ;
|
||||
con_attach(workspace, output, false);
|
||||
con_attach(workspace, content, false);
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||
}
|
||||
|
@ -214,8 +227,9 @@ void workspace_show(const char *num) {
|
|||
/* Check if the the currently focused con is on the same Output as the
|
||||
* workspace we chose as 'old'. If not, use the workspace of the currently
|
||||
* focused con */
|
||||
if (con_get_workspace(focused)->parent != old->parent)
|
||||
old = con_get_workspace(focused);
|
||||
Con *ws = con_get_workspace(focused);
|
||||
if (ws && ws->parent != old->parent)
|
||||
old = ws;
|
||||
|
||||
/* enable fullscreen for the target workspace. If it happens to be the
|
||||
* same one we are currently on anyways, we can stop here. */
|
||||
|
@ -228,7 +242,7 @@ void workspace_show(const char *num) {
|
|||
LOG("switching to %p\n", workspace);
|
||||
Con *next = con_descend_focused(workspace);
|
||||
|
||||
if (TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
|
||||
if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
|
||||
/* check if this workspace is currently visible */
|
||||
if (!workspace_is_visible(old)) {
|
||||
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
|
||||
|
|
2
src/x.c
2
src/x.c
|
@ -342,7 +342,7 @@ void x_draw_decoration(Con *con) {
|
|||
DLOG("il_parent = %p, layout = %d\n", il_parent, il_parent->layout);
|
||||
if (il_parent->layout == L_STACKED)
|
||||
indent_level++;
|
||||
if (il_parent->type == CT_WORKSPACE)
|
||||
if (il_parent->type == CT_WORKSPACE || il_parent->type == CT_DOCKAREA || il_parent->type == CT_OUTPUT)
|
||||
break;
|
||||
il_parent = il_parent->parent;
|
||||
indent_mult++;
|
||||
|
|
|
@ -18,7 +18,15 @@ sub fullscreen_windows {
|
|||
# get the output of this workspace
|
||||
my $tree = $i3->get_tree->recv;
|
||||
my @outputs = @{$tree->{nodes}};
|
||||
my $output = first { defined(first { $_->{name} eq $tmp } @{$_->{nodes}}) } @outputs;
|
||||
my $output;
|
||||
for my $o (@outputs) {
|
||||
# get the first CT_CON of each output
|
||||
my $content = first { $_->{type} == 2 } @{$o->{nodes}};
|
||||
if (defined(first { $_->{name} eq $tmp } @{$content->{nodes}})) {
|
||||
$output = $o;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
use_ok('X11::XCB::Window');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use i3test tests => 7;
|
||||
use List::MoreUtils qw(all none);
|
||||
use List::Util qw(first);
|
||||
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
|
||||
|
@ -41,8 +42,9 @@ ok((all { $_->{type} == 1 } @nodes), 'all nodes are of type CT_OUTPUT');
|
|||
ok((none { defined($_->{window}) } @nodes), 'no CT_OUTPUT contains a window');
|
||||
ok((all { @{$_->{nodes}} > 0 } @nodes), 'all nodes have at least one leaf (workspace)');
|
||||
my @workspaces;
|
||||
for my $ws (map { @{$_->{nodes}} } @nodes) {
|
||||
push @workspaces, $ws;
|
||||
for my $ws (@nodes) {
|
||||
my $content = first { $_->{type} == 2 } @{$ws->{nodes}};
|
||||
@workspaces = (@workspaces, @{$content->{nodes}});
|
||||
}
|
||||
|
||||
ok((all { $_->{type} == 4 } @workspaces), 'all workspaces are of type CT_WORKSPACE');
|
||||
|
|
|
@ -65,10 +65,15 @@ sub open_empty_con {
|
|||
|
||||
sub get_workspace_names {
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
# TODO: use correct command as soon as AnyEvent::i3 is updated
|
||||
my $tree = $i3->get_tree->recv;
|
||||
my @workspaces = map { @{$_->{nodes}} } @{$tree->{nodes}};
|
||||
[ map { $_->{name} } @workspaces ]
|
||||
my @outputs = @{$tree->{nodes}};
|
||||
my @cons;
|
||||
for my $output (@outputs) {
|
||||
# get the first CT_CON of each output
|
||||
my $content = first { $_->{type} == 2 } @{$output->{nodes}};
|
||||
@cons = (@cons, @{$content->{nodes}});
|
||||
}
|
||||
[ map { $_->{name} } @cons ]
|
||||
}
|
||||
|
||||
sub get_unused_workspace {
|
||||
|
@ -82,11 +87,18 @@ sub get_ws {
|
|||
my ($name) = @_;
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
my $tree = $i3->get_tree->recv;
|
||||
my @ws = map { @{$_->{nodes}} } @{$tree->{nodes}};
|
||||
|
||||
my @outputs = @{$tree->{nodes}};
|
||||
my @workspaces;
|
||||
for my $output (@outputs) {
|
||||
# get the first CT_CON of each output
|
||||
my $content = first { $_->{type} == 2 } @{$output->{nodes}};
|
||||
@workspaces = (@workspaces, @{$content->{nodes}});
|
||||
}
|
||||
|
||||
# as there can only be one workspace with this name, we can safely
|
||||
# return the first entry
|
||||
return first { $_->{name} eq $name } @ws;
|
||||
return first { $_->{name} eq $name } @workspaces;
|
||||
}
|
||||
|
||||
#
|
||||
|
@ -102,12 +114,7 @@ sub get_ws_content {
|
|||
|
||||
sub get_focused {
|
||||
my ($ws) = @_;
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
my $tree = $i3->get_tree->recv;
|
||||
|
||||
my @ws = map { @{$_->{nodes}} } @{$tree->{nodes}};
|
||||
my @cons = grep { $_->{name} eq $ws } @ws;
|
||||
my $con = $cons[0];
|
||||
my $con = get_ws($ws);
|
||||
|
||||
my @focused = @{$con->{focus}};
|
||||
my $lf;
|
||||
|
|
Loading…
Reference in New Issue