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
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__)
#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,
/* 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)
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);
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);
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);
/* 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 */
/* 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);
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");
}
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);
+ }
+ }
}
/* 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);