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)::
|
binding (5)::
|
||||||
Sent when a configured command binding is triggered with the keyboard or
|
Sent when a configured command binding is triggered with the keyboard or
|
||||||
mouse
|
mouse
|
||||||
|
shutdown (6)::
|
||||||
|
Sent when the ipc shuts down because of a restart or exit by user command
|
||||||
|
|
||||||
*Example:*
|
*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)
|
== See also (existing libraries)
|
||||||
|
|
||||||
[[libraries]]
|
[[libraries]]
|
||||||
|
|
|
@ -91,3 +91,6 @@ typedef struct i3_ipc_header {
|
||||||
|
|
||||||
/** The binding event will be triggered when bindings run */
|
/** The binding event will be triggered when bindings run */
|
||||||
#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
|
#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);
|
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
|
* Calls to ipc_shutdown() should provide a reason for the shutdown.
|
||||||
* when exiting or restarting only!
|
*/
|
||||||
|
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);
|
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
||||||
|
|
||||||
|
|
|
@ -1562,7 +1562,7 @@ void cmd_exit(I3_CMD) {
|
||||||
#ifdef I3_ASAN_ENABLED
|
#ifdef I3_ASAN_ENABLED
|
||||||
__lsan_do_leak_check();
|
__lsan_do_leak_check();
|
||||||
#endif
|
#endif
|
||||||
ipc_shutdown();
|
ipc_shutdown(SHUTDOWN_REASON_EXIT);
|
||||||
unlink(config.ipc_socket_path);
|
unlink(config.ipc_socket_path);
|
||||||
xcb_disconnect(conn);
|
xcb_disconnect(conn);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -1595,7 +1595,7 @@ void cmd_reload(I3_CMD) {
|
||||||
*/
|
*/
|
||||||
void cmd_restart(I3_CMD) {
|
void cmd_restart(I3_CMD) {
|
||||||
LOG("restarting i3\n");
|
LOG("restarting i3\n");
|
||||||
ipc_shutdown();
|
ipc_shutdown(SHUTDOWN_REASON_RESTART);
|
||||||
unlink(config.ipc_socket_path);
|
unlink(config.ipc_socket_path);
|
||||||
/* We need to call this manually since atexit handlers don’t get called
|
/* We need to call this manually since atexit handlers don’t get called
|
||||||
* when exec()ing */
|
* 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!
|
* when exiting or restarting only!
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void ipc_shutdown(void) {
|
void ipc_shutdown(shutdown_reason_t reason) {
|
||||||
|
ipc_send_shutdown_event(reason);
|
||||||
|
|
||||||
ipc_client *current;
|
ipc_client *current;
|
||||||
while (!TAILQ_EMPTY(&all_clients)) {
|
while (!TAILQ_EMPTY(&all_clients)) {
|
||||||
current = TAILQ_FIRST(&all_clients);
|
current = TAILQ_FIRST(&all_clients);
|
||||||
|
|
|
@ -259,7 +259,7 @@ void i3_restart(bool forget_layout) {
|
||||||
|
|
||||||
restore_geometry();
|
restore_geometry();
|
||||||
|
|
||||||
ipc_shutdown();
|
ipc_shutdown(SHUTDOWN_REASON_RESTART);
|
||||||
|
|
||||||
LOG("restarting \"%s\"...\n", start_argv[0]);
|
LOG("restarting \"%s\"...\n", start_argv[0]);
|
||||||
/* make sure -a is in the argument list or add it */
|
/* 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