#include "i3.h"
#include "xinerama.h"
-static bool focus_window_in_container(xcb_connection_t *conn, Container *container,
- direction_t direction) {
+bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
/* If this container is empty, we’re done */
if (container->currently_focused == NULL)
return false;
return false;
/* Set focus */
- set_focus(conn, candidate);
+ set_focus(conn, candidate, true);
return true;
}
/* There always is a container. If not, current_col or current_row is wrong */
assert(container != NULL);
+ if (container->workspace->fullscreen_client != NULL) {
+ LOG("You're in fullscreen mode. Won't switch focus\n");
+ return;
+ }
+
/* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */
if (direction == D_UP || direction == D_DOWN) {
if (thing == THING_WINDOW)
return;
if (direction == D_DOWN && cell_exists(current_col, current_row+1))
- new_row++;
- else if (direction == D_UP && cell_exists(current_col, current_row-1))
+ new_row = current_row + t_ws->table[current_col][current_row]->rowspan;
+ else if (direction == D_UP && cell_exists(current_col, current_row-1)) {
+ /* Set new_row as a sane default, but it may get overwritten in a second */
new_row--;
- else {
+
+ /* Search from the top to correctly handle rowspanned containers */
+ for (int rows = 0; rows < current_row; rows += t_ws->table[current_col][rows]->rowspan) {
+ if (new_row > (rows + (t_ws->table[current_col][rows]->rowspan - 1)))
+ continue;
+
+ new_row = rows;
+ break;
+ }
+ } else {
/* Let’s see if there is a screen down/up there to which we can switch */
LOG("container is at %d with height %d\n", container->y, container->height);
i3Screen *screen;
int destination_y = (direction == D_UP ? (container->y - 1) : (container->y + container->height + 1));
if ((screen = get_screen_containing(container->x, destination_y)) == NULL) {
- LOG("Not possible, no screen found.\n");
- return;
+ LOG("Wrapping screen around vertically\n");
+ /* No screen found? Then wrap */
+ screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP));
}
t_ws = &(workspaces[screen->current_workspace]);
new_row = (direction == D_UP ? (t_ws->rows - 1) : 0);
- /* Bounds checking */
- if (new_col >= t_ws->cols)
- new_col = (t_ws->cols - 1);
- if (new_row >= t_ws->rows)
- new_row = (t_ws->rows - 1);
}
} else if (direction == D_LEFT || direction == D_RIGHT) {
if (direction == D_RIGHT && cell_exists(current_col+1, current_row))
- new_col++;
- else if (direction == D_LEFT && cell_exists(current_col-1, current_row))
+ new_col = current_col + t_ws->table[current_col][current_row]->colspan;
+ else if (direction == D_LEFT && cell_exists(current_col-1, current_row)) {
+ /* Set new_col as a sane default, but it may get overwritten in a second */
new_col--;
- else {
+
+ /* Search from the left to correctly handle colspanned containers */
+ for (int cols = 0; cols < current_col; cols += t_ws->table[cols][current_row]->colspan) {
+ if (new_col > (cols + (t_ws->table[cols][current_row]->colspan - 1)))
+ continue;
+
+ new_col = cols;
+ break;
+ }
+ } else {
/* Let’s see if there is a screen left/right here to which we can switch */
LOG("container is at %d with width %d\n", container->x, container->width);
i3Screen *screen;
int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1));
if ((screen = get_screen_containing(destination_x, container->y)) == NULL) {
- LOG("Not possible, no screen found.\n");
- return;
+ LOG("Wrapping screen around horizontally\n");
+ screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT));
}
t_ws = &(workspaces[screen->current_workspace]);
new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0);
- /* Bounds checking */
- if (new_col >= t_ws->cols)
- new_col = (t_ws->cols - 1);
- if (new_row >= t_ws->rows)
- new_row = (t_ws->rows - 1);
}
} else {
LOG("direction unhandled\n");
return;
}
+ /* Bounds checking */
+ if (new_col >= t_ws->cols)
+ new_col = (t_ws->cols - 1);
+ if (new_row >= t_ws->rows)
+ new_row = (t_ws->rows - 1);
+
if (t_ws->table[new_col][new_row]->currently_focused != NULL)
- set_focus(conn, t_ws->table[new_col][new_row]->currently_focused);
+ set_focus(conn, t_ws->table[new_col][new_row]->currently_focused, true);
}
/*
LOG("i can do that\n");
/* We can move the client inside its current container */
- /* TODO: needs wrapping */
CIRCLEQ_REMOVE(&(client->container->clients), client, clients);
if (direction == D_UP)
CIRCLEQ_INSERT_BEFORE(&(client->container->clients), other, client, clients);
}
/*
- * Moves the current window to the given direction, creating a column/row if
- * necessary
+ * Moves the current window or whole container to the given direction, creating a column/row if
+ * necessary.
*
*/
static void move_current_window(xcb_connection_t *conn, direction_t direction) {
LOG("moving window to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" :
- (direction == D_LEFT ? "left" : "right"))));
+ (direction == D_LEFT ? "left" : "right"))));
/* Get current window */
Container *container = CUR_CELL,
*new = NULL;
container->currently_focused->dock)
return;
- /* As soon as the client is moved away, the next client in the old
+ /* As soon as the client is moved away, the last focused client in the old
* container needs to get focus, if any. Therefore, we save it here. */
Client *current_client = container->currently_focused;
- Client *to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients);
- if (to_focus == NULL)
- to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
+ Client *to_focus = get_last_focused_client(conn, container, current_client);
+
+ if (to_focus == NULL) {
+ to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients);
+ if (to_focus == NULL)
+ to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
+ }
switch (direction) {
case D_LEFT:
- /* TODO: If we’re at the left-most position, move the rest of the table right */
- if (current_col == 0)
- return;
-
- new = CUR_TABLE[--current_col][current_row];
+ /* If we’re at the left-most position, move the rest of the table right */
+ if (current_col == 0) {
+ expand_table_cols_at_head(c_ws);
+ new = CUR_CELL;
+ } else
+ new = CUR_TABLE[--current_col][current_row];
break;
case D_RIGHT:
if (current_col == (c_ws->cols-1))
new = CUR_TABLE[++current_col][current_row];
break;
case D_UP:
- /* TODO: if we’re at the up-most position, move the rest of the table down */
- if (move_current_window_in_container(conn, current_client, D_UP) ||
- current_row == 0)
+ if (move_current_window_in_container(conn, current_client, D_UP))
return;
- new = CUR_TABLE[current_col][--current_row];
+ /* if we’re at the up-most position, move the rest of the table down */
+ if (current_row == 0) {
+ expand_table_rows_at_head(c_ws);
+ new = CUR_CELL;
+ } else
+ new = CUR_TABLE[current_col][--current_row];
break;
case D_DOWN:
if (move_current_window_in_container(conn, current_client, D_DOWN))
}
/* Remove it from the old container and put it into the new one */
- CIRCLEQ_REMOVE(&(container->clients), current_client, clients);
- CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients);
+ remove_client_from_container(conn, current_client, container);
+
+ if (new->currently_focused != NULL)
+ CIRCLEQ_INSERT_AFTER(&(new->clients), new->currently_focused, current_client, clients);
+ else CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients);
+ SLIST_INSERT_HEAD(&(new->workspace->focus_stack), current_client, focus_clients);
/* Update data structures */
current_client->container = new;
container->currently_focused = to_focus;
new->currently_focused = current_client;
+ Workspace *workspace = container->workspace;
+
/* delete all empty columns/rows */
- cleanup_table(conn, container->workspace);
+ cleanup_table(conn, workspace);
/* Fix colspan/rowspan if it’d overlap */
- fix_colrowspan(conn, container->workspace);
+ fix_colrowspan(conn, workspace);
render_layout(conn);
- set_focus(conn, current_client);
+ set_focus(conn, current_client, true);
+}
+
+static void move_current_container(xcb_connection_t *conn, direction_t direction) {
+ LOG("moving container to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" :
+ (direction == D_LEFT ? "left" : "right"))));
+ /* Get current window */
+ Container *container = CUR_CELL,
+ *new = NULL;
+
+ Container **old = &CUR_CELL;
+
+ /* There has to be a container, see focus_window() */
+ assert(container != NULL);
+
+ switch (direction) {
+ case D_LEFT:
+ /* If we’re at the left-most position, move the rest of the table right */
+ if (current_col == 0) {
+ expand_table_cols_at_head(c_ws);
+ new = CUR_CELL;
+ old = &CUR_TABLE[current_col+1][current_row];
+ } else
+ new = CUR_TABLE[--current_col][current_row];
+ break;
+ case D_RIGHT:
+ if (current_col == (c_ws->cols-1))
+ expand_table_cols(c_ws);
+
+ new = CUR_TABLE[++current_col][current_row];
+ break;
+ case D_UP:
+ /* if we’re at the up-most position, move the rest of the table down */
+ if (current_row == 0) {
+ expand_table_rows_at_head(c_ws);
+ new = CUR_CELL;
+ old = &CUR_TABLE[current_col][current_row+1];
+ } else
+ new = CUR_TABLE[current_col][--current_row];
+ break;
+ case D_DOWN:
+ if (current_row == (c_ws->rows-1))
+ expand_table_rows(c_ws);
+
+ new = CUR_TABLE[current_col][++current_row];
+ break;
+ }
+
+ LOG("old = %d,%d and new = %d,%d\n", container->col, container->row, new->col, new->row);
+
+ /* Swap the containers */
+ int col = new->col;
+ int row = new->row;
+
+ *old = new;
+ new->col = container->col;
+ new->row = container->row;
+
+ CUR_CELL = container;
+ container->col = col;
+ container->row = row;
+
+ Workspace *workspace = container->workspace;
+
+ /* delete all empty columns/rows */
+ cleanup_table(conn, workspace);
+
+ /* Fix colspan/rowspan if it’d overlap */
+ fix_colrowspan(conn, workspace);
+
+ render_layout(conn);
}
/*
t_ws->screen = container->workspace->screen;
/* Copy the dimensions from the virtual screen */
memcpy(&(t_ws->rect), &(container->workspace->screen->rect), sizeof(Rect));
+ } else {
+ /* Check if there is already a fullscreen client on the destination workspace and
+ * stop moving if so. */
+ if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) {
+ LOG("Not moving: Fullscreen client already existing on destination workspace.\n");
+ return;
+ }
}
Container *to_container = t_ws->table[t_ws->current_col][t_ws->current_row];
assert(to_container != NULL);
- CIRCLEQ_REMOVE(&(container->clients), current_client, clients);
- SLIST_REMOVE(&(container->workspace->focus_stack), current_client, Client, focus_clients);
+ remove_client_from_container(conn, current_client, container);
+ if (container->workspace->fullscreen_client == current_client)
+ container->workspace->fullscreen_client = NULL;
CIRCLEQ_INSERT_TAIL(&(to_container->clients), current_client, clients);
SLIST_INSERT_HEAD(&(to_container->workspace->focus_stack), current_client, focus_clients);
+ if (current_client->fullscreen)
+ t_ws->fullscreen_client = current_client;
LOG("Moved.\n");
current_client->container = to_container;
render_layout(conn);
}
-static void show_workspace(xcb_connection_t *conn, int workspace) {
+/*
+ * Switches to the given workspace
+ *
+ */
+void show_workspace(xcb_connection_t *conn, int workspace) {
Client *client;
+ bool need_warp = false;
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
/* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
Workspace *t_ws = &(workspaces[workspace-1]);
if (c_ws->screen != t_ws->screen) {
/* We need to switch to the other screen first */
LOG("moving over to other screen.\n");
+
+ /* Store the old client */
+ Client *old_client = CUR_CELL->currently_focused;
+
c_ws = &(workspaces[t_ws->screen->current_workspace]);
current_col = c_ws->current_col;
current_row = c_ws->current_row;
if (CUR_CELL->currently_focused != NULL)
- warp_pointer_into(conn, CUR_CELL->currently_focused);
+ need_warp = true;
else {
Rect *dims = &(c_ws->screen->rect);
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
dims->x + (dims->width / 2), dims->y + (dims->height / 2));
}
+
+ /* Re-decorate the old client, it’s not focused anymore */
+ if ((old_client != NULL) && !old_client->dock)
+ redecorate_window(conn, old_client);
+ else xcb_flush(conn);
}
/* Check if we need to change something or if we’re already there */
- if (c_ws->screen->current_workspace == (workspace-1))
+ if (c_ws->screen->current_workspace == (workspace-1)) {
+ if (CUR_CELL->currently_focused != NULL) {
+ set_focus(conn, CUR_CELL->currently_focused, true);
+ if (need_warp) {
+ warp_pointer_into(conn, CUR_CELL->currently_focused);
+ xcb_flush(conn);
+ }
+ }
return;
+ }
t_ws->screen->current_workspace = workspace-1;
- /* TODO: does grabbing the server actually bring us any (speed)advantages? */
- //xcb_grab_server(conn);
-
/* Unmap all clients of the current workspace */
- int unmapped_clients = 0;
- for (int cols = 0; cols < c_ws->cols; cols++)
- for (int rows = 0; rows < c_ws->rows; rows++)
- CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) {
- xcb_unmap_window(conn, client->frame);
- unmapped_clients++;
- }
-
- /* If we did not unmap any clients, the workspace is empty and we can destroy it */
- if (unmapped_clients == 0)
- c_ws->screen = NULL;
-
- /* Unmap the stack windows on the current workspace, if any */
- struct Stack_Window *stack_win;
- SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
- if (stack_win->container->workspace == c_ws)
- xcb_unmap_window(conn, stack_win->window);
+ unmap_workspace(conn, c_ws);
c_ws = &workspaces[workspace-1];
current_row = c_ws->current_row;
current_col = c_ws->current_col;
LOG("new current row = %d, current col = %d\n", current_row, current_col);
+ ignore_enter_notify_forall(conn, c_ws, true);
+
/* Map all clients on the new workspace */
- for (int cols = 0; cols < c_ws->cols; cols++)
- for (int rows = 0; rows < c_ws->rows; rows++)
- CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
- xcb_map_window(conn, client->frame);
+ FOR_TABLE(c_ws)
+ CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
+ xcb_map_window(conn, client->frame);
/* Map all stack windows, if any */
+ struct Stack_Window *stack_win;
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
if (stack_win->container->workspace == c_ws)
xcb_map_window(conn, stack_win->window);
- /* Restore focus on the new workspace */
- if (CUR_CELL->currently_focused != NULL)
- set_focus(conn, CUR_CELL->currently_focused);
- else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+ ignore_enter_notify_forall(conn, c_ws, false);
- //xcb_ungrab_server(conn);
+ /* Restore focus on the new workspace */
+ if (CUR_CELL->currently_focused != NULL) {
+ set_focus(conn, CUR_CELL->currently_focused, true);
+ if (need_warp) {
+ warp_pointer_into(conn, CUR_CELL->currently_focused);
+ xcb_flush(conn);
+ }
+ } else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
render_layout(conn);
}
if (command[0] == '\0')
return;
- /* Is it an <exec>? */
- if (strncmp(command, "exec ", strlen("exec ")) == 0) {
+ /* Is it an <exec>? Then execute the given command. */
+ if (STARTS_WITH(command, "exec ")) {
LOG("starting \"%s\"\n", command + strlen("exec "));
start_application(command+strlen("exec "));
return;
}
- /* Is it <restart>? */
- if (strncmp(command, "restart", strlen("restart")) == 0) {
- LOG("restarting \"%s\"...\n", application_path);
- execl(application_path, application_path, NULL);
+ /* Is it an <exit>? */
+ if (STARTS_WITH(command, "exit")) {
+ LOG("User issued exit-command, exiting without error.\n");
+ exit(EXIT_SUCCESS);
+ }
+
+ /* Is it <restart>? Then restart in place. */
+ if (STARTS_WITH(command, "restart")) {
+ LOG("restarting \"%s\"...\n", start_argv[0]);
+ execvp(start_argv[0], start_argv);
/* not reached */
}
+ if (STARTS_WITH(command, "kill")) {
+ if (CUR_CELL->currently_focused == NULL) {
+ LOG("There is no window to kill\n");
+ return;
+ }
+
+ LOG("Killing current window\n");
+ kill_window(conn, CUR_CELL->currently_focused);
+ return;
+ }
+
/* Is it 'f' for fullscreen? */
if (command[0] == 'f') {
if (CUR_CELL->currently_focused == NULL)
return;
}
- if (action == ACTION_FOCUS) {
+ if (action == ACTION_FOCUS)
focus_thing(conn, direction, (with == WITH_WINDOW ? THING_WINDOW : THING_CONTAINER));
+ else if (action == ACTION_MOVE) {
+ if (with == WITH_WINDOW)
+ move_current_window(conn, direction);
+ else move_current_container(conn, direction);
}
- else if (action == ACTION_MOVE)
- move_current_window(conn, direction);
else if (action == ACTION_SNAP)
snap_current_container(conn, direction);