From d722d1b0e605be6b84f37053d05d43d40fe6d88c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 2 Oct 2018 18:13:06 -0400 Subject: [PATCH 1/2] i3-msg: check reply in quiet mode i3-msg currently exits right after sending the IPC message if the quiet flag is set. This means that if an error occurred when issuing a command, e.g. "i3-msg -q foobar", it gets silently ignored. What we really want is to just skip printing but still check the reply. At the same time, explicitly print the reply when we need to, instead of using an exit label. --- i3-msg/main.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/i3-msg/main.c b/i3-msg/main.c index 96edb2c3..9b34b062 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -246,9 +246,6 @@ int main(int argc, char *argv[]) { err(EXIT_FAILURE, "IPC: write()"); free(payload); - if (quiet) - return 0; - uint32_t reply_length; uint32_t reply_type; uint8_t *reply; @@ -275,8 +272,9 @@ int main(int argc, char *argv[]) { errx(EXIT_FAILURE, "IPC: Could not parse JSON reply."); } - /* NB: We still fall-through and print the reply, because even if one - * command failed, that doesn’t mean that all commands failed. */ + if (!quiet) { + printf("%.*s\n", reply_length, reply); + } } else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) { yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL); yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length); @@ -289,12 +287,12 @@ int main(int argc, char *argv[]) { case yajl_status_error: errx(EXIT_FAILURE, "IPC: Could not parse JSON reply."); } - - goto exit; + } else { + if (!quiet) { + printf("%.*s\n", reply_length, reply); + } } - printf("%.*s\n", reply_length, reply); -exit: free(reply); close(sockfd); From cff4fadd7275dd20b9de383c2f83e1e06a27b7a4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 5 Sep 2014 16:47:27 -0400 Subject: [PATCH 2/2] i3-msg: add support for SUBSCRIBE message type If i3-msg is invoked with -t subscribe, it will wait for the first event matching the given payload, before exiting. For instance, get the number of the next focused workspace with: i3-msg -t subscribe '[ "workspace" ]' | jshon -e current -e num Like inotifywait, the -m flag allows to wait indefinitely for events, instead of exiting right after receiving the first one. For example, continuously monitor the names of focused windows with: i3-msg -t subscribe -m '[ "window" ]' | jq .container.name --- i3-msg/main.c | 35 ++++++++++++++++++++++++++++++++--- man/i3-msg.man | 13 +++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/i3-msg/main.c b/i3-msg/main.c index 9b34b062..fe111416 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -166,16 +166,18 @@ int main(int argc, char *argv[]) { uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND; char *payload = NULL; bool quiet = false; + bool monitor = false; static struct option long_options[] = { {"socket", required_argument, 0, 's'}, {"type", required_argument, 0, 't'}, {"version", no_argument, 0, 'v'}, {"quiet", no_argument, 0, 'q'}, + {"monitor", no_argument, 0, 'm'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - char *options_string = "s:t:vhq"; + char *options_string = "s:t:vhqm"; while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { if (o == 's') { @@ -204,25 +206,34 @@ int main(int argc, char *argv[]) { message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG; } else if (strcasecmp(optarg, "send_tick") == 0) { message_type = I3_IPC_MESSAGE_TYPE_SEND_TICK; + } else if (strcasecmp(optarg, "subscribe") == 0) { + message_type = I3_IPC_MESSAGE_TYPE_SUBSCRIBE; } else { printf("Unknown message type\n"); - printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick\n"); + printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick, subscribe\n"); exit(EXIT_FAILURE); } } else if (o == 'q') { quiet = true; + } else if (o == 'm') { + monitor = true; } else if (o == 'v') { printf("i3-msg " I3_VERSION "\n"); return 0; } else if (o == 'h') { printf("i3-msg " I3_VERSION "\n"); - printf("i3-msg [-s ] [-t ] \n"); + printf("i3-msg [-s ] [-t ] [-m] \n"); return 0; } else if (o == '?') { exit(EXIT_FAILURE); } } + if (monitor && message_type != I3_IPC_MESSAGE_TYPE_SUBSCRIBE) { + fprintf(stderr, "The monitor option -m is used with -t SUBSCRIBE exclusively.\n"); + exit(EXIT_FAILURE); + } + /* Use all arguments, separated by whitespace, as payload. * This way, you don’t have to do i3-msg 'mark foo', you can use * i3-msg mark foo */ @@ -287,6 +298,24 @@ int main(int argc, char *argv[]) { case yajl_status_error: errx(EXIT_FAILURE, "IPC: Could not parse JSON reply."); } + } else if (reply_type == I3_IPC_REPLY_TYPE_SUBSCRIBE) { + do { + free(reply); + if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) { + if (ret == -1) + err(EXIT_FAILURE, "IPC: read()"); + exit(1); + } + + if (!(reply_type & I3_IPC_EVENT_MASK)) { + errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected an event", reply_type); + } + + if (!quiet) { + fprintf(stdout, "%.*s\n", reply_length, reply); + fflush(stdout); + } + } while (monitor); } else { if (!quiet) { printf("%.*s\n", reply_length, reply); diff --git a/man/i3-msg.man b/man/i3-msg.man index 04c71900..625131de 100644 --- a/man/i3-msg.man +++ b/man/i3-msg.man @@ -31,6 +31,11 @@ with an error. *-t* 'type':: Send ipc message, see below. This option defaults to "command". +*-m*, *--monitor*:: +Instead of exiting right after receiving the first subscribed event, +wait indefinitely for all of them. Can only be used with "-t subscribe". +See the "subscribe" IPC message type below for details. + *message*:: Send ipc message, see below. @@ -75,6 +80,11 @@ Gets the currently loaded i3 configuration. send_tick:: Sends a tick to all IPC connections which subscribe to tick events. +subscribe:: +The payload of the message describes the events to subscribe to. +Upon reception, each event will be dumped as a JSON-encoded object. +See the -m option for continuous monitoring. + == DESCRIPTION i3-msg is a sample implementation for a client using the unix socket IPC @@ -91,6 +101,9 @@ i3-msg border normal # Dump the layout tree i3-msg -t get_tree + +# Monitor window changes +i3-msg -t subscribe -m '[ "window" ]' ------------------------------------------------ == ENVIRONMENT