From 38c8541807d50e18bf5ea61995ec6b3ab3e8a068 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 11 Mar 2009 00:20:56 +0100 Subject: [PATCH] Bugfix: Change the event mask to ignore enter notifies when rendering the layout and changing workspaces --- include/layout.h | 1 + include/util.h | 4 ++ include/xcb.h | 15 +++++++ src/commands.c | 26 +++++++----- src/layout.c | 100 +++++++++++++++++++++++++++++++---------------- src/mainx.c | 10 +---- src/table.c | 53 ++++++++++++------------- src/util.c | 4 +- 8 files changed, 131 insertions(+), 82 deletions(-) diff --git a/include/layout.h b/include/layout.h index 94beb6d4..ca90391c 100644 --- a/include/layout.h +++ b/include/layout.h @@ -17,6 +17,7 @@ Rect get_unoccupied_space(Workspace *workspace); void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, xcb_gcontext_t gc, int offset); void redecorate_window(xcb_connection_t *conn, Client *client); void render_container(xcb_connection_t *conn, Container *container); +void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bool ignore_enter_notify); void render_layout(xcb_connection_t *conn); #endif diff --git a/include/util.h b/include/util.h index 31beeb80..12cd852c 100644 --- a/include/util.h +++ b/include/util.h @@ -20,6 +20,10 @@ CIRCLEQ_NEXT(elm, field) : NULL) #define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \ CIRCLEQ_PREV(elm, field) : NULL) +#define FOR_TABLE(workspace) \ + for (int cols = 0; cols < workspace->cols; cols++) \ + for (int rows = 0; rows < workspace->rows; rows++) + /* ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that is, delete the preceding comma */ #define LOG(fmt, ...) slog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) diff --git a/include/xcb.h b/include/xcb.h index 26317147..31307e33 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -22,6 +22,21 @@ #define XCB_CURSOR_SB_H_DOUBLE_ARROW 108 #define XCB_CURSOR_SB_V_DOUBLE_ARROW 116 +/* The event masks are defined here because we don’t only set them once but we need to set slight + variations of them (without XCB_EVENT_MASK_ENTER_WINDOW while rendering the layout) */ +/* The XCB_CW_EVENT_MASK for the child (= real window) */ +#define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_ENTER_WINDOW) + +/* The XCB_CW_EVENT_MASK for its frame */ +#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \ + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | /* …user moves cursor inside our window */ \ + XCB_EVENT_MASK_ENTER_WINDOW) /* …the application tries to resize itself */ + + enum { _NET_SUPPORTED = 0, _NET_SUPPORTING_WM_CHECK, _NET_WM_NAME, diff --git a/src/commands.c b/src/commands.c index e2907564..e47855f6 100644 --- a/src/commands.c +++ b/src/commands.c @@ -417,14 +417,15 @@ static void show_workspace(xcb_connection_t *conn, int workspace) { /* TODO: does grabbing the server actually bring us any (speed)advantages? */ //xcb_grab_server(conn); + ignore_enter_notify_forall(conn, c_ws, true); + /* Unmap all clients of the current workspace */ int unmapped_clients = 0; - for (int cols = 0; cols < c_ws->cols; cols++) - for (int rows = 0; rows < c_ws->rows; rows++) - CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) { - xcb_unmap_window(conn, client->frame); - unmapped_clients++; - } + FOR_TABLE(c_ws) + CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) { + xcb_unmap_window(conn, client->frame); + unmapped_clients++; + } /* If we did not unmap any clients, the workspace is empty and we can destroy it */ if (unmapped_clients == 0) @@ -436,22 +437,27 @@ static void show_workspace(xcb_connection_t *conn, int workspace) { if (stack_win->container->workspace == c_ws) xcb_unmap_window(conn, stack_win->window); + ignore_enter_notify_forall(conn, c_ws, false); + c_ws = &workspaces[workspace-1]; current_row = c_ws->current_row; current_col = c_ws->current_col; LOG("new current row = %d, current col = %d\n", current_row, current_col); + ignore_enter_notify_forall(conn, c_ws, true); + /* Map all clients on the new workspace */ - for (int cols = 0; cols < c_ws->cols; cols++) - for (int rows = 0; rows < c_ws->rows; rows++) - CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) - xcb_map_window(conn, client->frame); + FOR_TABLE(c_ws) + CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) + xcb_map_window(conn, client->frame); /* Map all stack windows, if any */ SLIST_FOREACH(stack_win, &stack_wins, stack_windows) if (stack_win->container->workspace == c_ws) xcb_map_window(conn, stack_win->window); + ignore_enter_notify_forall(conn, c_ws, false); + /* Restore focus on the new workspace */ if (CUR_CELL->currently_focused != NULL) set_focus(conn, CUR_CELL->currently_focused); diff --git a/src/layout.c b/src/layout.c index 68db98bd..3783e9bb 100644 --- a/src/layout.c +++ b/src/layout.c @@ -464,6 +464,34 @@ static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int wid LOG("done rendering internal\n"); } +/* + * Modifies the event mask of all clients on the given workspace to either ignore or to handle + * enter notifies. It is handy to ignore notifies because they will be sent when a window is mapped + * under the cursor, thus when the user didn’t enter the window actively at all. + * + */ +void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bool ignore_enter_notify) { + Client *client; + uint32_t values[1]; + + LOG("Ignore enter_notify = %d\n", ignore_enter_notify); + + FOR_TABLE(workspace) + CIRCLEQ_FOREACH(client, &(workspace->table[cols][rows]->clients), clients) { + /* Change event mask for the decorations */ + values[0] = FRAME_EVENT_MASK; + if (ignore_enter_notify) + values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW); + xcb_change_window_attributes(conn, client->frame, XCB_CW_EVENT_MASK, values); + + /* Change event mask for the child itself */ + values[0] = CHILD_EVENT_MASK; + if (ignore_enter_notify) + values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW); + xcb_change_window_attributes(conn, client->child, XCB_CW_EVENT_MASK, values); + } +} + void render_layout(xcb_connection_t *conn) { i3Screen *screen; i3Font *font = load_font(conn, config.font); @@ -496,41 +524,45 @@ void render_layout(xcb_connection_t *conn) { xoffset[rows] = r_ws->rect.x; dump_table(conn, r_ws); + + ignore_enter_notify_forall(conn, r_ws, true); + /* Go through the whole table and render what’s necessary */ - for (int cols = 0; cols < r_ws->cols; cols++) - for (int rows = 0; rows < r_ws->rows; rows++) { - Container *container = r_ws->table[cols][rows]; - int single_width, single_height; - LOG("\n"); - LOG("========\n"); - LOG("container has %d colspan, %d rowspan\n", - container->colspan, container->rowspan); - LOG("container at %d, %d\n", xoffset[rows], yoffset[cols]); - /* Update position of the container */ - container->row = rows; - container->col = cols; - container->x = xoffset[rows]; - container->y = yoffset[cols]; - - if (container->width_factor == 0) - container->width = (width / r_ws->cols); - else container->width = get_unoccupied_x(r_ws, rows) * container->width_factor; - single_width = container->width; - container->width *= container->colspan; - - if (container->height_factor == 0) - container->height = (height / r_ws->rows); - else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor; - single_height = container->height; - container->height *= container->rowspan; - - /* Render the container if it is not empty */ - render_container(conn, container); - - xoffset[rows] += single_width; - yoffset[cols] += single_height; - LOG("==========\n"); - } + FOR_TABLE(r_ws) { + Container *container = r_ws->table[cols][rows]; + int single_width, single_height; + LOG("\n"); + LOG("========\n"); + LOG("container has %d colspan, %d rowspan\n", + container->colspan, container->rowspan); + LOG("container at %d, %d\n", xoffset[rows], yoffset[cols]); + /* Update position of the container */ + container->row = rows; + container->col = cols; + container->x = xoffset[rows]; + container->y = yoffset[cols]; + + if (container->width_factor == 0) + container->width = (width / r_ws->cols); + else container->width = get_unoccupied_x(r_ws, rows) * container->width_factor; + single_width = container->width; + container->width *= container->colspan; + + if (container->height_factor == 0) + container->height = (height / r_ws->rows); + else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor; + single_height = container->height; + container->height *= container->rowspan; + + /* Render the container if it is not empty */ + render_container(conn, container); + + xoffset[rows] += single_width; + yoffset[cols] += single_height; + LOG("==========\n"); + } + + ignore_enter_notify_forall(conn, r_ws, false); render_bars(conn, r_ws, width, &height); render_internal_bar(conn, r_ws, width, font->height + 6); diff --git a/src/mainx.c b/src/mainx.c index 61d2bb54..ba5ece59 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -131,9 +131,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, /* 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; + values[0] = CHILD_EVENT_MASK; xcb_change_window_attributes(conn, child, mask, values); /* Map the window first to avoid flickering */ @@ -171,11 +169,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, /* We want to know when… */ mask |= XCB_CW_EVENT_MASK; - values[1] = XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ - XCB_EVENT_MASK_ENTER_WINDOW | /* …user moves cursor inside our window */ - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; /* …the application tries to resize itself */ + values[1] = FRAME_EVENT_MASK; LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame); diff --git a/src/table.c b/src/table.c index 3bc45019..4c13be83 100644 --- a/src/table.c +++ b/src/table.c @@ -170,18 +170,16 @@ static void move_rows_from(xcb_connection_t *conn, Workspace *workspace, int row void dump_table(xcb_connection_t *conn, Workspace *workspace) { LOG("dump_table()\n"); - for (int cols = 0; cols < workspace->cols; cols++) { - for (int rows = 0; rows < workspace->rows; rows++) { - Container *con = workspace->table[cols][rows]; - LOG("----\n"); - LOG("at col=%d, row=%d\n", cols, rows); - LOG("currently_focused = %p\n", con->currently_focused); - Client *loop; - CIRCLEQ_FOREACH(loop, &(con->clients), clients) { - LOG("got client %08x / %s\n", loop->child, loop->name); - } - LOG("----\n"); + FOR_TABLE(workspace) { + Container *con = workspace->table[cols][rows]; + LOG("----\n"); + LOG("at col=%d, row=%d\n", cols, rows); + LOG("currently_focused = %p\n", con->currently_focused); + Client *loop; + CIRCLEQ_FOREACH(loop, &(con->clients), clients) { + LOG("got client %08x / %s\n", loop->child, loop->name); } + LOG("----\n"); } LOG("done\n"); } @@ -258,22 +256,21 @@ void cleanup_table(xcb_connection_t *conn, Workspace *workspace) { void fix_colrowspan(xcb_connection_t *conn, Workspace *workspace) { LOG("Fixing col/rowspan\n"); - for (int cols = 0; cols < workspace->cols; cols++) - for (int rows = 0; rows < workspace->rows; rows++) { - Container *con = workspace->table[cols][rows]; - if (con->colspan > 1) { - LOG("gots one with colspan %d\n", con->colspan); - while (con->colspan > 1 && - workspace->table[cols + (con->colspan - 1)][rows]->currently_focused != NULL) - con->colspan--; - LOG("fixed it to %d\n", con->colspan); - } - if (con->rowspan > 1) { - LOG("gots one with rowspan %d\n", con->rowspan); - while (con->rowspan > 1 && - workspace->table[cols][rows + (con->rowspan - 1)]->currently_focused != NULL) - con->rowspan--; - LOG("fixed it to %d\n", con->rowspan); - } + FOR_TABLE(workspace) { + Container *con = workspace->table[cols][rows]; + if (con->colspan > 1) { + LOG("gots one with colspan %d\n", con->colspan); + while (con->colspan > 1 && + workspace->table[cols + (con->colspan - 1)][rows]->currently_focused != NULL) + con->colspan--; + LOG("fixed it to %d\n", con->colspan); } + if (con->rowspan > 1) { + LOG("gots one with rowspan %d\n", con->rowspan); + while (con->rowspan > 1 && + workspace->table[cols][rows + (con->rowspan - 1)]->currently_focused != NULL) + con->rowspan--; + LOG("fixed it to %d\n", con->rowspan); + } + } } diff --git a/src/util.c b/src/util.c index 2ca1e1ca..d44a2593 100644 --- a/src/util.c +++ b/src/util.c @@ -371,8 +371,8 @@ void toggle_fullscreen(xcb_connection_t *conn, Client *client) { /* Because the coordinates of the window haven’t changed, it would not be re-configured if we don’t set the following flag */ client->force_reconfigure = true; - /* We left fullscreen mode, redraw the container */ - render_container(conn, client->container); + /* We left fullscreen mode, redraw the whole layout to ensure enternotify events are disabled */ + render_layout(conn); } xcb_flush(conn); -- 2.39.5