#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;
}
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;
}
} 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;
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);
}
/*
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:
/* Remove it from the old container and put it into the new one */
remove_client_from_container(conn, current_client, container);
- CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients);
+
+ 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;
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) {
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);
-
- ignore_enter_notify_forall(conn, c_ws, true);
-
/* Unmap all clients of the current workspace */
- int unmapped_clients = 0;
- FOR_TABLE(c_ws)
- 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);
-
- ignore_enter_notify_forall(conn, c_ws, false);
+ unmap_workspace(conn, c_ws);
c_ws = &workspaces[workspace-1];
current_row = c_ws->current_row;
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);
ignore_enter_notify_forall(conn, c_ws, false);
/* 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);
-
- //xcb_ungrab_server(conn);
+ 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);
}
}
/* Is it an <exit>? */
- if (STARTS_WITH(command, "exit"))
- exit(0);
+ 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", application_path);
- execl(application_path, application_path, NULL);
+ 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)