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 == NULL)
00277 t_out = c_ws->output;
00278 if (t_out != c_ws->output) {
00279 DLOG("Dock client requested to be on output %s by geometry (%d, %d)\n",
00280 t_out->name, x, y);
00281 new->workspace = t_out->current_workspace;
00282 }
00283 new->dock = true;
00284 new->borderless = true;
00285 new->titlebar_position = TITLEBAR_OFF;
00286 new->force_reconfigure = true;
00287 new->container = NULL;
00288 SLIST_INSERT_HEAD(&(t_out->dock_clients), new, dock_clients);
00289
00290 new->floating = FLOATING_AUTO_OFF;
00291 break;
00292 } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
00293 atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
00294 atom[i] == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] ||
00295 atom[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) {
00296
00297 new->floating = FLOATING_AUTO_ON;
00298 DLOG("dialog/utility/toolbar/splash window, automatically floating\n");
00299 }
00300 }
00301
00302
00303 if (!new->dock && !client_is_floating(new) && new->leader != 0) {
00304 DLOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
00305 new->floating = FLOATING_AUTO_ON;
00306 }
00307
00308 if (new->workspace->auto_float) {
00309 new->floating = FLOATING_AUTO_ON;
00310 DLOG("workspace is in autofloat mode, setting floating\n");
00311 }
00312
00313 if (new->dock) {
00314
00315 uint32_t *strut;
00316 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
00317 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
00318
00319
00320
00321
00322 new->desired_height = strut[3];
00323 if (new->desired_height == 0) {
00324 DLOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", original_height);
00325 new->desired_height = original_height;
00326 }
00327 DLOG("the client wants to be %d pixels high\n", new->desired_height);
00328 } else {
00329 DLOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", original_height);
00330 new->desired_height = original_height;
00331 }
00332 } else {
00333
00334
00335
00336
00337
00338
00339 preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
00340 handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply);
00341
00342 preply = xcb_get_property_reply(conn, title_cookie, NULL);
00343 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
00344
00345 preply = xcb_get_property_reply(conn, class_cookie, NULL);
00346 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
00347
00348 preply = xcb_get_property_reply(conn, leader_cookie, NULL);
00349 handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
00350
00351
00352
00353
00354 if (new->leader != XCB_NONE) {
00355 DLOG("client->leader is set (to 0x%08x)\n", new->leader);
00356 Client *parent = table_get(&by_child, new->leader);
00357 if (parent != NULL && parent->container != NULL) {
00358 Workspace *t_ws = parent->workspace;
00359 new->container = t_ws->table[parent->container->col][parent->container->row];
00360 new->workspace = t_ws;
00361 old_focused = new->container->currently_focused;
00362 map_frame = workspace_is_visible(t_ws);
00363 new->urgent = true;
00364
00365
00366
00367
00368 t_ws->urgent = true;
00369 } else {
00370 DLOG("parent is not usable\n");
00371 }
00372 }
00373
00374 struct Assignment *assign;
00375 TAILQ_FOREACH(assign, &assignments, assignments) {
00376 if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
00377 continue;
00378
00379 if (assign->floating == ASSIGN_FLOATING_ONLY ||
00380 assign->floating == ASSIGN_FLOATING) {
00381 new->floating = FLOATING_AUTO_ON;
00382 LOG("Assignment matches, putting client into floating mode\n");
00383 if (assign->floating == ASSIGN_FLOATING_ONLY)
00384 break;
00385 }
00386
00387 LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
00388 assign->windowclass_title, assign->workspace);
00389
00390 if (c_ws->output->current_workspace->num == (assign->workspace-1)) {
00391 DLOG("We are already there, no need to do anything\n");
00392 break;
00393 }
00394
00395 DLOG("Changing container/workspace and unmapping the client\n");
00396 Workspace *t_ws = workspace_get(assign->workspace-1);
00397 workspace_initialize(t_ws, c_ws->output, false);
00398
00399 new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
00400 new->workspace = t_ws;
00401 old_focused = new->container->currently_focused;
00402
00403 map_frame = workspace_is_visible(t_ws);
00404 break;
00405 }
00406 }
00407
00408 if (new->workspace->fullscreen_client != NULL) {
00409 DLOG("Setting below fullscreen window\n");
00410
00411
00412
00413 uint32_t values[] = {
00414 new->workspace->fullscreen_client->frame,
00415 XCB_STACK_MODE_BELOW
00416 };
00417 xcb_configure_window(conn, new->frame,
00418 XCB_CONFIG_WINDOW_SIBLING |
00419 XCB_CONFIG_WINDOW_STACK_MODE, values);
00420 }
00421
00422
00423 if (!new->dock && !client_is_floating(new)) {
00424
00425
00426 if (old_focused != NULL && !old_focused->dock)
00427 CIRCLEQ_INSERT_AFTER(&(new->container->clients), old_focused, new, clients);
00428 else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
00429
00430 if (new->container->workspace->fullscreen_client != NULL)
00431 SLIST_INSERT_AFTER(new->container->workspace->fullscreen_client, new, focus_clients);
00432 else SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
00433
00434 client_set_below_floating(conn, new);
00435 }
00436
00437 if (client_is_floating(new)) {
00438 SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients);
00439
00440
00441 TAILQ_INSERT_TAIL(&(new->workspace->floating_clients), new, floating_clients);
00442
00443 new->container = NULL;
00444
00445 new->rect.width = new->floating_rect.width + 2 + 2;
00446 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
00447
00448
00449
00450
00451 if (new->leader != 0 && x == 0 && y == 0) {
00452 DLOG("Floating client wants to (0x0), moving it over its leader instead\n");
00453 Client *leader = table_get(&by_child, new->leader);
00454 if (leader == NULL) {
00455 DLOG("leader is NULL, centering it over current workspace\n");
00456
00457 x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
00458 y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
00459 } else {
00460 x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
00461 y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
00462 }
00463 }
00464 new->floating_rect.x = new->rect.x = x;
00465 new->floating_rect.y = new->rect.y = y;
00466 DLOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
00467 new->floating_rect.x, new->floating_rect.y,
00468 new->floating_rect.width, new->floating_rect.height);
00469 DLOG("outer rect (%d, %d) size (%d, %d)\n",
00470 new->rect.x, new->rect.y, new->rect.width, new->rect.height);
00471
00472
00473 xcb_raise_window(conn, new->frame);
00474 reposition_client(conn, new);
00475 resize_client(conn, new);
00476
00477 redecorate_window(conn, new);
00478 }
00479
00480 new->initialized = true;
00481
00482
00483 xcb_atom_t *state;
00484 if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
00485 (state = xcb_get_property_value(preply)) != NULL)
00486
00487 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
00488 if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
00489 continue;
00490
00491
00492
00493 client_toggle_fullscreen(conn, new);
00494 goto map;
00495 }
00496
00497 render_layout(conn);
00498
00499 map:
00500
00501 xcb_map_window(conn, child);
00502 if (map_frame)
00503 client_map(conn, new);
00504
00505 if ((CUR_CELL->workspace->fullscreen_client == NULL || new->fullscreen) && !new->dock) {
00506
00507 if ((new->workspace->fullscreen_client == NULL) || new->fullscreen) {
00508 if (!client_is_floating(new)) {
00509 new->container->currently_focused = new;
00510 if (map_frame)
00511 render_container(conn, new->container);
00512 }
00513 if (new->container == CUR_CELL || client_is_floating(new)) {
00514 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
00515 ewmh_update_active_window(new->child);
00516 }
00517 }
00518 }
00519
00520 xcb_flush(conn);
00521 }