]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #3214 from stapelberg/sync
authorIngo Bürk <admin@airblader.de>
Fri, 30 Mar 2018 19:33:03 +0000 (21:33 +0200)
committerGitHub <noreply@github.com>
Fri, 30 Mar 2018 19:33:03 +0000 (21:33 +0200)
unflake t/525-i3bar-mouse-bindings.t

12 files changed:
AnyEvent-I3/lib/AnyEvent/I3.pm
Makefile.am
docs/ipc
i3bar/src/ipc.c
i3bar/src/xcb.c
include/all.h
include/i3/ipc.h
include/sync.h [new file with mode: 0644]
src/handlers.c
src/ipc.c
src/sync.c [new file with mode: 0644]
testcases/t/525-i3bar-mouse-bindings.t

index 198c41c9a5eef1164a128150ec26496783134e69..ae9e5bea28af297a1613ec967b2fdea1b752676e 100644 (file)
@@ -100,11 +100,12 @@ use constant TYPE_GET_VERSION => 7;
 use constant TYPE_GET_BINDING_MODES => 8;
 use constant TYPE_GET_CONFIG => 9;
 use constant TYPE_SEND_TICK => 10;
+use constant TYPE_SYNC => 11;
 
 our %EXPORT_TAGS = ( 'all' => [
     qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
        TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
-       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK)
+       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC)
 ] );
 
 our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
@@ -534,6 +535,19 @@ sub send_tick {
     $self->message(TYPE_SEND_TICK, $payload);
 }
 
+=head2 sync
+
+Sends an i3 sync event. Requires i3 >= 4.16
+
+=cut
+sub sync {
+    my ($self, $payload) = @_;
+
+    $self->_ensure_connection;
+
+    $self->message(TYPE_SYNC, $payload);
+}
+
 =head2 command($content)
 
 Makes i3 execute the given command
index 184b07343f8cc0b143c1eeaa8feba3d969b1b74c..557c65a58fe9e4506c8529c8d549f3a033a78f58 100644 (file)
@@ -562,6 +562,7 @@ i3_SOURCES = \
        src/sd-daemon.c \
        src/sighandler.c \
        src/startup.c \
+       src/sync.c \
        src/tree.c \
        src/util.c \
        src/version.c \
index 8b767adebffa127bab432c2c9607b57ce491567b..f011e773c42b9b4a1218061df1ee6c9b2e5311ca 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -65,6 +65,7 @@ to do that).
 | 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes.
 | 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config.
 | 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload.
+| 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window.
 |======================================================
 
 So, a typical message could look like this:
@@ -654,6 +655,18 @@ events generated prior to the +SEND_TICK+ message (happened-before relation).
 { "success": true }
 -------------------
 
+[[_sync_reply]]
+=== SYNC reply
+
+The reply is a map containing the "success" member. After the reply was
+received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was
+responded to.
+
+*Example:*
+-------------------
+{ "success": true }
+-------------------
+
 == Events
 
 [[events]]
index cc3563ec0ad0039cc4867f79759c55e16a03e504..7a657338472e7fd6941e38a82b3e7eaf78e1654f 100644 (file)
@@ -114,13 +114,18 @@ void got_bar_config(char *reply) {
 
 /* Data structure to easily call the reply handlers later */
 handler_t reply_handlers[] = {
-    &got_command_reply,
-    &got_workspace_reply,
-    &got_subscribe_reply,
-    &got_output_reply,
-    NULL,
-    NULL,
-    &got_bar_config,
+    &got_command_reply,   /* I3_IPC_REPLY_TYPE_COMMAND */
+    &got_workspace_reply, /* I3_IPC_REPLY_TYPE_WORKSPACES */
+    &got_subscribe_reply, /* I3_IPC_REPLY_TYPE_SUBSCRIBE */
+    &got_output_reply,    /* I3_IPC_REPLY_TYPE_OUTPUTS */
+    NULL,                 /* I3_IPC_REPLY_TYPE_TREE */
+    NULL,                 /* I3_IPC_REPLY_TYPE_MARKS */
+    &got_bar_config,      /* I3_IPC_REPLY_TYPE_BAR_CONFIG */
+    NULL,                 /* I3_IPC_REPLY_TYPE_VERSION */
+    NULL,                 /* I3_IPC_REPLY_TYPE_BINDING_MODES */
+    NULL,                 /* I3_IPC_REPLY_TYPE_CONFIG */
+    NULL,                 /* I3_IPC_REPLY_TYPE_TICK */
+    NULL,                 /* I3_IPC_REPLY_TYPE_SYNC */
 };
 
 /*
index 542c86c3c645a4c117d15f22775d73fb900476f6..8843edbdc72e4532b8f6d1008be0018bfd9dd4a0 100644 (file)
@@ -694,21 +694,12 @@ static void handle_client_message(xcb_client_message_event_t *event) {
     if (event->type == atoms[I3_SYNC]) {
         xcb_window_t window = event->data.data32[0];
         uint32_t rnd = event->data.data32[1];
-        DLOG("[i3 sync protocol] Forwarding random value %d, X11 window 0x%08x to i3\n", rnd, window);
-
-        void *reply = scalloc(32, 1);
-        xcb_client_message_event_t *ev = reply;
-
-        ev->response_type = XCB_CLIENT_MESSAGE;
-        ev->window = window;
-        ev->type = atoms[I3_SYNC];
-        ev->format = 32;
-        ev->data.data32[0] = window;
-        ev->data.data32[1] = rnd;
-
-        xcb_send_event(conn, false, xcb_root, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)ev);
-        xcb_flush(conn);
-        free(reply);
+        /* Forward the request to i3 via the IPC interface so that all pending
+         * IPC messages are guaranteed to be handled. */
+        char *payload = NULL;
+        sasprintf(&payload, "{\"rnd\":%d, \"window\":%d}", rnd, window);
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_SYNC, payload);
+        free(payload);
     } else if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] &&
                event->format == 32) {
         DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");
index ecc875d08298f6b33b8b03340b84c8d1e33266a3..e93b066bc59f64058dce80ae77f7d775a61a8bc7 100644 (file)
@@ -82,4 +82,5 @@
 #include "fake_outputs.h"
 #include "display_version.h"
 #include "restore_layout.h"
+#include "sync.h"
 #include "main.h"
index 9e0280c9363322b48ef7f25534bfde2b9ccd9c4f..0c57f7fdf40c51541af0ca735b1e54ad63e7df4b 100644 (file)
@@ -63,6 +63,9 @@ typedef struct i3_ipc_header {
 /** Send a tick event to all subscribers. */
 #define I3_IPC_MESSAGE_TYPE_SEND_TICK 10
 
+/** Trigger an i3 sync protocol message via IPC. */
+#define I3_IPC_MESSAGE_TYPE_SYNC 11
+
 /*
  * Messages from i3 to clients
  *
@@ -78,6 +81,7 @@ typedef struct i3_ipc_header {
 #define I3_IPC_REPLY_TYPE_BINDING_MODES 8
 #define I3_IPC_REPLY_TYPE_CONFIG 9
 #define I3_IPC_REPLY_TYPE_TICK 10
+#define I3_IPC_REPLY_TYPE_SYNC 11
 
 /*
  * Events from i3 to clients. Events have the first bit set high.
diff --git a/include/sync.h b/include/sync.h
new file mode 100644 (file)
index 0000000..e726f99
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
+ *
+ */
+#pragma once
+
+#include <xcb/xcb.h>
+
+void sync_respond(xcb_window_t window, uint32_t rnd);
index 50fd85669223267e123a97ebe5bdfea09c9ab1e3..d5023b9d9eeddbc0d24ef119da9fafb0fa8568dd 100644 (file)
@@ -800,21 +800,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
     } else if (event->type == A_I3_SYNC) {
         xcb_window_t window = event->data.data32[0];
         uint32_t rnd = event->data.data32[1];
-        DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window);
-
-        void *reply = scalloc(32, 1);
-        xcb_client_message_event_t *ev = reply;
-
-        ev->response_type = XCB_CLIENT_MESSAGE;
-        ev->window = window;
-        ev->type = A_I3_SYNC;
-        ev->format = 32;
-        ev->data.data32[0] = window;
-        ev->data.data32[1] = rnd;
-
-        xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev);
-        xcb_flush(conn);
-        free(reply);
+        sync_respond(window, rnd);
     } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) {
         /*
          * A client can request an estimate for the frame size which the window
index 6b6383ec1ace2649bfc81546b2f8f79558d9e5da..d422217e69ba3b23281ab48e47f71fa29c3ddc48 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -1173,9 +1173,68 @@ IPC_HANDLER(send_tick) {
     DLOG("Sent tick event\n");
 }
 
+struct sync_state {
+    char *last_key;
+    uint32_t rnd;
+    xcb_window_t window;
+};
+
+static int _sync_json_key(void *extra, const unsigned char *val, size_t len) {
+    struct sync_state *state = extra;
+    FREE(state->last_key);
+    state->last_key = scalloc(len + 1, 1);
+    memcpy(state->last_key, val, len);
+    return 1;
+}
+
+static int _sync_json_int(void *extra, long long val) {
+    struct sync_state *state = extra;
+    if (strcasecmp(state->last_key, "rnd") == 0) {
+        state->rnd = val;
+    } else if (strcasecmp(state->last_key, "window") == 0) {
+        state->window = (xcb_window_t)val;
+    }
+    return 1;
+}
+
+IPC_HANDLER(sync) {
+    yajl_handle p;
+    yajl_status stat;
+
+    /* Setup the JSON parser */
+    static yajl_callbacks callbacks = {
+        .yajl_map_key = _sync_json_key,
+        .yajl_integer = _sync_json_int,
+    };
+
+    struct sync_state state;
+    memset(&state, '\0', sizeof(struct sync_state));
+    p = yalloc(&callbacks, (void *)&state);
+    stat = yajl_parse(p, (const unsigned char *)message, message_size);
+    FREE(state.last_key);
+    if (stat != yajl_status_ok) {
+        unsigned char *err;
+        err = yajl_get_error(p, true, (const unsigned char *)message,
+                             message_size);
+        ELOG("YAJL parse error: %s\n", err);
+        yajl_free_error(p, err);
+
+        const char *reply = "{\"success\":false}";
+        ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
+        yajl_free(p);
+        return;
+    }
+    yajl_free(p);
+
+    DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
+    sync_respond(state.window, state.rnd);
+    const char *reply = "{\"success\":true}";
+    ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
+}
+
 /* The index of each callback function corresponds to the numeric
  * value of the message type (see include/i3/ipc.h) */
-handler_t handlers[11] = {
+handler_t handlers[12] = {
     handle_run_command,
     handle_get_workspaces,
     handle_subscribe,
@@ -1187,6 +1246,7 @@ handler_t handlers[11] = {
     handle_get_binding_modes,
     handle_get_config,
     handle_send_tick,
+    handle_sync,
 };
 
 /*
diff --git a/src/sync.c b/src/sync.c
new file mode 100644 (file)
index 0000000..dafbc35
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
+ *
+ */
+#include "all.h"
+
+void sync_respond(xcb_window_t window, uint32_t rnd) {
+    DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window);
+
+    void *reply = scalloc(32, 1);
+    xcb_client_message_event_t *ev = reply;
+
+    ev->response_type = XCB_CLIENT_MESSAGE;
+    ev->window = window;
+    ev->type = A_I3_SYNC;
+    ev->format = 32;
+    ev->data.data32[0] = window;
+    ev->data.data32[1] = rnd;
+
+    xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev);
+    xcb_flush(conn);
+    free(reply);
+}
index 8755278508e0391563ee8f0ae96ab8db7ff12898..3593ea0b54cc70f72da6dc1b82336cd2dfe219cc 100644 (file)
@@ -100,11 +100,19 @@ sub focus_subtest {
     is_deeply(\@focus, $want, $msg);
 }
 
+sub sync {
+    # Ensure XTEST events were sent to i3, which grabs and hence needs to
+    # forward any events to i3bar:
+    xtest_sync_with_i3;
+    # Ensure any pending i3bar IPC messages were handled by i3:
+    xtest_sync_with($i3bar_window);
+}
+
 subtest 'button 1 moves focus left', \&focus_subtest,
     sub {
        xtest_button_press(1, 3, 3);
        xtest_button_release(1, 3, 3);
-       xtest_sync_with($i3bar_window);
+       sync;
     },
     [ $left->{id} ],
     'button 1 moves focus left';
@@ -113,7 +121,7 @@ subtest 'button 2 moves focus right', \&focus_subtest,
     sub {
        xtest_button_press(2, 3, 3);
        xtest_button_release(2, 3, 3);
-       xtest_sync_with($i3bar_window);
+       sync;
     },
     [ $right->{id} ],
     'button 2 moves focus right';
@@ -122,7 +130,7 @@ subtest 'button 3 moves focus left', \&focus_subtest,
     sub {
        xtest_button_press(3, 3, 3);
        xtest_button_release(3, 3, 3);
-       xtest_sync_with($i3bar_window);
+       sync;
     },
     [ $left->{id} ],
     'button 3 moves focus left';
@@ -131,7 +139,7 @@ subtest 'button 4 moves focus right', \&focus_subtest,
     sub {
        xtest_button_press(4, 3, 3);
        xtest_button_release(4, 3, 3);
-       xtest_sync_with($i3bar_window);
+       sync;
     },
     [ $right->{id} ],
     'button 4 moves focus right';
@@ -140,7 +148,7 @@ subtest 'button 5 moves focus left', \&focus_subtest,
     sub {
        xtest_button_press(5, 3, 3);
        xtest_button_release(5, 3, 3);
-       xtest_sync_with($i3bar_window);
+       sync;
     },
     [ $left->{id} ],
     'button 5 moves focus left';
@@ -152,7 +160,7 @@ my $old_focus = get_focused($ws);
 subtest 'button 6 does not move focus while pressed', \&focus_subtest,
     sub {
         xtest_button_press(6, 3, 3);
-        xtest_sync_with($i3bar_window);
+        sync;
     },
     [],
     'button 6 does not move focus while pressed';
@@ -161,7 +169,7 @@ is(get_focused($ws), $old_focus, 'focus unchanged');
 subtest 'button 6 release moves focus right', \&focus_subtest,
     sub {
         xtest_button_release(6, 3, 3);
-        xtest_sync_with($i3bar_window);
+        sync;
     },
     [ $right->{id} ],
     'button 6 release moves focus right';
@@ -171,7 +179,7 @@ subtest 'button 6 release moves focus right', \&focus_subtest,
 subtest 'button 7 press moves focus left', \&focus_subtest,
     sub {
         xtest_button_press(7, 3, 3);
-        xtest_sync_with($i3bar_window);
+        sync;
     },
     [ $left->{id} ],
     'button 7 press moves focus left';
@@ -179,7 +187,7 @@ subtest 'button 7 press moves focus left', \&focus_subtest,
 subtest 'button 7 release moves focus right', \&focus_subtest,
     sub {
         xtest_button_release(7, 3, 3);
-        xtest_sync_with($i3bar_window);
+        sync;
     },
     [ $right->{id} ],
     'button 7 release moves focus right';