Merge pull request #3214 from stapelberg/sync

unflake t/525-i3bar-mouse-bindings.t
This commit is contained in:
Ingo Bürk 2018-03-30 21:33:03 +02:00 committed by GitHub
commit 8a805cdd5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 173 additions and 48 deletions

View File

@ -100,11 +100,12 @@ use constant TYPE_GET_VERSION => 7;
use constant TYPE_GET_BINDING_MODES => 8; use constant TYPE_GET_BINDING_MODES => 8;
use constant TYPE_GET_CONFIG => 9; use constant TYPE_GET_CONFIG => 9;
use constant TYPE_SEND_TICK => 10; use constant TYPE_SEND_TICK => 10;
use constant TYPE_SYNC => 11;
our %EXPORT_TAGS = ( 'all' => [ our %EXPORT_TAGS = ( 'all' => [
qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS 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_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} } ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
@ -534,6 +535,19 @@ sub send_tick {
$self->message(TYPE_SEND_TICK, $payload); $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) =head2 command($content)
Makes i3 execute the given command Makes i3 execute the given command

View File

@ -562,6 +562,7 @@ i3_SOURCES = \
src/sd-daemon.c \ src/sd-daemon.c \
src/sighandler.c \ src/sighandler.c \
src/startup.c \ src/startup.c \
src/sync.c \
src/tree.c \ src/tree.c \
src/util.c \ src/util.c \
src/version.c \ src/version.c \

View File

@ -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. | 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. | 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. | 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: 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 } { "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
[[events]] [[events]]

View File

@ -114,13 +114,18 @@ void got_bar_config(char *reply) {
/* Data structure to easily call the reply handlers later */ /* Data structure to easily call the reply handlers later */
handler_t reply_handlers[] = { handler_t reply_handlers[] = {
&got_command_reply, &got_command_reply, /* I3_IPC_REPLY_TYPE_COMMAND */
&got_workspace_reply, &got_workspace_reply, /* I3_IPC_REPLY_TYPE_WORKSPACES */
&got_subscribe_reply, &got_subscribe_reply, /* I3_IPC_REPLY_TYPE_SUBSCRIBE */
&got_output_reply, &got_output_reply, /* I3_IPC_REPLY_TYPE_OUTPUTS */
NULL, NULL, /* I3_IPC_REPLY_TYPE_TREE */
NULL, NULL, /* I3_IPC_REPLY_TYPE_MARKS */
&got_bar_config, &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 */
}; };
/* /*

View File

@ -694,21 +694,12 @@ static void handle_client_message(xcb_client_message_event_t *event) {
if (event->type == atoms[I3_SYNC]) { if (event->type == atoms[I3_SYNC]) {
xcb_window_t window = event->data.data32[0]; xcb_window_t window = event->data.data32[0];
uint32_t rnd = event->data.data32[1]; uint32_t rnd = event->data.data32[1];
DLOG("[i3 sync protocol] Forwarding random value %d, X11 window 0x%08x to i3\n", rnd, window); /* Forward the request to i3 via the IPC interface so that all pending
* IPC messages are guaranteed to be handled. */
void *reply = scalloc(32, 1); char *payload = NULL;
xcb_client_message_event_t *ev = reply; sasprintf(&payload, "{\"rnd\":%d, \"window\":%d}", rnd, window);
i3_send_msg(I3_IPC_MESSAGE_TYPE_SYNC, payload);
ev->response_type = XCB_CLIENT_MESSAGE; free(payload);
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);
} else if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] && } else if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] &&
event->format == 32) { event->format == 32) {
DLOG("_NET_SYSTEM_TRAY_OPCODE received\n"); DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");

View File

@ -82,4 +82,5 @@
#include "fake_outputs.h" #include "fake_outputs.h"
#include "display_version.h" #include "display_version.h"
#include "restore_layout.h" #include "restore_layout.h"
#include "sync.h"
#include "main.h" #include "main.h"

View File

@ -63,6 +63,9 @@ typedef struct i3_ipc_header {
/** Send a tick event to all subscribers. */ /** Send a tick event to all subscribers. */
#define I3_IPC_MESSAGE_TYPE_SEND_TICK 10 #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 * 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_BINDING_MODES 8
#define I3_IPC_REPLY_TYPE_CONFIG 9 #define I3_IPC_REPLY_TYPE_CONFIG 9
#define I3_IPC_REPLY_TYPE_TICK 10 #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. * Events from i3 to clients. Events have the first bit set high.

14
include/sync.h Normal file
View File

@ -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);

View File

@ -800,21 +800,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
} else if (event->type == A_I3_SYNC) { } else if (event->type == A_I3_SYNC) {
xcb_window_t window = event->data.data32[0]; xcb_window_t window = event->data.data32[0];
uint32_t rnd = event->data.data32[1]; uint32_t rnd = event->data.data32[1];
DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window); sync_respond(window, rnd);
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);
} else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) { } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) {
/* /*
* A client can request an estimate for the frame size which the window * A client can request an estimate for the frame size which the window

View File

@ -1173,9 +1173,68 @@ IPC_HANDLER(send_tick) {
DLOG("Sent tick event\n"); 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 /* The index of each callback function corresponds to the numeric
* value of the message type (see include/i3/ipc.h) */ * value of the message type (see include/i3/ipc.h) */
handler_t handlers[11] = { handler_t handlers[12] = {
handle_run_command, handle_run_command,
handle_get_workspaces, handle_get_workspaces,
handle_subscribe, handle_subscribe,
@ -1187,6 +1246,7 @@ handler_t handlers[11] = {
handle_get_binding_modes, handle_get_binding_modes,
handle_get_config, handle_get_config,
handle_send_tick, handle_send_tick,
handle_sync,
}; };
/* /*

28
src/sync.c Normal file
View File

@ -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);
}

View File

@ -100,11 +100,19 @@ sub focus_subtest {
is_deeply(\@focus, $want, $msg); 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, subtest 'button 1 moves focus left', \&focus_subtest,
sub { sub {
xtest_button_press(1, 3, 3); xtest_button_press(1, 3, 3);
xtest_button_release(1, 3, 3); xtest_button_release(1, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $left->{id} ], [ $left->{id} ],
'button 1 moves focus left'; 'button 1 moves focus left';
@ -113,7 +121,7 @@ subtest 'button 2 moves focus right', \&focus_subtest,
sub { sub {
xtest_button_press(2, 3, 3); xtest_button_press(2, 3, 3);
xtest_button_release(2, 3, 3); xtest_button_release(2, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $right->{id} ], [ $right->{id} ],
'button 2 moves focus right'; 'button 2 moves focus right';
@ -122,7 +130,7 @@ subtest 'button 3 moves focus left', \&focus_subtest,
sub { sub {
xtest_button_press(3, 3, 3); xtest_button_press(3, 3, 3);
xtest_button_release(3, 3, 3); xtest_button_release(3, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $left->{id} ], [ $left->{id} ],
'button 3 moves focus left'; 'button 3 moves focus left';
@ -131,7 +139,7 @@ subtest 'button 4 moves focus right', \&focus_subtest,
sub { sub {
xtest_button_press(4, 3, 3); xtest_button_press(4, 3, 3);
xtest_button_release(4, 3, 3); xtest_button_release(4, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $right->{id} ], [ $right->{id} ],
'button 4 moves focus right'; 'button 4 moves focus right';
@ -140,7 +148,7 @@ subtest 'button 5 moves focus left', \&focus_subtest,
sub { sub {
xtest_button_press(5, 3, 3); xtest_button_press(5, 3, 3);
xtest_button_release(5, 3, 3); xtest_button_release(5, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $left->{id} ], [ $left->{id} ],
'button 5 moves focus left'; '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, subtest 'button 6 does not move focus while pressed', \&focus_subtest,
sub { sub {
xtest_button_press(6, 3, 3); xtest_button_press(6, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[], [],
'button 6 does not move focus while pressed'; '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, subtest 'button 6 release moves focus right', \&focus_subtest,
sub { sub {
xtest_button_release(6, 3, 3); xtest_button_release(6, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $right->{id} ], [ $right->{id} ],
'button 6 release moves focus right'; '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, subtest 'button 7 press moves focus left', \&focus_subtest,
sub { sub {
xtest_button_press(7, 3, 3); xtest_button_press(7, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $left->{id} ], [ $left->{id} ],
'button 7 press moves focus left'; '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, subtest 'button 7 release moves focus right', \&focus_subtest,
sub { sub {
xtest_button_release(7, 3, 3); xtest_button_release(7, 3, 3);
xtest_sync_with($i3bar_window); sync;
}, },
[ $right->{id} ], [ $right->{id} ],
'button 7 release moves focus right'; 'button 7 release moves focus right';