diff --git a/testcases/lib/i3test/XTEST.pm b/testcases/lib/i3test/XTEST.pm index 1ca964b154..40759cfdb1 100644 --- a/testcases/lib/i3test/XTEST.pm +++ b/testcases/lib/i3test/XTEST.pm @@ -14,6 +14,7 @@ use ExtUtils::PkgConfig; use Exporter (); our @EXPORT = qw( inlinec_connect + xtest_sync_with_i3 set_xkb_group xtest_key_press xtest_key_release @@ -38,7 +39,7 @@ i3test::XTEST - Inline::C wrappers for xcb-xtest and xcb-xkb # ineffective. my %sn_config; BEGIN { - %sn_config = ExtUtils::PkgConfig->find('xcb-xkb xcb-xtest'); + %sn_config = ExtUtils::PkgConfig->find('xcb-xkb xcb-xtest xcb-util'); } use Inline C => Config => LIBS => $sn_config{libs}, CCFLAGS => $sn_config{cflags}; @@ -53,8 +54,12 @@ use Inline C => <<'END_OF_C_CODE'; #include #include #include +#include static xcb_connection_t *conn = NULL; +static xcb_window_t sync_window; +static xcb_window_t root_window; +static xcb_atom_t i3_sync_atom; bool inlinec_connect() { int screen; @@ -89,9 +94,90 @@ bool inlinec_connect() { } free(usereply); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, xcb_intern_atom(conn, 0, strlen("I3_SYNC"), "I3_SYNC"), NULL); + i3_sync_atom = reply->atom; + free(reply); + + xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen); + root_window = root_screen->root; + sync_window = xcb_generate_id(conn); + xcb_create_window(conn, + XCB_COPY_FROM_PARENT, // depth + sync_window, // window + root_window, // parent + -15, // x + -15, // y + 1, // width + 1, // height + 0, // border_width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // class + XCB_COPY_FROM_PARENT, // visual + XCB_CW_OVERRIDE_REDIRECT, // value_mask + (uint32_t[]){ + 1, // override_redirect + }); // value_list + return true; } +void xtest_sync_with_i3() { + xcb_client_message_event_t ev; + memset(&ev, '\0', sizeof(xcb_client_message_event_t)); + + const int nonce = rand() % 255; + + ev.response_type = XCB_CLIENT_MESSAGE; + ev.window = sync_window; + ev.type = i3_sync_atom; + ev.format = 32; + ev.data.data32[0] = sync_window; + ev.data.data32[1] = nonce; + + xcb_send_event(conn, false, root_window, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)&ev); + xcb_flush(conn); + + xcb_generic_event_t *event = NULL; + while (1) { + free(event); + if ((event = xcb_wait_for_event(conn)) == NULL) { + break; + } + if (event->response_type == 0) { + fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence); + continue; + } + + /* Strip off the highest bit (set if the event is generated) */ + const int type = (event->response_type & 0x7F); + switch (type) { + case XCB_CLIENT_MESSAGE: { + xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event; + { + const uint32_t got = ev->data.data32[0]; + const uint32_t want = sync_window; + if (got != want) { + fprintf(stderr, "Ignoring ClientMessage: unknown window: got %d, want %d\n", got, want); + continue; + } + } + { + const uint32_t got = ev->data.data32[1]; + const uint32_t want = nonce; + if (got != want) { + fprintf(stderr, "Ignoring ClientMessage: unknown nonce: got %d, want %d\n", got, want); + continue; + } + } + return; + } + default: + fprintf(stderr, "Unexpected X11 event of type %d received (XCB_CLIENT_MESSAGE = %d)\n", type, XCB_CLIENT_MESSAGE); + break; + } + } + free(event); +} + // NOTE: while |group| should be a uint8_t, Inline::C will not define the // function unless we use an int. bool set_xkb_group(int group) { @@ -283,6 +369,10 @@ Sends a ButtonRelease event via XTEST, with the specified C<$button>. Returns false when there was an X11 error, true otherwise. +=head2 xtest_sync_with_i3() + +Ensures i3 has processed all X11 events which were triggered by this module. + =head1 AUTHOR Michael Stapelberg diff --git a/testcases/t/257-keypress-group1-fallback.t b/testcases/t/257-keypress-group1-fallback.t index f9166fadbc..e3874c92b2 100644 --- a/testcases/t/257-keypress-group1-fallback.t +++ b/testcases/t/257-keypress-group1-fallback.t @@ -44,6 +44,7 @@ is(listen_for_binding( sub { xtest_key_press(107); xtest_key_release(107); + xtest_sync_with_i3; }, ), 'Print', @@ -55,6 +56,7 @@ is(listen_for_binding( xtest_key_press(36); # Return xtest_key_release(36); # Return xtest_key_release(133); # Super_L + xtest_sync_with_i3; }, ), 'Mod4+Return', @@ -67,6 +69,7 @@ is(listen_for_binding( sub { xtest_key_press(107); xtest_key_release(107); + xtest_sync_with_i3; }, ), 'Print', @@ -78,6 +81,7 @@ is(listen_for_binding( xtest_key_press(36); # Return xtest_key_release(36); # Return xtest_key_release(133); # Super_L + xtest_sync_with_i3; }, ), 'Mod4+Return', diff --git a/testcases/t/258-keypress-release.t b/testcases/t/258-keypress-release.t index 72bfc86281..bf446003b3 100644 --- a/testcases/t/258-keypress-release.t +++ b/testcases/t/258-keypress-release.t @@ -43,6 +43,7 @@ is(listen_for_binding( sub { xtest_key_press(107); # Print xtest_key_release(107); # Print + xtest_sync_with_i3; }, ), 'Print', @@ -54,6 +55,7 @@ is(listen_for_binding( xtest_key_press(107); # Print xtest_key_release(107); # Print xtest_key_release(37); # Control_L + xtest_sync_with_i3; }, ), 'Control+Print', @@ -65,6 +67,7 @@ is(listen_for_binding( xtest_key_press(56); # b xtest_key_release(56); # b xtest_key_release(64); # Alt_L + xtest_sync_with_i3; }, ), 'Mod1+b', @@ -78,6 +81,7 @@ is(listen_for_binding( xtest_key_release(56); # b xtest_key_release(50); # Shift_L xtest_key_release(64); # Alt_L + xtest_sync_with_i3; }, ), 'Mod1+Shift+b release', diff --git a/testcases/t/286-root-window-mouse-binding.t b/testcases/t/286-root-window-mouse-binding.t index 6c1b5d6ace..0da373b74e 100644 --- a/testcases/t/286-root-window-mouse-binding.t +++ b/testcases/t/286-root-window-mouse-binding.t @@ -30,7 +30,7 @@ fresh_workspace; xtest_button_press(4, 50, 50); xtest_button_release(4, 50, 50); -sync_with_i3; +xtest_sync_with_i3; is(focused_ws(), 'special', 'the binding was triggered'); diff --git a/testcases/t/290-keypress-numlock.t b/testcases/t/290-keypress-numlock.t index 5137c35fc8..b896e4320f 100644 --- a/testcases/t/290-keypress-numlock.t +++ b/testcases/t/290-keypress-numlock.t @@ -57,6 +57,7 @@ is(listen_for_binding( sub { xtest_key_press(87); # KP_End xtest_key_release(87); # KP_End + xtest_sync_with_i3; }, ), 'KP_End', @@ -70,6 +71,7 @@ is(listen_for_binding( xtest_key_release(87); # KP_1 xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'KP_1', @@ -81,6 +83,7 @@ is(listen_for_binding( xtest_key_press(38); # a xtest_key_release(38); # a xtest_key_release(133); # Super_L + xtest_sync_with_i3; }, ), 'a', @@ -96,6 +99,7 @@ is(listen_for_binding( xtest_key_release(133); # Super_L xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'a', @@ -105,6 +109,7 @@ is(listen_for_binding( sub { xtest_key_press(9); # Escape xtest_key_release(9); # Escape + xtest_sync_with_i3; }, ), 'Escape', @@ -118,6 +123,7 @@ is(listen_for_binding( xtest_key_release(9); # Escape xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'Escape', @@ -129,6 +135,7 @@ is(listen_for_binding( xtest_key_press(9); # Escape xtest_key_release(9); # Escape xtest_key_release(50); # Shift_L + xtest_sync_with_i3; }, ), 'Shift+Escape', @@ -144,6 +151,7 @@ is(listen_for_binding( xtest_key_release(50); # Shift_L xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'Shift+Escape', @@ -157,6 +165,7 @@ is(listen_for_binding( xtest_key_release(24); # q xtest_key_release(64); # Alt_L xtest_key_release(50); # Shift_L + xtest_sync_with_i3; }, ), 'Mod1+Shift+q', @@ -174,6 +183,7 @@ is(listen_for_binding( xtest_key_release(50); # Shift_L xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'Mod1+Shift+q', @@ -183,6 +193,7 @@ is(listen_for_binding( sub { xtest_key_press(39); # s xtest_key_release(39); # s + xtest_sync_with_i3; }, ), 's', @@ -196,6 +207,7 @@ is(listen_for_binding( xtest_key_release(39); # s xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 's', @@ -228,6 +240,7 @@ is(listen_for_binding( sub { xtest_key_press(133); # Super_L xtest_key_release(133); # Super_L + xtest_sync_with_i3; }, ), 'Super_L', @@ -241,6 +254,7 @@ is(listen_for_binding( xtest_key_release(133); # Super_L xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'Super_L', @@ -252,6 +266,7 @@ is(listen_for_binding( xtest_key_press(36); # Return xtest_key_release(36); # Return xtest_key_release(133); # Super_L + xtest_sync_with_i3; }, ), 'Return', @@ -267,6 +282,7 @@ is(listen_for_binding( xtest_key_release(133); # Super_L xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'Return', @@ -297,6 +313,7 @@ is(listen_for_binding( sub { xtest_key_press(87); # KP_End xtest_key_release(87); # KP_End + xtest_sync_with_i3; }, ), 'KP_End', @@ -306,6 +323,7 @@ is(listen_for_binding( sub { xtest_key_press(88); # KP_Down xtest_key_release(88); # KP_Down + xtest_sync_with_i3; }, ), 'KP_Down', @@ -319,6 +337,7 @@ is(listen_for_binding( xtest_key_release(87); # KP_1 xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'timeout', @@ -332,6 +351,7 @@ is(listen_for_binding( xtest_key_release(88); # KP_2 xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'timeout', @@ -369,6 +389,7 @@ is(listen_for_binding( xtest_button_release(4, 50, 50); xtest_key_press(77); # disable Num_Lock xtest_key_release(77); # disable Num_Lock + xtest_sync_with_i3; }, ), 'button4', @@ -376,8 +397,9 @@ is(listen_for_binding( is(listen_for_binding( sub { - xtest_button_press(4, 50, 50); - xtest_button_release(4, 50, 50); + xtest_button_press(4, 50, 50); + xtest_button_release(4, 50, 50); + xtest_sync_with_i3; }, ), 'button4',