From cba36914a8371c9f2b8bdad66195de909989ad78 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 21 Jun 2009 16:14:15 +0200 Subject: [PATCH] Implement selecting the next tiling/floating window (using "focus") Also update documentation (manpage, userguide). To make the code easier to read/write when checking if a client is floating, introduce client_is_floating(). --- CMDMODE | 7 +++-- docs/userguide | 12 ++++++++- i3.config | 6 ++++- include/client.h | 8 ++++++ man/i3.man | 13 +++++++++ src/client.c | 12 ++++++++- src/commands.c | 69 +++++++++++++++++++++++++++++++++--------------- src/floating.c | 13 ++++----- src/handlers.c | 12 ++++----- src/layout.c | 5 ++-- src/mainx.c | 3 +++ src/manage.c | 6 ++--- src/util.c | 2 +- 13 files changed, 124 insertions(+), 44 deletions(-) diff --git a/CMDMODE b/CMDMODE index 7428729a..7d8f6f23 100644 --- a/CMDMODE +++ b/CMDMODE @@ -16,10 +16,13 @@ snap := cmd := [ ] [ | ] with := { [ ] }+ jump := [ "[/]" | [ ] ] -focus := focus [ ] +focus := focus [ | floating | tiling | ft ] (travels the focus stack backwards the given amount of times (by default 1), so it selects the window which had the focus before you focused the current one when - specifying "focus 1") + specifying "focus 1". + The special values 'floating' (select the next floating window), 'tiling' + (select the next tiling window), 'ft' (if the current window is floating, + select the next tiling window and vice-versa) are also valid) special := [ exec | kill | exit | restart ] input := [ | | | | ] diff --git a/docs/userguide b/docs/userguide index 4ba97ee9..aa7575bc 100644 --- a/docs/userguide +++ b/docs/userguide @@ -300,12 +300,22 @@ the focus stack and jumps to the window you focused before. *Syntax*: -------------- -focus [number] +focus [number] | floating | tilling | ft -------------- Where +number+ by default is 1 meaning that the next client in the focus stack will be selected. +The special values have the following meaning: + +floating:: + The next floating window is selected. +tiling:: + The next tiling window is selected. +ft:: + If the current window is floating, the next tiling window will be selected + and vice-versa. + === Changing colors You can change all colors which i3 uses to draw the window decorations and the diff --git a/i3.config b/i3.config index 7bc3fe80..19f3caee 100644 --- a/i3.config +++ b/i3.config @@ -18,9 +18,13 @@ bind Mod1+43 s # Default (Mod1+e) bind Mod1+26 d -# Toggle tiling/floating of the current window +# Toggle tiling/floating of the current window (Mod1+Shift+Space) bind Mod1+Shift+65 t +# Go into the tiling layer / floating layer, depending on whether +# the current window is tiling / floating (Mod1+t) +bind Mod1+28 focus ft + # Focus (Mod1+j/k/l/;) bind Mod1+44 h bind Mod1+45 j diff --git a/include/client.h b/include/client.h index 068a364a..964dd2ea 100644 --- a/include/client.h +++ b/include/client.h @@ -60,4 +60,12 @@ void client_toggle_fullscreen(xcb_connection_t *conn, Client *client); */ void client_set_below_floating(xcb_connection_t *conn, Client *client); +/** + * Returns true if the client is floating. Makes the code more beatiful, as floating + * is not simply a boolean, but also saves whether the user selected the current state + * or whether it was automatically set. + * + */ +bool client_is_floating(Client *client); + #endif diff --git a/man/i3.man b/man/i3.man index fbe2ac83..c1513ea4 100644 --- a/man/i3.man +++ b/man/i3.man @@ -106,6 +106,12 @@ Enable stacking layout for the current container. Mod1+e:: Enable default layout for the current container. +Mod1+Shift+Space:: +Toggle tiling/floating for the current window. + +Mod1+t:: +Select the first tiling window if the current window is floating and vice-versa. + Mod1+Shift+q:: Kills the current client. @@ -165,6 +171,13 @@ bind Mod1+43 s # Default (Mod1+e) bind Mod1+26 d +# Toggle tiling/floating of the current window (Mod1+Shift+Space) +bind Mod1+Shift+65 t + +# Go into the tiling layer / floating layer, depending on whether +# the current window is tiling / floating (Mod1+t) +bind Mod1+28 focus ft + # Focus (Mod1+j/k/l/;) bind Mod1+44 h bind Mod1+45 j diff --git a/src/client.c b/src/client.c index 18126a1a..5d78d38f 100644 --- a/src/client.c +++ b/src/client.c @@ -187,7 +187,7 @@ void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) { LOG("leaving fullscreen mode\n"); client->fullscreen = false; workspace->fullscreen_client = NULL; - if (client->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(client)) { /* For floating clients it’s enough if we just reconfigure that window (in fact, * re-rendering the layout will not update the client.) */ reposition_client(conn, client); @@ -223,3 +223,13 @@ void client_set_below_floating(xcb_connection_t *conn, Client *client) { xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); } } + +/* + * Returns true if the client is floating. Makes the code more beatiful, as floating + * is not simply a boolean, but also saves whether the user selected the current state + * or whether it was automatically set. + * + */ +bool client_is_floating(Client *client) { + return (client->floating >= FLOATING_AUTO_ON); +} diff --git a/src/commands.c b/src/commands.c index 89e19615..084a388e 100644 --- a/src/commands.c +++ b/src/commands.c @@ -720,29 +720,56 @@ static void jump_to_container(xcb_connection_t *conn, const char *arguments) { * was specified). That is, selects the window you were in before you focused * the current window. * + * The special values 'floating' (select the next floating window), 'tiling' + * (select the next tiling window), 'ft' (if the current window is floating, + * select the next tiling window and vice-versa) are also valid + * */ static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) { /* Start count at -1 to always skip the first element */ int times, count = -1; Client *current; + bool floating_criteria; - if (sscanf(arguments, "%u", ×) != 1) { - LOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments); - times = 1; - } - - Workspace *ws = CUR_CELL->workspace; - - SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) { - if (++count < times) { - LOG("Skipping\n"); - continue; + /* Either it’s one of the special values… */ + if (strcasecmp(arguments, "floating") == 0) { + floating_criteria = true; + } else if (strcasecmp(arguments, "tiling") == 0) { + floating_criteria = false; + } else if (strcasecmp(arguments, "ft") == 0) { + Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack)); + if (last_focused == SLIST_END(&(c_ws->focus_stack))) { + LOG("Cannot select the next floating/tiling client because there is no client at all\n"); + return; } - LOG("Focussing\n"); - set_focus(conn, current, true); - break; + floating_criteria = !client_is_floating(last_focused); + } else { + /* …or a number was specified */ + if (sscanf(arguments, "%u", ×) != 1) { + LOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments); + times = 1; + } + + SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) { + if (++count < times) { + LOG("Skipping\n"); + continue; + } + + LOG("Focussing\n"); + set_focus(conn, current, true); + break; + } + return; } + + /* Select the next client matching the criteria parsed above */ + SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) + if (client_is_floating(current) == floating_criteria) { + set_focus(conn, current, true); + break; + } } /* @@ -829,7 +856,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { /* Should we travel the focus stack? */ if (STARTS_WITH(command, "focus")) { - const char *arguments = command + strlen("focus"); + const char *arguments = command + strlen("focus "); travel_focus_stack(conn, arguments); return; } @@ -844,7 +871,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { /* Is it just 's' for stacking or 'd' for default? */ if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) { - if (last_focused == NULL || last_focused->floating >= FLOATING_AUTO_ON) { + if (last_focused == NULL || client_is_floating(last_focused)) { LOG("not switching, this is a floating client\n"); return; } @@ -930,7 +957,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { } if (*rest == '\0') { - if (last_focused != NULL && last_focused->floating >= FLOATING_AUTO_ON) + if (last_focused != NULL && client_is_floating(last_focused)) move_floating_window_to_workspace(conn, last_focused, workspace); else move_current_window_to_workspace(conn, workspace); return; @@ -941,8 +968,8 @@ void parse_command(xcb_connection_t *conn, const char *command) { return; } - if (last_focused->floating >= FLOATING_AUTO_ON && - (action != ACTION_FOCUS && action != ACTION_MOVE)) { + if (client_is_floating(last_focused) && + (action != ACTION_FOCUS && action != ACTION_MOVE)) { LOG("Not performing (floating)\n"); return; } @@ -964,7 +991,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { rest++; if (action == ACTION_FOCUS) { - if (last_focused->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(last_focused)) { floating_focus_direction(conn, last_focused, direction); continue; } @@ -973,7 +1000,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { } if (action == ACTION_MOVE) { - if (last_focused->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(last_focused)) { floating_move(conn, last_focused, direction); continue; } diff --git a/src/floating.c b/src/floating.c index 09b33ba1..1664d602 100644 --- a/src/floating.c +++ b/src/floating.c @@ -52,7 +52,7 @@ void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic LOG("This client is already in floating (container == NULL), re-inserting\n"); Client *next_tiling; SLIST_FOREACH(next_tiling, &(client->workspace->focus_stack), focus_clients) - if (next_tiling->floating <= FLOATING_USER_OFF) + if (!client_is_floating(next_tiling)) break; /* If there are no tiling clients on this workspace, there can only be one * container: the first one */ @@ -386,11 +386,12 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) { /* If we just unmapped all floating windows we should ensure that the focus * is set correctly, that ist, to the first non-floating client in stack */ if (workspace->floating_hidden) - SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) - if (client->floating <= FLOATING_USER_OFF) { - set_focus(conn, client, true); - return; - } + SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) { + if (client_is_floating(client)) + continue; + set_focus(conn, client, true); + return; + } xcb_flush(conn); } diff --git a/src/handlers.c b/src/handlers.c index 129eca95..3dbef90d 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -309,7 +309,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ LOG("Not handling, Mod1 was pressed and no client found\n"); return 1; } - if (client->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(client)) { floating_drag_window(conn, client, event); return 1; } @@ -350,7 +350,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ LOG("client. done.\n"); xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); /* Floating clients should be raised on click */ - if (client->floating >= FLOATING_AUTO_ON) + if (client_is_floating(client)) xcb_raise_window(conn, client->frame); xcb_flush(conn); return 1; @@ -362,7 +362,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ LOG("click on titlebar\n"); /* Floating clients can be dragged by grabbing their titlebar */ - if (client->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(client)) { /* Firstly, we raise it. Maybe the user just wanted to raise it without grabbing */ xcb_raise_window(conn, client->frame); xcb_flush(conn); @@ -372,7 +372,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ return 1; } - if (client->floating >= FLOATING_AUTO_ON) + if (client_is_floating(client)) return floating_border_click(conn, client, event); if (event->event_y < 2) { @@ -477,7 +477,7 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure } /* Floating clients can be reconfigured */ - if (client->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(client)) { i3Font *font = load_font(conn, config.font); if (event->value_mask & XCB_CONFIG_WINDOW_X) @@ -598,7 +598,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti /* Only if this is the active container, we need to really change focus */ if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen)) set_focus(conn, con->currently_focused, true); - } else if (client->floating >= FLOATING_AUTO_ON) { + } else if (client_is_floating(client)) { SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients); } diff --git a/src/layout.c b/src/layout.c index 952f3d60..2b01f90c 100644 --- a/src/layout.c +++ b/src/layout.c @@ -24,6 +24,7 @@ #include "util.h" #include "xinerama.h" #include "layout.h" +#include "client.h" /* * Updates *destination with new_value and returns true if it was changed or false @@ -107,9 +108,9 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw return; LOG("redecorating child %08x\n", client->child); - if (client->floating >= FLOATING_AUTO_ON || client->container->currently_focused == client) { + if (client_is_floating(client) || client->container->currently_focused == client) { /* Distinguish if the window is currently focused… */ - if (client->floating >= FLOATING_AUTO_ON || CUR_CELL->currently_focused == client) + if (client_is_floating(client) || CUR_CELL->currently_focused == client) color = &(config.client.focused); /* …or if it is the focused window in a not focused container */ else color = &(config.client.focused_inactive); diff --git a/src/mainx.c b/src/mainx.c index a832f098..d95ca465 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -375,6 +375,9 @@ int main(int argc, char *argv[], char *env[]) { c_ws = &workspaces[screen->current_workspace]; } + /* Handle the events which arrived until now */ + xcb_check_cb(NULL, NULL, 0); + /* Ungrab the server to receive events and enter libev’s eventloop */ xcb_ungrab_server(conn); ev_loop(loop, 0); diff --git a/src/manage.c b/src/manage.c index 8cacd2a0..203a66d9 100644 --- a/src/manage.c +++ b/src/manage.c @@ -356,7 +356,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, } else if (!new->dock) { /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */ if (new->container->workspace->fullscreen_client == NULL) { - if (new->floating <= FLOATING_USER_OFF) + if (!client_is_floating(new)) new->container->currently_focused = new; if (new->container == CUR_CELL) xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME); @@ -364,7 +364,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, } /* Insert into the currently active container, if it’s not a dock window */ - if (!new->dock && new->floating <= FLOATING_USER_OFF) { + if (!new->dock && !client_is_floating(new)) { /* Insert after the old active client, if existing. If it does not exist, the container is empty and it does not matter, where we insert it */ if (old_focused != NULL && !old_focused->dock) @@ -376,7 +376,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, client_set_below_floating(conn, new); } - if (new->floating >= FLOATING_AUTO_ON) { + if (client_is_floating(new)) { SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients); /* Add the client to the list of floating clients for its workspace */ diff --git a/src/util.c b/src/util.c index 762b9481..ed9dc49a 100644 --- a/src/util.c +++ b/src/util.c @@ -264,7 +264,7 @@ void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) { /* To find floating clients, we traverse the focus stack */ SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) { - if (client->floating <= FLOATING_USER_OFF) + if (!client_is_floating(client)) continue; xcb_unmap_window(conn, client->frame);