2 #define I3__FILE__ "con.c"
37 while (parent && parent->
type != CT_WORKSPACE && parent->
type != CT_DOCKAREA) {
53 new->aspect_ratio = 0.0;
57 new->current_border_width = -1;
59 new->depth = window->
depth;
61 new->depth = XCB_COPY_FROM_PARENT;
63 DLOG(
"opening window %d\n", cnt);
67 new->name = strdup(
colors[cnt]);
70 if ((cnt % (
sizeof(
colors) /
sizeof(
char *))) == 0)
107 struct nodes_head *nodes_head = &(parent->nodes_head);
108 struct focus_head *focus_head = &(parent->focus_head);
112 if (con->
type == CT_WORKSPACE) {
113 DLOG(
"it's a workspace. num = %d\n", con->
num);
118 if (con->
num < current->
num) {
122 while (current->
num != -1 && con->
num > current->
num) {
136 goto add_to_focus_head;
139 if (con->
type == CT_FLOATING_CON) {
140 DLOG(
"Inserting into floating containers\n");
146 if (loop->
type == CT_FLOATING_CON)
160 if (con->
window != NULL &&
161 parent->
type == CT_WORKSPACE &&
163 DLOG(
"Parent is a workspace. Applying default layout...\n");
167 nodes_head = &(target->nodes_head);
168 focus_head = &(target->focus_head);
177 if (current && parent->
type != CT_OUTPUT) {
178 DLOG(
"Inserting con = %p after last focused tiling con %p\n",
199 if (con->
type == CT_FLOATING_CON) {
215 DLOG(
"con_focus = %p\n", con);
286 if (con->
type == CT_WORKSPACE)
290 DLOG(
"container %p does not accept windows, it is a split container.\n", con);
295 return (con->
window == NULL);
305 while (result != NULL && result->
type != CT_OUTPUT)
309 assert(result != NULL);
319 while (result != NULL && result->
type != CT_WORKSPACE)
330 DLOG(
"Searching for parent of Con %p with orientation %d\n", con, orientation);
332 if (parent->
type == CT_FLOATING_CON)
335 DLOG(
"Need to go one level further up\n");
339 (parent->
type == CT_FLOATING_CON ||
340 parent->
type == CT_OUTPUT ||
346 DLOG(
"Result: %p\n", parent);
366 Con *current, *child;
377 current = entry->
con;
397 TAILQ_FOREACH (child, &(current->floating_head), floating_windows) {
412 return (con->
name[0] ==
'_' && con->
name[1] ==
'_');
421 DLOG(
"checking if con %p is floating\n", con);
422 return (con->
floating >= FLOATING_AUTO_ON);
432 if (con->
type == CT_FLOATING_CON)
435 if (con->
floating >= FLOATING_AUTO_ON)
438 if (con->
type == CT_WORKSPACE || con->
type == CT_OUTPUT)
477 if (con->
frame == frame)
497 if (store_match != NULL)
498 *store_match = match;
506 TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
510 if (store_match != NULL)
511 *store_match = match;
549 int children_with_percent = 0;
553 ++children_with_percent;
559 if (children_with_percent != children) {
562 if (children_with_percent == 0)
563 total += (child->
percent = 1.0);
565 total += (child->
percent = total / children_with_percent);
574 child->
percent = 1.0 / children;
575 }
else if (total != 1.0) {
588 Con *workspace, *fullscreen;
590 if (con->
type == CT_WORKSPACE) {
591 DLOG(
"You cannot make a workspace fullscreen.\n");
595 DLOG(
"toggling fullscreen for %p / %s\n", con, con->
name);
604 if (fullscreen != NULL) {
608 LOG(
"Disabling fullscreen for (%p/%s) upon user request\n",
609 fullscreen, fullscreen->
name);
632 unsigned int num = 0;
635 values[num++] = A__NET_WM_STATE_FULLSCREEN;
637 xcb_change_property(
conn, XCB_PROP_MODE_REPLACE, con->
window->
id,
661 LOG(
"Cannot move out of a fullscreen container");
666 DLOG(
"Using FLOATINGCON instead\n");
671 if (workspace == source_ws) {
672 DLOG(
"Not moving, already there\n");
676 if (con->
type == CT_WORKSPACE) {
679 while (!
TAILQ_EMPTY(&(source_ws->floating_head))) {
690 ELOG(
"Workspace failed to move its contents into a container!\n");
710 if (next->
type != CT_WORKSPACE) {
711 DLOG(
"next originally = %p / %s / type %d\n", next, next->
name, next->
type);
719 if (floatingcon != NULL) {
720 DLOG(
"floatingcon, going up even further\n");
721 next = floatingcon->
parent;
724 if (con->
type == CT_FLOATING_CON) {
726 DLOG(
"This is a floating window, using workspace %p / %s\n", ws, ws->
name);
730 if (source_output != dest_output) {
733 if (fix_coordinates && con->
type == CT_FLOATING_CON) {
736 DLOG(
"Not fixing coordinates, fix_coordinates flag = %d\n", fix_coordinates);
761 DLOG(
"Re-attaching container to %p / %s\n", next, next->
name);
799 if (source_ws == current_ws)
805 xcb_get_property_cookie_t cookie;
806 xcb_get_property_reply_t *startup_id_reply;
814 cookie = xcb_get_property(
conn,
false, child->
window->
id,
815 A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
816 startup_id_reply = xcb_get_property_reply(
conn, cookie, NULL);
819 if (sequence != NULL)
826 A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
827 startup_id_reply = xcb_get_property_reply(
conn, cookie, NULL);
830 if (sequence != NULL)
834 CALL(parent, on_remove_child);
856 DLOG(
"Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
862 DLOG(
"con_orientation() called on dockarea/output (%d) container %p\n", con->
layout, con);
867 DLOG(
"con_orientation() ran into default\n");
882 if (con->
type == CT_FLOATING_CON) {
883 DLOG(
"selecting next for CT_FLOATING_CON\n");
885 DLOG(
"next = %p\n", next);
887 next =
TAILQ_PREV(con, floating_head, floating_windows);
888 DLOG(
"using prev, next = %p\n", next);
893 DLOG(
"no more floating containers for next = %p, restoring workspace focus\n", next);
897 DLOG(
"skipping container itself, we want the next client\n");
901 if (next ==
TAILQ_END(&(ws->focus_head))) {
902 DLOG(
"Focus list empty, returning ws\n");
915 DLOG(
"selecting workspace for dock client\n");
923 DLOG(
"Using first entry %p\n", first);
947 DLOG(
"con_get_next(way=%c, orientation=%d)\n", way, orientation);
951 DLOG(
"need to go one level further up\n");
953 LOG(
"that's a workspace, we can't go further up\n");
964 if (next ==
TAILQ_END(&(parent->nodes_head)))
969 if (next ==
TAILQ_END(&(cur->nodes_head)))
972 DLOG(
"next = %p\n", next);
1007 if (child->
type == CT_FLOATING_CON)
1013 }
while (before != next && next !=
focused);
1028 DLOG(
"con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
1030 if (orientation ==
HORIZ) {
1036 most =
TAILQ_LAST(&(con->nodes_head), nodes_head);
1037 }
else if (orientation ==
VERT) {
1042 if (current->
type != CT_FLOATING_CON) {
1054 if (direction ==
D_UP || direction ==
D_DOWN) {
1055 if (orientation ==
VERT) {
1058 if (direction ==
D_UP)
1059 most =
TAILQ_LAST(&(con->nodes_head), nodes_head);
1062 }
else if (orientation ==
HORIZ) {
1067 if (current->
type != CT_FLOATING_CON) {
1102 DLOG(
"Effective border width is set to: %d\n", border_width);
1106 return (
Rect) {0, 0, 0, 0};
1109 result = (
Rect) {border_width, 0, -(2 * border_width), -(border_width)};
1111 result = (
Rect) {border_width, border_width, -(2 * border_width), -(2 * border_width)};
1120 result.
x -= border_width;
1121 result.
width += border_width;
1124 result.
width += border_width;
1127 result.
y -= border_width;
1128 result.
height += border_width;
1131 result.
height += border_width;
1167 DLOG(
"this one is fullscreen! overriding BS_NONE\n");
1201 DLOG(
"This is a floating container\n");
1209 parent->
rect.
y += deco_height;
1220 parent->
rect.
y -= deco_height;
1231 DLOG(
"con_set_layout(%p, %d), con->type = %d\n",
1232 con, layout, con->
type);
1237 if (con->
type != CT_WORKSPACE)
1250 if (con->
type == CT_WORKSPACE &&
1253 DLOG(
"Setting workspace_layout to %d\n", layout);
1256 DLOG(
"Creating new split container\n");
1267 if (old_focused ==
TAILQ_END(&(con->focus_head)))
1271 DLOG(
"Moving cons\n");
1280 DLOG(
"Attaching new split to ws\n");
1324 if (con->
type != CT_WORKSPACE)
1326 DLOG(
"con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
1328 if (strcmp(toggle_mode,
"split") == 0) {
1344 if (strcmp(toggle_mode,
"all") == 0)
1349 if (strcmp(toggle_mode,
"all") == 0) {
1371 DLOG(
"on_remove_child\n");
1375 if (con->
type == CT_OUTPUT ||
1376 con->
type == CT_ROOT ||
1377 con->
type == CT_DOCKAREA ||
1379 DLOG(
"not handling, type = %d, name = %s\n", con->
type, con->
name);
1384 if (con->
type == CT_WORKSPACE) {
1386 LOG(
"Closing old workspace (%p / %s), it is empty\n", con, con->
name);
1388 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE,
"{\"change\":\"empty\"}");
1400 if (children == 0) {
1401 DLOG(
"Container empty, closing\n");
1413 DLOG(
"Determining minimum size for con %p\n", con);
1416 DLOG(
"leaf node, returning 75x50\n");
1417 return (
Rect) {0, 0, 75, 50};
1420 if (con->
type == CT_FLOATING_CON) {
1421 DLOG(
"floating con\n");
1427 uint32_t max_width = 0, max_height = 0, deco_height = 0;
1432 max_width =
max(max_width, min.
width);
1433 max_height =
max(max_height, min.
height);
1435 DLOG(
"stacked/tabbed now, returning %d x %d + deco_rect = %d\n",
1436 max_width, max_height, deco_height);
1437 return (
Rect) {0, 0, max_width, max_height + deco_height};
1456 DLOG(
"split container, returning width = %d x height = %d\n", width,
height);
1460 ELOG(
"Unhandled case, type = %d, layout = %d, split = %d\n",
1502 if (fs->
type == CT_WORKSPACE)
1557 bool new_urgency_value = con->
urgent;
1558 while (parent && parent->
type != CT_WORKSPACE && parent->
type != CT_DOCKAREA) {
1559 if (new_urgency_value) {
1577 DLOG(
"Ignoring urgency flag for current client\n");
1586 DLOG(
"Discarding urgency WM_HINT because timer is running\n");
1600 if (con->
urgent == urgent)
1601 LOG(
"Urgency flag changed to %d\n", con->
urgent);
1648 ELOG(
"BUG: Code not updated to account for new layout type\n");
1659 (
TAILQ_FIRST(&(con->nodes_head)) == child ?
"" :
" "), child_txt);
1669 return complete_buf;
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void con_set_layout(Con *con, layout_t layout)
This function changes the layout of a given container.
bool con_is_floating(Con *con)
Returns true if the node is floating.
fullscreen_mode_t
Fullscreen modes.
Con * workspace_attach_to(Con *ws)
Called when a new con (with a window, not an empty or split con) should be attached to the workspace ...
Rect rect_add(Rect a, Rect b)
#define TAILQ_REMOVE(head, elm, field)
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
int default_floating_border_width
Con * workspace_encapsulate(Con *ws)
Creates a new container and re-parents all of children from the given workspace into it...
int render_deco_height(void)
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
#define TAILQ_INSERT_BEFORE(listelm, elm, field)
Stores a rectangle, for example the size of a window, the child window etc.
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
void con_detach(Con *con)
Detaches the given container from its current parent.
#define TAILQ_LAST(head, headname)
void workspace_show(Con *workspace)
Switches to the given workspace.
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
bool con_accepts_window(Con *con)
Returns true if this node accepts a window (if the node swallows windows, it might already have swall...
struct all_cons_head all_cons
Con * con_parent_with_orientation(Con *con, orientation_t orientation)
Searches parenst of the given 'con' until it reaches one with the specified 'orientation'.
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp)
Moves the given container to the currently focused container on the given workspace.
Con * con_descend_tiling_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
uint16_t depth
Depth of the window.
#define TAILQ_FIRST(head)
bool con_has_children(Con *con)
Returns true if this node has regular or floating children.
layout_t
Container layouts.
#define TAILQ_NEXT(elm, field)
Con * con_new(Con *parent, i3Window *window)
enum Con::@19 floating
floating? (= not in tiling layout) This cannot be simply a bool because we want to keep track of whet...
border_style_t border_style
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
A 'Window' is a type which contains an xcb_window_t and all the related information (hints like _NET_...
Con * con_descend_direction(Con *con, direction_t direction)
void x_set_warp_to(Rect *rect)
Set warp_to coordinates.
void ipc_send_event(const char *event, uint32_t message_type, const char *payload)
Sends the specified event to all IPC clients which are currently connected and subscribed to this kin...
void con_set_border_style(Con *con, int border_style, int border_width)
Sets the given border style on con, correctly keeping the position/size of a floating window...
char * con_get_tree_representation(Con *con)
Create a string representing the subtree under con.
Con * con_by_window_id(xcb_window_t window)
Returns the container with the given client window ID or NULL if no such container exists...
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 ...
static void con_force_split_parents_redraw(Con *con)
void startup_sequence_delete(struct Startup_Sequence *sequence)
Deletes a startup sequence, ignoring whether its timeout has elapsed.
Con * con_next_focused(Con *con)
Returns the container which will be focused next when the given container is not available anymore...
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
bool match_matches_window(Match *match, i3Window *window)
Check if a match data structure matches the given window.
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus)
Closes the given container including all children.
Rect con_border_style_rect(Con *con)
Returns a "relative" Rect which contains the amount of pixels that need to be added to the original R...
static void con_on_remove_child(Con *con)
struct Startup_Sequence * startup_sequence_get(i3Window *cwindow, xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader)
Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
bool con_is_leaf(Con *con)
Returns true when this node is a leaf node (has no children)
bool con_has_managed_window(Con *con)
Returns true when this con is a leaf node with a managed X11 window (e.g., excluding dock containers)...
bool con_has_urgent_child(Con *con)
Checks if the given container has an urgent child.
struct deco_render_params * deco_render_params
Cache for the decoration rendering.
Con * con_for_window(Con *con, i3Window *window, Match **store_match)
Returns the first container below 'con' which wants to swallow this window TODO: priority.
#define TAILQ_INSERT_HEAD(head, elm, field)
struct timeval urgent
When this window was marked urgent.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Rect rect_sub(Rect a, Rect b)
fullscreen_mode_t fullscreen_mode
bool con_fullscreen_permits_focusing(Con *con)
Returns true if changing the focus to con would be allowed considering the fullscreen focus constrain...
#define TAILQ_INSERT_AFTER(head, listelm, elm, field)
void con_focus(Con *con)
Sets input focus to the given container.
Con * con_by_frame_id(xcb_window_t frame)
Returns the container with the given frame ID or NULL if no such container exists.
#define TAILQ_EMPTY(head)
adjacent_t con_adjacent_borders(Con *con)
Returns adjacent borders of the window.
int con_num_children(Con *con)
Returns the number of children of this container.
void workspace_update_urgent_flag(Con *ws)
Goes through all clients on the given workspace and updates the workspace’s urgent flag accordingly...
adjacent_t hide_edge_borders
Remove borders if they are adjacent to the screen edge.
void tree_flatten(Con *con)
tree_flatten() removes pairs of redundant split containers, e.g.
bool con_is_split(Con *con)
#define TAILQ_PREV(elm, headname, field)
#define TAILQ_ENTRY(type)
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
border_style_t default_border
The default border style for new windows.
A 'Con' represents everything from the X11 root window down to a single X11 window.
void * scalloc(size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
layout_t last_split_layout
layout_t workspace_layout
void con_toggle_layout(Con *con, const char *toggle_mode)
This function toggles the layout of a given container.
bool con_inside_focused(Con *con)
Checks if the given container is inside a focused container.
#define CALL(obj, member,...)
#define TAILQ_INSERT_TAIL(head, elm, field)
int con_border_style(Con *con)
Use this function to get a container’s border style.
adjacent_t
describes if the window is adjacent to the output (physical screen) edges.
#define TAILQ_HEAD_INITIALIZER(head)
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
Con * con_inside_floating(Con *con)
Checks if the given container is either floating or inside some floating container.
Con * con_get_next(Con *con, char way, orientation_t orientation)
Get the next/previous container in the specified orientation.
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect)
Fixes the coordinates of the floating window whenever the window gets reassigned to a different outpu...
#define TAILQ_FOREACH(var, head, field)
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
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".
#define TAILQ_HEAD(name, type)
void con_set_urgency(Con *con, bool urgent)
Set urgency flag to the container, all the parent containers and the workspace.
void con_toggle_fullscreen(Con *con, int fullscreen_mode)
Toggles fullscreen mode for the given container.
char * workspace
workspace on which this startup was initiated
Con * con_new_skeleton(Con *parent, i3Window *window)
Create a new container (and attach it to the given parent, if not NULL).
Stores internal information about a startup sequence, like the workspace it was initiated on...
void x_con_init(Con *con, uint16_t depth)
Initializes the X11 part for the given container.
struct ev_timer * urgency_timer
Rect con_minimum_size(Con *con)
Determines the minimum size of the given con by looking at its children (for split/stacked/tabbed con...