i3
|
00001 /* 00002 * vim:ts=8:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * 00006 * © 2009 Michael Stapelberg and contributors 00007 * 00008 * See file LICENSE for license information. 00009 * 00010 * table.c: Functions/macros for easy modifying/accessing of _the_ table (defining our 00011 * layout). 00012 * 00013 */ 00014 #include <stdio.h> 00015 #include <assert.h> 00016 #include <stdlib.h> 00017 #include <string.h> 00018 #include <sys/types.h> 00019 #include <unistd.h> 00020 #include <stdbool.h> 00021 #include <assert.h> 00022 00023 #include "data.h" 00024 #include "table.h" 00025 #include "util.h" 00026 #include "i3.h" 00027 #include "layout.h" 00028 #include "config.h" 00029 #include "workspace.h" 00030 #include "log.h" 00031 00032 int current_workspace = 0; 00033 int num_workspaces = 1; 00034 struct workspaces_head *workspaces; 00035 /* Convenience pointer to the current workspace */ 00036 Workspace *c_ws; 00037 int current_col = 0; 00038 int current_row = 0; 00039 00040 /* 00041 * Initialize table 00042 * 00043 */ 00044 void init_table() { 00045 workspaces = scalloc(sizeof(struct workspaces_head)); 00046 TAILQ_INIT(workspaces); 00047 00048 c_ws = scalloc(sizeof(Workspace)); 00049 workspace_set_name(c_ws, NULL); 00050 TAILQ_INIT(&(c_ws->floating_clients)); 00051 TAILQ_INSERT_TAIL(workspaces, c_ws, workspaces); 00052 } 00053 00054 static void new_container(Workspace *workspace, Container **container, int col, int row, bool skip_layout_switch) { 00055 Container *new; 00056 new = *container = scalloc(sizeof(Container)); 00057 CIRCLEQ_INIT(&(new->clients)); 00058 new->colspan = 1; 00059 new->rowspan = 1; 00060 new->col = col; 00061 new->row = row; 00062 new->workspace = workspace; 00063 if (!skip_layout_switch) 00064 switch_layout_mode(global_conn, new, config.container_mode); 00065 new->stack_limit = config.container_stack_limit; 00066 new->stack_limit_value = config.container_stack_limit_value; 00067 } 00068 00069 /* 00070 * Add one row to the table 00071 * 00072 */ 00073 void expand_table_rows(Workspace *workspace) { 00074 workspace->rows++; 00075 00076 workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows); 00077 workspace->height_factor[workspace->rows-1] = 0; 00078 00079 for (int c = 0; c < workspace->cols; c++) { 00080 workspace->table[c] = realloc(workspace->table[c], sizeof(Container*) * workspace->rows); 00081 new_container(workspace, &(workspace->table[c][workspace->rows-1]), c, workspace->rows-1, true); 00082 } 00083 00084 /* We need to switch the layout in a separate step because it could 00085 * happen that render_layout() (being called by switch_layout_mode()) 00086 * would access containers which were not yet initialized. */ 00087 for (int c = 0; c < workspace->cols; c++) 00088 switch_layout_mode(global_conn, workspace->table[c][workspace->rows-1], config.container_mode); 00089 } 00090 00091 /* 00092 * Adds one row at the head of the table 00093 * 00094 */ 00095 void expand_table_rows_at_head(Workspace *workspace) { 00096 workspace->rows++; 00097 00098 workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows); 00099 00100 DLOG("rows = %d\n", workspace->rows); 00101 for (int rows = (workspace->rows - 1); rows >= 1; rows--) { 00102 DLOG("Moving height_factor %d (%f) to %d\n", rows-1, workspace->height_factor[rows-1], rows); 00103 workspace->height_factor[rows] = workspace->height_factor[rows-1]; 00104 } 00105 00106 workspace->height_factor[0] = 0; 00107 00108 for (int cols = 0; cols < workspace->cols; cols++) 00109 workspace->table[cols] = realloc(workspace->table[cols], sizeof(Container*) * workspace->rows); 00110 00111 /* Move the other rows */ 00112 for (int cols = 0; cols < workspace->cols; cols++) 00113 for (int rows = workspace->rows - 1; rows > 0; rows--) { 00114 DLOG("Moving row %d to %d\n", rows-1, rows); 00115 workspace->table[cols][rows] = workspace->table[cols][rows-1]; 00116 workspace->table[cols][rows]->row = rows; 00117 } 00118 00119 for (int cols = 0; cols < workspace->cols; cols++) 00120 new_container(workspace, &(workspace->table[cols][0]), cols, 0, false); 00121 } 00122 00123 /* 00124 * Add one column to the table 00125 * 00126 */ 00127 void expand_table_cols(Workspace *workspace) { 00128 workspace->cols++; 00129 00130 workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols); 00131 workspace->width_factor[workspace->cols-1] = 0; 00132 00133 workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols); 00134 workspace->table[workspace->cols-1] = scalloc(sizeof(Container*) * workspace->rows); 00135 00136 for (int c = 0; c < workspace->rows; c++) 00137 new_container(workspace, &(workspace->table[workspace->cols-1][c]), workspace->cols-1, c, true); 00138 00139 for (int c = 0; c < workspace->rows; c++) 00140 switch_layout_mode(global_conn, workspace->table[workspace->cols-1][c], config.container_mode); 00141 } 00142 00143 /* 00144 * Inserts one column at the table’s head 00145 * 00146 */ 00147 void expand_table_cols_at_head(Workspace *workspace) { 00148 workspace->cols++; 00149 00150 workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols); 00151 00152 DLOG("cols = %d\n", workspace->cols); 00153 for (int cols = (workspace->cols - 1); cols >= 1; cols--) { 00154 DLOG("Moving width_factor %d (%f) to %d\n", cols-1, workspace->width_factor[cols-1], cols); 00155 workspace->width_factor[cols] = workspace->width_factor[cols-1]; 00156 } 00157 00158 workspace->width_factor[0] = 0; 00159 00160 workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols); 00161 workspace->table[workspace->cols-1] = scalloc(sizeof(Container*) * workspace->rows); 00162 00163 /* Move the other columns */ 00164 for (int rows = 0; rows < workspace->rows; rows++) 00165 for (int cols = workspace->cols - 1; cols > 0; cols--) { 00166 DLOG("Moving col %d to %d\n", cols-1, cols); 00167 workspace->table[cols][rows] = workspace->table[cols-1][rows]; 00168 workspace->table[cols][rows]->col = cols; 00169 } 00170 00171 for (int rows = 0; rows < workspace->rows; rows++) 00172 new_container(workspace, &(workspace->table[0][rows]), 0, rows, false); 00173 } 00174 00175 /* 00176 * Shrinks the table by one column. 00177 * 00178 * The containers themselves are freed in move_columns_from() or move_rows_from(). Therefore, this 00179 * function may only be called from move_*() or after making sure that the containers are freed 00180 * properly. 00181 * 00182 */ 00183 static void shrink_table_cols(Workspace *workspace) { 00184 float free_space = workspace->width_factor[workspace->cols-1]; 00185 00186 workspace->cols--; 00187 00188 /* Shrink the width_factor array */ 00189 workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols); 00190 00191 /* Free the container-pointers */ 00192 free(workspace->table[workspace->cols]); 00193 00194 /* Re-allocate the table */ 00195 workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols); 00196 00197 /* Distribute the free space */ 00198 if (free_space == 0) 00199 return; 00200 00201 for (int cols = (workspace->cols-1); cols >= 0; cols--) { 00202 if (workspace->width_factor[cols] == 0) 00203 continue; 00204 00205 DLOG("Added free space (%f) to %d (had %f)\n", free_space, cols, 00206 workspace->width_factor[cols]); 00207 workspace->width_factor[cols] += free_space; 00208 break; 00209 } 00210 } 00211 00212 /* 00213 * See shrink_table_cols() 00214 * 00215 */ 00216 static void shrink_table_rows(Workspace *workspace) { 00217 float free_space = workspace->height_factor[workspace->rows-1]; 00218 00219 workspace->rows--; 00220 for (int cols = 0; cols < workspace->cols; cols++) 00221 workspace->table[cols] = realloc(workspace->table[cols], sizeof(Container*) * workspace->rows); 00222 00223 /* Shrink the height_factor array */ 00224 workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows); 00225 00226 /* Distribute the free space */ 00227 if (free_space == 0) 00228 return; 00229 00230 for (int rows = (workspace->rows-1); rows >= 0; rows--) { 00231 if (workspace->height_factor[rows] == 0) 00232 continue; 00233 00234 DLOG("Added free space (%f) to %d (had %f)\n", free_space, rows, 00235 workspace->height_factor[rows]); 00236 workspace->height_factor[rows] += free_space; 00237 break; 00238 } 00239 } 00240 00241 /* 00242 * Performs simple bounds checking for the given column/row 00243 * 00244 */ 00245 bool cell_exists(Workspace *ws, int col, int row) { 00246 return (col >= 0 && col < ws->cols) && 00247 (row >= 0 && row < ws->rows); 00248 } 00249 00250 static void free_container(xcb_connection_t *conn, Workspace *workspace, int col, int row) { 00251 Container *old_container = workspace->table[col][row]; 00252 00253 if (old_container->mode == MODE_STACK || old_container->mode == MODE_TABBED) 00254 leave_stack_mode(conn, old_container); 00255 00256 free(old_container); 00257 } 00258 00259 static void move_columns_from(xcb_connection_t *conn, Workspace *workspace, int cols) { 00260 DLOG("firstly freeing \n"); 00261 00262 /* Free the columns which are cleaned up */ 00263 for (int rows = 0; rows < workspace->rows; rows++) 00264 free_container(conn, workspace, cols-1, rows); 00265 00266 for (; cols < workspace->cols; cols++) 00267 for (int rows = 0; rows < workspace->rows; rows++) { 00268 DLOG("at col = %d, row = %d\n", cols, rows); 00269 Container *new_container = workspace->table[cols][rows]; 00270 00271 DLOG("moving cols = %d to cols -1 = %d\n", cols, cols-1); 00272 workspace->table[cols-1][rows] = new_container; 00273 00274 new_container->row = rows; 00275 new_container->col = cols-1; 00276 } 00277 } 00278 00279 static void move_rows_from(xcb_connection_t *conn, Workspace *workspace, int rows) { 00280 for (int cols = 0; cols < workspace->cols; cols++) 00281 free_container(conn, workspace, cols, rows-1); 00282 00283 for (; rows < workspace->rows; rows++) 00284 for (int cols = 0; cols < workspace->cols; cols++) { 00285 Container *new_container = workspace->table[cols][rows]; 00286 00287 DLOG("moving rows = %d to rows -1 = %d\n", rows, rows - 1); 00288 workspace->table[cols][rows-1] = new_container; 00289 00290 new_container->row = rows-1; 00291 new_container->col = cols; 00292 } 00293 } 00294 00295 /* 00296 * Prints the table’s contents in human-readable form for debugging 00297 * 00298 */ 00299 void dump_table(xcb_connection_t *conn, Workspace *workspace) { 00300 DLOG("dump_table()\n"); 00301 FOR_TABLE(workspace) { 00302 Container *con = workspace->table[cols][rows]; 00303 DLOG("----\n"); 00304 DLOG("at col=%d, row=%d\n", cols, rows); 00305 DLOG("currently_focused = %p\n", con->currently_focused); 00306 Client *loop; 00307 CIRCLEQ_FOREACH(loop, &(con->clients), clients) { 00308 DLOG("got client %08x / %s\n", loop->child, loop->name); 00309 } 00310 DLOG("----\n"); 00311 } 00312 DLOG("done\n"); 00313 } 00314 00315 /* 00316 * Shrinks the table by "compacting" it, that is, removing completely empty rows/columns 00317 * 00318 */ 00319 void cleanup_table(xcb_connection_t *conn, Workspace *workspace) { 00320 DLOG("cleanup_table()\n"); 00321 00322 /* Check for empty columns if we got more than one column */ 00323 for (int cols = 0; (workspace->cols > 1) && (cols < workspace->cols);) { 00324 bool completely_empty = true; 00325 for (int rows = 0; rows < workspace->rows; rows++) 00326 if (workspace->table[cols][rows]->currently_focused != NULL) { 00327 completely_empty = false; 00328 break; 00329 } 00330 if (completely_empty) { 00331 DLOG("Removing completely empty column %d\n", cols); 00332 if (cols < (workspace->cols - 1)) 00333 move_columns_from(conn, workspace, cols+1); 00334 else { 00335 for (int rows = 0; rows < workspace->rows; rows++) 00336 free_container(conn, workspace, cols, rows); 00337 } 00338 shrink_table_cols(workspace); 00339 00340 if (workspace->current_col >= workspace->cols) 00341 workspace->current_col = workspace->cols - 1; 00342 } else cols++; 00343 } 00344 00345 /* Check for empty rows if we got more than one row */ 00346 for (int rows = 0; (workspace->rows > 1) && (rows < workspace->rows);) { 00347 bool completely_empty = true; 00348 DLOG("Checking row %d\n", rows); 00349 for (int cols = 0; cols < workspace->cols; cols++) 00350 if (workspace->table[cols][rows]->currently_focused != NULL) { 00351 completely_empty = false; 00352 break; 00353 } 00354 if (completely_empty) { 00355 DLOG("Removing completely empty row %d\n", rows); 00356 if (rows < (workspace->rows - 1)) 00357 move_rows_from(conn, workspace, rows+1); 00358 else { 00359 for (int cols = 0; cols < workspace->cols; cols++) 00360 free_container(conn, workspace, cols, rows); 00361 } 00362 shrink_table_rows(workspace); 00363 00364 if (workspace->current_row >= workspace->rows) 00365 workspace->current_row = workspace->rows - 1; 00366 } else rows++; 00367 } 00368 00369 /* Boundary checking for current_col and current_row */ 00370 if (current_col >= c_ws->cols) 00371 current_col = c_ws->cols-1; 00372 00373 if (current_row >= c_ws->rows) 00374 current_row = c_ws->rows-1; 00375 00376 if (CUR_CELL->currently_focused != NULL) 00377 set_focus(conn, CUR_CELL->currently_focused, true); 00378 } 00379 00380 /* 00381 * Fixes col/rowspan (makes sure there are no overlapping windows, obeys borders). 00382 * 00383 */ 00384 void fix_colrowspan(xcb_connection_t *conn, Workspace *workspace) { 00385 DLOG("Fixing col/rowspan\n"); 00386 00387 FOR_TABLE(workspace) { 00388 Container *con = workspace->table[cols][rows]; 00389 if (con->colspan > 1) { 00390 DLOG("gots one with colspan %d (at %d c, %d r)\n", con->colspan, cols, rows); 00391 while (con->colspan > 1 && 00392 (!cell_exists(workspace, cols + (con->colspan-1), rows) && 00393 workspace->table[cols + (con->colspan - 1)][rows]->currently_focused != NULL)) 00394 con->colspan--; 00395 DLOG("fixed it to %d\n", con->colspan); 00396 } 00397 if (con->rowspan > 1) { 00398 DLOG("gots one with rowspan %d (at %d c, %d r)\n", con->rowspan, cols, rows); 00399 while (con->rowspan > 1 && 00400 (!cell_exists(workspace, cols, rows + (con->rowspan - 1)) && 00401 workspace->table[cols][rows + (con->rowspan - 1)]->currently_focused != NULL)) 00402 con->rowspan--; 00403 DLOG("fixed it to %d\n", con->rowspan); 00404 } 00405 } 00406 }