00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <stdbool.h>
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <assert.h>
00015 #include <unistd.h>
00016 #include <string.h>
00017
00018 #include <xcb/xcb.h>
00019
00020 #include "util.h"
00021 #include "data.h"
00022 #include "table.h"
00023 #include "layout.h"
00024 #include "i3.h"
00025 #include "randr.h"
00026 #include "client.h"
00027 #include "floating.h"
00028 #include "xcb.h"
00029 #include "config.h"
00030 #include "workspace.h"
00031 #include "commands.h"
00032 #include "resize.h"
00033 #include "log.h"
00034 #include "sighandler.h"
00035 #include "manage.h"
00036 #include "ipc.h"
00037
00038 bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
00039
00040 if (container->currently_focused == NULL)
00041 return false;
00042
00043
00044 Client *candidate = NULL;
00045 if (direction == D_UP) {
00046 if ((candidate = CIRCLEQ_PREV_OR_NULL(&(container->clients), container->currently_focused, clients)) == NULL)
00047 candidate = CIRCLEQ_LAST(&(container->clients));
00048 }
00049 else if (direction == D_DOWN) {
00050 if ((candidate = CIRCLEQ_NEXT_OR_NULL(&(container->clients), container->currently_focused, clients)) == NULL)
00051 candidate = CIRCLEQ_FIRST(&(container->clients));
00052 } else ELOG("Direction not implemented!\n");
00053
00054
00055 if (candidate == container->currently_focused)
00056 return false;
00057
00058
00059 set_focus(conn, candidate, true);
00060
00061 return true;
00062 }
00063
00064 typedef enum { THING_WINDOW, THING_CONTAINER, THING_SCREEN } thing_t;
00065
00066 static void jump_to_mark(xcb_connection_t *conn, const char *mark) {
00067 Client *current;
00068 LOG("Jumping to \"%s\"\n", mark);
00069
00070 Workspace *ws;
00071 TAILQ_FOREACH(ws, workspaces, workspaces)
00072 SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
00073 if (current->mark == NULL || strcmp(current->mark, mark) != 0)
00074 continue;
00075
00076 set_focus(conn, current, true);
00077 workspace_show(conn, current->workspace->num + 1);
00078 return;
00079 }
00080
00081 ELOG("No window with this mark found\n");
00082 }
00083
00084 static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t thing) {
00085 DLOG("focusing direction %d\n", direction);
00086
00087 int new_row = current_row,
00088 new_col = current_col;
00089 Container *container = CUR_CELL;
00090 Workspace *t_ws = c_ws;
00091
00092
00093 #define CHECK_COLROW_BOUNDARIES \
00094 do { \
00095 if (new_col >= t_ws->cols) \
00096 new_col = (t_ws->cols - 1); \
00097 if (new_row >= t_ws->rows) \
00098 new_row = (t_ws->rows - 1); \
00099 } while (0)
00100
00101
00102 assert(container != NULL);
00103
00104 if (container->workspace->fullscreen_client != NULL) {
00105 LOG("You're in fullscreen mode. Forcing focus to operate on whole screens\n");
00106 thing = THING_SCREEN;
00107 }
00108
00109
00110
00111
00112
00113 if (thing == THING_SCREEN) {
00114 Output *cs = c_ws->output;
00115 assert(cs != NULL);
00116 Rect bounds = cs->rect;
00117
00118 if (direction == D_RIGHT)
00119 bounds.x += bounds.width;
00120 else if (direction == D_LEFT)
00121 bounds.x -= bounds.width;
00122 else if (direction == D_UP)
00123 bounds.y -= bounds.height;
00124 else bounds.y += bounds.height;
00125
00126 Output *target = get_output_containing(bounds.x, bounds.y);
00127 if (target == NULL) {
00128 DLOG("Target output NULL\n");
00129
00130 if (direction == D_RIGHT)
00131 target = get_output_most(D_LEFT, cs);
00132 else if (direction == D_LEFT)
00133 target = get_output_most(D_RIGHT, cs);
00134 else if (direction == D_UP)
00135 target = get_output_most(D_DOWN, cs);
00136 else target = get_output_most(D_UP, cs);
00137 }
00138
00139 DLOG("Switching to ws %d\n", target->current_workspace + 1);
00140 workspace_show(conn, target->current_workspace->num + 1);
00141 return;
00142 }
00143
00144
00145 if (direction == D_UP || direction == D_DOWN) {
00146 if (thing == THING_WINDOW)
00147
00148 if (focus_window_in_container(conn, container, direction))
00149 return;
00150
00151 if (direction == D_DOWN && cell_exists(t_ws, current_col, current_row+1))
00152 new_row = current_row + t_ws->table[current_col][current_row]->rowspan;
00153 else if (direction == D_UP && cell_exists(c_ws, current_col, current_row-1)) {
00154
00155 new_row--;
00156
00157
00158 for (int rows = 0; rows < current_row; rows += t_ws->table[current_col][rows]->rowspan) {
00159 if (new_row > (rows + (t_ws->table[current_col][rows]->rowspan - 1)))
00160 continue;
00161
00162 new_row = rows;
00163 break;
00164 }
00165 } else {
00166
00167 DLOG("container is at %d with height %d\n", container->y, container->height);
00168 Output *output;
00169 int destination_y = (direction == D_UP ? (container->y - 1) : (container->y + container->height + 1));
00170 if ((output = get_output_containing(container->x, destination_y)) == NULL) {
00171 DLOG("Wrapping screen around vertically\n");
00172
00173 output = get_output_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->output);
00174 }
00175 t_ws = output->current_workspace;
00176 new_row = (direction == D_UP ? (t_ws->rows - 1) : 0);
00177 }
00178
00179 CHECK_COLROW_BOUNDARIES;
00180
00181 DLOG("new_col = %d, new_row = %d\n", new_col, new_row);
00182 if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
00183 DLOG("Cell empty, checking for colspanned client above...\n");
00184 for (int cols = 0; cols < new_col; cols += t_ws->table[cols][new_row]->colspan) {
00185 if (new_col > (cols + (t_ws->table[cols][new_row]->colspan - 1)))
00186 continue;
00187
00188 new_col = cols;
00189 DLOG("Fixed it to new col %d\n", new_col);
00190 break;
00191 }
00192 }
00193
00194 if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
00195 DLOG("Cell still empty, checking for full cols above spanned width...\n");
00196 DLOG("new_col = %d\n", new_col);
00197 DLOG("colspan = %d\n", container->colspan);
00198 for (int cols = new_col;
00199 cols < container->col + container->colspan;
00200 cols += t_ws->table[cols][new_row]->colspan) {
00201 DLOG("candidate: new_row = %d, cols = %d\n", new_row, cols);
00202 if (t_ws->table[cols][new_row]->currently_focused == NULL)
00203 continue;
00204
00205 new_col = cols;
00206 DLOG("Fixed it to new col %d\n", new_col);
00207 break;
00208 }
00209 }
00210 } else if (direction == D_LEFT || direction == D_RIGHT) {
00211 if (direction == D_RIGHT && cell_exists(t_ws, current_col+1, current_row))
00212 new_col = current_col + t_ws->table[current_col][current_row]->colspan;
00213 else if (direction == D_LEFT && cell_exists(t_ws, current_col-1, current_row)) {
00214
00215 new_col--;
00216
00217
00218 for (int cols = 0; cols < current_col; cols += t_ws->table[cols][current_row]->colspan) {
00219 if (new_col > (cols + (t_ws->table[cols][current_row]->colspan - 1)))
00220 continue;
00221
00222 new_col = cols;
00223 break;
00224 }
00225 } else {
00226
00227 DLOG("container is at %d with width %d\n", container->x, container->width);
00228 Output *output;
00229 int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1));
00230 if ((output = get_output_containing(destination_x, container->y)) == NULL) {
00231 DLOG("Wrapping screen around horizontally\n");
00232 output = get_output_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->output);
00233 }
00234 t_ws = output->current_workspace;
00235 new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0);
00236 }
00237
00238 CHECK_COLROW_BOUNDARIES;
00239
00240 DLOG("new_col = %d, new_row = %d\n", new_col, new_row);
00241 if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
00242 DLOG("Cell empty, checking for rowspanned client above...\n");
00243 for (int rows = 0; rows < new_row; rows += t_ws->table[new_col][rows]->rowspan) {
00244 if (new_row > (rows + (t_ws->table[new_col][rows]->rowspan - 1)))
00245 continue;
00246
00247 new_row = rows;
00248 DLOG("Fixed it to new row %d\n", new_row);
00249 break;
00250 }
00251 }
00252
00253 if (t_ws->table[new_col][new_row]->currently_focused == NULL) {
00254 DLOG("Cell still empty, checking for full cols near full spanned height...\n");
00255 DLOG("new_row = %d\n", new_row);
00256 DLOG("rowspan = %d\n", container->rowspan);
00257 for (int rows = new_row;
00258 rows < container->row + container->rowspan;
00259 rows += t_ws->table[new_col][rows]->rowspan) {
00260 DLOG("candidate: new_col = %d, rows = %d\n", new_col, rows);
00261 if (t_ws->table[new_col][rows]->currently_focused == NULL)
00262 continue;
00263
00264 new_row = rows;
00265 DLOG("Fixed it to new col %d\n", new_row);
00266 break;
00267 }
00268 }
00269
00270 } else {
00271 ELOG("direction unhandled\n");
00272 return;
00273 }
00274
00275 CHECK_COLROW_BOUNDARIES;
00276
00277 if (t_ws->table[new_col][new_row]->currently_focused != NULL)
00278 set_focus(conn, t_ws->table[new_col][new_row]->currently_focused, true);
00279 }
00280
00281
00282
00283
00284
00285
00286
00287 static bool move_current_window_in_container(xcb_connection_t *conn, Client *client,
00288 direction_t direction) {
00289 assert(client->container != NULL);
00290
00291 Client *other = (direction == D_UP ? CIRCLEQ_PREV(client, clients) :
00292 CIRCLEQ_NEXT(client, clients));
00293
00294 if (other == CIRCLEQ_END(&(client->container->clients)))
00295 return false;
00296
00297 DLOG("i can do that\n");
00298
00299 CIRCLEQ_REMOVE(&(client->container->clients), client, clients);
00300 if (direction == D_UP)
00301 CIRCLEQ_INSERT_BEFORE(&(client->container->clients), other, client, clients);
00302 else CIRCLEQ_INSERT_AFTER(&(client->container->clients), other, client, clients);
00303 render_layout(conn);
00304 return true;
00305 }
00306
00307
00308
00309
00310
00311
00312 static void move_current_window(xcb_connection_t *conn, direction_t direction) {
00313 LOG("moving window to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" :
00314 (direction == D_LEFT ? "left" : "right"))));
00315
00316 Container *container = CUR_CELL,
00317 *new = NULL;
00318
00319
00320 assert(container != NULL);
00321
00322
00323 if (container->currently_focused == NULL ||
00324 container->currently_focused->dock)
00325 return;
00326
00327
00328
00329 Client *current_client = container->currently_focused;
00330 Client *to_focus = get_last_focused_client(conn, container, current_client);
00331
00332 if (to_focus == NULL) {
00333 to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients);
00334 if (to_focus == NULL)
00335 to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
00336 }
00337
00338 switch (direction) {
00339 case D_LEFT:
00340
00341 if (current_col == 0) {
00342 expand_table_cols_at_head(c_ws);
00343 new = CUR_CELL;
00344 } else
00345 new = CUR_TABLE[--current_col][current_row];
00346 break;
00347 case D_RIGHT:
00348 if (current_col == (c_ws->cols-1))
00349 expand_table_cols(c_ws);
00350
00351 new = CUR_TABLE[++current_col][current_row];
00352 break;
00353 case D_UP:
00354 if (move_current_window_in_container(conn, current_client, D_UP))
00355 return;
00356
00357
00358 if (current_row == 0) {
00359 expand_table_rows_at_head(c_ws);
00360 new = CUR_CELL;
00361 } else
00362 new = CUR_TABLE[current_col][--current_row];
00363 break;
00364 case D_DOWN:
00365 if (move_current_window_in_container(conn, current_client, D_DOWN))
00366 return;
00367
00368 if (current_row == (c_ws->rows-1))
00369 expand_table_rows(c_ws);
00370
00371 new = CUR_TABLE[current_col][++current_row];
00372 break;
00373
00374 default:
00375 return;
00376 }
00377
00378
00379 client_remove_from_container(conn, current_client, container, true);
00380
00381 if (new->currently_focused != NULL)
00382 CIRCLEQ_INSERT_AFTER(&(new->clients), new->currently_focused, current_client, clients);
00383 else CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients);
00384 SLIST_INSERT_HEAD(&(new->workspace->focus_stack), current_client, focus_clients);
00385
00386
00387 current_client->container = new;
00388 current_client->workspace = new->workspace;
00389 container->currently_focused = to_focus;
00390 new->currently_focused = current_client;
00391
00392 Workspace *workspace = container->workspace;
00393
00394
00395 cleanup_table(conn, workspace);
00396
00397
00398 fix_colrowspan(conn, workspace);
00399
00400 render_workspace(conn, workspace->output, workspace);
00401 xcb_flush(conn);
00402
00403 set_focus(conn, current_client, true);
00404 }
00405
00406 static void move_current_container(xcb_connection_t *conn, direction_t direction) {
00407 LOG("moving container to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" :
00408 (direction == D_LEFT ? "left" : "right"))));
00409
00410 Container *container = CUR_CELL,
00411 *new = NULL;
00412
00413 Container **old = &CUR_CELL;
00414
00415
00416 assert(container != NULL);
00417
00418 switch (direction) {
00419 case D_LEFT:
00420
00421 if (current_col == 0) {
00422 expand_table_cols_at_head(c_ws);
00423 new = CUR_CELL;
00424 old = &CUR_TABLE[current_col+1][current_row];
00425 } else
00426 new = CUR_TABLE[--current_col][current_row];
00427 break;
00428 case D_RIGHT:
00429 if (current_col == (c_ws->cols-1))
00430 expand_table_cols(c_ws);
00431
00432 new = CUR_TABLE[++current_col][current_row];
00433 break;
00434 case D_UP:
00435
00436 if (current_row == 0) {
00437 expand_table_rows_at_head(c_ws);
00438 new = CUR_CELL;
00439 old = &CUR_TABLE[current_col][current_row+1];
00440 } else
00441 new = CUR_TABLE[current_col][--current_row];
00442 break;
00443 case D_DOWN:
00444 if (current_row == (c_ws->rows-1))
00445 expand_table_rows(c_ws);
00446
00447 new = CUR_TABLE[current_col][++current_row];
00448 break;
00449
00450 default:
00451 return;
00452 }
00453
00454 DLOG("old = %d,%d and new = %d,%d\n", container->col, container->row, new->col, new->row);
00455
00456
00457 int col = new->col;
00458 int row = new->row;
00459
00460 *old = new;
00461 new->col = container->col;
00462 new->row = container->row;
00463
00464 CUR_CELL = container;
00465 container->col = col;
00466 container->row = row;
00467
00468 Workspace *workspace = container->workspace;
00469
00470
00471 cleanup_table(conn, workspace);
00472
00473
00474 fix_colrowspan(conn, workspace);
00475
00476 render_layout(conn);
00477 }
00478
00479
00480
00481
00482
00483
00484 static void snap_current_container(xcb_connection_t *conn, direction_t direction) {
00485 LOG("snapping container to direction %d\n", direction);
00486
00487 Container *container = CUR_CELL;
00488
00489 assert(container != NULL);
00490
00491 switch (direction) {
00492 case D_LEFT:
00493
00494 if (!cell_exists(container->workspace, container->col - 1, container->row) ||
00495 CUR_TABLE[container->col-1][container->row]->currently_focused != NULL) {
00496 ELOG("cannot snap to left - the cell is already used\n");
00497 return;
00498 }
00499
00500 move_current_window(conn, D_LEFT);
00501 snap_current_container(conn, D_RIGHT);
00502 return;
00503 case D_RIGHT: {
00504
00505 int new_col = container->col + container->colspan;
00506 for (int i = 0; i < container->rowspan; i++)
00507 if (!cell_exists(container->workspace, new_col, container->row + i) ||
00508 CUR_TABLE[new_col][container->row + i]->currently_focused != NULL) {
00509 ELOG("cannot snap to right - the cell is already used\n");
00510 return;
00511 }
00512
00513
00514
00515 for (int i = container->row-1; i >= 0; i--) {
00516 DLOG("we got cell %d, %d with rowspan %d\n",
00517 new_col, i, CUR_TABLE[new_col][i]->rowspan);
00518 while ((CUR_TABLE[new_col][i]->rowspan-1) >= (container->row - i))
00519 CUR_TABLE[new_col][i]->rowspan--;
00520 DLOG("new rowspan = %d\n", CUR_TABLE[new_col][i]->rowspan);
00521 }
00522
00523 container->colspan++;
00524 break;
00525 }
00526 case D_UP:
00527 if (!cell_exists(container->workspace, container->col, container->row - 1) ||
00528 CUR_TABLE[container->col][container->row-1]->currently_focused != NULL) {
00529 ELOG("cannot snap to top - the cell is already used\n");
00530 return;
00531 }
00532
00533 move_current_window(conn, D_UP);
00534 snap_current_container(conn, D_DOWN);
00535 return;
00536 case D_DOWN: {
00537 DLOG("snapping down\n");
00538 int new_row = container->row + container->rowspan;
00539 for (int i = 0; i < container->colspan; i++)
00540 if (!cell_exists(container->workspace, container->col + i, new_row) ||
00541 CUR_TABLE[container->col + i][new_row]->currently_focused != NULL) {
00542 ELOG("cannot snap down - the cell is already used\n");
00543 return;
00544 }
00545
00546 for (int i = container->col-1; i >= 0; i--) {
00547 DLOG("we got cell %d, %d with colspan %d\n",
00548 i, new_row, CUR_TABLE[i][new_row]->colspan);
00549 while ((CUR_TABLE[i][new_row]->colspan-1) >= (container->col - i))
00550 CUR_TABLE[i][new_row]->colspan--;
00551 DLOG("new colspan = %d\n", CUR_TABLE[i][new_row]->colspan);
00552
00553 }
00554
00555 container->rowspan++;
00556 break;
00557 }
00558
00559 default:
00560 return;
00561 }
00562
00563 render_layout(conn);
00564 }
00565
00566 static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *client, int workspace) {
00567
00568 Workspace *t_ws = workspace_get(workspace-1),
00569 *old_ws = client->workspace;
00570
00571 LOG("moving floating\n");
00572
00573 workspace_initialize(t_ws, c_ws->output, false);
00574
00575
00576
00577 if (client->fullscreen && (t_ws->fullscreen_client != NULL)) {
00578 ELOG("Not moving: Fullscreen client already existing on destination workspace.\n");
00579 return;
00580 }
00581
00582 floating_assign_to_workspace(client, t_ws);
00583
00584
00585 if (!workspace_is_visible(t_ws)) {
00586 DLOG("This workspace is not visible, unmapping\n");
00587 client_unmap(conn, client);
00588 } else {
00589
00590
00591
00592 DLOG("before x = %d, y = %d\n", client->rect.x, client->rect.y);
00593 uint32_t relative_x = client->rect.x - old_ws->rect.x,
00594 relative_y = client->rect.y - old_ws->rect.y;
00595 DLOG("rel_x = %d, rel_y = %d\n", relative_x, relative_y);
00596 if (client->fullscreen) {
00597 client_enter_fullscreen(conn, client, false);
00598 memcpy(&(client->rect), &(t_ws->rect), sizeof(Rect));
00599 } else {
00600 client->rect.x = t_ws->rect.x + relative_x;
00601 client->rect.y = t_ws->rect.y + relative_y;
00602 DLOG("after x = %d, y = %d\n", client->rect.x, client->rect.y);
00603 reposition_client(conn, client);
00604 xcb_flush(conn);
00605 }
00606 }
00607
00608
00609
00610 if (t_ws->fullscreen_client != NULL) {
00611 uint32_t values[] = { t_ws->fullscreen_client->frame, XCB_STACK_MODE_BELOW };
00612 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00613 } else {
00614 Client *last_tiling;
00615 SLIST_FOREACH(last_tiling, &(t_ws->focus_stack), focus_clients)
00616 if (!client_is_floating(last_tiling))
00617 break;
00618 if (last_tiling != SLIST_END(&(t_ws->focus_stack))) {
00619 uint32_t values[] = { last_tiling->frame, XCB_STACK_MODE_ABOVE };
00620 xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
00621 }
00622 }
00623
00624 DLOG("done\n");
00625
00626 render_layout(conn);
00627
00628 if (workspace_is_visible(t_ws)) {
00629 client_warp_pointer_into(conn, client);
00630 set_focus(conn, client, true);
00631 }
00632 }
00633
00634
00635
00636
00637
00638 static void move_current_window_to_workspace(xcb_connection_t *conn, int workspace) {
00639 LOG("Moving current window to workspace %d\n", workspace);
00640
00641 Container *container = CUR_CELL;
00642
00643 assert(container != NULL);
00644
00645
00646 Workspace *t_ws = workspace_get(workspace-1);
00647
00648 Client *current_client = container->currently_focused;
00649 if (current_client == NULL) {
00650 ELOG("No currently focused client in current container.\n");
00651 return;
00652 }
00653 Client *to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients);
00654 if (to_focus == NULL)
00655 to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
00656
00657 workspace_initialize(t_ws, container->workspace->output, false);
00658
00659
00660 if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) {
00661 ELOG("Not moving: Fullscreen client already existing on destination workspace.\n");
00662 return;
00663 }
00664
00665 Container *to_container = t_ws->table[t_ws->current_col][t_ws->current_row];
00666
00667 assert(to_container != NULL);
00668
00669 client_remove_from_container(conn, current_client, container, true);
00670 if (container->workspace->fullscreen_client == current_client)
00671 container->workspace->fullscreen_client = NULL;
00672
00673
00674 CIRCLEQ_INSERT_TAIL(&(to_container->clients), current_client, clients);
00675
00676 SLIST_INSERT_HEAD(&(to_container->workspace->focus_stack), current_client, focus_clients);
00677 DLOG("Moved.\n");
00678
00679 current_client->container = to_container;
00680 current_client->workspace = to_container->workspace;
00681 container->currently_focused = to_focus;
00682 to_container->currently_focused = current_client;
00683
00684
00685 if (!workspace_is_visible(to_container->workspace)) {
00686 DLOG("This workspace is not visible, unmapping\n");
00687 client_unmap(conn, current_client);
00688 } else {
00689 if (current_client->fullscreen) {
00690 DLOG("Calling client_enter_fullscreen again\n");
00691 client_enter_fullscreen(conn, current_client, false);
00692 }
00693 }
00694
00695
00696 cleanup_table(conn, container->workspace);
00697
00698 render_layout(conn);
00699
00700 if (workspace_is_visible(to_container->workspace)) {
00701 client_warp_pointer_into(conn, current_client);
00702 set_focus(conn, current_client, true);
00703 }
00704 }
00705
00706
00707
00708
00709
00710
00711
00712
00713 static void jump_to_window(xcb_connection_t *conn, const char *arguments) {
00714 char *classtitle;
00715 Client *client;
00716
00717
00718 classtitle = sstrdup(arguments+1);
00719
00720 classtitle[strlen(classtitle)-1] = '\0';
00721
00722 if ((client = get_matching_client(conn, classtitle, NULL)) == NULL) {
00723 free(classtitle);
00724 ELOG("No matching client found.\n");
00725 return;
00726 }
00727
00728 free(classtitle);
00729 workspace_show(conn, client->workspace->num + 1);
00730 set_focus(conn, client, true);
00731 }
00732
00733
00734
00735
00736
00737
00738 static void jump_to_container(xcb_connection_t *conn, const char *arguments) {
00739 int ws, row, col;
00740 int result;
00741
00742 result = sscanf(arguments, "%d %d %d", &ws, &col, &row);
00743 LOG("Jump called with %d parameters (\"%s\")\n", result, arguments);
00744
00745
00746 if (result < 1) {
00747 ELOG("At least one valid argument required\n");
00748 return;
00749 }
00750
00751
00752 workspace_show(conn, ws);
00753
00754 if (result < 3)
00755 return;
00756
00757 DLOG("Boundary-checking col %d, row %d... (max cols %d, max rows %d)\n", col, row, c_ws->cols, c_ws->rows);
00758
00759
00760 if (row >= c_ws->rows)
00761 row = c_ws->rows - 1;
00762 if (col >= c_ws->cols)
00763 col = c_ws->cols - 1;
00764
00765 DLOG("Jumping to col %d, row %d\n", col, row);
00766 if (c_ws->table[col][row]->currently_focused != NULL)
00767 set_focus(conn, c_ws->table[col][row]->currently_focused, true);
00768 }
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780 static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) {
00781
00782 int times, count = -1;
00783 Client *current;
00784 bool floating_criteria;
00785
00786
00787 if (strcasecmp(arguments, "floating") == 0) {
00788 floating_criteria = true;
00789 } else if (strcasecmp(arguments, "tiling") == 0) {
00790 floating_criteria = false;
00791 } else if (strcasecmp(arguments, "ft") == 0) {
00792 Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
00793 if (last_focused == SLIST_END(&(c_ws->focus_stack))) {
00794 ELOG("Cannot select the next floating/tiling client because there is no client at all\n");
00795 return;
00796 }
00797
00798 floating_criteria = !client_is_floating(last_focused);
00799 } else {
00800
00801 if (sscanf(arguments, "%u", ×) != 1) {
00802 ELOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments);
00803 times = 1;
00804 }
00805
00806 SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) {
00807 if (++count < times) {
00808 DLOG("Skipping\n");
00809 continue;
00810 }
00811
00812 DLOG("Focussing\n");
00813 set_focus(conn, current, true);
00814 break;
00815 }
00816 return;
00817 }
00818
00819
00820 SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients)
00821 if (client_is_floating(current) == floating_criteria) {
00822 set_focus(conn, current, true);
00823 break;
00824 }
00825 }
00826
00827
00828
00829
00830
00831 static void next_previous_workspace(xcb_connection_t *conn, int direction) {
00832 Workspace *ws = c_ws;
00833
00834 if (direction == 'n') {
00835 while (1) {
00836 ws = TAILQ_NEXT(ws, workspaces);
00837
00838 if (ws == TAILQ_END(workspaces))
00839 ws = TAILQ_FIRST(workspaces);
00840
00841 if (ws == c_ws)
00842 return;
00843
00844 if (ws->output == NULL)
00845 continue;
00846
00847 workspace_show(conn, ws->num + 1);
00848 return;
00849 }
00850 } else if (direction == 'p') {
00851 while (1) {
00852 ws = TAILQ_PREV(ws, workspaces_head, workspaces);
00853
00854 if (ws == TAILQ_END(workspaces))
00855 ws = TAILQ_LAST(workspaces, workspaces_head);
00856
00857 if (ws == c_ws)
00858 return;
00859
00860 if (ws->output == NULL)
00861 continue;
00862
00863 workspace_show(conn, ws->num + 1);
00864 return;
00865 }
00866 }
00867 }
00868
00869 static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, const char *command) {
00870 int first, second;
00871 resize_orientation_t orientation = O_VERTICAL;
00872 Container *con = last_focused->container;
00873 Workspace *ws = last_focused->workspace;
00874
00875 if (client_is_floating(last_focused)) {
00876 DLOG("Resizing a floating client\n");
00877 if (STARTS_WITH(command, "left")) {
00878 command += strlen("left");
00879 last_focused->rect.width -= atoi(command);
00880 last_focused->rect.x += atoi(command);
00881 } else if (STARTS_WITH(command, "right")) {
00882 command += strlen("right");
00883 last_focused->rect.width += atoi(command);
00884 } else if (STARTS_WITH(command, "top")) {
00885 command += strlen("top");
00886 last_focused->rect.height -= atoi(command);
00887 last_focused->rect.y += atoi(command);
00888 } else if (STARTS_WITH(command, "bottom")) {
00889 command += strlen("bottom");
00890 last_focused->rect.height += atoi(command);
00891 } else {
00892 ELOG("Syntax: resize <left|right|top|bottom> [+|-]<pixels>\n");
00893 return;
00894 }
00895
00896
00897 resize_client(conn, last_focused);
00898
00899 return;
00900 }
00901
00902 if (STARTS_WITH(command, "left")) {
00903 if (con->col == 0)
00904 return;
00905 first = con->col - 1;
00906 second = con->col;
00907 command += strlen("left");
00908 } else if (STARTS_WITH(command, "right")) {
00909 first = con->col + (con->colspan - 1);
00910 DLOG("column %d\n", first);
00911
00912 if (!cell_exists(ws, first, con->row) ||
00913 (first == (ws->cols-1)))
00914 return;
00915
00916 second = first + 1;
00917 command += strlen("right");
00918 } else if (STARTS_WITH(command, "top")) {
00919 if (con->row == 0)
00920 return;
00921 first = con->row - 1;
00922 second = con->row;
00923 orientation = O_HORIZONTAL;
00924 command += strlen("top");
00925 } else if (STARTS_WITH(command, "bottom")) {
00926 first = con->row + (con->rowspan - 1);
00927 if (!cell_exists(ws, con->col, first) ||
00928 (first == (ws->rows-1)))
00929 return;
00930
00931 second = first + 1;
00932 orientation = O_HORIZONTAL;
00933 command += strlen("bottom");
00934 } else {
00935 ELOG("Syntax: resize <left|right|top|bottom> [+|-]<pixels>\n");
00936 return;
00937 }
00938
00939 int pixels = atoi(command);
00940 if (pixels == 0)
00941 return;
00942
00943 resize_container(conn, ws, first, second, orientation, pixels);
00944 }
00945
00946
00947
00948
00949
00950 void parse_command(xcb_connection_t *conn, const char *command) {
00951 LOG("--- parsing command \"%s\" ---\n", command);
00952
00953
00954 Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
00955 if (last_focused == SLIST_END(&(c_ws->focus_stack)))
00956 last_focused = NULL;
00957
00958
00959 if (command[0] == '\0')
00960 return;
00961
00962
00963 if (STARTS_WITH(command, "exec ")) {
00964 LOG("starting \"%s\"\n", command + strlen("exec "));
00965 start_application(command+strlen("exec "));
00966 return;
00967 }
00968
00969 if (STARTS_WITH(command, "mark")) {
00970 if (last_focused == NULL) {
00971 ELOG("There is no window to mark\n");
00972 return;
00973 }
00974 const char *rest = command + strlen("mark");
00975 while (*rest == ' ')
00976 rest++;
00977 if (*rest == '\0') {
00978 DLOG("interactive mark starting\n");
00979 start_application("i3-input -p 'mark ' -l 1 -P 'Mark: '");
00980 } else {
00981 LOG("mark with \"%s\"\n", rest);
00982 client_mark(conn, last_focused, rest);
00983 }
00984 return;
00985 }
00986
00987 if (STARTS_WITH(command, "goto")) {
00988 const char *rest = command + strlen("goto");
00989 while (*rest == ' ')
00990 rest++;
00991 if (*rest == '\0') {
00992 DLOG("interactive go to mark starting\n");
00993 start_application("i3-input -p 'goto ' -l 1 -P 'Goto: '");
00994 } else {
00995 LOG("go to \"%s\"\n", rest);
00996 jump_to_mark(conn, rest);
00997 }
00998 return;
00999 }
01000
01001 if (STARTS_WITH(command, "stack-limit ")) {
01002 if (last_focused == NULL || client_is_floating(last_focused)) {
01003 ELOG("No container focused\n");
01004 return;
01005 }
01006 const char *rest = command + strlen("stack-limit ");
01007 if (strncmp(rest, "rows ", strlen("rows ")) == 0) {
01008 last_focused->container->stack_limit = STACK_LIMIT_ROWS;
01009 rest += strlen("rows ");
01010 } else if (strncmp(rest, "cols ", strlen("cols ")) == 0) {
01011 last_focused->container->stack_limit = STACK_LIMIT_COLS;
01012 rest += strlen("cols ");
01013 } else {
01014 ELOG("Syntax: stack-limit <cols|rows> <limit>\n");
01015 return;
01016 }
01017
01018 last_focused->container->stack_limit_value = atoi(rest);
01019 if (last_focused->container->stack_limit_value == 0)
01020 last_focused->container->stack_limit = STACK_LIMIT_NONE;
01021
01022 return;
01023 }
01024
01025 if (STARTS_WITH(command, "resize ")) {
01026 if (last_focused == NULL)
01027 return;
01028 const char *rest = command + strlen("resize ");
01029 parse_resize_command(conn, last_focused, rest);
01030 return;
01031 }
01032
01033 if (STARTS_WITH(command, "mode ")) {
01034 const char *rest = command + strlen("mode ");
01035 switch_mode(conn, rest);
01036 return;
01037 }
01038
01039
01040 if (STARTS_WITH(command, "exit")) {
01041 LOG("User issued exit-command, exiting without error.\n");
01042 restore_geometry(global_conn);
01043 ipc_shutdown();
01044 exit(EXIT_SUCCESS);
01045 }
01046
01047
01048 if (STARTS_WITH(command, "reload")) {
01049 load_configuration(conn, NULL, true);
01050 render_layout(conn);
01051
01052 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
01053 return;
01054 }
01055
01056
01057 if (STARTS_WITH(command, "restart")) {
01058 i3_restart();
01059 }
01060
01061 if (STARTS_WITH(command, "kill")) {
01062 if (last_focused == NULL) {
01063 ELOG("There is no window to kill\n");
01064 return;
01065 }
01066
01067 LOG("Killing current window\n");
01068 client_kill(conn, last_focused);
01069 return;
01070 }
01071
01072
01073 if (STARTS_WITH(command, "jump ")) {
01074 const char *arguments = command + strlen("jump ");
01075 if (arguments[0] == '"')
01076 jump_to_window(conn, arguments);
01077 else jump_to_container(conn, arguments);
01078 return;
01079 }
01080
01081
01082 if (STARTS_WITH(command, "focus")) {
01083 const char *arguments = command + strlen("focus ");
01084 travel_focus_stack(conn, arguments);
01085 return;
01086 }
01087
01088
01089 if (command[0] == 'f') {
01090 if (last_focused == NULL)
01091 return;
01092 if (command[1] == 'g')
01093 client_toggle_fullscreen_global(conn, last_focused);
01094 else
01095 client_toggle_fullscreen(conn, last_focused);
01096 return;
01097 }
01098
01099
01100 if ((command[0] == 's' || command[0] == 'd' || command[0] == 'T') && (command[1] == '\0')) {
01101 if (last_focused != NULL && client_is_floating(last_focused)) {
01102 ELOG("not switching, this is a floating client\n");
01103 return;
01104 }
01105 LOG("Switching mode for current container\n");
01106 int new_mode = MODE_DEFAULT;
01107 if (command[0] == 's' && CUR_CELL->mode != MODE_STACK)
01108 new_mode = MODE_STACK;
01109 if (command[0] == 'T' && CUR_CELL->mode != MODE_TABBED)
01110 new_mode = MODE_TABBED;
01111 switch_layout_mode(conn, CUR_CELL, new_mode);
01112 return;
01113 }
01114
01115
01116
01117 if (command[0] == 'b') {
01118 if (last_focused == NULL) {
01119 ELOG("No window focused, cannot change border type\n");
01120 return;
01121 }
01122
01123 char com = command[1];
01124 if (command[1] == 't') {
01125 if (last_focused->titlebar_position == TITLEBAR_TOP &&
01126 !last_focused->borderless)
01127 com = 'p';
01128 else if (last_focused->titlebar_position == TITLEBAR_OFF &&
01129 !last_focused->borderless)
01130 com = 'b';
01131 else com = 'n';
01132 }
01133
01134 client_change_border(conn, last_focused, com);
01135 return;
01136 }
01137
01138 if (command[0] == 'H') {
01139 LOG("Hiding all floating windows\n");
01140 floating_toggle_hide(conn, c_ws);
01141 return;
01142 }
01143
01144 enum { WITH_WINDOW, WITH_CONTAINER, WITH_WORKSPACE, WITH_SCREEN } with = WITH_WINDOW;
01145
01146
01147 if (command[0] == 'w') {
01148 command++;
01149
01150 if (command[0] == 'c') {
01151 with = WITH_CONTAINER;
01152 command++;
01153 } else if (command[0] == 'w') {
01154 with = WITH_WORKSPACE;
01155 command++;
01156 } else if (command[0] == 's') {
01157 with = WITH_SCREEN;
01158 command++;
01159 } else {
01160 ELOG("not yet implemented.\n");
01161 return;
01162 }
01163 }
01164
01165
01166 if (command[0] == 't') {
01167 if (with == WITH_WORKSPACE) {
01168 c_ws->auto_float = !c_ws->auto_float;
01169 LOG("autofloat is now %d\n", c_ws->auto_float);
01170 return;
01171 }
01172 if (last_focused == NULL) {
01173 ELOG("Cannot toggle tiling/floating: workspace empty\n");
01174 return;
01175 }
01176
01177 Workspace *ws = last_focused->workspace;
01178
01179 if (last_focused->fullscreen)
01180 client_leave_fullscreen(conn, last_focused);
01181
01182 toggle_floating_mode(conn, last_focused, false);
01183
01184 cleanup_table(conn, ws);
01185
01186
01187 fix_colrowspan(conn, ws);
01188
01189 render_workspace(conn, ws->output, ws);
01190
01191
01192
01193 set_focus(conn, last_focused, true);
01194
01195 return;
01196 }
01197
01198
01199 if ((command[0] == 'n' || command[0] == 'p') && command[1] == 'w') {
01200 next_previous_workspace(conn, command[0]);
01201 return;
01202 }
01203
01204
01205 char *rest = NULL;
01206 enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
01207 direction_t direction;
01208 int times = strtol(command, &rest, 10);
01209 if (rest == NULL) {
01210 ELOG("Invalid command (\"%s\")\n", command);
01211 return;
01212 }
01213
01214 if (*rest == '\0') {
01215
01216 workspace_show(conn, times);
01217 return;
01218 }
01219
01220 if (*rest == 'm' || *rest == 's') {
01221 action = (*rest == 'm' ? ACTION_MOVE : ACTION_SNAP);
01222 rest++;
01223 }
01224
01225 int workspace = strtol(rest, &rest, 10);
01226
01227 if (rest == NULL) {
01228 ELOG("Invalid command (\"%s\")\n", command);
01229 return;
01230 }
01231
01232 if (*rest == '\0') {
01233 if (last_focused != NULL && client_is_floating(last_focused))
01234 move_floating_window_to_workspace(conn, last_focused, workspace);
01235 else move_current_window_to_workspace(conn, workspace);
01236 return;
01237 }
01238
01239 if (last_focused == NULL) {
01240 ELOG("Not performing (no window found)\n");
01241 return;
01242 }
01243
01244 if (client_is_floating(last_focused) &&
01245 (action != ACTION_FOCUS && action != ACTION_MOVE)) {
01246 ELOG("Not performing (floating)\n");
01247 return;
01248 }
01249
01250
01251 while (*rest != '\0') {
01252 if (*rest == 'h')
01253 direction = D_LEFT;
01254 else if (*rest == 'j')
01255 direction = D_DOWN;
01256 else if (*rest == 'k')
01257 direction = D_UP;
01258 else if (*rest == 'l')
01259 direction = D_RIGHT;
01260 else {
01261 ELOG("unknown direction: %c\n", *rest);
01262 return;
01263 }
01264 rest++;
01265
01266 if (action == ACTION_FOCUS) {
01267 if (with == WITH_SCREEN) {
01268 focus_thing(conn, direction, THING_SCREEN);
01269 continue;
01270 }
01271 if (client_is_floating(last_focused)) {
01272 floating_focus_direction(conn, last_focused, direction);
01273 continue;
01274 }
01275 focus_thing(conn, direction, (with == WITH_WINDOW ? THING_WINDOW : THING_CONTAINER));
01276 continue;
01277 }
01278
01279 if (action == ACTION_MOVE) {
01280 if (with == WITH_SCREEN) {
01281
01282
01283
01284 ELOG("Not yet implemented\n");
01285 continue;
01286 }
01287 if (client_is_floating(last_focused)) {
01288 floating_move(conn, last_focused, direction);
01289 continue;
01290 }
01291 if (with == WITH_WINDOW)
01292 move_current_window(conn, direction);
01293 else move_current_container(conn, direction);
01294 continue;
01295 }
01296
01297 if (action == ACTION_SNAP) {
01298 if (with == WITH_SCREEN) {
01299 ELOG("You cannot snap a screen (it makes no sense).\n");
01300 continue;
01301 }
01302 snap_current_container(conn, direction);
01303 continue;
01304 }
01305 }
01306 }