2 #define I3__FILE__ "tree.c"
28 __i3->
type = CT_OUTPUT;
31 x_set_name(__i3,
"[i3 con] pseudo-output __i3");
41 DLOG(
"adding main content container\n");
43 content->
type = CT_CON;
53 ws->
type = CT_WORKSPACE;
58 x_set_name(ws,
"[i3 con] workspace __i3_scratch");
68 bool tree_restore(
const char *path, xcb_get_geometry_reply_t *geometry) {
72 LOG(
"%s does not exist, not restoring tree\n", globbed);
89 DLOG(
"appended tree, using new root\n");
91 DLOG(
"new root = %p\n", croot);
93 DLOG(
"out = %p\n", out);
95 DLOG(
"ws = %p\n", ws);
99 if (strcmp(out->
name,
"__i3") != 0) {
100 DLOG(
"Adding pseudo-output __i3 during inplace restart\n");
121 croot->
name =
"root";
122 croot->
type = CT_ROOT;
150 if (con->
type == CT_FLOATING_CON) {
152 if (con->
type != CT_WORKSPACE)
155 DLOG(
"con = %p\n", con);
194 bool was_mapped = con->
mapped;
213 DLOG(
"next = %p, focused = %p\n", next, focused);
215 DLOG(
"closing %p, kill_window = %d\n", con, kill_window);
216 Con *child, *nextchild;
217 bool abort_kill =
false;
220 for (child =
TAILQ_FIRST(&(con->nodes_head)); child;) {
222 DLOG(
"killing child=%p\n", child);
229 DLOG(
"One of the children could not be killed immediately (WM_DELETE sent), aborting.\n");
233 if (con->
window != NULL) {
238 xcb_void_cookie_t cookie;
243 XCB_CW_EVENT_MASK, (uint32_t[]){XCB_NONE});
254 cookie = xcb_change_property(
conn, XCB_PROP_MODE_REPLACE,
255 con->
window->
id, A_WM_STATE, A_WM_STATE, 32, 2, data);
262 xcb_change_save_set(
conn, XCB_SET_MODE_DELETE, con->
window->
id);
277 if (con == focused) {
278 DLOG(
"This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
281 dont_kill_parent =
true;
282 DLOG(
"Alright, focusing %p\n", next);
292 if (con->urgency_timer != NULL) {
293 DLOG(
"Removing urgency timer of con %p\n", con);
295 ev_timer_stop(
main_loop, con->urgency_timer);
296 FREE(con->urgency_timer);
299 if (con->type != CT_FLOATING_CON) {
313 if (!dont_kill_parent)
320 DLOG(
"Container was floating, killing floating container\n");
322 DLOG(
"parent container killed\n");
326 FREE(con->deco_render_params);
339 DLOG(
"No next container, i will just exit now\n");
343 if (was_mapped || con == focused) {
344 if ((kill_window !=
DONT_KILL_WINDOW) || !dont_kill_parent || con == focused) {
345 DLOG(
"focusing %p / %s\n", next, next->name);
346 if (next->type == CT_DOCKAREA) {
350 if (!force_set_focus && con != focused)
351 DLOG(
"not changing focus, the container was not focused before\n");
356 DLOG(
"not focusing because we're not killing anybody\n");
359 DLOG(
"not focusing, was not mapped\n");
363 if (!dont_kill_parent)
364 CALL(parent, on_remove_child);
376 DLOG(
"Floating containers can't be split.\n");
380 if (con->
type == CT_WORKSPACE) {
382 DLOG(
"Just changing orientation of workspace\n");
404 DLOG(
"Just changing orientation of existing container\n");
408 DLOG(
"Splitting in orientation %d\n", orientation);
414 new->parent = parent;
432 if (focused->
parent->
type == CT_FLOATING_CON) {
440 focused->
type == CT_WORKSPACE) {
441 ELOG(
"'focus parent': Focus is already on the workspace, cannot go higher than that.\n");
455 if (next ==
TAILQ_END(&(focused->focus_head))) {
456 DLOG(
"cannot go down\n");
458 }
else if (next->
type == CT_FLOATING_CON) {
462 if (child ==
TAILQ_END(&(next->focus_head))) {
463 DLOG(
"cannot go down\n");
479 if (con->
type == CT_WORKSPACE) {
482 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
496 DLOG(
"-- BEGIN RENDERING --\n");
505 DLOG(
"-- END RENDERING --\n");
522 if (con->
type == CT_WORKSPACE) {
524 DLOG(
"Cannot change workspace while in global fullscreen mode.\n");
532 DLOG(
"Current output is %s\n", current_output->
name);
536 if (way ==
'n' && orientation ==
HORIZ)
538 else if (way ==
'p' && orientation ==
HORIZ)
540 else if (way ==
'n' && orientation ==
VERT)
542 else if (way ==
'p' && orientation ==
VERT)
550 DLOG(
"Next output is %s\n", next_output->
name);
553 Con *workspace = NULL;
573 if (focus == workspace)
585 if (con->
type == CT_FLOATING_CON) {
586 if (orientation !=
HORIZ)
594 next =
TAILQ_PREV(con, floating_head, floating_windows);
601 next =
TAILQ_LAST(&(parent->floating_head), floating_head);
610 while (
TAILQ_LAST(&(parent->floating_head), floating_head) != next) {
611 Con *last =
TAILQ_LAST(&(parent->floating_head), floating_head);
612 TAILQ_REMOVE(&(parent->floating_head), last, floating_windows);
624 return _tree_next(parent, way, orientation, wrap);
630 DLOG(
"nothing to focus\n");
638 next =
TAILQ_PREV(current, nodes_head, nodes);
645 if (
_tree_next(parent, way, orientation,
false))
655 next =
TAILQ_LAST(&(parent->nodes_head), nodes_head);
692 Con *current, *child, *parent = con->
parent;
693 DLOG(
"Checking if I can flatten con = %p / %s\n", con, con->
name);
696 if (con->
type != CT_CON ||
703 if (child == NULL ||
TAILQ_NEXT(child, nodes) != NULL)
706 DLOG(
"child = %p, con = %p, parent = %p\n", child, con, parent);
718 DLOG(
"Alright, I have to flatten this situation now. Stay calm.\n");
722 DLOG(
"detaching...\n");
726 DLOG(
"detaching current=%p / %s\n", current, current->
name);
728 DLOG(
"re-attaching\n");
735 DLOG(
"attaching to focus list\n");
739 DLOG(
"re-attached all\n");
742 if (focus_next != NULL &&
744 DLOG(
"restoring focus to focus_next=%p\n", focus_next);
745 TAILQ_REMOVE(&(parent->focus_head), focus_next, focused);
747 DLOG(
"restored focus.\n");
751 DLOG(
"closing redundant cons\n");
765 while (current != NULL) {
772 while (current != NULL) {
void x_window_kill(xcb_window_t window, kill_window_t kill_window)
Kills the given X11 window using WM_DELETE_WINDOW (if supported).
void add_ignore_event(const int sequence, const int response_type)
Adds the given sequence to the list of events which are ignored.
bool con_fullscreen_permits_focusing(Con *con)
Returns true if changing the focus to con would be allowed considering the fullscreen focus constrain...
static Con * _create___i3(void)
void tree_next(char way, orientation_t orientation)
Changes focus in the given way (next/previous) and given orientation (horizontal/vertical).
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
void con_focus(Con *con)
Sets input focus to the given container.
void con_set_urgency(Con *con, bool urgent)
Set urgency flag to the container, all the parent containers and the workspace.
Con * tree_open_con(Con *con, i3Window *window)
Opens an empty container in the current container.
#define TAILQ_EMPTY(head)
Con * output_get_content(Con *output)
Returns the output container below the given output container.
void tree_append_json(Con *con, const char *filename, char **errormsg)
#define TAILQ_INSERT_BEFORE(listelm, elm, field)
static bool _is_con_mapped(Con *con)
void workspace_update_urgent_flag(Con *ws)
Goes through all clients on the given workspace and updates the workspace’s urgent flag accordingly...
#define TAILQ_LAST(head, headname)
void tree_init(xcb_get_geometry_reply_t *geometry)
Initializes the tree by creating the root node, adding all RandR outputs to the tree (that means rand...
#define TAILQ_FOREACH(var, head, field)
void match_free(Match *match)
Frees the given match.
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus)
Closes the given container including all children.
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
void window_free(i3Window *win)
Frees an i3Window and all its members.
xcb_connection_t * conn
XCB connection and root screen.
fullscreen_mode_t fullscreen_mode
Output * get_output_next(direction_t direction, Output *current, output_close_far_t close_far)
Gets the output which is the next one in the given direction.
#define TAILQ_NEXT(elm, field)
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
void con_detach(Con *con)
Detaches the given container from its current parent.
bool level_up(void)
Moves focus one level up.
void workspace_show(Con *workspace)
Switches to the given workspace.
void x_con_kill(Con *con)
Kills the window decoration associated with the given container.
static void mark_unmapped(Con *con)
#define GREP_FIRST(dest, head, condition)
Con * con
Pointer to the Con which represents this output.
#define TAILQ_REMOVE(head, elm, field)
void tree_flatten(Con *con)
tree_flatten() removes pairs of redundant split containers, e.g.
void x_set_name(Con *con, const char *name)
Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways) of the given name...
#define XCB_ICCCM_WM_STATE_WITHDRAWN
#define CALL(obj, member,...)
kill_window_t
parameter to specify whether tree_close_internal() and x_window_kill() should kill only this specific...
#define TAILQ_INSERT_HEAD(head, elm, field)
#define TAILQ_REPLACE(head, elm, elm2, field)
void con_update_parents_urgency(Con *con)
Make all parent containers urgent if con is urgent or clear the urgent flag of all parent containers ...
void ipc_send_window_event(const char *property, Con *con)
For the window events we send, along the usual "change" field, also the window container, in "container".
Output * get_output_containing(unsigned int x, unsigned int y)
Returns the active (!) output which contains the coordinates x, y or NULL if there is no output which...
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Con * con_next_focused(Con *con)
Returns the container which will be focused next when the given container is not available anymore...
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
A 'Window' is a type which contains an xcb_window_t and all the related information (hints like _NET_...
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
bool level_down(void)
Moves focus one level down.
struct ev_loop * main_loop
bool path_exists(const char *path)
Checks if the given path exists by calling stat().
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
void x_set_warp_to(Rect *rect)
Set warp_to coordinates.
A 'Con' represents everything from the X11 root window down to a single X11 window.
void restore_open_placeholder_windows(Con *parent)
Open placeholder windows for all children of parent.
#define TAILQ_FIRST(head)
Con * con_descend_tiling_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
bool con_is_split(Con *con)
Returns true if a container should be considered split.
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
#define TAILQ_INSERT_TAIL(head, elm, field)
void x_push_changes(Con *con)
Pushes all changes (state of each node, see x_push_node() and the window stack) to X11...
int con_num_children(Con *con)
Returns the number of children of this container.
void render_con(Con *con, bool render_fullscreen)
"Renders" the given container (and its children), meaning that all rects are updated correctly...
void con_force_split_parents_redraw(Con *con)
force parent split containers to be redrawn
bool force_focus_wrapping
Think of the following layout: Horizontal workspace with a tabbed con on the left of the screen and a...
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
struct all_cons_head all_cons
Con * con_descend_direction(Con *con, direction_t direction)
An Output is a physical output on your graphics driver.
bool con_is_floating(Con *con)
Returns true if the node is floating.
bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry)
Loads tree from ~/.i3/_restart.json (used for in-place restarts).
Con * con_new(Con *parent, i3Window *window)
#define TAILQ_PREV(elm, headname, field)
char * name
Name of the output.
static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
Con * workspace_encapsulate(Con *ws)
Creates a new container and re-parents all of children from the given workspace into it...
void tree_split(Con *con, orientation_t orientation)
Splits (horizontally or vertically) the given container by creating a new container which contains th...
#define TAILQ_HEAD_INITIALIZER(head)