#include "xinerama.h"
#include "client.h"
#include "floating.h"
+#include "xcb.h"
bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
/* If this container is empty, we’re done */
int new_row = current_row,
new_col = current_col;
-
Container *container = CUR_CELL;
Workspace *t_ws = c_ws;
+ /* Makes sure new_col and new_row are within bounds of the new workspace */
+ void check_colrow_boundaries() {
+ if (new_col >= t_ws->cols)
+ new_col = (t_ws->cols - 1);
+ if (new_row >= t_ws->rows)
+ new_row = (t_ws->rows - 1);
+ }
+
/* There always is a container. If not, current_col or current_row is wrong */
assert(container != NULL);
t_ws = &(workspaces[screen->current_workspace]);
new_row = (direction == D_UP ? (t_ws->rows - 1) : 0);
}
+
+ check_colrow_boundaries();
+
+ LOG("new_col = %d, new_row = %d\n", new_col, new_row);
+ if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
+ LOG("Cell empty, checking for colspanned client above...\n");
+ for (int cols = 0; cols < new_col; cols += t_ws->table[cols][new_row]->colspan) {
+ if (new_col > (cols + (t_ws->table[cols][new_row]->colspan - 1)))
+ continue;
+
+ new_col = cols;
+ break;
+ }
+ LOG("Fixed it to new col %d\n", new_col);
+ }
} else if (direction == D_LEFT || direction == D_RIGHT) {
if (direction == D_RIGHT && cell_exists(current_col+1, current_row))
new_col = current_col + t_ws->table[current_col][current_row]->colspan;
t_ws = &(workspaces[screen->current_workspace]);
new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0);
}
+
+ check_colrow_boundaries();
+
+ LOG("new_col = %d, new_row = %d\n", new_col, new_row);
+ if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
+ LOG("Cell empty, checking for rowspanned client above...\n");
+ for (int rows = 0; rows < new_row; rows += t_ws->table[new_col][rows]->rowspan) {
+ if (new_row > (rows + (t_ws->table[new_col][rows]->rowspan - 1)))
+ continue;
+
+ new_row = rows;
+ break;
+ }
+ LOG("Fixed it to new row %d\n", new_row);
+ }
} 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);
+ check_colrow_boundaries();
if (t_ws->table[new_col][new_row]->currently_focused != NULL)
set_focus(conn, t_ws->table[new_col][new_row]->currently_focused, true);
new = CUR_TABLE[current_col][++current_row];
break;
+ /* To make static analyzers happy: */
+ default:
+ return;
}
/* Remove it from the old container and put it into the new one */
new = CUR_TABLE[current_col][++current_row];
break;
+ /* To make static analyzers happy: */
+ default:
+ return;
}
LOG("old = %d,%d and new = %d,%d\n", container->col, container->row, new->col, new->row);
case D_LEFT:
/* Snap to the left is actually a move to the left and then a snap right */
if (!cell_exists(container->col - 1, container->row) ||
- CUR_TABLE[container->col-1][container->row]->currently_focused != NULL) {
+ CUR_TABLE[container->col-1][container->row]->currently_focused != NULL) {
LOG("cannot snap to left - the cell is already used\n");
return;
}
case D_RIGHT: {
/* Check if the cell is used */
int new_col = container->col + container->colspan;
- if (!cell_exists(new_col, container->row) ||
- CUR_TABLE[new_col][container->row]->currently_focused != NULL) {
- LOG("cannot snap to right - the cell is already used\n");
- return;
- }
+ for (int i = 0; i < container->rowspan; i++)
+ if (!cell_exists(new_col, container->row + i) ||
+ CUR_TABLE[new_col][container->row + i]->currently_focused != NULL) {
+ LOG("cannot snap to right - the cell is already used\n");
+ return;
+ }
/* Check if there are other cells with rowspan, which are in our way.
* If so, reduce their rowspan. */
}
case D_UP:
if (!cell_exists(container->col, container->row - 1) ||
- CUR_TABLE[container->col][container->row-1]->currently_focused != NULL) {
+ CUR_TABLE[container->col][container->row-1]->currently_focused != NULL) {
LOG("cannot snap to top - the cell is already used\n");
return;
}
case D_DOWN: {
LOG("snapping down\n");
int new_row = container->row + container->rowspan;
- if (!cell_exists(container->col, new_row) ||
- CUR_TABLE[container->col][new_row]->currently_focused != NULL) {
- LOG("cannot snap down - the cell is already used\n");
- return;
- }
+ for (int i = 0; i < container->colspan; i++)
+ if (!cell_exists(container->col + i, new_row) ||
+ CUR_TABLE[container->col + i][new_row]->currently_focused != NULL) {
+ LOG("cannot snap down - the cell is already used\n");
+ return;
+ }
for (int i = container->col-1; i >= 0; i--) {
LOG("we got cell %d, %d with colspan %d\n",
container->rowspan++;
break;
}
+ /* To make static analyzers happy: */
+ default:
+ return;
}
render_layout(conn);
static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *client, int workspace) {
/* t_ws (to workspace) is just a container pointer to the workspace we’re switching to */
- Workspace *t_ws = &(workspaces[workspace-1]);
+ Workspace *t_ws = &(workspaces[workspace-1]),
+ *old_ws = client->workspace;
LOG("moving floating\n");
}
}
- /* Remove from focus stack */
- SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
+ floating_assign_to_workspace(client, t_ws);
- if (client->workspace->fullscreen_client == client)
- client->workspace->fullscreen_client = NULL;
-
- /* Insert into destination focus stack */
- client->workspace = t_ws;
- SLIST_INSERT_HEAD(&(t_ws->focus_stack), client, focus_clients);
- if (client->fullscreen)
- t_ws->fullscreen_client = client;
+ bool target_invisible = t_ws->screen->current_workspace != t_ws->num;
/* If we’re moving it to an invisible screen, we need to unmap it */
- if (t_ws->screen->current_workspace != t_ws->num) {
+ if (target_invisible) {
LOG("This workspace is not visible, unmapping\n");
xcb_unmap_window(conn, client->frame);
+ } else {
+ /* If this is not the case, we move the window to a workspace
+ * which is on another screen, so we also need to adjust its
+ * coordinates. */
+ LOG("before x = %d, y = %d\n", client->rect.x, client->rect.y);
+ uint32_t relative_x = client->rect.x - old_ws->rect.x,
+ relative_y = client->rect.y - old_ws->rect.y;
+ LOG("rel_x = %d, rel_y = %d\n", relative_x, relative_y);
+ client->rect.x = t_ws->rect.x + relative_x;
+ client->rect.y = t_ws->rect.y + relative_y;
+ LOG("after x = %d, y = %d\n", client->rect.x, client->rect.y);
+ reposition_client(conn, client);
+ xcb_flush(conn);
}
LOG("done\n");
render_layout(conn);
+
+ if (!target_invisible)
+ set_focus(conn, client, true);
}
/*
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;
container->currently_focused = to_focus;
to_container->currently_focused = current_client;
+ bool target_invisible = (to_container->workspace->screen->current_workspace != to_container->workspace->num);
+
/* If we’re moving it to an invisible screen, we need to unmap it */
- if (to_container->workspace->screen->current_workspace != to_container->workspace->num) {
+ if (target_invisible) {
LOG("This workspace is not visible, unmapping\n");
xcb_unmap_window(conn, current_client->frame);
+ } else {
+ if (current_client->fullscreen) {
+ LOG("Calling client_enter_fullscreen again\n");
+ client_enter_fullscreen(conn, current_client);
+ }
}
/* delete all empty columns/rows */
cleanup_table(conn, container->workspace);
render_layout(conn);
+
+ if (!target_invisible)
+ set_focus(conn, current_client, true);
}
/*
/* Check if we need to change something or if we’re already there */
if (c_ws->screen->current_workspace == (workspace-1)) {
- if (CUR_CELL->currently_focused != NULL) {
- set_focus(conn, CUR_CELL->currently_focused, true);
+ Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
+ if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
+ set_focus(conn, last_focused, true);
if (need_warp) {
- client_warp_pointer_into(conn, CUR_CELL->currently_focused);
+ client_warp_pointer_into(conn, last_focused);
xcb_flush(conn);
}
}
+
return;
}
xcb_map_window(conn, client->frame);
/* Map all floating clients */
- SLIST_FOREACH(client, &(c_ws->focus_stack), focus_clients) {
- if (!client->floating)
- continue;
-
- xcb_map_window(conn, client->frame);
- }
+ if (!c_ws->floating_hidden)
+ TAILQ_FOREACH(client, &(c_ws->floating_clients), floating_clients)
+ xcb_map_window(conn, client->frame);
/* Map all stack windows, if any */
struct Stack_Window *stack_win;
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, true);
+ Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
+ if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
+ set_focus(conn, last_focused, true);
if (need_warp) {
- client_warp_pointer_into(conn, CUR_CELL->currently_focused);
+ client_warp_pointer_into(conn, last_focused);
xcb_flush(conn);
}
} else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
* 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;
+
+ /* 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;
+ }
- if (sscanf(arguments, "%u", ×) != 1) {
- LOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments);
- times = 1;
- }
+ 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;
+ }
- Workspace *ws = CUR_CELL->workspace;
+ SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) {
+ if (++count < times) {
+ LOG("Skipping\n");
+ continue;
+ }
- SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
- if (++count < times) {
- LOG("Skipping\n");
- continue;
+ LOG("Focussing\n");
+ set_focus(conn, current, true);
+ break;
}
-
- 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;
+ }
}
/*
/* 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;
}
/* 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) {
+ if (last_focused == NULL || client_is_floating(last_focused)) {
LOG("not switching, this is a floating client\n");
return;
}
return;
}
+ if (command[0] == 'H') {
+ LOG("Hiding all floating windows\n");
+ floating_toggle_hide(conn, c_ws);
+ return;
+ }
+
+ enum { WITH_WINDOW, WITH_CONTAINER, WITH_WORKSPACE } with = WITH_WINDOW;
+
+ /* Is it a <with>? */
+ if (command[0] == 'w') {
+ command++;
+ /* TODO: implement */
+ if (command[0] == 'c') {
+ with = WITH_CONTAINER;
+ command++;
+ } else if (command[0] == 'w') {
+ with = WITH_WORKSPACE;
+ command++;
+ } else {
+ LOG("not yet implemented.\n");
+ return;
+ }
+ }
+
/* Is it 't' for toggle tiling/floating? */
if (command[0] == 't') {
+ if (with == WITH_WORKSPACE) {
+ c_ws->auto_float = !c_ws->auto_float;
+ LOG("autofloat is now %d\n", c_ws->auto_float);
+ return;
+ }
if (last_focused == NULL) {
LOG("Cannot toggle tiling/floating: workspace empty\n");
return;
}
- toggle_floating_mode(conn, last_focused);
+ toggle_floating_mode(conn, last_focused, false);
/* delete all empty columns/rows */
cleanup_table(conn, last_focused->workspace);
fix_colrowspan(conn, last_focused->workspace);
render_workspace(conn, last_focused->workspace->screen, last_focused->workspace);
- xcb_flush(conn);
- return;
- }
- enum { WITH_WINDOW, WITH_CONTAINER } with = WITH_WINDOW;
+ /* Re-focus the client because cleanup_table sets the focus to the last
+ * focused client inside a container only. */
+ set_focus(conn, last_focused, true);
- /* Is it a <with>? */
- if (command[0] == 'w') {
- command++;
- /* TODO: implement */
- if (command[0] == 'c') {
- with = WITH_CONTAINER;
- command++;
- } else {
- LOG("not yet implemented.\n");
- return;
- }
+ return;
}
/* It’s a normal <cmd> */
}
if (*rest == '\0') {
- /* No rest? This was a tag number, not a times specification */
+ /* No rest? This was a workspace number, not a times specification */
show_workspace(conn, times);
return;
}
}
if (*rest == '\0') {
- if (last_focused != NULL && last_focused->floating)
+ 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;
}
- if (last_focused == NULL || last_focused->floating) {
- LOG("Not performing (null or floating) \n");
+ if (last_focused == NULL) {
+ LOG("Not performing (no window found)\n");
+ return;
+ }
+
+ if (client_is_floating(last_focused) &&
+ (action != ACTION_FOCUS && action != ACTION_MOVE)) {
+ LOG("Not performing (floating)\n");
return;
}
LOG("unknown direction: %c\n", *rest);
return;
}
+ rest++;
- if (action == ACTION_FOCUS)
+ if (action == ACTION_FOCUS) {
+ if (client_is_floating(last_focused)) {
+ floating_focus_direction(conn, last_focused, direction);
+ continue;
+ }
focus_thing(conn, direction, (with == WITH_WINDOW ? THING_WINDOW : THING_CONTAINER));
- else if (action == ACTION_MOVE) {
+ continue;
+ }
+
+ if (action == ACTION_MOVE) {
+ if (client_is_floating(last_focused)) {
+ floating_move(conn, last_focused, direction);
+ continue;
+ }
if (with == WITH_WINDOW)
move_current_window(conn, direction);
else move_current_container(conn, direction);
+ continue;
}
- else if (action == ACTION_SNAP)
- snap_current_container(conn, direction);
- rest++;
+ if (action == ACTION_SNAP) {
+ snap_current_container(conn, direction);
+ continue;
+ }
}
LOG("--- done ---\n");