00001
00002
00003
00004
00005 #include "all.h"
00006
00007 struct Con *croot;
00008 struct Con *focused;
00009
00010 struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons);
00011
00012
00013
00014
00015
00016 bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
00017 char *globbed = resolve_tilde(path);
00018
00019 if (!path_exists(globbed)) {
00020 LOG("%s does not exist, not restoring tree\n", globbed);
00021 free(globbed);
00022 return false;
00023 }
00024
00025
00026 croot = con_new(NULL, NULL);
00027 croot->rect = (Rect){
00028 geometry->x,
00029 geometry->y,
00030 geometry->width,
00031 geometry->height
00032 };
00033 focused = croot;
00034
00035 tree_append_json(globbed);
00036
00037 printf("appended tree, using new root\n");
00038 croot = TAILQ_FIRST(&(croot->nodes_head));
00039 printf("new root = %p\n", croot);
00040 Con *out = TAILQ_FIRST(&(croot->nodes_head));
00041 printf("out = %p\n", out);
00042 Con *ws = TAILQ_FIRST(&(out->nodes_head));
00043 printf("ws = %p\n", ws);
00044
00045 return true;
00046 }
00047
00048
00049
00050
00051
00052
00053 void tree_init(xcb_get_geometry_reply_t *geometry) {
00054 croot = con_new(NULL, NULL);
00055 FREE(croot->name);
00056 croot->name = "root";
00057 croot->type = CT_ROOT;
00058 croot->rect = (Rect){
00059 geometry->x,
00060 geometry->y,
00061 geometry->width,
00062 geometry->height
00063 };
00064 }
00065
00066
00067
00068
00069
00070 Con *tree_open_con(Con *con, i3Window *window) {
00071 if (con == NULL) {
00072
00073 con = focused->parent;
00074
00075
00076 if (con->parent->type == CT_OUTPUT && con->type != CT_DOCKAREA) {
00077 con = focused;
00078 }
00079
00080
00081
00082
00083 if (con->type == CT_FLOATING_CON) {
00084 con = con_descend_tiling_focused(con->parent);
00085 if (con->type != CT_WORKSPACE)
00086 con = con->parent;
00087 }
00088 DLOG("con = %p\n", con);
00089 }
00090
00091 assert(con != NULL);
00092
00093
00094 Con *new = con_new(con, window);
00095
00096
00097 con_fix_percent(con);
00098
00099 return new;
00100 }
00101
00102 static bool _is_con_mapped(Con *con) {
00103 Con *child;
00104
00105 TAILQ_FOREACH(child, &(con->nodes_head), nodes)
00106 if (_is_con_mapped(child))
00107 return true;
00108
00109 return con->mapped;
00110 }
00111
00112
00113
00114
00115
00116
00117
00118 bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent) {
00119 bool was_mapped = con->mapped;
00120 Con *parent = con->parent;
00121
00122 if (!was_mapped) {
00123
00124
00125
00126 was_mapped = _is_con_mapped(con);
00127 }
00128
00129
00130 Con *next = con_next_focused(con);
00131 DLOG("next = %p, focused = %p\n", next, focused);
00132
00133 DLOG("closing %p, kill_window = %d\n", con, kill_window);
00134 Con *child, *nextchild;
00135 bool abort_kill = false;
00136
00137
00138 for (child = TAILQ_FIRST(&(con->nodes_head)); child; ) {
00139 nextchild = TAILQ_NEXT(child, nodes);
00140 DLOG("killing child=%p\n", child);
00141 if (!tree_close(child, kill_window, true))
00142 abort_kill = true;
00143 child = nextchild;
00144 }
00145
00146 if (abort_kill) {
00147 DLOG("One of the children could not be killed immediately (WM_DELETE sent), aborting.\n");
00148 return false;
00149 }
00150
00151 if (con->window != NULL) {
00152 if (kill_window != DONT_KILL_WINDOW) {
00153 x_window_kill(con->window->id, kill_window);
00154 return false;
00155 } else {
00156 xcb_void_cookie_t cookie;
00157
00158 cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
00159
00160
00161
00162 add_ignore_event(cookie.sequence, 0);
00163
00164
00165
00166 long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE };
00167 cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
00168 con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data);
00169
00170
00171
00172 add_ignore_event(cookie.sequence, 0);
00173 }
00174 FREE(con->window->class_class);
00175 FREE(con->window->class_instance);
00176 FREE(con->window->name_x);
00177 FREE(con->window->name_json);
00178 free(con->window);
00179 }
00180
00181
00182 x_con_kill(con);
00183
00184 con_detach(con);
00185 if (con->type != CT_FLOATING_CON) {
00186
00187
00188 con_fix_percent(parent);
00189 }
00190
00191 if (con_is_floating(con)) {
00192 Con *ws = con_get_workspace(con);
00193 DLOG("Container was floating, killing floating container\n");
00194 tree_close(parent, DONT_KILL_WINDOW, false);
00195 DLOG("parent container killed\n");
00196 if (con == focused) {
00197 DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
00198
00199 next = con_descend_focused(ws);
00200
00201 dont_kill_parent = true;
00202 DLOG("Alright, focusing %p\n", next);
00203 } else {
00204 next = NULL;
00205 }
00206 }
00207
00208 free(con->name);
00209 FREE(con->deco_render_params);
00210 TAILQ_REMOVE(&all_cons, con, all_cons);
00211 free(con);
00212
00213
00214
00215 if (!next) {
00216 DLOG("No next container, i will just exit now\n");
00217 return true;
00218 }
00219
00220 if (was_mapped || con == focused) {
00221 if ((kill_window != DONT_KILL_WINDOW) || !dont_kill_parent || con == focused) {
00222 DLOG("focusing %p / %s\n", next, next->name);
00223
00224 if (next->type == CT_DOCKAREA) {
00225
00226 con_focus(con_descend_focused(output_get_content(next->parent)));
00227 } else {
00228 con_focus(next);
00229 }
00230 }
00231 else {
00232 DLOG("not focusing because we're not killing anybody");
00233 }
00234 } else {
00235 DLOG("not focusing, was not mapped\n");
00236 }
00237
00238
00239 if (!dont_kill_parent)
00240 CALL(parent, on_remove_child);
00241
00242 return true;
00243 }
00244
00245
00246
00247
00248
00249 void tree_close_con(kill_window_t kill_window) {
00250 assert(focused != NULL);
00251 if (focused->type == CT_WORKSPACE) {
00252 LOG("Cannot close workspace\n");
00253 return;
00254 }
00255
00256
00257 assert(focused->type != CT_OUTPUT);
00258 assert(focused->type != CT_ROOT);
00259
00260
00261 tree_close(focused, kill_window, false);
00262 }
00263
00264
00265
00266
00267
00268
00269 void tree_split(Con *con, orientation_t orientation) {
00270
00271 if (con->type == CT_WORKSPACE) {
00272 DLOG("Workspace, simply changing orientation to %d\n", orientation);
00273 con->orientation = orientation;
00274 return;
00275 }
00276
00277 Con *parent = con->parent;
00278
00279
00280
00281 if (con_num_children(parent) == 1) {
00282 parent->orientation = orientation;
00283 DLOG("Just changing orientation of existing container\n");
00284 return;
00285 }
00286
00287 DLOG("Splitting in orientation %d\n", orientation);
00288
00289
00290 Con *new = con_new(NULL, NULL);
00291 TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
00292 TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
00293 new->parent = parent;
00294 new->orientation = orientation;
00295
00296
00297 new->percent = con->percent;
00298 con->percent = 0.0;
00299
00300
00301 con_attach(con, new, false);
00302 }
00303
00304
00305
00306
00307
00308 void level_up() {
00309
00310
00311 if (focused->fullscreen_mode != CF_NONE) {
00312 LOG("Currently in fullscreen, not going up\n");
00313 return;
00314 }
00315
00316 if ((focused->parent->type != CT_CON &&
00317 focused->parent->type != CT_WORKSPACE) ||
00318 focused->type == CT_WORKSPACE) {
00319 LOG("Cannot go up any further\n");
00320 return;
00321 }
00322 con_focus(focused->parent);
00323 }
00324
00325
00326
00327
00328
00329 void level_down() {
00330
00331 Con *next = TAILQ_FIRST(&(focused->focus_head));
00332 if (next == TAILQ_END(&(focused->focus_head))) {
00333 printf("cannot go down\n");
00334 return;
00335 }
00336 con_focus(next);
00337 }
00338
00339 static void mark_unmapped(Con *con) {
00340 Con *current;
00341
00342 con->mapped = false;
00343 TAILQ_FOREACH(current, &(con->nodes_head), nodes)
00344 mark_unmapped(current);
00345 if (con->type == CT_WORKSPACE) {
00346
00347
00348 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
00349 mark_unmapped(current);
00350 }
00351 }
00352
00353
00354
00355
00356
00357
00358 void tree_render() {
00359 if (croot == NULL)
00360 return;
00361
00362 DLOG("-- BEGIN RENDERING --\n");
00363
00364
00365 mark_unmapped(croot);
00366 croot->mapped = true;
00367
00368 render_con(croot, false);
00369
00370 x_push_changes(croot);
00371 DLOG("-- END RENDERING --\n");
00372 }
00373
00374
00375
00376
00377
00378 static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) {
00379
00380 if (con->type == CT_WORKSPACE)
00381 return false;
00382
00383 if (con->type == CT_FLOATING_CON) {
00384
00385 return false;
00386 }
00387
00388 Con *parent = con->parent;
00389
00390
00391
00392 if (con_orientation(parent) != orientation ||
00393 con_num_children(parent) == 1)
00394 return _tree_next(parent, way, orientation, wrap);
00395
00396 Con *current = TAILQ_FIRST(&(parent->focus_head));
00397
00398
00399 if (TAILQ_EMPTY(&(parent->nodes_head))) {
00400 DLOG("nothing to focus\n");
00401 return false;
00402 }
00403
00404 Con *next;
00405 if (way == 'n')
00406 next = TAILQ_NEXT(current, nodes);
00407 else next = TAILQ_PREV(current, nodes_head, nodes);
00408
00409 if (!next) {
00410 if (!config.force_focus_wrapping) {
00411
00412
00413
00414 if (_tree_next(parent, way, orientation, false))
00415 return true;
00416
00417 if (!wrap)
00418 return false;
00419 }
00420
00421 if (way == 'n')
00422 next = TAILQ_FIRST(&(parent->nodes_head));
00423 else next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
00424 }
00425
00426
00427
00428
00429 con_focus(con_descend_focused(next));
00430 return true;
00431 }
00432
00433
00434
00435
00436
00437
00438 void tree_next(char way, orientation_t orientation) {
00439 _tree_next(focused, way, orientation, true);
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 void tree_flatten(Con *con) {
00456 Con *current, *child, *parent = con->parent;
00457 DLOG("Checking if I can flatten con = %p / %s\n", con, con->name);
00458
00459
00460 if (con->type != CT_CON || con->window != NULL)
00461 goto recurse;
00462
00463
00464 child = TAILQ_FIRST(&(con->nodes_head));
00465 if (child == NULL || TAILQ_NEXT(child, nodes) != NULL)
00466 goto recurse;
00467
00468
00469
00470 if (con->orientation == NO_ORIENTATION ||
00471 child->orientation == NO_ORIENTATION ||
00472 con->orientation == child->orientation ||
00473 child->orientation != parent->orientation)
00474 goto recurse;
00475
00476 DLOG("Alright, I have to flatten this situation now. Stay calm.\n");
00477
00478 Con *focus_next = TAILQ_FIRST(&(child->focus_head));
00479
00480 DLOG("detaching...\n");
00481
00482 while (!TAILQ_EMPTY(&(child->nodes_head))) {
00483 current = TAILQ_FIRST(&(child->nodes_head));
00484 DLOG("detaching current=%p / %s\n", current, current->name);
00485 con_detach(current);
00486 DLOG("re-attaching\n");
00487
00488
00489
00490
00491 current->parent = parent;
00492 TAILQ_INSERT_BEFORE(con, current, nodes);
00493 DLOG("attaching to focus list\n");
00494 TAILQ_INSERT_TAIL(&(parent->focus_head), current, focused);
00495 current->percent = con->percent;
00496 }
00497 DLOG("re-attached all\n");
00498
00499
00500 if (focus_next != NULL &&
00501 TAILQ_FIRST(&(parent->focus_head)) == con) {
00502 DLOG("restoring focus to focus_next=%p\n", focus_next);
00503 TAILQ_REMOVE(&(parent->focus_head), focus_next, focused);
00504 TAILQ_INSERT_HEAD(&(parent->focus_head), focus_next, focused);
00505 DLOG("restored focus.\n");
00506 }
00507
00508
00509 DLOG("closing redundant cons\n");
00510 tree_close(con, DONT_KILL_WINDOW, true);
00511
00512
00513
00514
00515
00516
00517 return;
00518
00519 recurse:
00520
00521
00522 current = TAILQ_FIRST(&(con->nodes_head));
00523 while (current != NULL) {
00524 Con *next = TAILQ_NEXT(current, nodes);
00525 tree_flatten(current);
00526 current = next;
00527 }
00528
00529 current = TAILQ_FIRST(&(con->floating_head));
00530 while (current != NULL) {
00531 Con *next = TAILQ_NEXT(current, floating_windows);
00532 tree_flatten(current);
00533 current = next;
00534 }
00535 }