Add the ipc shutdown event (#2652)
This event is triggered when the connection to the ipc is about to shutdown because of a user action such as with a `restart` or `exit` command. The `change` field indicates why the ipc is shutting down. It can be either "restart" or "exit". fixes #2318
This commit is contained in:
parent
564945bc14
commit
04dcf42397
16
docs/ipc
16
docs/ipc
|
@ -671,6 +671,8 @@ barconfig_update (4)::
|
|||
binding (5)::
|
||||
Sent when a configured command binding is triggered with the keyboard or
|
||||
mouse
|
||||
shutdown (6)::
|
||||
Sent when the ipc shuts down because of a restart or exit by user command
|
||||
|
||||
*Example:*
|
||||
--------------------------------------------------------------------
|
||||
|
@ -829,6 +831,20 @@ input_type (string)::
|
|||
}
|
||||
---------------------------
|
||||
|
||||
=== shutdown event
|
||||
|
||||
This event is triggered when the connection to the ipc is about to shutdown
|
||||
because of a user action such as a +restart+ or +exit+ command. The +change
|
||||
(string)+ field indicates why the ipc is shutting down. It can be either
|
||||
+"restart"+ or +"exit"+.
|
||||
|
||||
*Example:*
|
||||
---------------------------
|
||||
{
|
||||
"change": "restart"
|
||||
}
|
||||
---------------------------
|
||||
|
||||
== See also (existing libraries)
|
||||
|
||||
[[libraries]]
|
||||
|
|
|
@ -91,3 +91,6 @@ typedef struct i3_ipc_header {
|
|||
|
||||
/** The binding event will be triggered when bindings run */
|
||||
#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
|
||||
|
||||
/** The shutdown event will be triggered when the ipc shuts down */
|
||||
#define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)
|
||||
|
|
|
@ -77,11 +77,18 @@ int ipc_create_socket(const char *filename);
|
|||
void ipc_send_event(const char *event, uint32_t message_type, const char *payload);
|
||||
|
||||
/**
|
||||
* Calls shutdown() on each socket and closes it. This function to be called
|
||||
* when exiting or restarting only!
|
||||
* Calls to ipc_shutdown() should provide a reason for the shutdown.
|
||||
*/
|
||||
typedef enum {
|
||||
SHUTDOWN_REASON_RESTART,
|
||||
SHUTDOWN_REASON_EXIT
|
||||
} shutdown_reason_t;
|
||||
|
||||
/**
|
||||
* Calls shutdown() on each socket and closes it.
|
||||
*
|
||||
*/
|
||||
void ipc_shutdown(void);
|
||||
void ipc_shutdown(shutdown_reason_t reason);
|
||||
|
||||
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
||||
|
||||
|
|
|
@ -1562,7 +1562,7 @@ void cmd_exit(I3_CMD) {
|
|||
#ifdef I3_ASAN_ENABLED
|
||||
__lsan_do_leak_check();
|
||||
#endif
|
||||
ipc_shutdown();
|
||||
ipc_shutdown(SHUTDOWN_REASON_EXIT);
|
||||
unlink(config.ipc_socket_path);
|
||||
xcb_disconnect(conn);
|
||||
exit(0);
|
||||
|
@ -1595,7 +1595,7 @@ void cmd_reload(I3_CMD) {
|
|||
*/
|
||||
void cmd_restart(I3_CMD) {
|
||||
LOG("restarting i3\n");
|
||||
ipc_shutdown();
|
||||
ipc_shutdown(SHUTDOWN_REASON_RESTART);
|
||||
unlink(config.ipc_socket_path);
|
||||
/* We need to call this manually since atexit handlers don’t get called
|
||||
* when exec()ing */
|
||||
|
|
32
src/ipc.c
32
src/ipc.c
|
@ -62,11 +62,39 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
|
|||
}
|
||||
|
||||
/*
|
||||
* Calls shutdown() on each socket and closes it. This function to be called
|
||||
* For shutdown events, we send the reason for the shutdown.
|
||||
*/
|
||||
static void ipc_send_shutdown_event(shutdown_reason_t reason) {
|
||||
yajl_gen gen = ygenalloc();
|
||||
y(map_open);
|
||||
|
||||
ystr("change");
|
||||
|
||||
if (reason == SHUTDOWN_REASON_RESTART) {
|
||||
ystr("restart");
|
||||
} else if (reason == SHUTDOWN_REASON_EXIT) {
|
||||
ystr("exit");
|
||||
}
|
||||
|
||||
y(map_close);
|
||||
|
||||
const unsigned char *payload;
|
||||
ylength length;
|
||||
|
||||
y(get_buf, &payload, &length);
|
||||
ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
|
||||
|
||||
y(free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls shutdown() on each socket and closes it. This function is to be called
|
||||
* when exiting or restarting only!
|
||||
*
|
||||
*/
|
||||
void ipc_shutdown(void) {
|
||||
void ipc_shutdown(shutdown_reason_t reason) {
|
||||
ipc_send_shutdown_event(reason);
|
||||
|
||||
ipc_client *current;
|
||||
while (!TAILQ_EMPTY(&all_clients)) {
|
||||
current = TAILQ_FIRST(&all_clients);
|
||||
|
|
|
@ -259,7 +259,7 @@ void i3_restart(bool forget_layout) {
|
|||
|
||||
restore_geometry();
|
||||
|
||||
ipc_shutdown();
|
||||
ipc_shutdown(SHUTDOWN_REASON_RESTART);
|
||||
|
||||
LOG("restarting \"%s\"...\n", start_argv[0]);
|
||||
/* make sure -a is in the argument list or add it */
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
#
|
||||
# Please read the following documents before working on tests:
|
||||
# • http://build.i3wm.org/docs/testsuite.html
|
||||
# (or docs/testsuite)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/lib-i3test.html
|
||||
# (alternatively: perldoc ./testcases/lib/i3test.pm)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/ipc.html
|
||||
# (or docs/ipc)
|
||||
#
|
||||
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||
# (unless you are already familiar with Perl)
|
||||
#
|
||||
# Test the ipc shutdown event. This event is triggered when the connection to
|
||||
# the ipc is about to shutdown because of a user action such as with a
|
||||
# `restart` or `exit` command. The `change` field indicates why the ipc is
|
||||
# shutting down. It can be either "restart" or "exit".
|
||||
#
|
||||
# Ticket: #2318
|
||||
# Bug still in: 4.12-46-g2123888
|
||||
use i3test;
|
||||
|
||||
SKIP: {
|
||||
skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
|
||||
|
||||
my $i3 = i3(get_socket_path());
|
||||
$i3->connect->recv;
|
||||
|
||||
my $cv = AE::cv;
|
||||
my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
|
||||
|
||||
$i3->subscribe({
|
||||
shutdown => sub {
|
||||
$cv->send(shift);
|
||||
}
|
||||
})->recv;
|
||||
|
||||
cmd 'restart';
|
||||
|
||||
my $e = $cv->recv;
|
||||
|
||||
diag "Event:\n", Dumper($e);
|
||||
ok($e, 'the shutdown event should emit when the ipc is restarted by command');
|
||||
is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
|
||||
|
||||
# restarting kills the ipc client so we have to make a new one
|
||||
$i3 = i3(get_socket_path());
|
||||
$i3->connect->recv;
|
||||
|
||||
$cv = AE::cv;
|
||||
$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
|
||||
|
||||
$i3->subscribe({
|
||||
shutdown => sub {
|
||||
$cv->send(shift);
|
||||
}
|
||||
})->recv;
|
||||
|
||||
cmd 'exit';
|
||||
|
||||
$e = $cv->recv;
|
||||
|
||||
diag "Event:\n", Dumper($e);
|
||||
ok($e, 'the shutdown event should emit when the ipc is exited by command');
|
||||
is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
|
||||
}
|
||||
|
||||
done_testing;
|
Loading…
Reference in New Issue