X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fmanage.c;h=ff7fdc6d09bae81c641cbc66162667995f00ec91;hb=2c249b6949566fe9e2414d86b26bf5d29c7c1a6b;hp=9ee3dd72a8add59d806dc5a8ede7e3819d40da41;hpb=6fdb2c0df209b21c3329f8fea0fba001600014bd;p=i3%2Fi3 diff --git a/src/manage.c b/src/manage.c index 9ee3dd72..ff7fdc6d 100644 --- a/src/manage.c +++ b/src/manage.c @@ -1,14 +1,14 @@ +#undef I3__FILE__ +#define I3__FILE__ "manage.c" /* * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * - * manage.c: Contains all functions for initially managing new windows - * (or existing ones on restart). + * manage.c: Initially managing new windows (or existing ones on restart). * */ - #include "all.h" /* @@ -49,7 +49,7 @@ void manage_existing_windows(xcb_window_t root) { * side-effects which are to be expected when continuing to run i3. * */ -void restore_geometry() { +void restore_geometry(void) { DLOG("Restoring geometry\n"); Con *con; @@ -64,8 +64,12 @@ void restore_geometry() { con->rect.x, con->rect.y); } + /* Strictly speaking, this line doesn’t really belong here, but since we + * are syncing, let’s un-register as a window manager first */ + xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT }); + /* Make sure our changes reach the X server, we restart/exit now */ - xcb_flush(conn); + xcb_aux_sync(conn); } /* @@ -79,11 +83,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki xcb_get_geometry_reply_t *geom; xcb_get_window_attributes_reply_t *attr = NULL; - DLOG("---> looking at window 0x%08x\n", window); - xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie, utf8_title_cookie, title_cookie, - class_cookie, leader_cookie, transient_cookie; + class_cookie, leader_cookie, transient_cookie, + role_cookie, startup_id_cookie, wm_hints_cookie; geomc = xcb_get_geometry(conn, d); @@ -101,13 +104,11 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki } if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) { - DLOG("map_state unviewable\n"); FREE_GEOMETRY(); goto out; } /* Don’t manage clients with the override_redirect flag */ - DLOG("override_redirect is %d\n", attr->override_redirect); if (attr->override_redirect) { FREE_GEOMETRY(); goto out; @@ -129,11 +130,22 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki uint32_t values[1]; /* Set a temporary event mask for the new window, consisting only of - * PropertyChange. We need to be notified of PropertyChanges because the - * client can change its properties *after* we requested them but *before* - * we actually reparented it and have set our final event mask. */ - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); + * PropertyChange and StructureNotify. We need to be notified of + * PropertyChanges because the client can change its properties *after* we + * requested them but *before* we actually reparented it and have set our + * final event mask. + * We need StructureNotify because the client may unmap the window before + * we get to re-parent it. + * If this request fails, we assume the client has already unmapped the + * window between the MapRequest and our event mask change. */ + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_void_cookie_t event_mask_cookie = + xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values); + if (xcb_request_check(conn, event_mask_cookie) != NULL) { + LOG("Could not change event mask, the window probably already disappeared.\n"); + goto out; + } #define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len) @@ -145,12 +157,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX); title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128); class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128); + role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128); + startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512); + wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window); /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */ - DLOG("reparenting!\n"); + DLOG("Managing window 0x%08x\n", window); i3Window *cwindow = scalloc(sizeof(i3Window)); cwindow->id = window; + cwindow->depth = get_visual_depth(attr->visual); /* We need to grab the mouse buttons for click to focus */ xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, @@ -160,9 +176,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, - 3 /* right mouse button */, + 2 /* middle mouse button */, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); + xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, + XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, + 3 /* right mouse button */, + XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); /* update as much information as possible so far (some replies may be NULL) */ window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); @@ -171,6 +191,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL)); window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL)); window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL)); + window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true); + window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL)); + + xcb_get_property_reply_t *startup_id_reply; + startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL); + char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply); + DLOG("startup workspace = %s\n", startup_ws); /* check if the window needs WM_TAKE_FOCUS */ cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS); @@ -211,7 +238,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height); Con *nc = NULL; - Match *match; + Match *match = NULL; Assignment *assignment; /* TODO: two matches for one container */ @@ -230,6 +257,15 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki else nc = tree_open_con(nc->parent, cwindow); } /* TODO: handle assignments with type == A_TO_OUTPUT */ + } else if (startup_ws) { + /* If it’s not assigned, but was started on a specific workspace, + * we want to open it there */ + DLOG("Using workspace on which this application was started (%s)\n", startup_ws); + nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL)); + DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name); + if (nc->type == CT_WORKSPACE) + nc = tree_open_con(nc, cwindow); + else nc = tree_open_con(nc->parent, cwindow); } else { /* If not, insert it at the currently focused position */ if (focused->type == CT_CON && con_accepts_window(focused)) { @@ -253,7 +289,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki nc->border_width = geom->border_width; char *name; - asprintf(&name, "[i3 con] container around %p", cwindow); + sasprintf(&name, "[i3 con] container around %p", cwindow); x_set_name(nc, name); free(name); @@ -265,10 +301,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (fs == NULL) { DLOG("Not in fullscreen mode, focusing\n"); if (!cwindow->dock) { - /* Check that the workspace is visible. If the window was assigned - * to an invisible workspace, we should not steal focus. */ - if (workspace_is_visible(ws)) { - con_focus(nc); + /* Check that the workspace is visible and on the same output as + * the current focused container. If the window was assigned to an + * invisible workspace, we should not steal focus. */ + Con *current_output = con_get_output(focused); + Con *target_output = con_get_output(ws); + + if (workspace_is_visible(ws) && current_output == target_output) { + if (!match || !match->restart_mode) { + con_focus(nc); + } else DLOG("not focusing, matched with restart_mode == true\n"); } else DLOG("workspace not visible, not focusing\n"); } else DLOG("dock, not focusing\n"); } else { @@ -302,13 +344,19 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki (cwindow->leader != XCB_NONE && cwindow->leader != cwindow->id && con_by_window_id(cwindow->leader) != NULL)) { - LOG("This window is transiert for another window, setting floating\n"); + LOG("This window is transient for another window, setting floating\n"); want_floating = true; if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && fs != NULL) { LOG("There is a fullscreen window, leaving fullscreen mode\n"); con_toggle_fullscreen(fs, CF_OUTPUT); + } else if (config.popup_during_fullscreen == PDF_SMART && + fs != NULL && + fs->window != NULL && + fs->window->id == cwindow->transient_for) { + LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n"); + con_focus(nc); } } @@ -358,6 +406,26 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* Check if any assignments match */ run_assignments(cwindow); + /* 'ws' may be invalid because of the assignments, e.g. when the user uses + * "move window to workspace 1", but had it assigned to workspace 2. */ + ws = con_get_workspace(nc); + + /* If this window was put onto an invisible workspace (via assignments), we + * render this workspace. It wouldn’t be rendered in our normal code path + * because only the visible workspaces get rendered. + * + * By rendering the workspace, we assign proper coordinates (read: not + * width=0, height=0) to the window, which is important for windows who + * actually use them to position their GUI elements, e.g. rhythmbox. */ + if (ws && !workspace_is_visible(ws)) { + /* This is a bit hackish: we need to copy the content container’s rect + * to the workspace, because calling render_con() on the content + * container would also take the shortcut and not render the invisible + * workspace at all. However, just calling render_con() on the + * workspace isn’t enough either — it needs the rect. */ + ws->rect = ws->parent->rect; + render_con(ws, true); + } tree_render(); geom_out: