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
|
* borderless and the only element in the tabbed container, the border is not
|
||||||
* rendered.
|
* rendered.
|
||||||
*
|
*
|
||||||
|
* For children of a CT_DOCKAREA, the border style is always none.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int con_border_style(Con *con);
|
int con_border_style(Con *con);
|
||||||
|
|
||||||
|
|
|
@ -263,23 +263,40 @@ struct Match {
|
||||||
|
|
||||||
enum { M_USER = 0, M_RESTART } source;
|
enum { M_USER = 0, M_RESTART } source;
|
||||||
|
|
||||||
/* wo das fenster eingefügt werden soll. bei here wird es direkt
|
/* Where the window looking for a match should be inserted:
|
||||||
* diesem Con zugewiesen, also layout saving. bei active ist es
|
*
|
||||||
* ein assignment, welches an der momentan fokussierten stelle einfügt */
|
* M_HERE = the matched container will be replaced by the window
|
||||||
enum { M_HERE = 0, M_ACTIVE } insert_where;
|
* (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;
|
TAILQ_ENTRY(Match) matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Con {
|
struct Con {
|
||||||
bool mapped;
|
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;
|
orientation_t orientation;
|
||||||
struct Con *parent;
|
struct Con *parent;
|
||||||
|
|
||||||
struct Rect rect;
|
struct Rect rect;
|
||||||
struct Rect window_rect;
|
struct Rect window_rect;
|
||||||
struct Rect deco_rect;
|
struct Rect deco_rect;
|
||||||
|
/** the geometry this window requested when getting mapped */
|
||||||
|
struct Rect geometry;
|
||||||
|
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
|
@ -332,7 +349,7 @@ struct Con {
|
||||||
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
||||||
|
|
||||||
enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode;
|
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;
|
border_style_t border_style;
|
||||||
/** floating? (= not in tiling layout) This cannot be simply a bool
|
/** 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
|
* 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
|
* 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
|
* Splits (horizontally or vertically) the given container by creating a new
|
||||||
|
|
|
@ -403,7 +403,7 @@ open:
|
||||||
TOK_OPEN
|
TOK_OPEN
|
||||||
{
|
{
|
||||||
printf("opening new container\n");
|
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);
|
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;
|
Con *result = con;
|
||||||
while (result != NULL && result->type != CT_WORKSPACE)
|
while (result != NULL && result->type != CT_WORKSPACE)
|
||||||
result = result->parent;
|
result = result->parent;
|
||||||
assert(result != NULL);
|
|
||||||
return result;
|
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
|
* borderless and the only element in the tabbed container, the border is not
|
||||||
* rendered.
|
* rendered.
|
||||||
*
|
*
|
||||||
|
* For children of a CT_DOCKAREA, the border style is always none.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int con_border_style(Con *con) {
|
int con_border_style(Con *con) {
|
||||||
Con *fs = con_get_fullscreen_con(con->parent);
|
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)
|
if (con->parent->layout == L_TABBED && con->border_style != BS_NORMAL)
|
||||||
return (con_num_children(con->parent) == 1 ? 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;
|
return con->border_style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,8 +777,12 @@ void con_set_layout(Con *con, int layout) {
|
||||||
static void con_on_remove_child(Con *con) {
|
static void con_on_remove_child(Con *con) {
|
||||||
DLOG("on_remove_child\n");
|
DLOG("on_remove_child\n");
|
||||||
|
|
||||||
/* Nothing to do for workspaces */
|
/* Every container 'above' (in the hierarchy) the workspace content should
|
||||||
if (con->type == CT_WORKSPACE || con->type == CT_OUTPUT || con->type == CT_ROOT) {
|
* 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);
|
DLOG("not handling, type = %d\n", con->type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
63
src/ipc.c
63
src/ipc.c
|
@ -288,44 +288,47 @@ IPC_HANDLER(get_workspaces) {
|
||||||
|
|
||||||
Con *output;
|
Con *output;
|
||||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
Con *ws;
|
Con *child;
|
||||||
TAILQ_FOREACH(ws, &(output->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(output->nodes_head), nodes) {
|
||||||
assert(ws->type == CT_WORKSPACE);
|
Con *ws;
|
||||||
y(map_open);
|
TAILQ_FOREACH(ws, &(child->nodes_head), nodes) {
|
||||||
|
assert(ws->type == CT_WORKSPACE);
|
||||||
|
y(map_open);
|
||||||
|
|
||||||
ystr("num");
|
ystr("num");
|
||||||
if (ws->num == -1)
|
if (ws->num == -1)
|
||||||
y(null);
|
y(null);
|
||||||
else y(integer, ws->num);
|
else y(integer, ws->num);
|
||||||
|
|
||||||
ystr("name");
|
ystr("name");
|
||||||
ystr(ws->name);
|
ystr(ws->name);
|
||||||
|
|
||||||
ystr("visible");
|
ystr("visible");
|
||||||
y(bool, workspace_is_visible(ws));
|
y(bool, workspace_is_visible(ws));
|
||||||
|
|
||||||
ystr("focused");
|
ystr("focused");
|
||||||
y(bool, ws == focused_ws);
|
y(bool, ws == focused_ws);
|
||||||
|
|
||||||
ystr("rect");
|
ystr("rect");
|
||||||
y(map_open);
|
y(map_open);
|
||||||
ystr("x");
|
ystr("x");
|
||||||
y(integer, ws->rect.x);
|
y(integer, ws->rect.x);
|
||||||
ystr("y");
|
ystr("y");
|
||||||
y(integer, ws->rect.y);
|
y(integer, ws->rect.y);
|
||||||
ystr("width");
|
ystr("width");
|
||||||
y(integer, ws->rect.width);
|
y(integer, ws->rect.width);
|
||||||
ystr("height");
|
ystr("height");
|
||||||
y(integer, ws->rect.height);
|
y(integer, ws->rect.height);
|
||||||
y(map_close);
|
y(map_close);
|
||||||
|
|
||||||
ystr("output");
|
ystr("output");
|
||||||
ystr(output->name);
|
ystr(output->name);
|
||||||
|
|
||||||
ystr("urgent");
|
ystr("urgent");
|
||||||
y(bool, ws->urgent);
|
y(bool, ws->urgent);
|
||||||
|
|
||||||
y(map_close);
|
y(map_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",
|
LOG("using current container, focused = %p, focused->name = %s\n",
|
||||||
focused, focused->name);
|
focused, focused->name);
|
||||||
nc = focused;
|
nc = focused;
|
||||||
} else nc = tree_open_con(NULL);
|
} else nc = tree_open_con(NULL, true);
|
||||||
} else {
|
} else {
|
||||||
|
/* M_ACTIVE are assignments */
|
||||||
if (match != NULL && match->insert_where == M_ACTIVE) {
|
if (match != NULL && match->insert_where == M_ACTIVE) {
|
||||||
/* We need to go down the focus stack starting from nc */
|
/* We need to go down the focus stack starting from nc */
|
||||||
while (TAILQ_FIRST(&(nc->focus_head)) != TAILQ_END(&(nc->focus_head))) {
|
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 */
|
/* We need to open a new con */
|
||||||
/* TODO: make a difference between match-once containers (directly assign
|
/* TODO: make a difference between match-once containers (directly assign
|
||||||
* cwindow) and match-multiple (tree_open_con first) */
|
* 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);
|
DLOG("new container = %p\n", nc);
|
||||||
nc->window = cwindow;
|
nc->window = cwindow;
|
||||||
x_reinit(nc);
|
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))
|
con_by_window_id(cwindow->leader) != NULL))
|
||||||
want_floating = true;
|
want_floating = true;
|
||||||
|
|
||||||
|
nc->geometry = (Rect){ geom->x, geom->y, geom->width, geom->height };
|
||||||
|
|
||||||
if (want_floating) {
|
if (want_floating) {
|
||||||
nc->rect.x = geom->x;
|
nc->rect.x = geom->x;
|
||||||
nc->rect.y = geom->y;
|
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);
|
FREE(con->name);
|
||||||
con->name = sstrdup(output->name);
|
con->name = sstrdup(output->name);
|
||||||
con->type = CT_OUTPUT;
|
con->type = CT_OUTPUT;
|
||||||
|
con->layout = L_OUTPUT;
|
||||||
}
|
}
|
||||||
con->rect = output->rect;
|
con->rect = output->rect;
|
||||||
output->con = con;
|
output->con = con;
|
||||||
|
@ -257,12 +258,43 @@ void output_init_con(Output *output) {
|
||||||
char *name;
|
char *name;
|
||||||
asprintf(&name, "[i3 con] output %s", con->name);
|
asprintf(&name, "[i3 con] output %s", con->name);
|
||||||
x_set_name(con, name);
|
x_set_name(con, name);
|
||||||
free(name);
|
FREE(name);
|
||||||
|
|
||||||
if (reused) {
|
if (reused) {
|
||||||
DLOG("Not adding workspace, this was a reused con\n");
|
DLOG("Not adding workspace, this was a reused con\n");
|
||||||
return;
|
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");
|
DLOG("Now adding a workspace\n");
|
||||||
|
|
||||||
/* add a workspace to this output */
|
/* 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);
|
DLOG("result for ws %s / %d: exists = %d\n", ws->name, c, exists);
|
||||||
}
|
}
|
||||||
ws->num = c;
|
ws->num = c;
|
||||||
con_attach(ws, con, false);
|
con_attach(ws, content, false);
|
||||||
|
|
||||||
asprintf(&name, "[i3 con] workspace %s", ws->name);
|
asprintf(&name, "[i3 con] workspace %s", ws->name);
|
||||||
x_set_name(ws, name);
|
x_set_name(ws, name);
|
||||||
|
|
91
src/render.c
91
src/render.c
|
@ -8,6 +8,73 @@
|
||||||
* container (for debugging purposes) */
|
* container (for debugging purposes) */
|
||||||
static bool show_debug_borders = false;
|
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
|
* "Renders" the given container (and its children), meaning that all rects are
|
||||||
* updated correctly. Note that this function does not call any xcb_*
|
* 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 */
|
/* 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) {
|
if (fullscreen) {
|
||||||
DLOG("got fs node: %p\n", fullscreen);
|
DLOG("got fs node: %p\n", fullscreen);
|
||||||
fullscreen->rect = rect;
|
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;
|
Con *child;
|
||||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
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",
|
DLOG("child at (%d, %d) with (%d x %d)\n",
|
||||||
child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||||
DLOG("x now %d, y now %d\n", x, y);
|
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);
|
render_con(foc, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Con *child;
|
||||||
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
||||||
DLOG("render floating:\n");
|
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);
|
DLOG("floating child at (%d,%d) with %d x %d\n", child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||||
|
|
10
src/tree.c
10
src/tree.c
|
@ -55,18 +55,21 @@ void tree_init() {
|
||||||
* Opens an empty container in the current container
|
* 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) {
|
if (con == NULL) {
|
||||||
/* every focusable Con has a parent (outputs have parent root) */
|
/* every focusable Con has a parent (outputs have parent root) */
|
||||||
con = focused->parent;
|
con = focused->parent;
|
||||||
/* If the parent is an output, we are on a workspace. In this case,
|
/* 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. */
|
* 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;
|
con = focused;
|
||||||
|
}
|
||||||
|
|
||||||
/* If the currently focused container is a floating container, we
|
/* If the currently focused container is a floating container, we
|
||||||
* attach the new container to the workspace */
|
* attach the new container to the workspace */
|
||||||
if (con->type == CT_FLOATING_CON)
|
if (con->type == CT_FLOATING_CON)
|
||||||
con = con->parent;
|
con = con->parent;
|
||||||
|
DLOG("con = %p\n", con);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(con != NULL);
|
assert(con != NULL);
|
||||||
|
@ -78,7 +81,8 @@ Con *tree_open_con(Con *con) {
|
||||||
con_fix_percent(con);
|
con_fix_percent(con);
|
||||||
|
|
||||||
/* 5: focus the new container */
|
/* 5: focus the new container */
|
||||||
con_focus(new);
|
if (focus_it)
|
||||||
|
con_focus(new);
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,23 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Con *workspace_get(const char *num) {
|
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?
|
/* TODO: could that look like this in the future?
|
||||||
GET_MATCHING_NODE(workspace, croot, strcasecmp(current->name, num) != 0);
|
GET_MATCHING_NODE(workspace, croot, strcasecmp(current->name, num) != 0);
|
||||||
*/
|
*/
|
||||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
TAILQ_FOREACH(current, &(output->nodes_head), nodes) {
|
TAILQ_FOREACH(current, &(output->nodes_head), nodes) {
|
||||||
if (strcasecmp(current->name, num) != 0)
|
if (current->type != CT_CON)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
workspace = current;
|
TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
|
||||||
break;
|
if (strcasecmp(child->name, num) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
workspace = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +42,15 @@ Con *workspace_get(const char *num) {
|
||||||
if (workspace == NULL) {
|
if (workspace == NULL) {
|
||||||
LOG("need to create this one\n");
|
LOG("need to create this one\n");
|
||||||
output = con_get_output(focused);
|
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
|
/* We need to attach this container after setting its type. con_attach
|
||||||
* will handle CT_WORKSPACEs differently */
|
* will handle CT_WORKSPACEs differently */
|
||||||
workspace = con_new(NULL);
|
workspace = con_new(NULL);
|
||||||
|
@ -60,7 +73,7 @@ Con *workspace_get(const char *num) {
|
||||||
else workspace->num = parsed_num;
|
else workspace->num = parsed_num;
|
||||||
LOG("num = %d\n", workspace->num);
|
LOG("num = %d\n", workspace->num);
|
||||||
workspace->orientation = HORIZ;
|
workspace->orientation = HORIZ;
|
||||||
con_attach(workspace, output, false);
|
con_attach(workspace, content, false);
|
||||||
|
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
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
|
/* 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
|
* workspace we chose as 'old'. If not, use the workspace of the currently
|
||||||
* focused con */
|
* focused con */
|
||||||
if (con_get_workspace(focused)->parent != old->parent)
|
Con *ws = con_get_workspace(focused);
|
||||||
old = con_get_workspace(focused);
|
if (ws && ws->parent != old->parent)
|
||||||
|
old = ws;
|
||||||
|
|
||||||
/* enable fullscreen for the target workspace. If it happens to be the
|
/* enable fullscreen for the target workspace. If it happens to be the
|
||||||
* same one we are currently on anyways, we can stop here. */
|
* 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);
|
LOG("switching to %p\n", workspace);
|
||||||
Con *next = con_descend_focused(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 */
|
/* check if this workspace is currently visible */
|
||||||
if (!workspace_is_visible(old)) {
|
if (!workspace_is_visible(old)) {
|
||||||
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
|
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);
|
DLOG("il_parent = %p, layout = %d\n", il_parent, il_parent->layout);
|
||||||
if (il_parent->layout == L_STACKED)
|
if (il_parent->layout == L_STACKED)
|
||||||
indent_level++;
|
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;
|
break;
|
||||||
il_parent = il_parent->parent;
|
il_parent = il_parent->parent;
|
||||||
indent_mult++;
|
indent_mult++;
|
||||||
|
|
|
@ -18,7 +18,15 @@ sub fullscreen_windows {
|
||||||
# get the output of this workspace
|
# get the output of this workspace
|
||||||
my $tree = $i3->get_tree->recv;
|
my $tree = $i3->get_tree->recv;
|
||||||
my @outputs = @{$tree->{nodes}};
|
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 {
|
BEGIN {
|
||||||
use_ok('X11::XCB::Window');
|
use_ok('X11::XCB::Window');
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use i3test tests => 7;
|
use i3test tests => 7;
|
||||||
use List::MoreUtils qw(all none);
|
use List::MoreUtils qw(all none);
|
||||||
|
use List::Util qw(first);
|
||||||
|
|
||||||
my $i3 = i3("/tmp/nestedcons");
|
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((none { defined($_->{window}) } @nodes), 'no CT_OUTPUT contains a window');
|
||||||
ok((all { @{$_->{nodes}} > 0 } @nodes), 'all nodes have at least one leaf (workspace)');
|
ok((all { @{$_->{nodes}} > 0 } @nodes), 'all nodes have at least one leaf (workspace)');
|
||||||
my @workspaces;
|
my @workspaces;
|
||||||
for my $ws (map { @{$_->{nodes}} } @nodes) {
|
for my $ws (@nodes) {
|
||||||
push @workspaces, $ws;
|
my $content = first { $_->{type} == 2 } @{$ws->{nodes}};
|
||||||
|
@workspaces = (@workspaces, @{$content->{nodes}});
|
||||||
}
|
}
|
||||||
|
|
||||||
ok((all { $_->{type} == 4 } @workspaces), 'all workspaces are of type CT_WORKSPACE');
|
ok((all { $_->{type} == 4 } @workspaces), 'all workspaces are of type CT_WORKSPACE');
|
||||||
|
|
|
@ -65,10 +65,15 @@ sub open_empty_con {
|
||||||
|
|
||||||
sub get_workspace_names {
|
sub get_workspace_names {
|
||||||
my $i3 = i3("/tmp/nestedcons");
|
my $i3 = i3("/tmp/nestedcons");
|
||||||
# TODO: use correct command as soon as AnyEvent::i3 is updated
|
|
||||||
my $tree = $i3->get_tree->recv;
|
my $tree = $i3->get_tree->recv;
|
||||||
my @workspaces = map { @{$_->{nodes}} } @{$tree->{nodes}};
|
my @outputs = @{$tree->{nodes}};
|
||||||
[ map { $_->{name} } @workspaces ]
|
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 {
|
sub get_unused_workspace {
|
||||||
|
@ -82,11 +87,18 @@ sub get_ws {
|
||||||
my ($name) = @_;
|
my ($name) = @_;
|
||||||
my $i3 = i3("/tmp/nestedcons");
|
my $i3 = i3("/tmp/nestedcons");
|
||||||
my $tree = $i3->get_tree->recv;
|
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
|
# as there can only be one workspace with this name, we can safely
|
||||||
# return the first entry
|
# 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 {
|
sub get_focused {
|
||||||
my ($ws) = @_;
|
my ($ws) = @_;
|
||||||
my $i3 = i3("/tmp/nestedcons");
|
my $con = get_ws($ws);
|
||||||
my $tree = $i3->get_tree->recv;
|
|
||||||
|
|
||||||
my @ws = map { @{$_->{nodes}} } @{$tree->{nodes}};
|
|
||||||
my @cons = grep { $_->{name} eq $ws } @ws;
|
|
||||||
my $con = $cons[0];
|
|
||||||
|
|
||||||
my @focused = @{$con->{focus}};
|
my @focused = @{$con->{focus}};
|
||||||
my $lf;
|
my $lf;
|
||||||
|
|
Loading…
Reference in New Issue