diff --git a/include/data.h b/include/data.h index 5ac2ccf1..3ed605a6 100644 --- a/include/data.h +++ b/include/data.h @@ -204,6 +204,8 @@ struct Client { /* Backpointer. A client is inside a container */ Container *container; + /* Because dock clients don’t have a container, we have this workspace-backpointer */ + Workspace *workspace; /* x, y, width, height of the frame */ Rect rect; diff --git a/include/handlers.h b/include/handlers.h index e3f5d144..61396da8 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -18,7 +18,6 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_event_t *event); int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event); int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure_request_event_t *event); -int handle_destroy_notify_event(void *data, xcb_connection_t *conn, xcb_destroy_notify_event_t *event); int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_notify_event_t *event); int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop); diff --git a/src/handlers.c b/src/handlers.c index 7475cd8f..4c3024b5 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -158,7 +158,9 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_ /* Do plausibility checks: This event may be useless for us if it occurs on a window which is in a stacked container but not the focused one */ - if (client->container->mode == MODE_STACK && client->container->currently_focused != client) { + if (client->container != NULL && + client->container->mode == MODE_STACK && + client->container->currently_focused != client) { LOG("Plausibility check says: no\n"); return 1; } @@ -410,6 +412,7 @@ int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_eve wa.u.override_redirect = reply->override_redirect; LOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence); add_ignore_event(event->sequence); + manage_window(prophs, conn, event->window, wa); return 1; } @@ -536,13 +539,19 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti } } + if (client->dock) { + LOG("Removing from dock clients\n"); + SLIST_REMOVE(&(client->workspace->dock_clients), client, Client, dock_clients); + } + LOG("child of 0x%08x.\n", client->frame); xcb_reparent_window(conn, client->child, root, 0, 0); xcb_destroy_window(conn, client->frame); xcb_flush(conn); table_remove(byParent, client->frame); - cleanup_table(conn, client->container->workspace); + if (client->container != NULL) + cleanup_table(conn, client->container->workspace); free(client); @@ -551,20 +560,6 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti return 1; } -/* - * This handler makes sure that windows are closed correctly as we drop the first unmap event we - * get because they are sometimes generated by remapping. - * - * FIXME: Rather than providing this event, I’d rather know why some applications need to have - * unmap events ignored (like VLC’s open dialog, just open it two times) while others don’t (GIMP). - * For now, just make it work. - * - */ -int handle_destroy_notify_event(void *data, xcb_connection_t *conn, xcb_destroy_notify_event_t *event) { - LOG("Destroynotify, faking unmapnotify\n"); - return handle_unmap_notify_event(data, conn, (xcb_unmap_notify_event_t*)event); -} - /* * Called when a window changes its title * @@ -608,6 +603,10 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, if (old_name != NULL) free(old_name); + /* If the client is a dock window, we don’t need to render anything */ + if (client->dock) + return 1; + if (client->container->mode == MODE_STACK) render_container(conn, client->container); else decorate_window(conn, client, client->frame, client->titlegc, 0); @@ -649,6 +648,11 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * } LOG("got client %s\n", client->name); + if (client->dock) { + LOG("this is a dock\n"); + return 1; + } + if (client->container->mode != MODE_STACK) decorate_window(conn, client, client->frame, client->titlegc, 0); else { diff --git a/src/layout.c b/src/layout.c index 17c99a6a..68db98bd 100644 --- a/src/layout.c +++ b/src/layout.c @@ -179,7 +179,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw * */ static void reposition_client(xcb_connection_t *conn, Client *client) { - LOG("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y); + LOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y); /* Note: We can use a pointer to client->x like an array of uint32_ts because it is followed by client->y by definition */ xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x)); @@ -192,7 +192,7 @@ static void reposition_client(xcb_connection_t *conn, Client *client) { static void resize_client(xcb_connection_t *conn, Client *client) { i3Font *font = load_font(conn, config.font); - LOG("resizing client to %d x %d\n", client->rect.width, client->rect.height); + LOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height); xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, &(client->rect.width)); @@ -205,7 +205,7 @@ static void resize_client(xcb_connection_t *conn, Client *client) { XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; Rect *rect = &(client->child_rect); - switch (client->container->mode) { + switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) { case MODE_STACK: rect->x = 2; rect->y = 0; @@ -397,6 +397,7 @@ void render_container(xcb_connection_t *conn, Container *container) { static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) { Client *client; SLIST_FOREACH(client, &(r_ws->dock_clients), dock_clients) { + LOG("client is at %d, should be at %d\n", client->rect.y, *height); if (client->force_reconfigure | update_if_necessary(&(client->rect.x), 0) | update_if_necessary(&(client->rect.y), *height)) diff --git a/src/mainx.c b/src/mainx.c index 4d96a8c4..61d2bb54 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -126,14 +126,23 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, int16_t x, int16_t y, uint16_t width, uint16_t height) { xcb_get_property_cookie_t wm_type_cookie, strut_cookie; + uint32_t mask = 0; + uint32_t values[3]; + + /* We are interested in property changes */ + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW; + xcb_change_window_attributes(conn, child, mask, values); + + /* Map the window first to avoid flickering */ + xcb_map_window(conn, child); /* Place requests for properties ASAP */ wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX); strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX); - /* Map the window first to avoid flickering */ - xcb_map_window(conn, child); - Client *new = table_get(byChild, child); /* Events for already managed windows should already be filtered in manage_window() */ @@ -142,19 +151,20 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, LOG("reparenting new client\n"); new = calloc(sizeof(Client), 1); new->force_reconfigure = true; - uint32_t mask = 0; - uint32_t values[3]; /* Update the data structures */ Client *old_focused = CUR_CELL->currently_focused; new->container = CUR_CELL; + new->workspace = new->container->workspace; new->frame = xcb_generate_id(conn); new->child = child; new->rect.width = width; new->rect.height = height; + mask = 0; + /* Don’t generate events for our new window, it should *not* be managed */ mask |= XCB_CW_OVERRIDE_REDIRECT; values[0] = 1; @@ -199,19 +209,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, return; } - /* We are interested in property changes */ - mask = XCB_CW_EVENT_MASK; - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_ENTER_WINDOW; - cookie = xcb_change_window_attributes_checked(conn, child, mask, values); - if (xcb_request_check(conn, cookie) != NULL) { - LOG("Could not change window attributes, aborting\n"); - xcb_destroy_window(conn, new->frame); - free(new); - return; - } - /* Put our data structure (Client) into the table */ table_put(byParent, new->frame, new); table_put(byChild, child, new); @@ -222,16 +219,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, 1 /* left mouse button */, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); - /* Focus the new window if we’re not in fullscreen mode */ - if (CUR_CELL->workspace->fullscreen_client == NULL) { - CUR_CELL->currently_focused = new; - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME); - } else { - /* If we are in fullscreen, we should lower the window to not be annoying */ - uint32_t values[] = { XCB_STACK_MODE_BELOW }; - xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values); - } - /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */ xcb_atom_t *atom; xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL); @@ -247,23 +234,44 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, } } - /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */ - uint32_t *strut; - preply = xcb_get_property_reply(conn, strut_cookie, NULL); - if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) { - /* We only use a subset of the provided values, namely the reserved space at the top/bottom - of the screen. This is because the only possibility for bars is at to be at the top/bottom - with maximum horizontal size. - TODO: bars at the top */ - new->desired_height = strut[3]; - LOG("the client wants to be %d pixels height\n", new->desired_height); + if (new->dock) { + /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */ + uint32_t *strut; + preply = xcb_get_property_reply(conn, strut_cookie, NULL); + if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) { + /* We only use a subset of the provided values, namely the reserved space at the top/bottom + of the screen. This is because the only possibility for bars is at to be at the top/bottom + with maximum horizontal size. + TODO: bars at the top */ + new->desired_height = strut[3]; + if (new->desired_height == 0) { + LOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", height); + new->desired_height = height; + } + LOG("the client wants to be %d pixels high\n", new->desired_height); + } else { + LOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", height); + new->desired_height = height; + } + } + + /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */ + if (CUR_CELL->workspace->fullscreen_client == NULL) { + if (!new->dock) { + CUR_CELL->currently_focused = new; + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME); + } + } else { + /* If we are in fullscreen, we should lower the window to not be annoying */ + uint32_t values[] = { XCB_STACK_MODE_BELOW }; + xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values); } /* Insert into the currently active container, if it’s not a dock window */ if (!new->dock) { /* Insert after the old active client, if existing. If it does not exist, the container is empty and it does not matter, where we insert it */ - if (old_focused != NULL) + if (old_focused != NULL && !old_focused->dock) CIRCLEQ_INSERT_AFTER(&(CUR_CELL->clients), old_focused, new, clients); else CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients); @@ -408,10 +416,6 @@ int main(int argc, char *argv[], char *env[]) { it any longer. Usually, the client destroys the window shortly afterwards. */ xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL); - /* Destroy notify is a step further than unmap notify. We handle it to make sure - that windows whose unmap notify was falsely ignored will get deleted properly */ - xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL); - /* Configure notify = window’s configuration (geometry, stacking, …). We only need it to set up ignore the following enter_notify events */ xcb_event_set_configure_notify_handler(&evenths, handle_configure_event, NULL); diff --git a/src/util.c b/src/util.c index c79aa3d1..2ca1e1ca 100644 --- a/src/util.c +++ b/src/util.c @@ -233,7 +233,7 @@ void set_focus(xcb_connection_t *conn, Client *client) { /* If we’re in stacking mode, this renders the container to update changes in the title bars and to raise the focused client */ - if ((old_client != NULL) && (old_client != client)) + if ((old_client != NULL) && (old_client != client) && !old_client->dock) redecorate_window(conn, old_client); SLIST_REMOVE(&(client->container->workspace->focus_stack), client, Client, focus_clients);