• Main Page
  • Data Structures
  • Files
  • File List
  • Globals

src/table.c

Go to the documentation of this file.
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 }

Generated on Mon Aug 22 2011 for i3 by  doxygen 1.7.1