From fb9d77305e96e78247b25138978d9c6708446499 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 10 Jun 2011 18:27:20 +0200 Subject: [PATCH] Implement 'fullscreen global' --- docs/userguide | 4 ---- include/con.h | 4 ++-- include/tree.h | 4 ++-- src/cmdparse.y | 13 +++++++++---- src/con.c | 26 +++++++++++++++++--------- src/handlers.c | 2 +- src/ipc.c | 2 +- src/main.c | 14 ++++++++++++-- src/manage.c | 8 +++++--- src/render.c | 15 ++++++++++++--- src/tree.c | 24 ++++++++++++++++-------- src/workspace.c | 2 +- src/x.c | 2 +- 13 files changed, 79 insertions(+), 41 deletions(-) diff --git a/docs/userguide b/docs/userguide index 0cdb2566..6c5ba10b 100644 --- a/docs/userguide +++ b/docs/userguide @@ -99,12 +99,8 @@ image:modes.png[Container modes] To display a window fullscreen or to go out of fullscreen mode again, press +mod+f+. -///////////////////////////////////////////////////////////////////////////// -TODO: not yet implemented - There is also a global fullscreen mode in i3 in which the client will use all available outputs. To use it, or to get out of it again, press +mod+Shift+f+. -///////////////////////////////////////////////////////////////////////////// === Opening other applications diff --git a/include/con.h b/include/con.h index 37bca3bb..6ce7bf84 100644 --- a/include/con.h +++ b/include/con.h @@ -53,7 +53,7 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation); * Returns the first fullscreen node below this node. * */ -Con *con_get_fullscreen_con(Con *con); +Con *con_get_fullscreen_con(Con *con, int fullscreen_mode); /** * Returns true if the node is floating. @@ -126,7 +126,7 @@ void con_fix_percent(Con *con); * entered when there already is a fullscreen container on this workspace. * */ -void con_toggle_fullscreen(Con *con); +void con_toggle_fullscreen(Con *con, int fullscreen_mode); /** * Moves the given container to the currently focused container on the given diff --git a/include/tree.h b/include/tree.h index b66aa3f7..b9bf7f54 100644 --- a/include/tree.h +++ b/include/tree.h @@ -18,7 +18,7 @@ extern struct all_cons_head all_cons; * assigning a workspace to each RandR output. * */ -void tree_init(); +void tree_init(xcb_get_geometry_reply_t *geometry); /** * Opens an empty container in the current container @@ -77,7 +77,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent); * Loads tree from ~/.i3/_restart.json (used for in-place restarts). * */ -bool tree_restore(const char *path); +bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry); /** * tree_flatten() removes pairs of redundant split containers, e.g.: diff --git a/src/cmdparse.y b/src/cmdparse.y index 1c65fbc5..fbc2307f 100644 --- a/src/cmdparse.y +++ b/src/cmdparse.y @@ -173,6 +173,7 @@ char *parse_cmd(const char *new) { %type direction %type split_direction +%type fullscreen_mode %type level %type boolean %type border_style @@ -521,23 +522,27 @@ open: ; fullscreen: - TOK_FULLSCREEN + TOK_FULLSCREEN fullscreen_mode { - printf("toggling fullscreen\n"); + printf("toggling fullscreen, mode = %s\n", ($2 == CF_OUTPUT ? "normal" : "global")); owindow *current; - HANDLE_EMPTY_MATCH; TAILQ_FOREACH(current, &owindows, owindows) { printf("matching: %p / %s\n", current->con, current->con->name); - con_toggle_fullscreen(current->con); + con_toggle_fullscreen(current->con, $2); } tree_render(); } ; +fullscreen_mode: + /* empty */ { $$ = CF_OUTPUT; } + | TOK_GLOBAL { $$ = CF_GLOBAL; } + ; + split: TOK_SPLIT split_direction { diff --git a/src/con.c b/src/con.c index 1a45076a..185cbd3f 100644 --- a/src/con.c +++ b/src/con.c @@ -290,7 +290,7 @@ struct bfs_entry { * Returns the first fullscreen node below this node. * */ -Con *con_get_fullscreen_con(Con *con) { +Con *con_get_fullscreen_con(Con *con, int fullscreen_mode) { Con *current, *child; /* TODO: is breadth-first-search really appropriate? (check as soon as @@ -303,7 +303,7 @@ Con *con_get_fullscreen_con(Con *con) { while (!TAILQ_EMPTY(&bfs_head)) { entry = TAILQ_FIRST(&bfs_head); current = entry->con; - if (current != con && current->fullscreen_mode != CF_NONE) { + if (current != con && current->fullscreen_mode == fullscreen_mode) { /* empty the queue */ while (!TAILQ_EMPTY(&bfs_head)) { entry = TAILQ_FIRST(&bfs_head); @@ -490,7 +490,7 @@ void con_fix_percent(Con *con) { * entered when there already is a fullscreen container on this workspace. * */ -void con_toggle_fullscreen(Con *con) { +void con_toggle_fullscreen(Con *con, int fullscreen_mode) { Con *workspace, *fullscreen; if (con->type == CT_WORKSPACE) { @@ -501,19 +501,27 @@ void con_toggle_fullscreen(Con *con) { DLOG("toggling fullscreen for %p / %s\n", con, con->name); if (con->fullscreen_mode == CF_NONE) { /* 1: check if there already is a fullscreen con */ - workspace = con_get_workspace(con); - if ((fullscreen = con_get_fullscreen_con(workspace)) != NULL) { + if (fullscreen_mode == CF_GLOBAL) + fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL); + else { + workspace = con_get_workspace(con); + fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); + } + if (fullscreen != NULL) { LOG("Not entering fullscreen mode, container (%p/%s) " "already is in fullscreen mode\n", fullscreen, fullscreen->name); - } else { - /* 2: enable fullscreen */ - con->fullscreen_mode = CF_OUTPUT; + goto update_netwm_state; } + + /* 2: enable fullscreen */ + con->fullscreen_mode = fullscreen_mode; } else { /* 1: disable fullscreen */ con->fullscreen_mode = CF_NONE; } + +update_netwm_state: DLOG("mode now: %d\n", con->fullscreen_mode); /* update _NET_WM_STATE if this container has a window */ @@ -797,7 +805,7 @@ Rect con_border_style_rect(Con *con) { * */ int con_border_style(Con *con) { - Con *fs = con_get_fullscreen_con(con->parent); + Con *fs = con_get_fullscreen_con(con->parent, CF_OUTPUT); if (fs == con) { DLOG("this one is fullscreen! overriding BS_NONE\n"); return BS_NONE; diff --git a/src/handlers.c b/src/handlers.c index eaf71ddb..b97dd043 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -648,7 +648,7 @@ static int handle_client_message(xcb_client_message_event_t *event) { (event->data.data32[0] == _NET_WM_STATE_ADD || event->data.data32[0] == _NET_WM_STATE_TOGGLE))) { DLOG("toggling fullscreen\n"); - con_toggle_fullscreen(con); + con_toggle_fullscreen(con, CF_OUTPUT); } tree_render(); diff --git a/src/ipc.c b/src/ipc.c index fa3513bc..1dd9512a 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -437,7 +437,7 @@ IPC_HANDLER(get_outputs) { ystr("current_workspace"); Con *ws = NULL; - if (output->con && (ws = con_get_fullscreen_con(output->con))) + if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT))) ystr(ws->name); else y(null); diff --git a/src/main.c b/src/main.c index c079d8dc..b4ed4a1a 100644 --- a/src/main.c +++ b/src/main.c @@ -257,6 +257,7 @@ int main(int argc, char *argv[]) { xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; root_depth = root_screen->root_depth; + xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root); load_configuration(conn, override_configpath, false); if (only_check_config) { @@ -284,6 +285,13 @@ int main(int argc, char *argv[]) { cookie = xcb_change_window_attributes_checked(conn, root, mask, values); check_error(conn, cookie, "Another window manager seems to be running"); + xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL); + if (greply == NULL) { + ELOG("Could not get geometry of the root window, exiting\n"); + return 1; + } + DLOG("root geometry reply: (%d, %d) %d x %d\n", greply->x, greply->y, greply->width, greply->height); + /* Place requests for the atoms we need as soon as possible */ #define xmacro(atom) \ xcb_intern_atom_cookie_t atom ## _cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom); @@ -368,13 +376,15 @@ int main(int argc, char *argv[]) { bool needs_tree_init = true; if (layout_path) { LOG("Trying to restore the layout from %s...", layout_path); - needs_tree_init = !tree_restore(layout_path); + needs_tree_init = !tree_restore(layout_path, greply); if (delete_layout_path) unlink(layout_path); free(layout_path); } if (needs_tree_init) - tree_init(); + tree_init(greply); + + free(greply); if (force_xinerama) { xinerama_init(); diff --git a/src/manage.c b/src/manage.c index 85beb4cb..e609501a 100644 --- a/src/manage.c +++ b/src/manage.c @@ -251,7 +251,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki free(name); Con *ws = con_get_workspace(nc); - Con *fs = (ws ? con_get_fullscreen_con(ws) : NULL); + Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL); + if (fs == NULL) + fs = con_get_fullscreen_con(croot, CF_GLOBAL); if (fs == NULL) { DLOG("Not in fullscreen mode, focusing\n"); @@ -293,7 +295,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && fs != NULL) { LOG("There is a fullscreen window, leaving fullscreen mode\n"); - con_toggle_fullscreen(fs); + con_toggle_fullscreen(fs, CF_OUTPUT); } } @@ -331,7 +333,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki reply = xcb_get_property_reply(conn, state_cookie, NULL); if (xcb_reply_contains_atom(reply, A__NET_WM_STATE_FULLSCREEN)) - con_toggle_fullscreen(nc); + con_toggle_fullscreen(nc, CF_OUTPUT); /* Put the client inside the save set. Upon termination (whether killed or * normal exit does not matter) of the window manager, these clients will diff --git a/src/render.c b/src/render.c index a59d418b..b1388b31 100644 --- a/src/render.c +++ b/src/render.c @@ -42,8 +42,8 @@ static void render_l_output(Con *con) { /* We need to find out if there is a fullscreen con on the current workspace * and take the short-cut to render it directly (the user does not want to * see the dockareas in that case) */ - Con *ws = con_get_fullscreen_con(content); - Con *fullscreen = con_get_fullscreen_con(ws); + Con *ws = con_get_fullscreen_con(content, CF_OUTPUT); + Con *fullscreen = con_get_fullscreen_con(ws, CF_OUTPUT); if (fullscreen) { DLOG("got fs node: %p\n", fullscreen); fullscreen->rect = con->rect; @@ -185,7 +185,10 @@ void render_con(Con *con, bool render_fullscreen) { } /* Check for fullscreen nodes */ - Con *fullscreen = (con->type == CT_OUTPUT ? NULL : con_get_fullscreen_con(con)); + Con *fullscreen = NULL; + if (con->type != CT_OUTPUT) { + fullscreen = con_get_fullscreen_con(con, (con->type == CT_ROOT ? CF_GLOBAL : CF_OUTPUT)); + } if (fullscreen) { DLOG("got fs node: %p\n", fullscreen); fullscreen->rect = rect; @@ -222,6 +225,12 @@ void render_con(Con *con, bool render_fullscreen) { if (con->layout == L_OUTPUT) { render_l_output(con); + } else if (con->type == CT_ROOT) { + DLOG("Root node, rendering outputs\n"); + Con *child; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + render_con(child, false); + } } else { /* FIXME: refactor this into separate functions: */ diff --git a/src/tree.c b/src/tree.c index a32723df..7e006485 100644 --- a/src/tree.c +++ b/src/tree.c @@ -13,7 +13,7 @@ struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons); * Loads tree from ~/.i3/_restart.json (used for in-place restarts). * */ -bool tree_restore(const char *path) { +bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { char *globbed = resolve_tilde(path); if (!path_exists(globbed)) { @@ -24,6 +24,12 @@ bool tree_restore(const char *path) { /* TODO: refactor the following */ croot = con_new(NULL, NULL); + croot->rect = (Rect){ + geometry->x, + geometry->y, + geometry->width, + geometry->height + }; focused = croot; tree_append_json(globbed); @@ -44,11 +50,17 @@ bool tree_restore(const char *path) { * root node are created in randr.c for each Output. * */ -void tree_init() { +void tree_init(xcb_get_geometry_reply_t *geometry) { croot = con_new(NULL, NULL); FREE(croot->name); croot->name = "root"; croot->type = CT_ROOT; + croot->rect = (Rect){ + geometry->x, + geometry->y, + geometry->width, + geometry->height + }; } /* @@ -343,12 +355,8 @@ void tree_render() { mark_unmapped(croot); croot->mapped = true; - /* We start rendering at an output */ - Con *output; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - DLOG("output %p / %s\n", output, output->name); - render_con(output, false); - } + render_con(croot, false); + x_push_changes(croot); DLOG("-- END RENDERING --\n"); } diff --git a/src/workspace.c b/src/workspace.c index ec57df8d..4021dd14 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -95,7 +95,7 @@ bool workspace_is_visible(Con *ws) { Con *output = con_get_output(ws); if (output == NULL) return false; - Con *fs = con_get_fullscreen_con(output); + Con *fs = con_get_fullscreen_con(output, CF_OUTPUT); LOG("workspace visible? fs = %p, ws = %p\n", fs, ws); return (fs == ws); } diff --git a/src/x.c b/src/x.c index 0228cd23..64f5394b 100644 --- a/src/x.c +++ b/src/x.c @@ -308,7 +308,7 @@ void x_draw_decoration(Con *con) { /* If the con is in fullscreen mode, the decoration height we work with is set to 0 */ Rect deco_rect = con->deco_rect; - if (con_get_fullscreen_con(parent) == con) + if (con_get_fullscreen_con(parent, CF_OUTPUT) == con) deco_rect.height = 0; /* 2: draw the client.background, but only for the parts around the client_rect */ -- 2.39.5