aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am11
-rw-r--r--docs/userguide5
-rw-r--r--i3-nagbar/main.c34
-rw-r--r--i3bar/src/child.c2
-rw-r--r--i3bar/src/xcb.c31
-rw-r--r--include/click.h2
-rw-r--r--include/i3.h2
-rw-r--r--include/xcb.h15
-rw-r--r--include/xcursor.h1
-rw-r--r--src/click.c50
-rw-r--r--src/commands.c11
-rw-r--r--src/drag.c2
-rw-r--r--src/handlers.c161
-rw-r--r--src/ipc.c16
-rw-r--r--src/main.c6
-rw-r--r--src/match.c8
-rw-r--r--src/startup.c10
-rw-r--r--src/xcb.c34
-rw-r--r--src/xcursor.c16
-rw-r--r--testcases/t/116-nestedcons.t1
-rw-r--r--testcases/t/119-match.t18
-rw-r--r--testcases/t/180-fd-leaks.t40
-rw-r--r--testcases/t/210-mark-unmark.t10
-rw-r--r--testcases/t/307-focus-next-prev.t12
-rwxr-xr-xtravis/check-spelling.pl7
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);
+ }
}
}
diff --git a/src/drag.c b/src/drag.c
index 6b05311a..67ccff40 100644
--- a/src/drag.c
+++ b/src/drag.c
@@ -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);
}
diff --git a/src/ipc.c b/src/ipc.c
index 08bdde33..100e1f70 100644
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -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));
diff --git a/src/main.c b/src/main.c
index 47874e41..369f2f66 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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:
diff --git a/src/xcb.c b/src/xcb.c
index bdfb08bc..5258dcc2 100644
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -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;