00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <limits.h>
00011
00012 #include "all.h"
00013
00014
00015
00016
00017
00018
00019
00020 Con *workspace_get(const char *num, bool *created) {
00021 Con *output, *workspace = NULL;
00022
00023 TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
00024 GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num));
00025
00026 if (workspace == NULL) {
00027 LOG("Creating new workspace \"%s\"\n", num);
00028
00029 output = con_get_output(focused);
00030
00031 struct Workspace_Assignment *assignment;
00032 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
00033 if (strcmp(assignment->name, num) != 0)
00034 continue;
00035
00036 LOG("Found workspace assignment to output \"%s\"\n", assignment->output);
00037 GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
00038 break;
00039 }
00040 Con *content = output_get_content(output);
00041 LOG("got output %p with content %p\n", output, content);
00042
00043
00044 workspace = con_new(NULL, NULL);
00045 char *name;
00046 asprintf(&name, "[i3 con] workspace %s", num);
00047 x_set_name(workspace, name);
00048 free(name);
00049 workspace->type = CT_WORKSPACE;
00050 FREE(workspace->name);
00051 workspace->name = sstrdup(num);
00052
00053
00054 char *end;
00055 long parsed_num = strtol(num, &end, 10);
00056 if (parsed_num == LONG_MIN ||
00057 parsed_num == LONG_MAX ||
00058 parsed_num < 0 ||
00059 (end && *end != '\0'))
00060 workspace->num = -1;
00061 else workspace->num = parsed_num;
00062 LOG("num = %d\n", workspace->num);
00063
00064
00065
00066
00067 if (config.default_orientation == NO_ORIENTATION) {
00068 workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
00069 DLOG("Auto orientation. Output resolution set to (%d,%d), setting orientation to %d.\n",
00070 workspace->rect.width, workspace->rect.height, workspace->orientation);
00071 } else {
00072 workspace->orientation = config.default_orientation;
00073 }
00074
00075 con_attach(workspace, content, false);
00076
00077 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
00078 if (created != NULL)
00079 *created = true;
00080 }
00081 else if (created != NULL) {
00082 *created = false;
00083 }
00084
00085 return workspace;
00086 }
00087
00088
00089
00090
00091
00092
00093
00094 bool workspace_is_visible(Con *ws) {
00095 Con *output = con_get_output(ws);
00096 if (output == NULL)
00097 return false;
00098 Con *fs = con_get_fullscreen_con(output, CF_OUTPUT);
00099 LOG("workspace visible? fs = %p, ws = %p\n", fs, ws);
00100 return (fs == ws);
00101 }
00102
00103
00104
00105
00106
00107 Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
00108 Con *current;
00109
00110 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
00111 if (current != exclude &&
00112 current->sticky_group != NULL &&
00113 current->window != NULL &&
00114 strcmp(current->sticky_group, sticky_group) == 0)
00115 return current;
00116
00117 Con *recurse = _get_sticky(current, sticky_group, exclude);
00118 if (recurse != NULL)
00119 return recurse;
00120 }
00121
00122 TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
00123 if (current != exclude &&
00124 current->sticky_group != NULL &&
00125 current->window != NULL &&
00126 strcmp(current->sticky_group, sticky_group) == 0)
00127 return current;
00128
00129 Con *recurse = _get_sticky(current, sticky_group, exclude);
00130 if (recurse != NULL)
00131 return recurse;
00132 }
00133
00134 return NULL;
00135 }
00136
00137
00138
00139
00140
00141
00142
00143
00144 static void workspace_reassign_sticky(Con *con) {
00145 Con *current;
00146
00147
00148
00149 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
00150 if (current->sticky_group == NULL) {
00151 workspace_reassign_sticky(current);
00152 continue;
00153 }
00154
00155 LOG("Ah, this one is sticky: %s / %p\n", current->name, current);
00156
00157 Con *output = con_get_output(current);
00158 Con *src = _get_sticky(output, current->sticky_group, current);
00159
00160 if (src == NULL) {
00161 LOG("No window found for this sticky group\n");
00162 workspace_reassign_sticky(current);
00163 continue;
00164 }
00165
00166 x_move_win(src, current);
00167 current->window = src->window;
00168 current->mapped = true;
00169 src->window = NULL;
00170 src->mapped = false;
00171
00172 x_reparent_child(current, src);
00173
00174 LOG("re-assigned window from src %p to dest %p\n", src, current);
00175 }
00176
00177 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
00178 workspace_reassign_sticky(current);
00179 }
00180
00181
00182
00183
00184
00185 void workspace_show(const char *num) {
00186 Con *workspace, *current, *old = NULL;
00187
00188 bool changed_num_workspaces;
00189 workspace = workspace_get(num, &changed_num_workspaces);
00190
00191
00192
00193 TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) {
00194 if (current->fullscreen_mode == CF_OUTPUT)
00195 old = current;
00196 current->fullscreen_mode = CF_NONE;
00197 }
00198
00199
00200
00201 workspace->fullscreen_mode = CF_OUTPUT;
00202 if (workspace == con_get_workspace(focused)) {
00203 DLOG("Not switching, already there.\n");
00204 return;
00205 }
00206
00207 workspace_reassign_sticky(workspace);
00208
00209 LOG("switching to %p\n", workspace);
00210 Con *next = con_descend_focused(workspace);
00211
00212 if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
00213
00214 if (!workspace_is_visible(old)) {
00215 LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
00216 tree_close(old, DONT_KILL_WINDOW, false);
00217 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
00218 changed_num_workspaces = true;
00219 }
00220 }
00221
00222 con_focus(next);
00223 workspace->fullscreen_mode = CF_OUTPUT;
00224 LOG("focused now = %p / %s\n", focused, focused->name);
00225
00226
00227 if (changed_num_workspaces)
00228 ewmh_update_workarea();
00229 ewmh_update_current_desktop();
00230
00231 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"focus\"}");
00232 }
00233
00234
00235
00236
00237
00238 void workspace_next() {
00239 Con *ws = con_get_workspace(focused);
00240 Con *next = TAILQ_NEXT(ws, nodes);
00241 if (!next)
00242 next = TAILQ_FIRST(&(ws->parent->nodes_head));
00243
00244 workspace_show(next->name);
00245 }
00246
00247
00248
00249
00250
00251 void workspace_prev() {
00252 Con *ws = con_get_workspace(focused);
00253 Con *prev = TAILQ_PREV(ws, nodes_head, nodes);
00254 if (!prev)
00255 prev = TAILQ_LAST(&(ws->parent->nodes_head), nodes_head);
00256
00257 workspace_show(prev->name);
00258 }
00259
00260 static bool get_urgency_flag(Con *con) {
00261 Con *child;
00262 TAILQ_FOREACH(child, &(con->nodes_head), nodes)
00263 if (child->urgent || get_urgency_flag(child))
00264 return true;
00265
00266 TAILQ_FOREACH(child, &(con->floating_head), floating_windows)
00267 if (child->urgent || get_urgency_flag(child))
00268 return true;
00269
00270 return false;
00271 }
00272
00273
00274
00275
00276
00277
00278 void workspace_update_urgent_flag(Con *ws) {
00279 bool old_flag = ws->urgent;
00280 ws->urgent = get_urgency_flag(ws);
00281 DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
00282
00283 if (old_flag != ws->urgent)
00284 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
00285 }
00286
00287
00288
00289
00290
00291
00292
00293 void ws_force_orientation(Con *ws, orientation_t orientation) {
00294
00295 Con *split = con_new(NULL, NULL);
00296 split->parent = ws;
00297
00298
00299 split->layout = ws->layout;
00300 split->orientation = ws->orientation;
00301
00302 Con *old_focused = TAILQ_FIRST(&(ws->focus_head));
00303
00304
00305 DLOG("Moving cons\n");
00306 while (!TAILQ_EMPTY(&(ws->nodes_head))) {
00307 Con *child = TAILQ_FIRST(&(ws->nodes_head));
00308 con_detach(child);
00309 con_attach(child, split, true);
00310 }
00311
00312
00313 ws->orientation = orientation;
00314
00315
00316 DLOG("Attaching new split to ws\n");
00317 con_attach(split, ws, false);
00318
00319
00320 con_fix_percent(ws);
00321
00322 if (old_focused)
00323 con_focus(old_focused);
00324 }
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 Con *workspace_attach_to(Con *ws) {
00337 DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name);
00338
00339 if (config.default_layout == L_DEFAULT) {
00340 DLOG("Default layout, just attaching it to the workspace itself.\n");
00341 return ws;
00342 }
00343
00344 DLOG("Non-default layout, creating a new split container\n");
00345
00346 Con *new = con_new(NULL, NULL);
00347 new->parent = ws;
00348
00349
00350 new->layout = config.default_layout;
00351
00352
00353
00354
00355 if (config.default_orientation == NO_ORIENTATION) {
00356 new->orientation = (ws->rect.height > ws->rect.width) ? VERT : HORIZ;
00357 } else {
00358 new->orientation = config.default_orientation;
00359 }
00360
00361
00362 DLOG("Attaching new split %p to workspace %p\n", new, ws);
00363 con_attach(new, ws, false);
00364
00365 return new;
00366 }