/* Event watchers, to interact with the user */
ev_prepare *xcb_prep;
-ev_check *xcb_chk;
ev_io *xcb_io;
ev_io *xkb_io;
}
/*
- * This function is called immediately before the main loop locks. We flush xcb
- * then (and only then)
+ * This function is called immediately before the main loop locks. We check for
+ * events from X11, handle them, then flush our outgoing queue.
*
*/
void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
- xcb_flush(xcb_connection);
-}
-
-/*
- * This function is called immediately after the main loop locks, so when one
- * of the watchers registered an event.
- * We check whether an X-Event arrived and handle it.
- *
- */
-void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
xcb_generic_event_t *event;
if (xcb_connection_has_error(xcb_connection)) {
}
free(event);
}
+
+ xcb_flush(xcb_connection);
}
/*
/* The various watchers to communicate with xcb */
xcb_io = smalloc(sizeof(ev_io));
xcb_prep = smalloc(sizeof(ev_prepare));
- xcb_chk = smalloc(sizeof(ev_check));
ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
ev_prepare_init(xcb_prep, &xcb_prep_cb);
- ev_check_init(xcb_chk, &xcb_chk_cb);
-
- /* Within an event loop iteration, run the xcb_chk watcher last: other
- * watchers might call xcb_flush(), which, unexpectedly, can also read
- * events into the queue (see _xcb_conn_wait). Hence, we need to drain xcb’s
- * queue last, otherwise we risk dead-locking. */
- ev_set_priority(xcb_chk, EV_MINPRI);
ev_io_start(main_loop, xcb_io);
ev_prepare_start(main_loop, xcb_prep);
- ev_check_start(main_loop, xcb_chk);
/* Now we get the atoms and save them in a nice data structure */
get_atoms();
xcb_aux_sync(xcb_connection);
xcb_disconnect(xcb_connection);
- ev_check_stop(main_loop, xcb_chk);
ev_prepare_stop(main_loop, xcb_prep);
ev_io_stop(main_loop, xcb_io);
- FREE(xcb_chk);
FREE(xcb_prep);
FREE(xcb_io);
}
extern xcb_window_t root;
extern struct ev_loop *main_loop;
extern bool only_check_config;
+extern bool force_xinerama;
/* Custom data structure used to track dragging-related events. */
struct drag_x11_cb {
- ev_check check;
+ ev_prepare prepare;
/* Whether this modal event loop should be exited and with which result. */
drag_result_t result;
const void *extra;
};
-static void xcb_drag_check_cb(EV_P_ ev_check *w, int revents) {
+static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) {
struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w->data;
xcb_motion_notify_event_t *last_motion_notify = NULL;
xcb_generic_event_t *event;
dragloop->extra);
}
free(last_motion_notify);
+
+ xcb_flush(conn);
}
/*
.callback = callback,
.extra = extra,
};
- ev_check *check = &loop.check;
+ ev_prepare *prepare = &loop.prepare;
if (con)
loop.old_rect = con->rect;
- ev_check_init(check, xcb_drag_check_cb);
- check->data = &loop;
+ ev_prepare_init(prepare, xcb_drag_prepare_cb);
+ prepare->data = &loop;
main_set_x11_cb(false);
- ev_check_start(main_loop, check);
+ ev_prepare_start(main_loop, prepare);
while (loop.result == DRAGGING)
ev_run(main_loop, EVRUN_ONCE);
- ev_check_stop(main_loop, check);
+ ev_prepare_stop(main_loop, prepare);
main_set_x11_cb(true);
xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME);
scratchpad_show(con);
} else {
workspace_show(ws);
+ /* Re-set focus, even if unchanged from i3’s perspective. */
+ focused_id = XCB_NONE;
con_focus(con);
}
} else {
}
DLOG("ConfigureNotify for root window 0x%08x\n", event->event);
+ if (force_xinerama) {
+ return;
+ }
randr_query_outputs();
}
/** The number of file descriptors passed via socket activation. */
int listen_fds;
-/* We keep the xcb_check watcher around to be able to enable and disable it
+/* We keep the xcb_prepare watcher around to be able to enable and disable it
* temporarily for drag_pointer(). */
-static struct ev_check *xcb_check;
+static struct ev_prepare *xcb_prepare;
extern Con *focused;
bool xcursor_supported = true;
bool xkb_supported = true;
+bool force_xinerama = false;
+
/*
- * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
+ * This callback is only a dummy, see xcb_prepare_cb.
* See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
*
*/
static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
- /* empty, because xcb_prepare_cb and xcb_check_cb are used */
+ /* empty, because xcb_prepare_cb are used */
}
/*
- * Flush before blocking (and waiting for new events)
+ * Called just before the event loop sleeps. Ensures xcb’s incoming and outgoing
+ * queues are empty so that any activity will trigger another event loop
+ * iteration, and hence another xcb_prepare_cb invocation.
*
*/
static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
- xcb_flush(conn);
-}
-
-/*
- * Instead of polling the X connection socket we leave this to
- * xcb_poll_for_event() which knows better than we can ever know.
- *
- */
-static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
+ /* Process all queued (and possibly new) events before the event loop
+ sleeps. */
xcb_generic_event_t *event;
while ((event = xcb_poll_for_event(conn)) != NULL) {
free(event);
}
+
+ /* Flush all queued events to X11. */
+ xcb_flush(conn);
}
/*
void main_set_x11_cb(bool enable) {
DLOG("Setting main X11 callback to enabled=%d\n", enable);
if (enable) {
- ev_check_start(main_loop, xcb_check);
+ ev_prepare_start(main_loop, xcb_prepare);
/* Trigger the watcher explicitly to handle all remaining X11 events.
* drag_pointer()’s event handler exits in the middle of the loop. */
- ev_feed_event(main_loop, xcb_check, 0);
+ ev_feed_event(main_loop, xcb_prepare, 0);
} else {
- ev_check_stop(main_loop, xcb_check);
+ ev_prepare_stop(main_loop, xcb_prepare);
}
}
bool autostart = true;
char *layout_path = NULL;
bool delete_layout_path = false;
- bool force_xinerama = false;
bool disable_randr15 = false;
char *fake_outputs = NULL;
bool disable_signalhandler = false;
config.ipc_socket_path = sstrdup(config.ipc_socket_path);
}
+ if (config.force_xinerama) {
+ force_xinerama = true;
+ }
+
xcb_void_cookie_t cookie;
cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
fake_outputs_init(fake_outputs);
FREE(fake_outputs);
config.fake_outputs = NULL;
- } else if (force_xinerama || config.force_xinerama) {
+ } else if (force_xinerama) {
/* Force Xinerama (for drivers which don't support RandR yet, esp. the
* nVidia binary graphics driver), when specified either in the config
* file or on command-line */
ewmh_update_desktop_viewport();
struct ev_io *xcb_watcher = scalloc(1, sizeof(struct ev_io));
- xcb_check = scalloc(1, sizeof(struct ev_check));
- struct ev_prepare *xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
+ xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
ev_io_start(main_loop, xcb_watcher);
- ev_check_init(xcb_check, xcb_check_cb);
- ev_check_start(main_loop, xcb_check);
-
ev_prepare_init(xcb_prepare, xcb_prepare_cb);
ev_prepare_start(main_loop, xcb_prepare);
static xcb_connection_t *restore_conn;
static struct ev_io *xcb_watcher;
-static struct ev_check *xcb_check;
static struct ev_prepare *xcb_prepare;
static void restore_handle_event(int type, xcb_generic_event_t *event);
}
static void restore_xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
- xcb_flush(restore_conn);
-}
-
-static void restore_xcb_check_cb(EV_P_ ev_check *w, int revents) {
xcb_generic_event_t *event;
if (xcb_connection_has_error(restore_conn)) {
free(event);
}
+
+ xcb_flush(restore_conn);
}
/*
/* This is not the initial connect, but a reconnect, most likely
* because our X11 connection was killed (e.g. by a user with xkill. */
ev_io_stop(main_loop, xcb_watcher);
- ev_check_stop(main_loop, xcb_check);
ev_prepare_stop(main_loop, xcb_prepare);
placeholder_state *state;
*/
xcb_disconnect(restore_conn);
free(xcb_watcher);
- free(xcb_check);
free(xcb_prepare);
}
}
xcb_watcher = scalloc(1, sizeof(struct ev_io));
- xcb_check = scalloc(1, sizeof(struct ev_check));
xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, restore_xcb_got_event, xcb_get_file_descriptor(restore_conn), EV_READ);
ev_io_start(main_loop, xcb_watcher);
- ev_check_init(xcb_check, restore_xcb_check_cb);
- ev_check_start(main_loop, xcb_check);
-
ev_prepare_init(xcb_prepare, restore_xcb_prepare_cb);
ev_prepare_start(main_loop, xcb_prepare);
}
my ($class, %args) = @_;
my $pkg = caller;
+ $x ||= i3test::X11->new;
+ # set the pointer to a predictable position in case a previous test has
+ # disturbed it
+ $x->warp_pointer(
+ 0, # src_window (None)
+ $x->get_root_window(), # dst_window (None)
+ 0, # src_x
+ 0, # src_y
+ 0, # src_width
+ 0, # src_height
+ 0, # dst_x
+ 0); # dst_y
+ # Synchronize with X11 to ensure the pointer has been warped before i3
+ # starts up.
+ $x->get_input_focus_reply($x->get_input_focus()->{sequence});
+
$i3_autostart = delete($args{i3_autostart}) // 1;
my $i3_config = delete($args{i3_config}) // '-default';
strict->import;
warnings->import;
- $x ||= i3test::X11->new;
- # set the pointer to a predictable position in case a previous test has
- # disturbed it
- $x->root->warp_pointer(0, 0);
$cv->recv if $i3_autostart;
@_ = ($class);
sub wait_for_event {
my ($timeout, $cb) = @_;
- my $cv = AE::cv;
-
$x->flush;
- # unfortunately, there is no constant for this
- my $ae_read = 0;
-
- my $guard = AE::io $x->get_file_descriptor, $ae_read, sub {
- while (defined(my $event = $x->poll_for_event)) {
- if ($cb->($event)) {
- $cv->send(1);
- last;
- }
- }
- };
-
- # Trigger timeout after $timeout seconds (can be fractional)
- my $t = AE::timer $timeout, 0, sub { warn "timeout ($timeout secs)"; $cv->send(0) };
-
- my $result = $cv->recv;
- undef $t;
- undef $guard;
- return $result;
+ while (defined(my $event = $x->wait_for_event)) {
+ return 1 if $cb->($event);
+ }
}
=head2 wait_for_map($window)
$window->map;
wait_for_map($window);
+
+ # MapWindow is sent before i3 even starts rendering: the window is placed at
+ # temporary off-screen coordinates first, and x_push_changes() sends further
+ # X11 requests to set focus etc. Hence, we sync with i3 before continuing.
+ sync_with_i3();
+
return $window;
}
$_sync_window = open_window(
rect => [ -15, -15, 10, 10 ],
override_redirect => 1,
+ dont_map => 1,
);
}
is($events[0]->{container}->{name}, $name, "$name focused");
}
-subtest 'focus left (1)', \&focus_subtest, 'focus left', 'Window 1';
-subtest 'focus left (2)', \&focus_subtest, 'focus left', 'Window 0';
-subtest 'focus right (1)', \&focus_subtest, 'focus right', 'Window 1';
-subtest 'focus right (2)', \&focus_subtest, 'focus right', 'Window 2';
-subtest 'focus right (3)', \&focus_subtest, 'focus right', 'Window 0';
-subtest 'focus left', \&focus_subtest, 'focus left', 'Window 2';
+subtest 'focus left (1)', \&focus_subtest, 'focus left', $win1->name;
+subtest 'focus left (2)', \&focus_subtest, 'focus left', $win0->name;
+subtest 'focus right (1)', \&focus_subtest, 'focus right', $win1->name;
+subtest 'focus right (2)', \&focus_subtest, 'focus right', $win2->name;
+subtest 'focus right (3)', \&focus_subtest, 'focus right', $win0->name;
+subtest 'focus left', \&focus_subtest, 'focus left', $win2->name;
done_testing;
my $left = open_window;
my $right = open_window;
sync_with_i3;
-my $con = $cv->recv;
-is($con->{window}, $right->{id}, 'focus is initially on the right container');
sub focus_subtest {
my ($subscribecb, $want, $msg) = @_;