]> git.sur5r.net Git - i3/i3/commitdiff
Add the ipc shutdown event (#2652)
authorTony Crisci <tony@dubstepdish.com>
Sun, 22 Jan 2017 22:08:32 +0000 (17:08 -0500)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Sun, 22 Jan 2017 22:08:32 +0000 (14:08 -0800)
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

docs/ipc
include/i3/ipc.h
include/ipc.h
src/commands.c
src/ipc.c
src/util.c
testcases/t/264-ipc-shutdown-event.t [new file with mode: 0644]

index fda289a0a21c2776d30c29a7c4207c7b6484b3bb..5d47bcbd52fb249186408bcdf0b9a7e944619d7e 100644 (file)
--- a/docs/ipc
+++ b/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]]
index 98ac35b0eb8b6fba24250e04ba86378234d02c04..249cc32e3d6e1e85c977db546c63b00dcce51b47 100644 (file)
@@ -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)
index 7ff4704c626195517b0a4efde49a91031f92842d..7ffbf7a831f9de3418f9d4ddad304beee5cbff22 100644 (file)
@@ -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);
 
index 2387ddd7fd06615e6e0070075e4d7a33f99d6900..56c07b6ab6fc9a4aa7dccf4017354b71a87b5273 100644 (file)
@@ -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 */
index db2fa362e286f8cf995c98a7d9d25dd9af87269f..bb20b340cb8323990b6048faa7773c5d161f26dd 100644 (file)
--- a/src/ipc.c
+++ b/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);
index cfe4c953db029c1333e4f9ed90396cb8d79c58a6..5c8fc774bd389a8b710ea4755bb0e432f7379604 100644 (file)
@@ -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 */
diff --git a/testcases/t/264-ipc-shutdown-event.t b/testcases/t/264-ipc-shutdown-event.t
new file mode 100644 (file)
index 0000000..379b9bf
--- /dev/null
@@ -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;