From f38809288af6d2dca4a5a96312982c5413342dee Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 27 Sep 2009 14:00:54 +0200 Subject: [PATCH] Make number of workspaces dynamic (no longer limited by 10) Warning: This is not yet thoroughly tested, so be prepared to encounter some segfaults. Please enable logging and coredumps, so we can fix bugs quickly. --- include/table.h | 3 +- include/workspace.h | 3 ++ src/click.c | 19 ++++++----- src/client.c | 6 ++-- src/commands.c | 29 +++++++++------- src/config.c | 6 ++-- src/floating.c | 31 +++++++++-------- src/layout.c | 5 +-- src/manage.c | 2 +- src/table.c | 20 +++++------ src/util.c | 2 +- src/workspace.c | 82 +++++++++++++++++++++++++++++++++++++++++---- src/xinerama.c | 6 ++-- 13 files changed, 149 insertions(+), 65 deletions(-) diff --git a/include/table.h b/include/table.h index 355839b1..05f543d6 100644 --- a/include/table.h +++ b/include/table.h @@ -21,7 +21,8 @@ #define CUR_CELL (CUR_TABLE[current_col][current_row]) extern Workspace *c_ws; -extern Workspace workspaces[10]; +extern Workspace *workspaces; +extern int num_workspaces; extern int current_col; extern int current_row; diff --git a/include/workspace.h b/include/workspace.h index c77bc868..6a17f597 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -16,6 +16,9 @@ #ifndef _WORKSPACE_H #define _WORKSPACE_H + +Workspace *workspace_get(int number); + /** * Sets the name (or just its number) for the given workspace. This has to * be called for every workspace as the rendering function diff --git a/src/click.c b/src/click.c index 4b27d1be..d9aff341 100644 --- a/src/click.c +++ b/src/click.c @@ -129,7 +129,7 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e /* Check if the button was one of button4 or button5 (scroll up / scroll down) */ if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) { int add = (event->detail == XCB_BUTTON_INDEX_4 ? -1 : 1); - for (int i = c_ws->num + add; (i >= 0) && (i < 10); i += add) + for (int i = c_ws->num + add; (i >= 0) && (i < num_workspaces); i += add) if (workspaces[i].screen == screen) { workspace_show(conn, i+1); return true; @@ -139,7 +139,7 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e int drawn = 0; /* Because workspaces can be on different screens, we need to loop through all of them and decide to count it based on its ->screen */ - for (int i = 0; i < 10; i++) { + for (int i = 0; i < num_workspaces; i++) { if (workspaces[i].screen != screen) continue; LOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n", @@ -198,6 +198,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ to_bottom = client->rect.height - event->event_y; resize_orientation_t orientation = O_VERTICAL; Container *con = client->container; + Workspace *ws = con->workspace; int first = 0, second = 0; LOG("click was %d px to the right, %d px to the left, %d px to top, %d px to bottom\n", @@ -211,7 +212,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ LOG("column %d\n", first); if (!cell_exists(first, con->row) || - (first == (con->workspace->cols-1))) + (first == (ws->cols-1))) return 1; second = first + 1; @@ -239,14 +240,14 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ /* …bottom border */ first = con->row + (con->rowspan - 1); if (!cell_exists(con->col, first) || - (first == (con->workspace->rows-1))) + (first == (ws->rows-1))) return 1; second = first + 1; orientation = O_HORIZONTAL; } - return resize_graphical_handler(conn, con->workspace, first, second, orientation, event); + return resize_graphical_handler(conn, ws, first, second, orientation, event); } } } @@ -323,6 +324,8 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ if (client_is_floating(client)) return floating_border_click(conn, client, event); + Workspace *ws = con->workspace; + if (event->event_y < 2) { /* This was a press on the top border */ if (con->row == 0) @@ -334,7 +337,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ /* …bottom border */ first = con->row + (con->rowspan - 1); if (!cell_exists(con->col, first) || - (first == (con->workspace->rows-1))) + (first == (ws->rows-1))) return 1; second = first + 1; @@ -352,11 +355,11 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ LOG("column %d\n", first); if (!cell_exists(first, con->row) || - (first == (con->workspace->cols-1))) + (first == (ws->cols-1))) return 1; second = first + 1; } - return resize_graphical_handler(conn, con->workspace, first, second, orientation, event); + return resize_graphical_handler(conn, ws, first, second, orientation, event); } diff --git a/src/client.c b/src/client.c index 93f9315d..bd5be673 100644 --- a/src/client.c +++ b/src/client.c @@ -25,6 +25,7 @@ #include "layout.h" #include "client.h" #include "table.h" +#include "workspace.h" /* * Removes the given client from the container, either because it will be inserted into another @@ -236,8 +237,9 @@ void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) { */ void client_set_below_floating(xcb_connection_t *conn, Client *client) { /* Ensure that it is below all floating clients */ - Client *first_floating = TAILQ_FIRST(&(client->workspace->floating_clients)); - if (first_floating != TAILQ_END(&(client->workspace->floating_clients))) { + Workspace *ws = client->workspace; + Client *first_floating = TAILQ_FIRST(&(ws->floating_clients)); + if (first_floating != TAILQ_END(&(ws->floating_clients))) { LOG("Setting below floating\n"); uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW }; xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); diff --git a/src/commands.c b/src/commands.c index 51459c14..0517a075 100644 --- a/src/commands.c +++ b/src/commands.c @@ -63,7 +63,7 @@ static void jump_to_mark(xcb_connection_t *conn, const char *mark) { Client *current; LOG("Jumping to \"%s\"\n", mark); - for (int c = 0; c < 10; c++) + for (int c = 0; c < num_workspaces; c++) SLIST_FOREACH(current, &(workspaces[c].focus_stack), focus_clients) { if (current->mark == NULL || strcmp(current->mark, mark) != 0) continue; @@ -165,7 +165,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t /* No screen found? Then wrap */ screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->screen); } - t_ws = &(workspaces[screen->current_workspace]); + t_ws = workspace_get(screen->current_workspace); new_row = (direction == D_UP ? (t_ws->rows - 1) : 0); } @@ -207,7 +207,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t LOG("Wrapping screen around horizontally\n"); screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->screen); } - t_ws = &(workspaces[screen->current_workspace]); + t_ws = workspace_get(screen->current_workspace); new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0); } @@ -523,7 +523,7 @@ static void snap_current_container(xcb_connection_t *conn, direction_t direction 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 = workspace_get(workspace-1), *old_ws = client->workspace; LOG("moving floating\n"); @@ -578,7 +578,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa assert(container != NULL); /* 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 = workspace_get(workspace-1); Client *current_client = container->currently_focused; if (current_client == NULL) { @@ -789,10 +789,10 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) { if (direction == 'n') { /* If we are on the last workspace, we cannot go any further */ - if (c_ws->num == 9) + if (c_ws->num == (num_workspaces-1)) return; - for (i = c_ws->num + 1; i <= 9; i++) { + for (i = c_ws->num + 1; i < num_workspaces; i++) { t_ws = &(workspaces[i]); if (t_ws->screen != NULL) break; @@ -815,6 +815,7 @@ static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, c int first, second; resize_orientation_t orientation = O_VERTICAL; Container *con = last_focused->container; + Workspace *ws = con->workspace; if (STARTS_WITH(command, "left")) { if (con->col == 0) @@ -827,7 +828,7 @@ static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, c LOG("column %d\n", first); if (!cell_exists(first, con->row) || - (first == (con->workspace->cols-1))) + (first == (ws->cols-1))) return; second = first + 1; @@ -842,7 +843,7 @@ static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, c } else if (STARTS_WITH(command, "bottom")) { first = con->row + (con->rowspan - 1); if (!cell_exists(con->col, first) || - (first == (con->workspace->rows-1))) + (first == (ws->rows-1))) return; second = first + 1; @@ -857,7 +858,7 @@ static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, c if (pixels == 0) return; - resize_container(conn, con->workspace, first, second, orientation, pixels); + resize_container(conn, ws, first, second, orientation, pixels); } /* @@ -1082,14 +1083,16 @@ void parse_command(xcb_connection_t *conn, const char *command) { return; } + Workspace *ws = last_focused->workspace; + toggle_floating_mode(conn, last_focused, false); /* delete all empty columns/rows */ - cleanup_table(conn, last_focused->workspace); + cleanup_table(conn, ws); /* Fix colspan/rowspan if it’d overlap */ - fix_colrowspan(conn, last_focused->workspace); + fix_colrowspan(conn, ws); - render_workspace(conn, last_focused->workspace->screen, last_focused->workspace); + render_workspace(conn, ws->screen, ws); /* Re-focus the client because cleanup_table sets the focus to the last * focused client inside a container only. */ diff --git a/src/config.c b/src/config.c index b9722fc5..a9de2909 100644 --- a/src/config.c +++ b/src/config.c @@ -418,7 +418,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, if ((end = strchr(screen, ' ')) != NULL) *end = '\0'; LOG("Setting preferred screen for workspace %d to \"%s\"\n", ws_num, screen); - workspaces[ws_num - 1].preferred_screen = screen; + workspace_get(ws_num-1)->preferred_screen = screen; name += strlen("screen ") + strlen(screen); } @@ -484,7 +484,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, while (*target == '~') target++; - if (atoi(target) >= 1 && atoi(target) <= 10) { + if (atoi(target) >= 1) { if (new->floating == ASSIGN_FLOATING_ONLY) new->floating = ASSIGN_FLOATING; new->workspace = atoi(target); @@ -546,7 +546,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, REQUIRED_OPTION(font); /* Set an empty name for every workspace which got no name */ - for (int i = 0; i < 10; i++) { + for (int i = 0; i < num_workspaces; i++) { Workspace *ws = &(workspaces[i]); if (ws->name != NULL) { /* If the font was not specified when the workspace name diff --git a/src/floating.c b/src/floating.c index 5f9eab3c..180ec429 100644 --- a/src/floating.c +++ b/src/floating.c @@ -26,6 +26,7 @@ #include "layout.h" #include "client.h" #include "floating.h" +#include "workspace.h" /* * Toggles floating mode for the given client. @@ -43,17 +44,18 @@ void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic if (con == NULL) { 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) + Workspace *ws = client->workspace; + SLIST_FOREACH(next_tiling, &(ws->focus_stack), focus_clients) 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 */ - if (next_tiling == TAILQ_END(&(client->workspace->focus_stack))) - con = client->workspace->table[0][0]; + if (next_tiling == TAILQ_END(&(ws->focus_stack))) + con = ws->table[0][0]; else con = next_tiling->container; /* Remove the client from the list of floating clients */ - TAILQ_REMOVE(&(client->workspace->floating_clients), client, floating_clients); + TAILQ_REMOVE(&(ws->floating_clients), client, floating_clients); LOG("destination container = %p\n", con); Client *old_focused = con->currently_focused; @@ -139,20 +141,21 @@ void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic * */ void floating_assign_to_workspace(Client *client, Workspace *new_workspace) { - /* Remove from focus stack and list of floating clients */ - SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients); - TAILQ_REMOVE(&(client->workspace->floating_clients), client, floating_clients); + Workspace *ws = client->workspace; - if (client->workspace->fullscreen_client == client) - client->workspace->fullscreen_client = NULL; + /* Remove from focus stack and list of floating clients */ + SLIST_REMOVE(&(ws->focus_stack), client, Client, focus_clients); + TAILQ_REMOVE(&(ws->floating_clients), client, floating_clients); + + if (ws->fullscreen_client == client) + ws->fullscreen_client = NULL; /* Insert into destination focus stack and list of floating clients */ - client->workspace = new_workspace; - SLIST_INSERT_HEAD(&(client->workspace->focus_stack), client, focus_clients); - TAILQ_INSERT_TAIL(&(client->workspace->floating_clients), client, floating_clients); + ws = new_workspace; + SLIST_INSERT_HEAD(&(ws->focus_stack), client, focus_clients); + TAILQ_INSERT_TAIL(&(ws->floating_clients), client, floating_clients); if (client->fullscreen) - client->workspace->fullscreen_client = client; - + ws->fullscreen_client = client; } /* diff --git a/src/layout.c b/src/layout.c index aadc5dd6..7ae3672c 100644 --- a/src/layout.c +++ b/src/layout.c @@ -27,6 +27,7 @@ #include "client.h" #include "floating.h" #include "handlers.h" +#include "workspace.h" /* * Updates *destination with new_value and returns true if it was changed or false @@ -237,7 +238,7 @@ void reposition_client(xcb_connection_t *conn, Client *client) { LOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen); LOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen); - floating_assign_to_workspace(client, &workspaces[screen->current_workspace]); + floating_assign_to_workspace(client, workspace_get(screen->current_workspace)); } /* @@ -585,7 +586,7 @@ static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int wid xcb_change_gc_single(conn, screen->bargc, XCB_GC_FONT, font->id); int drawn = 0; - for (int c = 0; c < 10; c++) { + for (int c = 0; c < num_workspaces; c++) { if (workspaces[c].screen != screen) continue; diff --git a/src/manage.c b/src/manage.c index e2a32416..5dcb837c 100644 --- a/src/manage.c +++ b/src/manage.c @@ -335,7 +335,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, } LOG("Changing container/workspace and unmapping the client\n"); - Workspace *t_ws = &(workspaces[assign->workspace-1]); + Workspace *t_ws = workspace_get(assign->workspace-1); workspace_initialize(t_ws, c_ws->screen); new->container = t_ws->table[t_ws->current_col][t_ws->current_row]; diff --git a/src/table.c b/src/table.c index 43df8d3d..6612a5e3 100644 --- a/src/table.c +++ b/src/table.c @@ -27,9 +27,10 @@ #include "layout.h" int current_workspace = 0; -Workspace workspaces[10]; +int num_workspaces = 1; +Workspace *workspaces; /* Convenience pointer to the current workspace */ -Workspace *c_ws = &workspaces[0]; +Workspace *c_ws; int current_col = 0; int current_row = 0; @@ -38,15 +39,14 @@ int current_row = 0; * */ void init_table() { - memset(workspaces, 0, sizeof(workspaces)); + workspaces = scalloc(sizeof(Workspace)); + c_ws = workspaces; - for (int i = 0; i < 10; i++) { - workspaces[i].screen = NULL; - workspaces[i].num = i; - TAILQ_INIT(&(workspaces[i].floating_clients)); - expand_table_cols(&(workspaces[i])); - expand_table_rows(&(workspaces[i])); - } + workspaces[0].screen = NULL; + workspaces[0].num = 0; + TAILQ_INIT(&(workspaces[0].floating_clients)); + expand_table_cols(&(workspaces[0])); + expand_table_rows(&(workspaces[0])); } static void new_container(Workspace *workspace, Container **container, int col, int row) { diff --git a/src/util.c b/src/util.c index 64ab82a9..94a5e336 100644 --- a/src/util.c +++ b/src/util.c @@ -455,7 +455,7 @@ Client *get_matching_client(xcb_connection_t *conn, const char *window_classtitl } LOG("Getting clients for class \"%s\" / title \"%s\"\n", to_class, to_title); - for (int workspace = 0; workspace < 10; workspace++) { + for (int workspace = 0; workspace < num_workspaces; workspace++) { if (workspaces[workspace].screen == NULL) continue; diff --git a/src/workspace.c b/src/workspace.c index 64db0cbc..c4f1ead1 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -27,6 +27,74 @@ #include "workspace.h" #include "client.h" +Workspace *workspace_get(int number) { + if (number > (num_workspaces-1)) { + int old_num_workspaces = num_workspaces; + + /* Convert all container->workspace and client->workspace + * pointers to numbers representing their workspace. Necessary + * because the realloc() may make all the pointers invalid, so + * we need to preserve them this way and restore them later. + * + * To distinguish between the first workspace and a NULL + * pointer, we store + 1. */ + for (int c = 0; c < num_workspaces; c++) + FOR_TABLE(&(workspaces[c])) { + Container *con = workspaces[c].table[cols][rows]; + if (con->workspace != NULL) { + LOG("Handling con %p with pointer %p (num %d)\n", con, con->workspace, con->workspace->num); + con->workspace = (Workspace*)(con->workspace->num + 1); + } + Client *current; + SLIST_FOREACH(current, &(workspaces[c].focus_stack), focus_clients) { + if (current->workspace == NULL) + continue; + LOG("Handling client %p with pointer %p (num %d)\n", current, current->workspace, current->workspace->num); + current->workspace = (Workspace*)(current->workspace->num + 1); + } + } + + /* preserve c_ws */ + c_ws = (Workspace*)(c_ws->num); + + LOG("We need to initialize that one\n"); + num_workspaces = number+1; + workspaces = realloc(workspaces, num_workspaces * sizeof(Workspace)); + for (int c = old_num_workspaces; c < num_workspaces; c++) { + memset(&workspaces[c], 0, sizeof(Workspace)); + workspaces[c].screen = NULL; + workspaces[c].num = c; + TAILQ_INIT(&(workspaces[c].floating_clients)); + expand_table_cols(&(workspaces[c])); + expand_table_rows(&(workspaces[c])); + workspace_set_name(&(workspaces[c]), NULL); + } + + c_ws = workspace_get((int)c_ws); + + for (int c = 0; c < old_num_workspaces; c++) + FOR_TABLE(&(workspaces[c])) { + Container *con = workspaces[c].table[cols][rows]; + if (con->workspace != NULL) { + LOG("Handling con %p with (num %d)\n", con, con->workspace); + con->workspace = workspace_get((int)con->workspace - 1); + } + Client *current; + SLIST_FOREACH(current, &(workspaces[c].focus_stack), focus_clients) { + if (current->workspace == NULL) + continue; + LOG("Handling client %p with (num %d)\n", current, current->workspace); + current->workspace = workspace_get((int)current->workspace - 1); + } + } + + + LOG("done\n"); + } + + return &(workspaces[number]); +} + /* * Sets the name (or just its number) for the given workspace. This has to * be called for every workspace as the rendering function @@ -73,7 +141,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { 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]); + Workspace *t_ws = workspace_get(workspace-1); LOG("show_workspace(%d)\n", workspace); @@ -91,7 +159,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { /* Store the old client */ Client *old_client = CUR_CELL->currently_focused; - c_ws = &(workspaces[t_ws->screen->current_workspace]); + c_ws = workspace_get(t_ws->screen->current_workspace); current_col = c_ws->current_col; current_row = c_ws->current_row; if (CUR_CELL->currently_focused != NULL) @@ -123,7 +191,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { t_ws->screen->current_workspace = workspace-1; Workspace *old_workspace = c_ws; - c_ws = &workspaces[workspace-1]; + c_ws = workspace_get(workspace-1); /* Unmap all clients of the old workspace */ workspace_unmap_clients(conn, old_workspace); @@ -243,8 +311,8 @@ void workspace_initialize(Workspace *ws, i3Screen *screen) { Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen) { Workspace *result = NULL; - for (int c = 0; c < 10; c++) { - Workspace *ws = &(workspaces[c]); + for (int c = 0; c < num_workspaces; c++) { + Workspace *ws = workspace_get(c); if (ws->preferred_screen == NULL || !screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen)) continue; @@ -255,11 +323,11 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen * if (result == NULL) { /* No assignment found, returning first unused workspace */ - for (int c = 0; c < 10; c++) { + for (int c = 0; c < num_workspaces; c++) { if (workspaces[c].screen != NULL) continue; - result = &(workspaces[c]); + result = workspace_get(c); break; } } diff --git a/src/xinerama.c b/src/xinerama.c index f40ec84b..db5b9467 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -298,7 +298,7 @@ void xinerama_requery_screens(xcb_connection_t *conn) { int screen_count = 0; /* Mark each workspace which currently is assigned to a screen, so we * can garbage-collect afterwards */ - for (int c = 0; c < 10; c++) + for (int c = 0; c < num_workspaces; c++) workspaces[c].reassigned = (workspaces[c].screen == NULL); TAILQ_FOREACH(screen, new_screens, screens) { @@ -334,7 +334,7 @@ void xinerama_requery_screens(xcb_connection_t *conn) { screen->dock_clients = old_screen->dock_clients; /* Update the dimensions */ - for (int c = 0; c < 10; c++) { + for (int c = 0; c < num_workspaces; c++) { Workspace *ws = &(workspaces[c]); if (ws->screen != old_screen) continue; @@ -363,7 +363,7 @@ void xinerama_requery_screens(xcb_connection_t *conn) { } /* Check for workspaces which are out of bounds */ - for (int c = 0; c < 10; c++) { + for (int c = 0; c < num_workspaces; c++) { if (workspaces[c].reassigned) continue;