X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fipc.c;h=eed63bd4e0072820095561d5b044b1feb19b9f81;hb=a415d56048a18720bfdab8144652b87cb39ce626;hp=b4356fda74efdff42915b660d572d26b5b68d97c;hpb=fde11f9a5c715c3deacd58f2263d39004ad771a9;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index b4356fda..eed63bd4 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -3,38 +3,25 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * * ipc.c: Everything about the UNIX domain sockets for IPC * */ -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include -#include "queue.h" -#include "i3/ipc.h" -#include "i3.h" -#include "util.h" -#include "commands.h" -#include "log.h" +#include "all.h" -typedef struct ipc_client { - int fd; - - TAILQ_ENTRY(ipc_client) clients; -} ipc_client; +/* Shorter names for all those yajl_gen_* functions */ +#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__) +#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str)) TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients); @@ -51,49 +38,389 @@ static void set_nonblock(int sockfd) { err(-1, "Could not set O_NONBLOCK"); } -#if 0 -void broadcast(EV_P_ struct ev_timer *t, int revents) { +static void ipc_send_message(int fd, const unsigned char *payload, + int message_type, int message_size) { + int buffer_size = strlen("i3-ipc") + sizeof(uint32_t) + + sizeof(uint32_t) + message_size; + char msg[buffer_size]; + char *walk = msg; + + strcpy(walk, "i3-ipc"); + walk += strlen("i3-ipc"); + memcpy(walk, &message_size, sizeof(uint32_t)); + walk += sizeof(uint32_t); + memcpy(walk, &message_type, sizeof(uint32_t)); + walk += sizeof(uint32_t); + memcpy(walk, payload, message_size); + + int sent_bytes = 0; + int bytes_to_go = buffer_size; + while (sent_bytes < bytes_to_go) { + int n = write(fd, msg + sent_bytes, bytes_to_go); + if (n == -1) { + DLOG("write() failed: %s\n", strerror(errno)); + return; + } + + sent_bytes += n; + bytes_to_go -= n; + } +} + +/* + * Sends the specified event to all IPC clients which are currently connected + * and subscribed to this kind of event. + * + */ +void ipc_send_event(const char *event, uint32_t message_type, const char *payload) { ipc_client *current; TAILQ_FOREACH(current, &all_clients, clients) { - write(current->fd, "hi there!\n", strlen("hi there!\n")); + /* see if this client is interested in this event */ + bool interested = false; + for (int i = 0; i < current->num_events; i++) { + if (strcasecmp(current->events[i], event) != 0) + continue; + interested = true; + break; + } + if (!interested) + continue; + + ipc_send_message(current->fd, (const unsigned char*)payload, + message_type, strlen(payload)); } } -#endif /* - * Decides what to do with the received message. - * - * message is the raw packet, as received from the UNIX domain socket. size - * is the remaining size of bytes for this packet. + * Calls shutdown() on each socket and closes it. This function to be called + * when exiting or restarting only! * - * message_size is the size of the message as the sender specified it. - * message_type is the type of the message as the sender specified it. + */ +void ipc_shutdown() { + ipc_client *current; + TAILQ_FOREACH(current, &all_clients, clients) { + shutdown(current->fd, SHUT_RDWR); + close(current->fd); + } +} + +/* + * Executes the command and returns whether it could be successfully parsed + * or not (at the moment, always returns true). * */ -static void ipc_handle_message(uint8_t *message, int size, - uint32_t message_size, uint32_t message_type) { - DLOG("handling message of size %d\n", size); - DLOG("sender specified size %d\n", message_size); - DLOG("sender specified type %d\n", message_type); - DLOG("payload as a string = %s\n", message); - - switch (message_type) { - case I3_IPC_MESSAGE_TYPE_COMMAND: { - /* To get a properly terminated buffer, we copy - * message_size bytes out of the buffer */ - char *command = scalloc(message_size); - strncpy(command, (const char*)message, message_size); - parse_command(global_conn, (const char*)command); - free(command); +IPC_HANDLER(command) { + /* To get a properly terminated buffer, we copy + * message_size bytes out of the buffer */ + char *command = scalloc(message_size + 1); + strncpy(command, (const char*)message, message_size); + LOG("IPC: received: *%s*\n", command); + const char *reply = parse_cmd((const char*)command); + tree_render(); + free(command); + + /* If no reply was provided, we just use the default success message */ + if (reply == NULL) + reply = "{\"success\":true}"; + ipc_send_message(fd, (const unsigned char*)reply, + I3_IPC_REPLY_TYPE_COMMAND, strlen(reply)); +} - break; +void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { + y(map_open); + ystr("id"); + y(integer, (long int)con); + + ystr("type"); + y(integer, con->type); + + ystr("orientation"); + y(integer, con->orientation); + + ystr("urgent"); + y(integer, con->urgent); + + ystr("focused"); + y(integer, (con == focused)); + + ystr("layout"); + y(integer, con->layout); + + ystr("border"); + y(integer, con->border_style); + + ystr("rect"); + y(map_open); + ystr("x"); + y(integer, con->rect.x); + ystr("y"); + y(integer, con->rect.y); + ystr("width"); + y(integer, con->rect.width); + ystr("height"); + y(integer, con->rect.height); + y(map_close); + + ystr("name"); + ystr(con->name); + + ystr("window"); + if (con->window) + y(integer, con->window->id); + else y(null); + + ystr("nodes"); + y(array_open); + Con *node; + TAILQ_FOREACH(node, &(con->nodes_head), nodes) { + dump_node(gen, node, inplace_restart); + } + y(array_close); + + ystr("floating-nodes"); + y(array_open); + TAILQ_FOREACH(node, &(con->floating_head), floating_windows) { + dump_node(gen, node, inplace_restart); + } + y(array_close); + + ystr("focus"); + y(array_open); + TAILQ_FOREACH(node, &(con->focus_head), nodes) { + y(integer, (long int)node); + } + y(array_close); + + ystr("fullscreen_mode"); + y(integer, con->fullscreen_mode); + + if (inplace_restart) { + if (con->window != NULL) { + ystr("swallows"); + y(array_open); + y(map_open); + ystr("id"); + y(integer, con->window->id); + y(map_close); + y(array_close); } - default: - DLOG("unhandled ipc message\n"); - break; } + + y(map_close); } +IPC_HANDLER(tree) { + printf("tree\n"); + yajl_gen gen = yajl_gen_alloc(NULL, NULL); + dump_node(gen, croot, false); + + const unsigned char *payload; + unsigned int length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_TREE, length); + y(free); + +} + +#if 0 +/* + * Formats the reply message for a GET_WORKSPACES request and sends it to the + * client + * + */ +IPC_HANDLER(get_workspaces) { + Workspace *ws; + + Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack)); + if (last_focused == SLIST_END(&(c_ws->focus_stack))) + last_focused = NULL; + + yajl_gen gen = yajl_gen_alloc(NULL, NULL); + y(array_open); + + TAILQ_FOREACH(ws, workspaces, workspaces) { + if (ws->output == NULL) + continue; + + y(map_open); + ystr("num"); + y(integer, ws->num + 1); + + ystr("name"); + ystr(ws->utf8_name); + + ystr("visible"); + y(bool, ws->output->current_workspace == ws); + + ystr("focused"); + y(bool, c_ws == ws); + + ystr("rect"); + y(map_open); + ystr("x"); + y(integer, ws->rect.x); + ystr("y"); + y(integer, ws->rect.y); + ystr("width"); + y(integer, ws->rect.width); + ystr("height"); + y(integer, ws->rect.height); + y(map_close); + + ystr("output"); + ystr(ws->output->name); + + ystr("urgent"); + y(bool, ws->urgent); + + y(map_close); + } + + y(array_close); + + const unsigned char *payload; + unsigned int length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_WORKSPACES, length); + y(free); +} + +/* + * Formats the reply message for a GET_OUTPUTS request and sends it to the + * client + * + */ +IPC_HANDLER(get_outputs) { + Output *output; + + yajl_gen gen = yajl_gen_alloc(NULL, NULL); + y(array_open); + + TAILQ_FOREACH(output, &outputs, outputs) { + y(map_open); + + ystr("name"); + ystr(output->name); + + ystr("active"); + y(bool, output->active); + + ystr("rect"); + y(map_open); + ystr("x"); + y(integer, output->rect.x); + ystr("y"); + y(integer, output->rect.y); + ystr("width"); + y(integer, output->rect.width); + ystr("height"); + y(integer, output->rect.height); + y(map_close); + + ystr("current_workspace"); + if (output->current_workspace == NULL) + y(null); + else y(integer, output->current_workspace->num + 1); + + y(map_close); + } + + y(array_close); + + const unsigned char *payload; + unsigned int length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_OUTPUTS, length); + y(free); +} + +/* + * Callback for the YAJL parser (will be called when a string is parsed). + * + */ +static int add_subscription(void *extra, const unsigned char *s, + unsigned int len) { + ipc_client *client = extra; + + DLOG("should add subscription to extra %p, sub %.*s\n", client, len, s); + int event = client->num_events; + + client->num_events++; + client->events = realloc(client->events, client->num_events * sizeof(char*)); + /* We copy the string because it is not null-terminated and strndup() + * is missing on some BSD systems */ + client->events[event] = scalloc(len+1); + memcpy(client->events[event], s, len); + + DLOG("client is now subscribed to:\n"); + for (int i = 0; i < client->num_events; i++) + DLOG("event %s\n", client->events[i]); + DLOG("(done)\n"); + + return 1; +} + +/* + * Subscribes this connection to the event types which were given as a JSON + * serialized array in the payload field of the message. + * + */ +IPC_HANDLER(subscribe) { + yajl_handle p; + yajl_callbacks callbacks; + yajl_status stat; + ipc_client *current, *client = NULL; + + /* Search the ipc_client structure for this connection */ + TAILQ_FOREACH(current, &all_clients, clients) { + if (current->fd != fd) + continue; + + client = current; + break; + } + + if (client == NULL) { + ELOG("Could not find ipc_client data structure for fd %d\n", fd); + return; + } + + /* Setup the JSON parser */ + memset(&callbacks, 0, sizeof(yajl_callbacks)); + callbacks.yajl_string = add_subscription; + + p = yajl_alloc(&callbacks, NULL, NULL, (void*)client); + stat = yajl_parse(p, (const unsigned char*)message, message_size); + if (stat != yajl_status_ok) { + unsigned char *err; + err = yajl_get_error(p, true, (const unsigned char*)message, + message_size); + ELOG("YAJL parse error: %s\n", err); + yajl_free_error(p, err); + + const char *reply = "{\"success\":false}"; + ipc_send_message(fd, (const unsigned char*)reply, + I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(reply)); + yajl_free(p); + return; + } + yajl_free(p); + const char *reply = "{\"success\":true}"; + ipc_send_message(fd, (const unsigned char*)reply, + I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(reply)); +} +#endif + +/* The index of each callback function corresponds to the numeric + * value of the message type (see include/i3/ipc.h) */ +handler_t handlers[2] = { + handle_command, + handle_tree +}; + /* * Handler for activity on a client connection, receives a message from a * client. @@ -123,11 +450,13 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { close(w->fd); /* Delete the client from the list of clients */ - struct ipc_client *current; + ipc_client *current; TAILQ_FOREACH(current, &all_clients, clients) { if (current->fd != w->fd) continue; + for (int i = 0; i < current->num_events; i++) + free(current->events[i]); /* We can call TAILQ_REMOVE because we break out of the * TAILQ_FOREACH afterwards */ TAILQ_REMOVE(&all_clients, current, clients); @@ -175,7 +504,12 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { message += sizeof(uint32_t); n -= sizeof(uint32_t); - ipc_handle_message(message, n, message_size, message_type); + if (message_type >= (sizeof(handlers) / sizeof(handler_t))) + DLOG("Unhandled message type: %d\n", message_type); + else { + handler_t h = handlers[message_type]; + h(w->fd, message, n, message_size, message_type); + } n -= message_size; message += message_size; } @@ -201,13 +535,13 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents) { set_nonblock(client); - struct ev_io *package = calloc(sizeof(struct ev_io), 1); + struct ev_io *package = scalloc(sizeof(struct ev_io)); ev_io_init(package, ipc_receive_message, client, EV_READ); ev_io_start(EV_A_ package); DLOG("IPC: new client connected\n"); - struct ipc_client *new = calloc(sizeof(struct ipc_client), 1); + ipc_client *new = scalloc(sizeof(ipc_client)); new->fd = client; TAILQ_INSERT_TAIL(&all_clients, new, clients);