diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 723537d..e1f09cc 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -37,8 +37,10 @@ find_file(I3_IPC_H i3/ipc.h) if (NOT I3_IPC_H) message(FATAL_ERROR "cannot find header file: i3/ipc.h") endif () +add_library(i3-common STATIC EXCLUDE_FROM_ALL i3-common.c i3-common.h) + add_library(i3 ${lib_type} i3.c) -target_link_libraries(i3 module-sdk dynlist PkgConfig::json) +target_link_libraries(i3 module-sdk i3-common dynlist PkgConfig::json) if (ENABLE_X11) target_link_libraries(i3 xcb-stuff) endif () diff --git a/modules/i3-common.c b/modules/i3-common.c new file mode 100644 index 0000000..31546b5 --- /dev/null +++ b/modules/i3-common.c @@ -0,0 +1,311 @@ +#include "i3-common.h" + +#include +#include +#include +#include + +#include + +#if defined(ENABLE_X11) + #include + #include +#endif + +#include +#include + +#define LOG_MODULE "i3:common" +#include "../log.h" + +#if defined(ENABLE_X11) + #include "../xcb.h" +#endif + +#if defined(ENABLE_X11) +static bool +get_socket_address_x11(struct sockaddr_un *addr) +{ + int default_screen; + xcb_connection_t *conn = xcb_connect(NULL, &default_screen); + if (xcb_connection_has_error(conn) > 0) { + LOG_ERR("failed to connect to X"); + xcb_disconnect(conn); + return false; + } + + xcb_screen_t *screen = xcb_aux_get_screen(conn, default_screen); + + xcb_atom_t atom = get_atom(conn, "I3_SOCKET_PATH"); + assert(atom != XCB_ATOM_NONE); + + xcb_get_property_cookie_t cookie + = xcb_get_property_unchecked( + conn, false, screen->root, atom, + XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); + + xcb_generic_error_t *err = NULL; + xcb_get_property_reply_t *reply = + xcb_get_property_reply(conn, cookie, &err); + bool ret = false; + + if (err != NULL) { + LOG_ERR("failed to get i3 socket path: %s", xcb_error(err)); + goto err; + } + + const int len = xcb_get_property_value_length(reply); + assert(len < sizeof(addr->sun_path)); + + if (len == 0) { + LOG_ERR("failed to get i3 socket path: empty reply"); + goto err; + } + + memcpy(addr->sun_path, xcb_get_property_value(reply), len); + addr->sun_path[len] = '\0'; + + ret = true; + +err: + free(err); + free(reply); + xcb_disconnect(conn); + return ret; +} +#endif + +bool +i3_get_socket_address(struct sockaddr_un *addr) +{ + *addr = (struct sockaddr_un){.sun_family = AF_UNIX}; + + const char *sway_sock = getenv("SWAYSOCK"); + if (sway_sock == NULL) { + sway_sock = getenv("I3SOCK"); + if (sway_sock == NULL) { +#if defined(ENABLE_X11) + return get_socket_address_x11(addr); +#else + return false; +#endif + } + } + + strncpy(addr->sun_path, sway_sock, sizeof(addr->sun_path) - 1); + return true; +} + +bool +i3_send_pkg(int sock, int cmd, char *data) +{ + const size_t size = data != NULL ? strlen(data) : 0; + const i3_ipc_header_t hdr = { + .magic = I3_IPC_MAGIC, + .size = size, + .type = cmd + }; + + if (write(sock, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) + return false; + + if (data != NULL) { + if (write(sock, data, size) != (ssize_t)size) + return false; + } + + return true; +} + +bool +i3_receive_loop(int abort_fd, int sock, + const struct i3_ipc_callbacks *cbs, void *data) +{ + /* Initial reply typically requires a couple of KB. But we often + * need more later. For example, switching workspaces can result + * in quite big notification messages. */ + size_t reply_buf_size = 4096; + char *buf = malloc(reply_buf_size); + size_t buf_idx = 0; + + bool err = false; + + while (!err) { + struct pollfd fds[] = { + {.fd = abort_fd, .events = POLLIN}, + {.fd = sock, .events = POLLIN} + }; + + int res = poll(fds, 2, -1); + if (res <= 0) { + LOG_ERRNO("failed to poll()"); + err = true; + break; + } + + if (fds[0].revents & POLLIN) { + LOG_DBG("aborted"); + break; + } + + if (fds[1].revents & POLLHUP) { + LOG_DBG("disconnected"); + break; + } + + assert(fds[1].revents & POLLIN); + + /* Grow receive buffer, if necessary */ + if (buf_idx == reply_buf_size) { + LOG_DBG("growing reply buffer: %zu -> %zu", + reply_buf_size, reply_buf_size * 2); + + char *new_buf = realloc(buf, reply_buf_size * 2); + if (new_buf == NULL) { + LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", + reply_buf_size, reply_buf_size * 2); + err = true; + break; + } + + buf = new_buf; + reply_buf_size *= 2; + } + + assert(reply_buf_size > buf_idx); + + ssize_t bytes = read(sock, &buf[buf_idx], reply_buf_size - buf_idx); + if (bytes < 0) { + LOG_ERRNO("failed to read from i3's socket"); + err = true; + break; + } + + buf_idx += bytes; + + while (!err && buf_idx >= sizeof(i3_ipc_header_t)) { + const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf; + if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) { + LOG_ERR( + "i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", + (int)sizeof(hdr->magic), I3_IPC_MAGIC, + (int)sizeof(hdr->magic), hdr->magic); + + err = true; + break; + } + + size_t total_size = sizeof(i3_ipc_header_t) + hdr->size; + + if (total_size > buf_idx) { + LOG_DBG("got %zd bytes, need %zu", bytes, total_size); + break; + } + + /* Json-c expects a NULL-terminated string */ + char json_str[hdr->size + 1]; + memcpy(json_str, &buf[sizeof(*hdr)], hdr->size); + json_str[hdr->size] = '\0'; + //printf("raw: %s\n", json_str); + LOG_DBG("raw: %s\n", json_str); + + //json_tokener *tokener = json_tokener_new(); + struct json_object *json = json_tokener_parse(json_str); + if (json == NULL) { + LOG_ERR("failed to parse json"); + err = true; + break; + } + + //err = pkt_handler(hdr, json, data); + i3_ipc_callback_t pkt_handler = NULL; + switch (hdr->type) { + case I3_IPC_REPLY_TYPE_COMMAND: + pkt_handler = cbs->reply_command; + break; + case I3_IPC_REPLY_TYPE_WORKSPACES: + pkt_handler = cbs->reply_workspaces; + break; + case I3_IPC_REPLY_TYPE_SUBSCRIBE: + pkt_handler = cbs->reply_subscribe; + break; + case I3_IPC_REPLY_TYPE_OUTPUTS: + pkt_handler = cbs->reply_outputs; + break; + case I3_IPC_REPLY_TYPE_TREE: + pkt_handler = cbs->reply_tree; + break; + case I3_IPC_REPLY_TYPE_MARKS: + pkt_handler = cbs->reply_marks; + break; + case I3_IPC_REPLY_TYPE_BAR_CONFIG: + pkt_handler = cbs->reply_bar_config; + break; + case I3_IPC_REPLY_TYPE_VERSION: + pkt_handler = cbs->reply_version; + break; + case I3_IPC_REPLY_TYPE_BINDING_MODES: + pkt_handler = cbs->reply_binding_modes; + break; + case I3_IPC_REPLY_TYPE_CONFIG: + pkt_handler = cbs->reply_config; + break; + case I3_IPC_REPLY_TYPE_TICK: + pkt_handler = cbs->reply_tick; + break; +#if defined(I3_IPC_REPLY_TYPE_SYNC) + case I3_IPC_REPLY_TYPE_SYNC: + pkt_handler = cbs->reply_sync; + break; +#endif + + case I3_IPC_EVENT_WORKSPACE: + pkt_handler = cbs->event_workspace; + break; + case I3_IPC_EVENT_OUTPUT: + pkt_handler = cbs->event_output; + break; + case I3_IPC_EVENT_MODE: + pkt_handler = cbs->event_mode; + break; + case I3_IPC_EVENT_WINDOW: + pkt_handler = cbs->event_window; + break; + case I3_IPC_EVENT_BARCONFIG_UPDATE: + pkt_handler = cbs->event_barconfig_update; + break; + case I3_IPC_EVENT_BINDING: + pkt_handler = cbs->event_binding; + break; + case I3_IPC_EVENT_SHUTDOWN: + pkt_handler = cbs->event_shutdown; + break; + case I3_IPC_EVENT_TICK: + pkt_handler = cbs->event_tick; + break; + + default: + LOG_ERR("unimplemented IPC reply type: %d", hdr->type); + pkt_handler = NULL; + break; + } + + if (pkt_handler != NULL) + err = !pkt_handler(hdr->type, json, data); + else + LOG_DBG("no handler for reply/event %d; ignoring", hdr->type); + + json_object_put(json); + + assert(total_size <= buf_idx); + memmove(buf, &buf[total_size], buf_idx - total_size); + buf_idx -= total_size; + } + + if (cbs->burst_done != NULL) + cbs->burst_done(data); + } + + free(buf); + return !err; +} diff --git a/modules/i3-common.h b/modules/i3-common.h new file mode 100644 index 0000000..975d42a --- /dev/null +++ b/modules/i3-common.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include +#include + +//#include +#include + +bool i3_get_socket_address(struct sockaddr_un *addr); +bool i3_send_pkg(int sock, int cmd, char *data); + +typedef bool (*i3_ipc_callback_t)(int type, const struct json_object *json, void *data); + +struct i3_ipc_callbacks { + void (*burst_done)(void *data); + + i3_ipc_callback_t reply_command; + i3_ipc_callback_t reply_workspaces; + i3_ipc_callback_t reply_subscribe; + i3_ipc_callback_t reply_outputs; + i3_ipc_callback_t reply_tree; + i3_ipc_callback_t reply_marks; + i3_ipc_callback_t reply_bar_config; + i3_ipc_callback_t reply_version; + i3_ipc_callback_t reply_binding_modes; + i3_ipc_callback_t reply_config; + i3_ipc_callback_t reply_tick; + i3_ipc_callback_t reply_sync; + + i3_ipc_callback_t event_workspace; + i3_ipc_callback_t event_output; + i3_ipc_callback_t event_mode; + i3_ipc_callback_t event_window; + i3_ipc_callback_t event_barconfig_update; + i3_ipc_callback_t event_binding; + i3_ipc_callback_t event_shutdown; + i3_ipc_callback_t event_tick; +}; + +bool i3_receive_loop( + int abort_fd, int sock, + const struct i3_ipc_callbacks *callbacks, void *data); diff --git a/modules/i3.c b/modules/i3.c index 65dc702..6ec1b20 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -1,29 +1,14 @@ #include -#include #include #include #include #include -#include - #include -#include -#include -#include #include -#if defined(ENABLE_X11) - #include - #include -#endif - #include -#include -#include -#include - #define LOG_MODULE "i3" #define LOG_ENABLE_DBG 0 #include "../log.h" @@ -33,9 +18,7 @@ #include "../particles/dynlist.h" #include "../plugin.h" -#if defined(ENABLE_X11) - #include "../xcb.h" -#endif +#include "i3-common.h" struct ws_content { char *name; @@ -60,6 +43,8 @@ struct private { int left_spacing; int right_spacing; + bool dirty; + struct { struct ws_content *v; size_t count; @@ -185,29 +170,9 @@ workspace_lookup(struct private *m, const char *name) return NULL; } -static bool -send_pkg(int sock, int cmd, char *data) -{ - size_t size = data != NULL ? strlen(data) : 0; - i3_ipc_header_t hdr = { - .magic = I3_IPC_MAGIC, - .size = size, - .type = cmd - }; - - if (write(sock, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) - return false; - - if (data != NULL) { - if (write(sock, data, size) != (ssize_t)size) - return false; - } - - return true; -} static bool -handle_get_version_reply(struct private *m, const struct json_object *json) +handle_get_version_reply(int type, const struct json_object *json, void *_m) { if (!json_object_is_type(json, json_type_object)) { LOG_ERR("'version' reply is not of type 'object'"); @@ -226,7 +191,7 @@ handle_get_version_reply(struct private *m, const struct json_object *json) } static bool -handle_subscribe_reply(struct private *m, const struct json_object *json) +handle_subscribe_reply(int type, const struct json_object *json, void *_m) { if (!json_object_is_type(json, json_type_object)) { LOG_ERR("'subscribe' reply is not of type 'object'"); @@ -249,14 +214,20 @@ handle_subscribe_reply(struct private *m, const struct json_object *json) } static bool -handle_get_workspaces_reply(struct private *m, const struct json_object *json) +handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod) { + struct module *mod = _mod; + struct private *m = mod->private; + if (!json_object_is_type(json, json_type_array)) { LOG_ERR("'workspaces' reply is not of type 'array'"); return false; } + mtx_lock(&mod->lock); + workspaces_free(m); + m->dirty = true; size_t count = json_object_array_length(json); m->workspaces.count = count; @@ -266,18 +237,23 @@ handle_get_workspaces_reply(struct private *m, const struct json_object *json) if (!workspace_from_json( json_object_array_get_idx(json, i), &m->workspaces.v[i])) { workspaces_free(m); + mtx_unlock(&mod->lock); return false; } LOG_DBG("#%zu: %s", i, m->workspaces.v[i].name); } + mtx_unlock(&mod->lock); return true; } static bool -handle_workspace_event(struct private *m, const struct json_object *json) +handle_workspace_event(int type, const struct json_object *json, void *_mod) { + struct module *mod = _mod; + struct private *m = mod->private; + if (!json_object_is_type(json, json_type_object)) { LOG_ERR("'workspace' event is not of type 'object'"); return false; @@ -312,12 +288,14 @@ handle_workspace_event(struct private *m, const struct json_object *json) } } + mtx_lock(&mod->lock); + if (strcmp(change_str, "init") == 0) { assert(workspace_lookup(m, json_object_get_string(current_name)) == NULL); struct workspace ws; if (!workspace_from_json(current, &ws)) - return false; + goto err; workspace_add(m, ws); } @@ -330,14 +308,14 @@ handle_workspace_event(struct private *m, const struct json_object *json) else if (strcmp(change_str, "focus") == 0) { if (old == NULL || !json_object_is_type(old, json_type_object)) { LOG_ERR("'workspace' event did not contain a 'old' object value"); - return false; + goto err; } struct json_object *old_name = json_object_object_get(old, "name"); if (old_name == NULL || !json_object_is_type(old_name, json_type_string)) { LOG_ERR("'workspace' event's 'old' object did not " "contain a 'name' string value"); - return false; + goto err; } struct workspace *w = workspace_lookup( @@ -356,7 +334,7 @@ handle_workspace_event(struct private *m, const struct json_object *json) if (urgent == NULL || !json_object_is_type(urgent, json_type_boolean)) { LOG_ERR("'workspace' event's 'current' object did not " "contain a 'urgent' boolean value"); - return false; + goto err; } w->urgent = json_object_get_boolean(urgent); @@ -375,7 +353,7 @@ handle_workspace_event(struct private *m, const struct json_object *json) if (urgent == NULL || !json_object_is_type(urgent, json_type_boolean)) { LOG_ERR("'workspace' event's 'current' object did not " "contain a 'urgent' boolean value"); - return false; + goto err; } struct workspace *w = workspace_lookup( @@ -386,12 +364,23 @@ handle_workspace_event(struct private *m, const struct json_object *json) else if (strcmp(change_str, "reload") == 0) LOG_WARN("unimplemented: 'reload' event"); + m->dirty = true; + mtx_unlock(&mod->lock); return true; + +err: + mtx_unlock(&mod->lock); + return false; } static bool -handle_window_event(struct private *m, const struct json_object *json) +handle_window_event(int type, const struct json_object *json, void *_mod) { + struct module *mod = _mod; + struct private *m = mod->private; + + mtx_lock(&mod->lock); + struct workspace *ws = NULL; size_t focused = 0; for (size_t i = 0; i < m->workspaces.count; i++) { @@ -406,15 +395,16 @@ handle_window_event(struct private *m, const struct json_object *json) if (!json_object_is_type(json, json_type_object)) { LOG_ERR("'window' event is not of type 'object'"); - return false; + goto err; } struct json_object *change = json_object_object_get(json, "change"); if (change == NULL || !json_object_is_type(change, json_type_string)) { LOG_ERR("'window' event did not contain a 'change' string value"); - return false; + goto err; } + m->dirty = true; const char *change_str = json_object_get_string(change); if (strcmp(change_str, "close") == 0 || strcmp(change_str, "new") == 0) { @@ -423,13 +413,15 @@ handle_window_event(struct private *m, const struct json_object *json) ws->window.title = ws->window.application = NULL; ws->window.pid = -1; + + mtx_unlock(&mod->lock); return true; } const struct json_object *container = json_object_object_get(json, "container"); if (container == NULL || !json_object_is_type(container, json_type_object)) { LOG_ERR("'window' event (%s) did not contain a 'container' object", change_str); - return false; + goto err; } struct json_object *name = json_object_object_get(container, "name"); @@ -437,7 +429,7 @@ handle_window_event(struct private *m, const struct json_object *json) LOG_ERR( "'window' event (%s) did not contain a 'container.name' string value", change_str); - return false; + goto err; } free(ws->window.title); @@ -448,7 +440,7 @@ handle_window_event(struct private *m, const struct json_object *json) LOG_ERR( "'window' event (%s) did not contain a 'container.pid' integer value", change_str); - return false; + goto err; } /* If PID has changed, update application name from /proc//comm */ @@ -464,6 +456,8 @@ handle_window_event(struct private *m, const struct json_object *json) /* Application may simply have terminated */ free(ws->window.application); ws->window.application = NULL; ws->window.pid = -1; + + mtx_unlock(&mod->lock); return true; } @@ -476,88 +470,31 @@ handle_window_event(struct private *m, const struct json_object *json) close(fd); } + mtx_unlock(&mod->lock); return true; + +err: + mtx_unlock(&mod->lock); + return false; } -#if defined(ENABLE_X11) -static bool -get_socket_address_x11(struct sockaddr_un *addr) +static void +burst_done(void *_mod) { - int default_screen; - xcb_connection_t *conn = xcb_connect(NULL, &default_screen); - if (xcb_connection_has_error(conn) > 0) { - LOG_ERR("failed to connect to X"); - xcb_disconnect(conn); - return false; + struct module *mod = _mod; + struct private *m = mod->private; + + if (m->dirty) { + m->dirty = false; + mod->bar->refresh(mod->bar); } - - xcb_screen_t *screen = xcb_aux_get_screen(conn, default_screen); - - xcb_atom_t atom = get_atom(conn, "I3_SOCKET_PATH"); - assert(atom != XCB_ATOM_NONE); - - xcb_get_property_cookie_t cookie - = xcb_get_property_unchecked( - conn, false, screen->root, atom, - XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); - - xcb_generic_error_t *err; - xcb_get_property_reply_t *reply = - xcb_get_property_reply(conn, cookie, &err); - - if (err != NULL) { - LOG_ERR("failed to get i3 socket path: %s", xcb_error(err)); - free(err); - free(reply); - return false; - } - - const int len = xcb_get_property_value_length(reply); - assert(len < sizeof(addr->sun_path)); - - if (len == 0) { - LOG_ERR("failed to get i3 socket path: empty reply"); - free(reply); - return false; - } - - memcpy(addr->sun_path, xcb_get_property_value(reply), len); - addr->sun_path[len] = '\0'; - - free(reply); - xcb_disconnect(conn); - return true; -} -#endif - -static bool -get_socket_address(struct sockaddr_un *addr) -{ - *addr = (struct sockaddr_un){.sun_family = AF_UNIX}; - - const char *sway_sock = getenv("SWAYSOCK"); - if (sway_sock == NULL) { - sway_sock = getenv("I3SOCK"); - if (sway_sock == NULL) { -#if defined(ENABLE_X11) - return get_socket_address_x11(addr); -#else - return false; -#endif - } - } - - strncpy(addr->sun_path, sway_sock, sizeof(addr->sun_path) - 1); - return true; } static int run(struct module *mod) { - struct private *m = mod->private; - struct sockaddr_un addr; - if (!get_socket_address(&addr)) + if (!i3_get_socket_address(&addr)) return 1; int sock = socket(AF_UNIX, SOCK_STREAM, 0); @@ -573,154 +510,22 @@ run(struct module *mod) return 1; } - send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_VERSION, NULL); - send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\"]"); - send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_VERSION, NULL); + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\"]"); + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); - /* Initial reply typically requires a couple of KB. But we often - * need more later. For example, switching workspaces can result - * in quite big notification messages. */ - size_t reply_buf_size = 4096; - char *buf = malloc(reply_buf_size); - size_t buf_idx = 0; + static const struct i3_ipc_callbacks callbacks = { + .burst_done = &burst_done, + .reply_version = &handle_get_version_reply, + .reply_subscribe = &handle_subscribe_reply, + .reply_workspaces = &handle_get_workspaces_reply, + .event_workspace = &handle_workspace_event, + .event_window = &handle_window_event, + }; - while (true) { - struct pollfd fds[] = { - {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = sock, .events = POLLIN} - }; - - int res = poll(fds, 2, -1); - if (res <= 0) { - LOG_ERRNO("failed to poll()"); - break; - } - - if (fds[0].revents & POLLIN) - break; - - assert(fds[1].revents & POLLIN); - - /* Grow receive buffer, if necessary */ - if (buf_idx == reply_buf_size) { - LOG_DBG("growing reply buffer: %zu -> %zu", - reply_buf_size, reply_buf_size * 2); - - char *new_buf = realloc(buf, reply_buf_size * 2); - if (new_buf == NULL) { - LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", - reply_buf_size, reply_buf_size * 2); - break; - } - - buf = new_buf; - reply_buf_size *= 2; - } - - assert(reply_buf_size > buf_idx); - - ssize_t bytes = read(sock, &buf[buf_idx], reply_buf_size - buf_idx); - if (bytes < 0) { - LOG_ERRNO("failed to read from i3's socket"); - break; - } - - buf_idx += bytes; - - bool err = false; - bool need_bar_refresh = false; - - while (!err && buf_idx >= sizeof(i3_ipc_header_t)) { - const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf; - if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) { - LOG_ERR( - "i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", - (int)sizeof(hdr->magic), I3_IPC_MAGIC, - (int)sizeof(hdr->magic), hdr->magic); - - err = true; - break; - } - - size_t total_size = sizeof(i3_ipc_header_t) + hdr->size; - - if (total_size > buf_idx) { - LOG_DBG("got %zd bytes, need %zu", bytes, total_size); - break; - } - - /* Json-c expects a NULL-terminated string */ - char json_str[hdr->size + 1]; - memcpy(json_str, &buf[sizeof(*hdr)], hdr->size); - json_str[hdr->size] = '\0'; - //printf("raw: %s\n", json_str); - LOG_DBG("raw: %s\n", json_str); - - json_tokener *tokener = json_tokener_new(); - struct json_object *json = json_tokener_parse(json_str); - if (json == NULL) { - LOG_ERR("failed to parse json"); - err = true; - break; - } - - mtx_lock(&mod->lock); - - switch (hdr->type) { - case I3_IPC_REPLY_TYPE_VERSION: - handle_get_version_reply(m, json); - break; - - case I3_IPC_REPLY_TYPE_SUBSCRIBE: - handle_subscribe_reply(m, json); - break; - - case I3_IPC_REPLY_TYPE_WORKSPACES: - handle_get_workspaces_reply(m, json); - need_bar_refresh = true; - break; - - case I3_IPC_EVENT_WORKSPACE: - handle_workspace_event(m, json); - need_bar_refresh = true; - break; - - case I3_IPC_EVENT_WINDOW: - handle_window_event(m, json); - need_bar_refresh = true; - break; - - case I3_IPC_EVENT_OUTPUT: - case I3_IPC_EVENT_MODE: - case I3_IPC_EVENT_BARCONFIG_UPDATE: - case I3_IPC_EVENT_BINDING: - case I3_IPC_EVENT_SHUTDOWN: - case I3_IPC_EVENT_TICK: - break; - - default: assert(false); - } - - mtx_unlock(&mod->lock); - - json_object_put(json); - json_tokener_free(tokener); - - assert(total_size <= buf_idx); - memmove(buf, &buf[total_size], buf_idx - total_size); - buf_idx -= total_size; - } - - if (err) - break; - - if (need_bar_refresh) - mod->bar->refresh(mod->bar); - } - - free(buf); + bool ret = i3_receive_loop(mod->abort_fd, sock, &callbacks, mod); close(sock); - return 0; + return ret ? 0 : 1; } static void