diff options
-rw-r--r-- | Makefile.am | 11 | ||||
-rw-r--r-- | docs/userguide | 5 | ||||
-rw-r--r-- | i3-nagbar/main.c | 34 | ||||
-rw-r--r-- | i3bar/src/child.c | 2 | ||||
-rw-r--r-- | i3bar/src/xcb.c | 31 | ||||
-rw-r--r-- | include/click.h | 2 | ||||
-rw-r--r-- | include/i3.h | 2 | ||||
-rw-r--r-- | include/xcb.h | 15 | ||||
-rw-r--r-- | include/xcursor.h | 1 | ||||
-rw-r--r-- | src/click.c | 50 | ||||
-rw-r--r-- | src/commands.c | 11 | ||||
-rw-r--r-- | src/drag.c | 2 | ||||
-rw-r--r-- | src/handlers.c | 161 | ||||
-rw-r--r-- | src/ipc.c | 16 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/match.c | 8 | ||||
-rw-r--r-- | src/startup.c | 10 | ||||
-rw-r--r-- | src/xcb.c | 34 | ||||
-rw-r--r-- | src/xcursor.c | 16 | ||||
-rw-r--r-- | testcases/t/116-nestedcons.t | 1 | ||||
-rw-r--r-- | testcases/t/119-match.t | 18 | ||||
-rw-r--r-- | testcases/t/180-fd-leaks.t | 40 | ||||
-rw-r--r-- | testcases/t/210-mark-unmark.t | 10 | ||||
-rw-r--r-- | testcases/t/307-focus-next-prev.t | 12 | ||||
-rwxr-xr-x | travis/check-spelling.pl | 7 |
25 files changed, 177 insertions, 328 deletions
diff --git a/Makefile.am b/Makefile.am index 8d0fd31e..872fdff7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,14 @@ dist_i3conf_DATA = \ etc/config \ etc/config.keycodes +I3STATUS_INSTALL_NAME = $(shell echo i3status | sed '@program_transform_name@') + +etc/config: etc/$(dirstamp) + $(AM_V_GEN) sed "s,status_command i3status,status_command $(I3STATUS_INSTALL_NAME),g" $(top_srcdir)/etc/config > etc/config + +etc/config.keycodes: etc/$(dirstamp) + $(AM_V_GEN) sed "s,status_command i3status,status_command $(I3STATUS_INSTALL_NAME),g" $(top_srcdir)/etc/config.keycodes > etc/config.keycodes + applicationsdir = $(datarootdir)/applications xsessionsdir = $(datarootdir)/xsessions dist_applications_DATA = \ @@ -146,7 +154,8 @@ dirstamp = .dirstamp dirstamps = \ docs/$(dirstamp) \ man/$(dirstamp) \ - parser/$(dirstamp) + parser/$(dirstamp) \ + etc/$(dirstamp) DISTCLEANFILES = $(dirstamps) $(dirstamps): diff --git a/docs/userguide b/docs/userguide index 597ff20e..4cda35f8 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1409,6 +1409,11 @@ button4:: Scroll wheel up. button5:: Scroll wheel down. +button6:: + Scroll wheel right. +button7:: + Scroll wheel left. + Please note that the old +wheel_up_cmd+ and +wheel_down_cmd+ commands are deprecated and will be removed in a future release. We strongly recommend using the more general diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 5099cf54..e59f5a6a 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -38,10 +38,6 @@ xcb_visualtype_t *visual_type = NULL; #include "i3-nagbar.h" -/** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a - * constant for that. */ -#define XCB_CURSOR_LEFT_PTR 68 - #define MSG_PADDING logical_px(8) #define BTN_PADDING logical_px(3) #define BTN_BORDER logical_px(3) @@ -108,10 +104,6 @@ void debuglog(char *fmt, ...) { * fork to avoid zombie processes. As the started application’s parent exits * (immediately), the application is reparented to init (process-id 1), which * correctly handles children, so we don’t have to do it :-). - * - * The shell is determined by looking for the SHELL environment variable. If it - * does not exist, /bin/sh is used. - * */ static void start_application(const char *command) { printf("executing: %s\n", command); @@ -177,7 +169,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve warn("Could not fdopen() temporary script to store the nagbar command"); return; } - fprintf(script, "#!/bin/sh\nrm %s\n%s", script_path, button->action); + fprintf(script, "#!%s\nrm %s\n%s", _PATH_BSHELL, script_path, button->action); /* Also closes fd */ fclose(script); @@ -357,8 +349,8 @@ int main(int argc, char *argv[]) { unlink(argv[0]); cmd = sstrdup(argv[0]); *(cmd + argv0_len - strlen(".nagbar_cmd")) = '\0'; - execl("/bin/sh", "/bin/sh", cmd, NULL); - err(EXIT_FAILURE, "execv(/bin/sh, /bin/sh, %s)", cmd); + execl(_PATH_BSHELL, _PATH_BSHELL, cmd, NULL); + err(EXIT_FAILURE, "execl(%s, %s, %s)", _PATH_BSHELL, _PATH_BSHELL, cmd); } argv0 = argv[0]; @@ -469,24 +461,12 @@ int main(int argc, char *argv[]) { xcb_rectangle_t win_pos = get_window_position(); - xcb_cursor_t cursor; xcb_cursor_context_t *cursor_ctx; - if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) == 0) { - cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr"); - xcb_cursor_context_free(cursor_ctx); - } else { - cursor = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - xcb_create_glyph_cursor( - conn, - cursor, - cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, - XCB_CURSOR_LEFT_PTR, - XCB_CURSOR_LEFT_PTR + 1, - 0, 0, 0, - 65535, 65535, 65535); + if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) < 0) { + errx(EXIT_FAILURE, "Cannot allocate xcursor context"); } + xcb_cursor_t cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr"); + xcb_cursor_context_free(cursor_ctx); /* Open an input window */ win = xcb_generate_id(conn); diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 4065d901..b145e5d5 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -528,7 +528,7 @@ static void child_write_output(void) { /* * Start a child process with the specified command and reroute stdin. - * We actually start a $SHELL to execute the command so we don't have to care + * We actually start a shell to execute the command so we don't have to care * about arguments and such. * * If `command' is NULL, such as in the case when no `status_command' is given diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index c001b376..06cff98d 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -36,10 +36,6 @@ #include "libi3.h" -/** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a - * constant for that. */ -#define XCB_CURSOR_LEFT_PTR 68 - /* We save the atoms in an easy to access array, indexed by an enum */ enum { #define ATOM_DO(name) name, @@ -677,9 +673,6 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) { continue; } if (output->bar.id == event->window) { - if (output->visible == visible) { - return; - } output->visible = visible; } num_visible += output->visible; @@ -687,10 +680,7 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) { if (num_visible == 0) { stop_child(); - } else if (num_visible == visible) { - /* Wake the child only when transitioning from 0 to 1 visible bar. - * We cannot transition from 0 to 2 or more visible bars at once since - * visibility events are delivered to each window separately */ + } else { cont_child(); } } @@ -1305,22 +1295,11 @@ char *init_xcb_early(void) { } xcb_cursor_context_t *cursor_ctx; - if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) == 0) { - cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr"); - xcb_cursor_context_free(cursor_ctx); - } else { - cursor = xcb_generate_id(xcb_connection); - i3Font cursor_font = load_font("cursor", false); - xcb_create_glyph_cursor( - xcb_connection, - cursor, - cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, - XCB_CURSOR_LEFT_PTR, - XCB_CURSOR_LEFT_PTR + 1, - 0, 0, 0, - 65535, 65535, 65535); + if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) < 0) { + errx(EXIT_FAILURE, "Cannot allocate xcursor context"); } + cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr"); + xcb_cursor_context_free(cursor_ctx); /* The various watchers to communicate with xcb */ xcb_io = smalloc(sizeof(ev_io)); diff --git a/include/click.h b/include/click.h index 0d57abf7..898f1870 100644 --- a/include/click.h +++ b/include/click.h @@ -19,4 +19,4 @@ * Then, route_click is called on the appropriate con. * */ -int handle_button_press(xcb_button_press_event_t *event); +void handle_button_press(xcb_button_press_event_t *event); diff --git a/include/i3.h b/include/i3.h index e7afe7e5..aef34715 100644 --- a/include/i3.h +++ b/include/i3.h @@ -71,7 +71,7 @@ extern uint8_t root_depth; extern xcb_visualid_t visual_id; extern xcb_colormap_t colormap; -extern bool xcursor_supported, xkb_supported, shape_supported; +extern bool xkb_supported, shape_supported; extern xcb_window_t root; extern struct ev_loop *main_loop; extern bool only_check_config; diff --git a/include/xcb.h b/include/xcb.h index 36cc6fb9..15132609 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -18,13 +18,6 @@ #define _NET_WM_STATE_ADD 1 #define _NET_WM_STATE_TOGGLE 2 -/** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a - * constant for that. */ -#define XCB_CURSOR_LEFT_PTR 68 -#define XCB_CURSOR_SB_H_DOUBLE_ARROW 108 -#define XCB_CURSOR_SB_V_DOUBLE_ARROW 116 -#define XCB_CURSOR_WATCH 150 - /* from X11/keysymdef.h */ #define XCB_NUM_LOCK 0xff7f @@ -102,14 +95,6 @@ xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply); bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom); /** - * Set the cursor of the root window to the given cursor id. - * This function should only be used if xcursor_supported == false. - * Otherwise, use xcursor_set_root_cursor(). - * - */ -void xcb_set_root_cursor(int cursor); - -/** * Get depth of visual specified by visualid * */ diff --git a/include/xcursor.h b/include/xcursor.h index 804e1f84..ad33d506 100644 --- a/include/xcursor.h +++ b/include/xcursor.h @@ -28,7 +28,6 @@ enum xcursor_cursor_t { void xcursor_load_cursors(void); xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c); -int xcursor_get_xcb_cursor(enum xcursor_cursor_t c); /** * Sets the cursor of the root window to the 'pointer' cursor. diff --git a/src/click.c b/src/click.c index ffa6813b..811f74d5 100644 --- a/src/click.c +++ b/src/click.c @@ -48,7 +48,11 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press bool res = resize_find_tiling_participants(&first, &second, search_direction, false); if (!res) { - LOG("No second container in this direction found.\n"); + DLOG("No second container in this direction found.\n"); + return false; + } + if (first->fullscreen_mode != second->fullscreen_mode) { + DLOG("Avoiding resize between containers with different fullscreen modes, %d != %d\n", first->fullscreen_mode, second->fullscreen_mode); return false; } @@ -147,7 +151,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click * functions for resizing/dragging. * */ -static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod_pressed, const click_destination_t dest) { +static void route_click(Con *con, xcb_button_press_event_t *event, const bool mod_pressed, const click_destination_t dest) { DLOG("--> click properties: mod = %d, destination = %d\n", mod_pressed, dest); DLOG("--> OUTCOME = %p\n", con); DLOG("type = %d, name = %s\n", con->type, con->name); @@ -174,7 +178,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod xcb_flush(conn); command_result_free(result); - return 0; + return; } } @@ -233,7 +237,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod /* 4: floating_modifier plus left mouse button drags */ if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) { floating_drag_window(floatingcon, event, false); - return 1; + return; } /* 5: resize (floating) if this was a (left or right) click on the @@ -242,7 +246,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) { DLOG("floating resize due to floatingmodifier\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) && @@ -256,20 +260,20 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_RIGHT) { DLOG("floating resize due to decoration right click\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } if (dest == CLICK_BORDER && is_left_or_right_click) { DLOG("floating resize due to border click\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } /* 6: dragging, if this was a click on a decoration (which did not lead * to a resize) */ if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_LEFT) { floating_drag_window(floatingcon, event, !was_focused); - return 1; + return; } goto done; @@ -277,8 +281,10 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod /* 7: floating modifier pressed, initiate a resize */ if (dest == CLICK_INSIDE && mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) { - if (floating_mod_on_tiled_client(con, event)) - return 1; + floating_mod_on_tiled_client(con, event); + /* Avoid propagating events to clients, since the user expects + * $mod + click to be handled by i3. */ + return; } /* 8: otherwise, check for border/decoration clicks and resize */ else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) && @@ -291,8 +297,6 @@ done: xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); tree_render(); - - return 0; } /* @@ -303,7 +307,7 @@ done: * Then, route_click is called on the appropriate con. * */ -int handle_button_press(xcb_button_press_event_t *event) { +void handle_button_press(xcb_button_press_event_t *event) { Con *con; DLOG("Button %d (state %d) %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n", event->detail, event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"), @@ -315,8 +319,10 @@ int handle_button_press(xcb_button_press_event_t *event) { const uint32_t mod = (config.floating_modifier & 0xFFFF); const bool mod_pressed = (mod != 0 && (event->state & mod) == mod); DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail); - if ((con = con_by_window_id(event->event))) - return route_click(con, event, mod_pressed, CLICK_INSIDE); + if ((con = con_by_window_id(event->event))) { + route_click(con, event, mod_pressed, CLICK_INSIDE); + return; + } if (!(con = con_by_frame_id(event->event))) { /* Run bindings on the root window as well, see #2097. We only run it @@ -344,15 +350,15 @@ int handle_button_press(xcb_button_press_event_t *event) { workspace_show(ws); tree_render(); } - return 1; + return; } - return 0; + return; } ELOG("Clicked into unknown window?!\n"); xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); - return 0; + return; } /* Check if the click was on the decoration of a child */ @@ -361,13 +367,15 @@ int handle_button_press(xcb_button_press_event_t *event) { if (!rect_contains(child->deco_rect, event->event_x, event->event_y)) continue; - return route_click(child, event, mod_pressed, CLICK_DECORATION); + route_click(child, event, mod_pressed, CLICK_DECORATION); + return; } if (event->child != XCB_NONE) { DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n"); - return route_click(con, event, mod_pressed, CLICK_INSIDE); + route_click(con, event, mod_pressed, CLICK_INSIDE); + return; } - return route_click(con, event, mod_pressed, CLICK_BORDER); + route_click(con, event, mod_pressed, CLICK_BORDER); } diff --git a/src/commands.c b/src/commands.c index 897ba288..14bd877d 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1299,7 +1299,16 @@ void cmd_focus_sibling(I3_CMD, const char *direction_str) { } Con *next = get_tree_next_sibling(current->con, direction); if (next) { - con_activate(next); + if (next->type == CT_WORKSPACE) { + /* On the workspace level, we need to make sure that the + * workspace change happens properly. However, workspace_show + * descends focus so we also have to put focus on the workspace + * itself to maintain consistency. See #3997. */ + workspace_show(next); + con_focus(next); + } else { + con_activate(next); + } } } @@ -175,7 +175,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, int cursor, bool use_threshold, callback_t callback, const void *extra) { - xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; + xcb_cursor_t xcursor = cursor ? xcursor_get_cursor(cursor) : XCB_NONE; /* Grab the pointer */ xcb_grab_pointer_cookie_t cookie; diff --git a/src/handlers.c b/src/handlers.c index 7926fec5..22a974ac 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -558,12 +558,7 @@ static bool window_name_changed(i3Window *window, char *old_name) { * Called when a window changes its title * */ -static bool 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) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowname_change(Con *con, xcb_get_property_reply_t *prop) { char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); window_update_name(con->window, prop); @@ -585,12 +580,7 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t * window_update_name_legacy(). * */ -static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowname_change_legacy(Con *con, xcb_get_property_reply_t *prop) { char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); window_update_name_legacy(con->window, prop); @@ -611,12 +601,7 @@ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, * Called when a window changes its WM_WINDOW_ROLE. * */ -static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowrole_change(Con *con, xcb_get_property_reply_t *prop) { window_update_role(con->window, prop); con = remanage_window(con); @@ -956,12 +941,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { } } -static bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t atom, xcb_get_property_reply_t *reply) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_window_type(Con *con, xcb_get_property_reply_t *reply) { window_update_type(con->window, reply); return true; } @@ -973,14 +953,7 @@ static bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state * See ICCCM 4.1.2.3 for more details * */ -static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { - Con *con = con_by_window_id(window); - if (con == NULL) { - DLOG("Received WM_NORMAL_HINTS for unknown client\n"); - return false; - } - +static bool handle_normal_hints(Con *con, xcb_get_property_reply_t *reply) { bool changed = window_update_normal_hints(con->window, reply, NULL); if (changed) { @@ -999,21 +972,11 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat * Handles the WM_HINTS property for extracting the urgency state of the window. * */ -static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { - Con *con = con_by_window_id(window); - if (con == NULL) { - DLOG("Received WM_HINTS for unknown client\n"); - return false; - } - +static bool handle_hints(Con *con, xcb_get_property_reply_t *reply) { bool urgency_hint; - if (reply == NULL) - reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL); window_update_hints(con->window, reply, &urgency_hint); con_set_urgency(con, urgency_hint); tree_render(); - return true; } @@ -1024,24 +987,8 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_ * See ICCCM 4.1.2.6 for more details * */ -static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { - DLOG("No such window\n"); - return false; - } - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), - NULL); - if (prop == NULL) - return false; - } - +static bool handle_transient_for(Con *con, xcb_get_property_reply_t *prop) { window_update_transient_for(con->window, prop); - return true; } @@ -1050,21 +997,8 @@ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t sta * toolwindow (or similar) and to which window it belongs (logical parent). * */ -static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), - NULL); - if (prop == NULL) - return false; - } - +static bool handle_clientleader_change(Con *con, xcb_get_property_reply_t *prop) { window_update_leader(con->window, prop); - return true; } @@ -1144,24 +1078,9 @@ static void handle_configure_notify(xcb_configure_notify_event_t *event) { * Handles the WM_CLASS property for assignments and criteria selection. * */ -static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), - NULL); - - if (prop == NULL) - return false; - } - +static bool handle_class_change(Con *con, xcb_get_property_reply_t *prop) { window_update_class(con->window, prop); - con = remanage_window(con); - return true; } @@ -1169,20 +1088,7 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings. * */ -static bool handle_motif_hints_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A__MOTIF_WM_HINTS, XCB_GET_PROPERTY_TYPE_ANY, 0, 5 * sizeof(uint64_t)), - NULL); - - if (prop == NULL) - return false; - } - +static bool handle_motif_hints_change(Con *con, xcb_get_property_reply_t *prop) { border_style_t motif_border_style; window_update_motif_hints(con->window, prop, &motif_border_style); @@ -1200,34 +1106,7 @@ static bool handle_motif_hints_change(void *data, xcb_connection_t *conn, uint8_ * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients. * */ -static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - DLOG("strut partial change for window 0x%08x\n", window); - - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { - return false; - } - - if (prop == NULL) { - xcb_generic_error_t *err = NULL; - xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL, - XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX); - prop = xcb_get_property_reply(conn, strut_cookie, &err); - - if (err != NULL) { - DLOG("got error when getting strut partial property: %d\n", err->error_code); - free(err); - return false; - } - - if (prop == NULL) { - return false; - } - } - - DLOG("That is con %p / %s\n", con, con->name); - +static bool handle_strut_partial_change(Con *con, xcb_get_property_reply_t *prop) { window_update_strut_partial(con->window, prop); /* we only handle this change for dock clients */ @@ -1279,7 +1158,7 @@ static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ -typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property); +typedef bool (*cb_property_handler_t)(Con *con, xcb_get_property_reply_t *property); struct property_handler_t { xcb_atom_t atom; @@ -1325,6 +1204,8 @@ void property_handlers_init(void) { static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { struct property_handler_t *handler = NULL; xcb_get_property_reply_t *propr = NULL; + xcb_generic_error_t *err = NULL; + Con *con; for (size_t c = 0; c < NUM_HANDLERS; c++) { if (property_handlers[c].atom != atom) @@ -1339,13 +1220,23 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) return; } + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { + DLOG("Received property for atom %d for unknown client\n", atom); + return; + } + if (state != XCB_PROPERTY_DELETE) { xcb_get_property_cookie_t cookie = xcb_get_property(conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->long_len); - propr = xcb_get_property_reply(conn, cookie, 0); + propr = xcb_get_property_reply(conn, cookie, &err); + if (err != NULL) { + DLOG("got error %d when getting property of atom %d\n", err->error_code, atom); + FREE(err); + return; + } } /* the handler will free() the reply unless it returns false */ - if (!handler->cb(NULL, conn, state, window, atom, propr)) + if (!handler->cb(con, propr)) FREE(propr); } @@ -410,17 +410,13 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("urgent"); y(bool, con->urgent); - if (!TAILQ_EMPTY(&(con->marks_head))) { - ystr("marks"); - y(array_open); - - mark_t *mark; - TAILQ_FOREACH (mark, &(con->marks_head), marks) { - ystr(mark->name); - } - - y(array_close); + ystr("marks"); + y(array_open); + mark_t *mark; + TAILQ_FOREACH (mark, &(con->marks_head), marks) { + ystr(mark->name); } + y(array_close); ystr("focused"); y(bool, (con == focused)); @@ -87,7 +87,6 @@ struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignments); /* We hope that those are supported and set them to true */ -bool xcursor_supported = true; bool xkb_supported = true; bool shape_supported = true; @@ -641,10 +640,7 @@ int main(int argc, char *argv[]) { /* Set a cursor for the root window (otherwise the root window will show no cursor until the first client is launched). */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); - else - xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); + xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); const xcb_query_extension_reply_t *extreply; xcb_prefetch_extension_data(conn, &xcb_xkb_id); diff --git a/src/match.c b/src/match.c index 7b7881b6..a8850af4 100644 --- a/src/match.c +++ b/src/match.c @@ -92,11 +92,9 @@ bool match_matches_window(Match *match, i3Window *window) { #define CHECK_WINDOW_FIELD(match_field, window_field, type) \ do { \ if (match->match_field != NULL) { \ - if (window->window_field == NULL) { \ - return false; \ - } \ - \ - const char *window_field_str = GET_FIELD_##type(window->window_field); \ + const char *window_field_str = window->window_field == NULL \ + ? "" \ + : GET_FIELD_##type(window->window_field); \ if (strcmp(match->match_field->pattern, "__focused__") == 0 && \ focused && focused->window && focused->window->window_field && \ strcmp(window_field_str, GET_FIELD_##type(focused->window->window_field)) == 0) { \ diff --git a/src/startup.c b/src/startup.c index 757a4e40..e994e0a8 100644 --- a/src/startup.c +++ b/src/startup.c @@ -201,10 +201,7 @@ void start_application(const char *command, bool no_startup_id) { if (!no_startup_id) { /* Change the pointer of the root window to indicate progress */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_WATCH); - else - xcb_set_root_cursor(XCURSOR_CURSOR_WATCH); + xcursor_set_root_cursor(XCURSOR_CURSOR_WATCH); } } @@ -246,10 +243,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) { if (_prune_startup_sequences() == 0) { DLOG("No more startup sequences running, changing root window cursor to default pointer.\n"); /* Change the pointer of the root window to indicate progress */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); - else - xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); + xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); } break; default: @@ -45,20 +45,8 @@ xcb_window_t create_window(xcb_connection_t *conn, Rect dims, } /* Set the cursor */ - if (xcursor_supported) { - mask = XCB_CW_CURSOR; - values[0] = xcursor_get_cursor(cursor); - xcb_change_window_attributes(conn, result, mask, values); - } else { - xcb_cursor_t cursor_id = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, - 65535, 65535, 65535); - xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id); - xcb_free_cursor(conn, cursor_id); - } + uint32_t cursor_values[] = {xcursor_get_cursor(cursor)}; + xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, cursor_values); /* Map the window (= make it visible) */ if (map) @@ -176,24 +164,6 @@ bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom) { } /* - * Set the cursor of the root window to the given cursor id. - * This function should only be used if xcursor_supported == false. - * Otherwise, use xcursor_set_root_cursor(). - * - */ -void xcb_set_root_cursor(int cursor) { - xcb_cursor_t cursor_id = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, - 65535, 65535, 65535); - xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id); - xcb_free_cursor(conn, cursor_id); - xcb_flush(conn); -} - -/* * Get depth of visual specified by visualid * */ diff --git a/src/xcursor.c b/src/xcursor.c index cbfe808b..cffb094b 100644 --- a/src/xcursor.c +++ b/src/xcursor.c @@ -10,6 +10,7 @@ #include <config.h> #include <assert.h> +#include <err.h> #include <xcb/xcb_cursor.h> #include "i3.h" @@ -19,17 +20,9 @@ static xcb_cursor_context_t *ctx; static xcb_cursor_t cursors[XCURSOR_CURSOR_MAX]; -static const int xcb_cursors[XCURSOR_CURSOR_MAX] = { - XCB_CURSOR_LEFT_PTR, - XCB_CURSOR_SB_H_DOUBLE_ARROW, - XCB_CURSOR_SB_V_DOUBLE_ARROW, - XCB_CURSOR_WATCH}; - void xcursor_load_cursors(void) { if (xcb_cursor_context_new(conn, root_screen, &ctx) < 0) { - ELOG("xcursor support unavailable\n"); - xcursor_supported = false; - return; + errx(EXIT_FAILURE, "Cannot allocate xcursor context"); } #define LOAD_CURSOR(constant, name) \ do { \ @@ -63,8 +56,3 @@ xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c) { assert(c < XCURSOR_CURSOR_MAX); return cursors[c]; } - -int xcursor_get_xcb_cursor(enum xcursor_cursor_t c) { - assert(c < XCURSOR_CURSOR_MAX); - return xcb_cursors[c]; -} diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 4f13b1e5..6a2274d4 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -72,6 +72,7 @@ my $expected = { 'floating_nodes' => $ignore, workspace_layout => 'default', current_border_width => -1, + marks => $ignore, }; # a shallow copy is sufficient, since we only ignore values at the root diff --git a/testcases/t/119-match.t b/testcases/t/119-match.t index 64ffa4ed..65c41f1f 100644 --- a/testcases/t/119-match.t +++ b/testcases/t/119-match.t @@ -33,9 +33,7 @@ my $win = $content->[0]; # not match it ###################################################################### # TODO: specify more match types -# we can match on any (non-empty) class here since that window does not have -# WM_CLASS set -cmd q|[class=".*"] kill|; +# Try matching with an empty pattern since there isn't a WM_CLASS set. cmd q|[con_id="99999"] kill|; is_num_children($tmp, 1, 'window still there'); @@ -103,4 +101,18 @@ cmd '[title="^\w [3]$"] kill'; wait_for_unmap $left; is_num_children($tmp, 0, 'window killed'); +###################################################################### +# check that we can match empty properties +###################################################################### + +$tmp = fresh_workspace; + +$left = open_window(name => 'class is empty', wm_class => ''); +ok($left->mapped, 'left window mapped'); +is_num_children($tmp, 1, 'window opened'); + +cmd '[class="^$"] kill'; +wait_for_unmap $left; +is_num_children($tmp, 0, 'window killed'); + done_testing; diff --git a/testcases/t/180-fd-leaks.t b/testcases/t/180-fd-leaks.t index 4e3369e9..6ca0dc6f 100644 --- a/testcases/t/180-fd-leaks.t +++ b/testcases/t/180-fd-leaks.t @@ -29,6 +29,10 @@ my $tmp = tmpnam(); mkfifo($tmp, 0600) or die "Could not create FIFO in $tmp"; my ($outfh, $outname) = tempfile('/tmp/i3-ls-output.XXXXXX', UNLINK => 1); +# Get fds from a clean shell +my $shoutput = `sh -c "ls -l /proc/self/fd"`; + +# Get fds from i3 cmd qq|exec ls -l /proc/self/fd >$outname && echo done >$tmp|; open(my $fh, '<', $tmp); @@ -38,29 +42,39 @@ close($fh); unlink($tmp); # Get the ls /proc/self/fd output -my $output; +my $i3output; { local $/; - $output = <$outfh>; + $i3output = <$outfh>; } close($outfh); -# Split lines, keep only those which are symlinks. -my @lines = grep { /->/ } split("\n", $output); +sub extract_fds { + my $output = @_; + + # Split lines, keep only those which are symlinks. + my @lines = grep { /->/ } split("\n", $output); -my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines; + my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines; -# Filter out 0, 1, 2 (stdin, stdout, stderr). -delete $fds{0}; -delete $fds{1}; -delete $fds{2}; + # Filter out 0, 1, 2 (stdin, stdout, stderr). + delete $fds{0}; + delete $fds{1}; + delete $fds{2}; -# Filter out the fd which is caused by ls calling readdir(). -for my $fd (keys %fds) { - delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,; + # filter out the fd which is caused by ls calling readdir(). + for my $fd (keys %fds) { + delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,; + } + + return %fds; } -is(scalar keys %fds, 0, 'No file descriptors leaked'); +my %i3fds = extract_fds($i3output); +my %shfds = extract_fds($shoutput); + +# Diff the fds to account for services that keep fds open, such as the System Security Services Daemon (sssd) +is(scalar keys %i3fds, scalar keys %shfds, 'No file descriptors leaked'); } diff --git a/testcases/t/210-mark-unmark.t b/testcases/t/210-mark-unmark.t index 32f898e6..0d9a073b 100644 --- a/testcases/t/210-mark-unmark.t +++ b/testcases/t/210-mark-unmark.t @@ -98,7 +98,7 @@ cmd 'focus left'; cmd 'mark important'; is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'first container now has the mark'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second container lost the mark'); ############################################################## # 5: mark a con, toggle the mark, check that the mark is gone @@ -107,7 +107,7 @@ ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the $con = open_window; cmd 'mark important'; cmd 'mark --toggle important'; -ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $con), [], 'container no longer has the mark'); ############################################################## # 6: toggle a mark on an unmarked con, check it is marked @@ -140,7 +140,7 @@ cmd 'focus left'; cmd 'mark --toggle important'; is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'left container has the mark now'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second containr no longer has the mark'); ############################################################## # 9: try to mark two cons with the same mark and check that @@ -154,8 +154,8 @@ my $result = cmd "[instance=iamnotunique] mark important"; is($result->[0]->{success}, 0, 'command was unsuccessful'); is($result->[0]->{error}, 'A mark must not be put onto more than one window', 'correct error is returned'); -ok(!get_mark_for_window_on_workspace($tmp, $first), 'first container is not marked'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr is not marked'); +is_deeply(get_mark_for_window_on_workspace($tmp, $first), [], 'first container is not marked'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second containr is not marked'); ############################################################## diff --git a/testcases/t/307-focus-next-prev.t b/testcases/t/307-focus-next-prev.t index c7f06589..4f8a8ecd 100644 --- a/testcases/t/307-focus-next-prev.t +++ b/testcases/t/307-focus-next-prev.t @@ -69,4 +69,16 @@ cmp_tree( cmd 'focus parent, focus next sibling'; }); +# See #3997 +cmd 'workspace 2'; +open_window; +cmd 'workspace 1'; +open_window; +cmd 'focus parent, focus parent, focus next sibling, focus prev sibling'; +does_i3_live; +is(focused_ws, '1', 'Back and forth between workspaces'); + +cmd 'focus parent, focus parent, focus next sibling'; +is(focused_ws, '2', "Workspace 2 focused with 'focus next sibling'"); + done_testing; diff --git a/travis/check-spelling.pl b/travis/check-spelling.pl index 2a112c50..45770c5e 100755 --- a/travis/check-spelling.pl +++ b/travis/check-spelling.pl @@ -17,8 +17,11 @@ use Lintian::Spelling qw(check_spelling); # Lintian complains if we don’t set a vendor. use Lintian::Data; use Lintian::Profile; -Lintian::Data->set_vendor( - Lintian::Profile->new('debian', ['/usr/share/lintian'], {})); + +my $profile = Lintian::Profile->new; +$profile->load('debian', ['/usr/share/lintian']); + +Lintian::Data->set_vendor($profile); my $exitcode = 0; |