00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <assert.h>
00017
00018 #include <xcb/xcb.h>
00019 #include <xcb/xcb_icccm.h>
00020
00021 #include "xcb.h"
00022 #include "data.h"
00023 #include "util.h"
00024 #include "i3.h"
00025 #include "table.h"
00026 #include "config.h"
00027 #include "handlers.h"
00028 #include "layout.h"
00029 #include "manage.h"
00030 #include "floating.h"
00031 #include "client.h"
00032 #include "workspace.h"
00033 #include "log.h"
00034 #include "ewmh.h"
00035
00036
00037
00038
00039
00040 void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) {
00041 xcb_query_tree_reply_t *reply;
00042 int i, len;
00043 xcb_window_t *children;
00044 xcb_get_window_attributes_cookie_t *cookies;
00045
00046
00047 if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
00048 return;
00049
00050 len = xcb_query_tree_children_length(reply);
00051 cookies = smalloc(len * sizeof(*cookies));
00052
00053
00054 children = xcb_query_tree_children(reply);
00055 for (i = 0; i < len; ++i)
00056 cookies[i] = xcb_get_window_attributes(conn, children[i]);
00057
00058
00059 for (i = 0; i < len; ++i)
00060 manage_window(prophs, conn, children[i], cookies[i], true);
00061
00062 free(reply);
00063 free(cookies);
00064 }
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 void restore_geometry(xcb_connection_t *conn) {
00075 Workspace *ws;
00076 Client *client;
00077 DLOG("Restoring geometry\n");
00078
00079 TAILQ_FOREACH(ws, workspaces, workspaces)
00080 SLIST_FOREACH(client, &(ws->focus_stack), focus_clients)
00081 xcb_reparent_window(conn, client->child, root,
00082 client->rect.x, client->rect.y);
00083
00084
00085 xcb_flush(conn);
00086 }
00087
00088
00089
00090
00091
00092 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
00093 xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
00094 bool needs_to_be_mapped) {
00095 xcb_drawable_t d = { window };
00096 xcb_get_geometry_cookie_t geomc;
00097 xcb_get_geometry_reply_t *geom;
00098 xcb_get_window_attributes_reply_t *attr = 0;
00099
00100 geomc = xcb_get_geometry(conn, d);
00101
00102
00103
00104 if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
00105 ELOG("Could not get attributes\n");
00106 return;
00107 }
00108
00109 if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE)
00110 goto out;
00111
00112
00113 if (attr->override_redirect)
00114 goto out;
00115
00116
00117 if (table_get(&by_child, window))
00118 goto out;
00119
00120
00121 if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL)
00122 goto out;
00123
00124
00125 reparent_window(conn, window, attr->visual, geom->root, geom->depth,
00126 geom->x, geom->y, geom->width, geom->height,
00127 geom->border_width);
00128
00129
00130 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
00131 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
00132 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
00133 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS);
00134 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
00135 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
00136 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
00137
00138 free(geom);
00139 out:
00140 free(attr);
00141 return;
00142 }
00143
00144
00145
00146
00147
00148
00149
00150
00151 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
00152 xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
00153 int16_t x, int16_t y, uint16_t width, uint16_t height,
00154 uint32_t border_width) {
00155
00156 xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
00157 utf8_title_cookie, title_cookie,
00158 class_cookie, leader_cookie;
00159 uint32_t mask = 0;
00160 uint32_t values[3];
00161 uint16_t original_height = height;
00162 bool map_frame = true;
00163
00164
00165 mask = XCB_CW_EVENT_MASK;
00166 values[0] = CHILD_EVENT_MASK;
00167 xcb_change_window_attributes(conn, child, mask, values);
00168
00169
00170 wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
00171 strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
00172 state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
00173 utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
00174 leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX);
00175 title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
00176 class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
00177
00178 Client *new = table_get(&by_child, child);
00179
00180
00181 assert(new == NULL);
00182
00183 LOG("Managing window 0x%08x\n", child);
00184 DLOG("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
00185 new = scalloc(sizeof(Client));
00186 new->force_reconfigure = true;
00187
00188
00189 Client *old_focused = CUR_CELL->currently_focused;
00190
00191 new->container = CUR_CELL;
00192 new->workspace = new->container->workspace;
00193
00194
00195 width = max(width, 75);
00196 height = max(height, 50);
00197
00198 new->frame = xcb_generate_id(conn);
00199 new->child = child;
00200 new->rect.width = width;
00201 new->rect.height = height;
00202 new->width_increment = 1;
00203 new->height_increment = 1;
00204 new->border_width = border_width;
00205
00206 new->floating_rect.x = -1;
00207 new->floating_rect.width = width;
00208 new->floating_rect.height = height;
00209
00210 if (config.default_border != NULL)
00211 client_init_border(conn, new, config.default_border[1]);
00212
00213 mask = 0;
00214
00215
00216 mask |= XCB_CW_OVERRIDE_REDIRECT;
00217 values[0] = 1;
00218
00219
00220 mask |= XCB_CW_EVENT_MASK;
00221 values[1] = FRAME_EVENT_MASK;
00222
00223 i3Font *font = load_font(conn, config.font);
00224 width = min(width, c_ws->rect.x + c_ws->rect.width);
00225 height = min(height, c_ws->rect.y + c_ws->rect.height);
00226
00227 Rect framerect = {x, y,
00228 width + 2 + 2,
00229 height + 2 + 2 + font->height};
00230
00231
00232 new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, false, mask, values);
00233
00234
00235
00236
00237 xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
00238
00239
00240 new->titlegc = xcb_generate_id(conn);
00241 xcb_create_gc(conn, new->titlegc, new->frame, 0, 0);
00242
00243
00244 new->awaiting_useless_unmap = true;
00245 xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height);
00246 if (xcb_request_check(conn, cookie) != NULL) {
00247 DLOG("Could not reparent the window, aborting\n");
00248 xcb_destroy_window(conn, new->frame);
00249 free(new);
00250 return;
00251 }
00252
00253
00254 table_put(&by_parent, new->frame, new);
00255 table_put(&by_child, child, new);
00256
00257
00258 xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
00259 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
00260 1 ,
00261 XCB_BUTTON_MASK_ANY );
00262
00263 xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
00264 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
00265 3 ,
00266 XCB_BUTTON_MASK_ANY );
00267
00268
00269 xcb_atom_t *atom;
00270 xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
00271 if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
00272 for (int i = 0; i < xcb_get_property_value_length(preply); i++)
00273 if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
00274 DLOG("Window is a dock.\n");
00275 Output *t_out = get_output_containing(x, y);
00276 if (t_out != c_ws->output) {
00277 DLOG("Dock client requested to be on output %s by geometry (%d, %d)\n",
00278 t_out->name, x, y);
00279 new->workspace = t_out->current_workspace;
00280 }
00281 new->dock = true;
00282 new->borderless = true;
00283 new->titlebar_position = TITLEBAR_OFF;
00284 new->force_reconfigure = true;
00285 new->container = NULL;
00286 SLIST_INSERT_HEAD(&(t_out->dock_clients), new, dock_clients);
00287
00288 new->floating = FLOATING_AUTO_OFF;
00289 break;
00290 } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
00291 atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
00292 atom[i] == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] ||
00293 atom[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) {
00294
00295 new->floating = FLOATING_AUTO_ON;
00296 DLOG("dialog/utility/toolbar/splash window, automatically floating\n");
00297 }
00298 }
00299
00300
00301 if (!new->dock && !client_is_floating(new) && new->leader != 0) {
00302 DLOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
00303 new->floating = FLOATING_AUTO_ON;
00304 }
00305
00306 if (new->workspace->auto_float) {
00307 new->floating = FLOATING_AUTO_ON;
00308 DLOG("workspace is in autofloat mode, setting floating\n");
00309 }
00310
00311 if (new->dock) {
00312
00313 uint32_t *strut;
00314 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
00315 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
00316
00317
00318
00319
00320 new->desired_height = strut[3];
00321 if (new->desired_height == 0) {
00322 DLOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", original_height);
00323 new->desired_height = original_height;
00324 }
00325 DLOG("the client wants to be %d pixels high\n", new->desired_height);
00326 } else {
00327 DLOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", original_height);
00328 new->desired_height = original_height;
00329 }
00330 } else {
00331
00332
00333
00334
00335
00336
00337 preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
00338 handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply);
00339
00340 preply = xcb_get_property_reply(conn, title_cookie, NULL);
00341 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
00342
00343 preply = xcb_get_property_reply(conn, class_cookie, NULL);
00344 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
00345
00346 preply = xcb_get_property_reply(conn, leader_cookie, NULL);
00347 handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
00348
00349
00350
00351
00352 if (new->leader != XCB_NONE) {
00353 DLOG("client->leader is set (to 0x%08x)\n", new->leader);
00354 Client *parent = table_get(&by_child, new->leader);
00355 if (parent != NULL && parent->container != NULL) {
00356 Workspace *t_ws = parent->workspace;
00357 new->container = t_ws->table[parent->container->col][parent->container->row];
00358 new->workspace = t_ws;
00359 old_focused = new->container->currently_focused;
00360 map_frame = workspace_is_visible(t_ws);
00361 new->urgent = true;
00362
00363
00364
00365
00366 t_ws->urgent = true;
00367 } else {
00368 DLOG("parent is not usable\n");
00369 }
00370 }
00371
00372 struct Assignment *assign;
00373 TAILQ_FOREACH(assign, &assignments, assignments) {
00374 if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
00375 continue;
00376
00377 if (assign->floating == ASSIGN_FLOATING_ONLY ||
00378 assign->floating == ASSIGN_FLOATING) {
00379 new->floating = FLOATING_AUTO_ON;
00380 LOG("Assignment matches, putting client into floating mode\n");
00381 if (assign->floating == ASSIGN_FLOATING_ONLY)
00382 break;
00383 }
00384
00385 LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
00386 assign->windowclass_title, assign->workspace);
00387
00388 if (c_ws->output->current_workspace->num == (assign->workspace-1)) {
00389 DLOG("We are already there, no need to do anything\n");
00390 break;
00391 }
00392
00393 DLOG("Changing container/workspace and unmapping the client\n");
00394 Workspace *t_ws = workspace_get(assign->workspace-1);
00395 workspace_initialize(t_ws, c_ws->output, false);
00396
00397 new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
00398 new->workspace = t_ws;
00399 old_focused = new->container->currently_focused;
00400
00401 map_frame = workspace_is_visible(t_ws);
00402 break;
00403 }
00404 }
00405
00406 if (new->workspace->fullscreen_client != NULL) {
00407 DLOG("Setting below fullscreen window\n");
00408
00409
00410
00411 uint32_t values[] = {
00412 new->workspace->fullscreen_client->frame,
00413 XCB_STACK_MODE_BELOW
00414 };
00415 xcb_configure_window(conn, new->frame,
00416 XCB_CONFIG_WINDOW_SIBLING |
00417 XCB_CONFIG_WINDOW_STACK_MODE, values);
00418 }
00419
00420
00421 if (!new->dock && !client_is_floating(new)) {
00422
00423
00424 if (old_focused != NULL && !old_focused->dock)
00425 CIRCLEQ_INSERT_AFTER(&(new->container->clients), old_focused, new, clients);
00426 else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
00427
00428 if (new->container->workspace->fullscreen_client != NULL)
00429 SLIST_INSERT_AFTER(new->container->workspace->fullscreen_client, new, focus_clients);
00430 else SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
00431
00432 client_set_below_floating(conn, new);
00433 }
00434
00435 if (client_is_floating(new)) {
00436 SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients);
00437
00438
00439 TAILQ_INSERT_TAIL(&(new->workspace->floating_clients), new, floating_clients);
00440
00441 new->container = NULL;
00442
00443 new->rect.width = new->floating_rect.width + 2 + 2;
00444 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
00445
00446
00447
00448
00449 if (new->leader != 0 && x == 0 && y == 0) {
00450 DLOG("Floating client wants to (0x0), moving it over its leader instead\n");
00451 Client *leader = table_get(&by_child, new->leader);
00452 if (leader == NULL) {
00453 DLOG("leader is NULL, centering it over current workspace\n");
00454
00455 x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
00456 y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
00457 } else {
00458 x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
00459 y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
00460 }
00461 }
00462 new->floating_rect.x = new->rect.x = x;
00463 new->floating_rect.y = new->rect.y = y;
00464 DLOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
00465 new->floating_rect.x, new->floating_rect.y,
00466 new->floating_rect.width, new->floating_rect.height);
00467 DLOG("outer rect (%d, %d) size (%d, %d)\n",
00468 new->rect.x, new->rect.y, new->rect.width, new->rect.height);
00469
00470
00471 xcb_raise_window(conn, new->frame);
00472 reposition_client(conn, new);
00473 resize_client(conn, new);
00474
00475 redecorate_window(conn, new);
00476 }
00477
00478 new->initialized = true;
00479
00480
00481 xcb_atom_t *state;
00482 if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
00483 (state = xcb_get_property_value(preply)) != NULL)
00484
00485 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
00486 if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
00487 continue;
00488
00489
00490
00491 client_toggle_fullscreen(conn, new);
00492 goto map;
00493 }
00494
00495 render_layout(conn);
00496
00497 map:
00498
00499 xcb_map_window(conn, child);
00500 if (map_frame)
00501 client_map(conn, new);
00502
00503 if ((CUR_CELL->workspace->fullscreen_client == NULL || new->fullscreen) && !new->dock) {
00504
00505 if ((new->workspace->fullscreen_client == NULL) || new->fullscreen) {
00506 if (!client_is_floating(new)) {
00507 new->container->currently_focused = new;
00508 if (map_frame)
00509 render_container(conn, new->container);
00510 }
00511 if (new->container == CUR_CELL || client_is_floating(new)) {
00512 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
00513 ewmh_update_active_window(new->child);
00514 }
00515 }
00516 }
00517
00518 xcb_flush(conn);
00519 }