Merge pull request #3214 from stapelberg/sync
unflake t/525-i3bar-mouse-bindings.t
This commit is contained in:
commit
8a805cdd5c
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
13
docs/ipc
13
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]]
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -82,4 +82,5 @@
|
|||
#include "fake_outputs.h"
|
||||
#include "display_version.h"
|
||||
#include "restore_layout.h"
|
||||
#include "sync.h"
|
||||
#include "main.h"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
62
src/ipc.c
62
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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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';
|
||||
|
|
Loading…
Reference in New Issue