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().
This commit is contained in:
Michael Stapelberg 2009-06-21 16:14:15 +02:00
parent a5489d6546
commit cba36914a8
13 changed files with 124 additions and 44 deletions

View File

@ -16,10 +16,13 @@ snap := <s>
cmd := [ <times> ] [ <move> | <snap> ] <where> cmd := [ <times> ] [ <move> | <snap> ] <where>
with := <w> { [ <times> ] <where> }+ <space> <cmd> with := <w> { [ <times> ] <where> }+ <space> <cmd>
jump := [ "<window class>[/<window title>]" | <workspace> [ <column> <row> ] ] jump := [ "<window class>[/<window title>]" | <workspace> [ <column> <row> ] ]
focus := focus [ <times> ] focus := focus [ <times> | floating | tiling | ft ]
(travels the focus stack backwards the given amount of times (by default 1), so (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 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 <path> | kill | exit | restart ] special := [ exec <path> | kill | exit | restart ]
input := [ <cmd> | <with> | <jump> | <focus> | <special> ] input := [ <cmd> | <with> | <jump> | <focus> | <special> ]

View File

@ -300,12 +300,22 @@ the focus stack and jumps to the window you focused before.
*Syntax*: *Syntax*:
-------------- --------------
focus [number] focus [number] | floating | tilling | ft
-------------- --------------
Where +number+ by default is 1 meaning that the next client in the focus stack will Where +number+ by default is 1 meaning that the next client in the focus stack will
be selected. 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 === Changing colors
You can change all colors which i3 uses to draw the window decorations and the You can change all colors which i3 uses to draw the window decorations and the

View File

@ -18,9 +18,13 @@ bind Mod1+43 s
# Default (Mod1+e) # Default (Mod1+e)
bind Mod1+26 d 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 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/;) # Focus (Mod1+j/k/l/;)
bind Mod1+44 h bind Mod1+44 h
bind Mod1+45 j bind Mod1+45 j

View File

@ -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); 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 #endif

View File

@ -106,6 +106,12 @@ Enable stacking layout for the current container.
Mod1+e:: Mod1+e::
Enable default layout for the current container. 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:: Mod1+Shift+q::
Kills the current client. Kills the current client.
@ -165,6 +171,13 @@ bind Mod1+43 s
# Default (Mod1+e) # Default (Mod1+e)
bind Mod1+26 d 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/;) # Focus (Mod1+j/k/l/;)
bind Mod1+44 h bind Mod1+44 h
bind Mod1+45 j bind Mod1+45 j

View File

@ -187,7 +187,7 @@ void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) {
LOG("leaving fullscreen mode\n"); LOG("leaving fullscreen mode\n");
client->fullscreen = false; client->fullscreen = false;
workspace->fullscreen_client = NULL; workspace->fullscreen_client = NULL;
if (client->floating >= FLOATING_AUTO_ON) { if (client_is_floating(client)) {
/* For floating clients its enough if we just reconfigure that window (in fact, /* For floating clients its enough if we just reconfigure that window (in fact,
* re-rendering the layout will not update the client.) */ * re-rendering the layout will not update the client.) */
reposition_client(conn, 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); 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);
}

View File

@ -720,20 +720,38 @@ 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 * was specified). That is, selects the window you were in before you focused
* the current window. * 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) { static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) {
/* Start count at -1 to always skip the first element */ /* Start count at -1 to always skip the first element */
int times, count = -1; int times, count = -1;
Client *current; Client *current;
bool floating_criteria;
/* Either its 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;
}
floating_criteria = !client_is_floating(last_focused);
} else {
/* …or a number was specified */
if (sscanf(arguments, "%u", &times) != 1) { if (sscanf(arguments, "%u", &times) != 1) {
LOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments); LOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments);
times = 1; times = 1;
} }
Workspace *ws = CUR_CELL->workspace; SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) {
SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
if (++count < times) { if (++count < times) {
LOG("Skipping\n"); LOG("Skipping\n");
continue; continue;
@ -743,6 +761,15 @@ static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) {
set_focus(conn, current, true); set_focus(conn, current, true);
break; 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? */ /* Should we travel the focus stack? */
if (STARTS_WITH(command, "focus")) { if (STARTS_WITH(command, "focus")) {
const char *arguments = command + strlen("focus"); const char *arguments = command + strlen("focus ");
travel_focus_stack(conn, arguments); travel_focus_stack(conn, arguments);
return; 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? */ /* Is it just 's' for stacking or 'd' for default? */
if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) { 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"); LOG("not switching, this is a floating client\n");
return; return;
} }
@ -930,7 +957,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
} }
if (*rest == '\0') { 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); move_floating_window_to_workspace(conn, last_focused, workspace);
else move_current_window_to_workspace(conn, workspace); else move_current_window_to_workspace(conn, workspace);
return; return;
@ -941,7 +968,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
return; return;
} }
if (last_focused->floating >= FLOATING_AUTO_ON && if (client_is_floating(last_focused) &&
(action != ACTION_FOCUS && action != ACTION_MOVE)) { (action != ACTION_FOCUS && action != ACTION_MOVE)) {
LOG("Not performing (floating)\n"); LOG("Not performing (floating)\n");
return; return;
@ -964,7 +991,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
rest++; rest++;
if (action == ACTION_FOCUS) { if (action == ACTION_FOCUS) {
if (last_focused->floating >= FLOATING_AUTO_ON) { if (client_is_floating(last_focused)) {
floating_focus_direction(conn, last_focused, direction); floating_focus_direction(conn, last_focused, direction);
continue; continue;
} }
@ -973,7 +1000,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
} }
if (action == ACTION_MOVE) { if (action == ACTION_MOVE) {
if (last_focused->floating >= FLOATING_AUTO_ON) { if (client_is_floating(last_focused)) {
floating_move(conn, last_focused, direction); floating_move(conn, last_focused, direction);
continue; continue;
} }

View File

@ -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"); LOG("This client is already in floating (container == NULL), re-inserting\n");
Client *next_tiling; Client *next_tiling;
SLIST_FOREACH(next_tiling, &(client->workspace->focus_stack), focus_clients) SLIST_FOREACH(next_tiling, &(client->workspace->focus_stack), focus_clients)
if (next_tiling->floating <= FLOATING_USER_OFF) if (!client_is_floating(next_tiling))
break; break;
/* If there are no tiling clients on this workspace, there can only be one /* If there are no tiling clients on this workspace, there can only be one
* container: the first one */ * container: the first one */
@ -386,8 +386,9 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
/* If we just unmapped all floating windows we should ensure that the focus /* 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 */ * is set correctly, that ist, to the first non-floating client in stack */
if (workspace->floating_hidden) if (workspace->floating_hidden)
SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) {
if (client->floating <= FLOATING_USER_OFF) { if (client_is_floating(client))
continue;
set_focus(conn, client, true); set_focus(conn, client, true);
return; return;
} }

View File

@ -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"); LOG("Not handling, Mod1 was pressed and no client found\n");
return 1; return 1;
} }
if (client->floating >= FLOATING_AUTO_ON) { if (client_is_floating(client)) {
floating_drag_window(conn, client, event); floating_drag_window(conn, client, event);
return 1; return 1;
} }
@ -350,7 +350,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
LOG("client. done.\n"); LOG("client. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
/* Floating clients should be raised on click */ /* 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_raise_window(conn, client->frame);
xcb_flush(conn); xcb_flush(conn);
return 1; return 1;
@ -362,7 +362,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
LOG("click on titlebar\n"); LOG("click on titlebar\n");
/* Floating clients can be dragged by grabbing their titlebar */ /* 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 */ /* Firstly, we raise it. Maybe the user just wanted to raise it without grabbing */
xcb_raise_window(conn, client->frame); xcb_raise_window(conn, client->frame);
xcb_flush(conn); xcb_flush(conn);
@ -372,7 +372,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
return 1; return 1;
} }
if (client->floating >= FLOATING_AUTO_ON) if (client_is_floating(client))
return floating_border_click(conn, client, event); return floating_border_click(conn, client, event);
if (event->event_y < 2) { 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 */ /* Floating clients can be reconfigured */
if (client->floating >= FLOATING_AUTO_ON) { if (client_is_floating(client)) {
i3Font *font = load_font(conn, config.font); i3Font *font = load_font(conn, config.font);
if (event->value_mask & XCB_CONFIG_WINDOW_X) 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 */ /* Only if this is the active container, we need to really change focus */
if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen)) if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen))
set_focus(conn, con->currently_focused, true); 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); SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
} }

View File

@ -24,6 +24,7 @@
#include "util.h" #include "util.h"
#include "xinerama.h" #include "xinerama.h"
#include "layout.h" #include "layout.h"
#include "client.h"
/* /*
* Updates *destination with new_value and returns true if it was changed or false * 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; return;
LOG("redecorating child %08x\n", client->child); 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… */ /* 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); color = &(config.client.focused);
/* …or if it is the focused window in a not focused container */ /* …or if it is the focused window in a not focused container */
else color = &(config.client.focused_inactive); else color = &(config.client.focused_inactive);

View File

@ -375,6 +375,9 @@ int main(int argc, char *argv[], char *env[]) {
c_ws = &workspaces[screen->current_workspace]; 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 libevs eventloop */ /* Ungrab the server to receive events and enter libevs eventloop */
xcb_ungrab_server(conn); xcb_ungrab_server(conn);
ev_loop(loop, 0); ev_loop(loop, 0);

View File

@ -356,7 +356,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
} else if (!new->dock) { } else if (!new->dock) {
/* Focus the new window if were not in fullscreen mode and if it is not a dock window */ /* Focus the new window if were not in fullscreen mode and if it is not a dock window */
if (new->container->workspace->fullscreen_client == NULL) { if (new->container->workspace->fullscreen_client == NULL) {
if (new->floating <= FLOATING_USER_OFF) if (!client_is_floating(new))
new->container->currently_focused = new; new->container->currently_focused = new;
if (new->container == CUR_CELL) if (new->container == CUR_CELL)
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME); 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 its not a dock window */ /* Insert into the currently active container, if its 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 /* 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 */ container is empty and it does not matter, where we insert it */
if (old_focused != NULL && !old_focused->dock) 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); 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); SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients);
/* Add the client to the list of floating clients for its workspace */ /* Add the client to the list of floating clients for its workspace */

View File

@ -264,7 +264,7 @@ void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) {
/* To find floating clients, we traverse the focus stack */ /* To find floating clients, we traverse the focus stack */
SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) { SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
if (client->floating <= FLOATING_USER_OFF) if (!client_is_floating(client))
continue; continue;
xcb_unmap_window(conn, client->frame); xcb_unmap_window(conn, client->frame);