2 #define I3__FILE__ "ipc.c"
15 #include <sys/socket.h>
20 #include <yajl/yajl_gen.h>
21 #include <yajl/yajl_parse.h>
33 static
void set_nonblock(
int sockfd) {
34 int flags = fcntl(sockfd, F_GETFL, 0);
36 if (fcntl(sockfd, F_SETFL, flags) < 0)
37 err(-1,
"Could not set O_NONBLOCK");
45 if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
47 if (errno != ENOENT) {
48 ELOG(
"mkdir(%s) failed: %s\n", path, strerror(errno));
53 while (copy[strlen(copy) - 1] ==
'/')
54 copy[strlen(copy) - 1] =
'\0';
56 char *sep = strrchr(copy,
'/');
75 void ipc_send_event(
const char *event, uint32_t message_type,
const char *payload) {
79 bool interested =
false;
80 for (
int i = 0; i < current->
num_events; i++) {
81 if (strcasecmp(current->
events[i], event) != 0)
89 ipc_send_message(current->
fd, strlen(payload), message_type, (
const uint8_t *)payload);
102 shutdown(current->
fd, SHUT_RDWR);
117 char *command =
scalloc(message_size + 1);
118 strncpy(command, (
const char *)message, message_size);
119 LOG(
"IPC: received: *%s*\n", command);
120 yajl_gen gen = yajl_gen_alloc(NULL);
130 const unsigned char *reply;
132 yajl_gen_get_buf(gen, &reply, &length);
135 (
const uint8_t *)reply);
173 for (
int i = 0; i < 8; i++) {
174 if (bind->
mods & (1 << i)) {
176 case XCB_MOD_MASK_SHIFT:
179 case XCB_MOD_MASK_LOCK:
182 case XCB_MOD_MASK_CONTROL:
211 y(integer, (
long int)con);
224 case CT_FLOATING_CON:
225 ystr(
"floating_con");
234 DLOG(
"About to dump unknown container type=%d. This is a bug.\n", con->type);
250 ystr(
"scratchpad_state");
251 switch (con->scratchpad_state) {
252 case SCRATCHPAD_NONE:
255 case SCRATCHPAD_FRESH:
258 case SCRATCHPAD_CHANGED:
264 if (con->percent == 0.0)
267 y(
double, con->percent);
270 y(
bool, con->urgent);
272 if (con->mark != NULL) {
281 switch (con->layout) {
283 DLOG(
"About to dump layout=default, this is a bug in the code.\n");
306 ystr(
"workspace_layout");
307 switch (con->workspace_layout) {
318 DLOG(
"About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
323 ystr(
"last_split_layout");
324 switch (con->layout) {
334 switch (con->border_style) {
346 ystr(
"current_border_width");
347 y(integer, con->current_border_width);
350 dump_rect(gen,
"deco_rect", con->deco_rect);
351 dump_rect(gen,
"window_rect", con->window_rect);
352 dump_rect(gen,
"geometry", con->geometry);
355 if (con->window && con->window->name)
357 else if (con->name != NULL)
362 if (con->type == CT_WORKSPACE) {
364 y(integer, con->num);
369 y(integer, con->window->id);
373 if (con->window && !inplace_restart) {
377 ystr(
"window_properties");
380 #define DUMP_PROPERTY(key, prop_name) \
382 if (con->window->prop_name != NULL) { \
384 ystr(con->window->prop_name); \
392 if (con->window->name != NULL) {
397 ystr(
"transient_for");
398 if (con->window->transient_for == XCB_NONE)
401 y(integer, con->window->transient_for);
409 if (con->type != CT_DOCKAREA || !inplace_restart) {
416 ystr(
"floating_nodes");
418 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
426 y(integer, (
long int)node);
430 ystr(
"fullscreen_mode");
431 y(integer, con->fullscreen_mode);
434 switch (con->floating) {
435 case FLOATING_AUTO_OFF:
438 case FLOATING_AUTO_ON:
441 case FLOATING_USER_OFF:
444 case FLOATING_USER_ON:
458 if (match->
dock != -1) {
460 y(integer, match->
dock);
461 ystr(
"insert_where");
465 #define DUMP_REGEX(re_name) \
467 if (match->re_name != NULL) { \
469 ystr(match->re_name->pattern); \
482 if (inplace_restart) {
483 if (con->window != NULL) {
486 y(integer, con->window->id);
487 ystr(
"restart_mode");
494 if (inplace_restart && con->window != NULL) {
496 y(integer, con->depth);
516 #define YSTR_IF_SET(name) \
518 if (config->name) { \
520 ystr(config->name); \
528 switch (config->
mode) {
541 ystr(
"hidden_state");
583 ystr(
"wheel_up_cmd");
588 ystr(
"wheel_down_cmd");
602 ystr(
"separator_symbol");
606 ystr(
"workspace_buttons");
609 ystr(
"strip_workspace_numbers");
612 ystr(
"binding_mode_indicator");
619 #define YSTR_IF_SET(name) \
621 if (config->colors.name) { \
623 ystr(config->colors.name); \
651 setlocale(LC_NUMERIC,
"C");
654 setlocale(LC_NUMERIC,
"");
656 const unsigned char *payload;
658 y(get_buf, &payload, &length);
681 assert(ws->
type == CT_WORKSPACE);
694 y(
bool, ws == focused_ws);
720 const unsigned char *payload;
722 y(get_buf, &payload, &length);
753 y(integer, output->
rect.
x);
755 y(integer, output->
rect.
y);
762 ystr(
"current_workspace");
774 const unsigned char *payload;
776 y(get_buf, &payload, &length);
793 if (con->
mark != NULL)
798 const unsigned char *payload;
800 y(get_buf, &payload, &length);
815 y(integer, MAJOR_VERSION);
818 y(integer, MINOR_VERSION);
821 y(integer, PATCH_VERSION);
823 ystr(
"human_readable");
828 const unsigned char *payload;
830 y(get_buf, &payload, &length);
845 if (message_size == 0) {
853 const unsigned char *payload;
855 y(get_buf, &payload, &length);
864 char *bar_id =
scalloc(message_size + 1);
865 strncpy(bar_id, (
const char *)message, message_size);
866 LOG(
"IPC: looking for config for bar ID \"%s\"\n", bar_id);
869 if (strcmp(current->
id, bar_id) != 0)
889 const unsigned char *payload;
891 y(get_buf, &payload, &length);
905 DLOG(
"should add subscription to extra %p, sub %.*s\n", client, (
int)len, s);
913 memcpy(client->
events[event], s, len);
915 DLOG(
"client is now subscribed to:\n");
935 if (current->
fd != fd)
942 if (client == NULL) {
943 ELOG(
"Could not find ipc_client data structure for fd %d\n", fd);
948 static yajl_callbacks callbacks = {
952 p =
yalloc(&callbacks, (
void *)client);
953 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
954 if (stat != yajl_status_ok) {
956 err = yajl_get_error(p,
true, (
const unsigned char *)message,
958 ELOG(
"YAJL parse error: %s\n", err);
959 yajl_free_error(p, err);
961 const char *reply =
"{\"success\":false}";
962 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (
const uint8_t *)reply);
967 const char *reply =
"{\"success\":true}";
968 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (
const uint8_t *)reply);
975 handle_get_workspaces,
980 handle_get_bar_config,
995 uint32_t message_type;
996 uint32_t message_length;
997 uint8_t *message = NULL;
999 int ret =
ipc_recv_message(w->fd, &message_type, &message_length, &message);
1003 if (ret == -1 && errno == EAGAIN) {
1015 if (current->
fd != w->fd)
1018 for (
int i = 0; i < current->
num_events; i++)
1019 free(current->
events[i]);
1027 ev_io_stop(EV_A_ w);
1031 DLOG(
"IPC: client disconnected\n");
1036 DLOG(
"Unhandled message type: %d\n", message_type);
1039 h(w->fd, message, 0, message_length, message_type);
1053 struct sockaddr_un peer;
1054 socklen_t len =
sizeof(
struct sockaddr_un);
1056 if ((client = accept(w->fd, (
struct sockaddr *)&peer, &len)) < 0) {
1065 (void)fcntl(client, F_SETFD, FD_CLOEXEC);
1067 set_nonblock(client);
1069 struct ev_io *
package = scalloc(sizeof(struct ev_io));
1071 ev_io_start(EV_A_ package);
1073 DLOG(
"IPC: new client connected on fd %d\n", w->fd);
1092 DLOG(
"Creating IPC-socket at %s\n", resolved);
1093 char *copy =
sstrdup(resolved);
1094 const char *dir = dirname(copy);
1102 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1108 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1110 struct sockaddr_un addr;
1111 memset(&addr, 0,
sizeof(
struct sockaddr_un));
1112 addr.sun_family = AF_LOCAL;
1113 strncpy(addr.sun_path, resolved,
sizeof(addr.sun_path) - 1);
1114 if (bind(sockfd, (
struct sockaddr *)&addr,
sizeof(
struct sockaddr_un)) < 0) {
1120 set_nonblock(sockfd);
1122 if (listen(sockfd, 5) < 0) {
1137 setlocale(LC_NUMERIC,
"C");
1146 if (current == NULL)
1159 setlocale(LC_NUMERIC,
"");
1172 const unsigned char *payload;
1174 y(get_buf, &payload, &length);
1176 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE, (
const char *)payload);
1186 DLOG(
"Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1187 property, con, (con->
window ? con->
window->
id : XCB_WINDOW_NONE));
1189 setlocale(LC_NUMERIC,
"C");
1202 const unsigned char *payload;
1204 y(get_buf, &payload, &length);
1206 ipc_send_event(
"window", I3_IPC_EVENT_WINDOW, (
const char *)payload);
1208 setlocale(LC_NUMERIC,
"");
1215 DLOG(
"Issue barconfig_update event for id = %s\n", barconfig->
id);
1216 setlocale(LC_NUMERIC,
"C");
1221 const unsigned char *payload;
1223 y(get_buf, &payload, &length);
1225 ipc_send_event(
"barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (
const char *)payload);
1227 setlocale(LC_NUMERIC,
"");
1234 DLOG(
"Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->
symbol, bind->
keycode);
1236 setlocale(LC_NUMERIC,
"C");
1250 const unsigned char *payload;
1252 y(get_buf, &payload, &length);
1254 ipc_send_event(
"binding", I3_IPC_EVENT_BINDING, (
const char *)payload);
1257 setlocale(LC_NUMERIC,
"");
const char * i3string_as_utf8(i3String *str)
Returns the UTF-8 encoded version of the i3String.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void ipc_shutdown(void)
Calls shutdown() on each socket and closes it.
char * name
Name of the output.
char * symbol
Symbol the user specified in configfile, if any.
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
bool mkdirp(const char *path)
Emulates mkdir -p (creates any missing folders)
#define TAILQ_REMOVE(head, elm, field)
bool hide_binding_mode_indicator
Hide mode button? Configuration option is 'binding_mode_indicator no' but we invert the bool for the ...
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
static void dump_bar_config(yajl_gen gen, Barconfig *config)
Stores a rectangle, for example the size of a window, the child window etc.
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
enum Barconfig::@5 mode
Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mo...
enum Barconfig::@8 position
Bar position (bottom by default).
struct all_cons_head all_cons
Rect rect
x, y, width, height
CommandResult * parse_command(const char *input, yajl_gen gen)
Parses and executes the given command.
bool verbose
Enable verbose mode? Useful for debugging purposes.
#define TAILQ_FIRST(head)
TAILQ_HEAD(ipc_client_head, ipc_client)
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
An Output is a physical output on your graphics driver.
int ipc_recv_message(int sockfd, uint32_t *message_type, uint32_t *reply_length, uint8_t **reply)
Reads a message from the given socket file descriptor and stores its length (reply_length) as well as...
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
void command_result_free(CommandResult *result)
Frees a CommandResult.
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
void ipc_send_event(const char *event, uint32_t message_type, const char *payload)
Sends the specified event to all IPC clients which are currently connected and subscribed to this kin...
static void dump_binding(yajl_gen gen, Binding *bind)
A struct that contains useful information about the result of a command as a whole (e...
char * command
Command, like in command mode.
int ipc_create_socket(const char *filename)
Creates the UNIX domain socket at the given path, sets it to non-blocking mode, bind()s and listen()s...
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
char * wheel_up_cmd
Command that should be run when mouse wheel up button is pressed over i3bar to override the default b...
struct outputs_head outputs
char * wheel_down_cmd
Command that should be run when mouse wheel down button is pressed over i3bar to override the default...
Con * con
Pointer to the Con which represents this output.
char * id
Automatically generated ID for this bar config.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
enum Barconfig::@7 modifier
Bar modifier (to show bar when in hide mode).
static void dump_rect(yajl_gen gen, const char *name, Rect r)
#define TAILQ_EMPTY(head)
int ipc_send_message(int sockfd, const uint32_t message_size, const uint32_t message_type, const uint8_t *payload)
Formats a message (payload) of the given size and type and sends it to i3 via the given socket file d...
#define yalloc(callbacks, client)
#define YSTR_IF_SET(name)
bool strip_workspace_numbers
Strip workspace numbers? Configuration option is 'strip_workspace_numbers yes'.
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
char * separator_symbol
A custom separator to use instead of a vertical line.
bool con_is_split(Con *con)
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
static int add_subscription(void *extra, const unsigned char *s, ylength len)
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
A 'Con' represents everything from the X11 root window down to a single X11 window.
enum Barconfig::@6 hidden_state
void * scalloc(size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
char * current_socketpath
void ipc_send_workspace_event(const char *change, Con *current, Con *old)
For the workspace events we send, along with the usual "change" field, also the workspace container i...
uint32_t keycode
Keycode to bind.
enum Match::@15 insert_where
#define TAILQ_INSERT_TAIL(head, elm, field)
uint32_t mods
Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, …
bool hide_workspace_buttons
Hide workspace buttons? Configuration option is 'workspace_buttons no' but we invert the bool to get ...
#define TAILQ_HEAD_INITIALIZER(head)
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old)
Generates a json workspace event.
bool path_exists(const char *path)
Checks if the given path exists by calling stat().
void ipc_new_client(EV_P_ struct ev_io *w, int revents)
Handler for activity on the listening socket, meaning that a new client has just connected and we sho...
struct barconfig_head barconfigs
char ** outputs
Outputs on which this bar should show up on.
Con * output_get_content(Con *output)
Returns the output container below the given output container.
#define TAILQ_FOREACH(var, head, field)
void(* handler_t)(int, uint8_t *, int, uint32_t, uint32_t)
#define DUMP_PROPERTY(key, prop_name)
Holds the status bar configuration (i3bar).
void ipc_send_window_event(const char *property, Con *con)
For the window events we send, along the usual "change" field, also the window container, in "container".
#define DUMP_REGEX(re_name)
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
int num_outputs
Number of outputs in the outputs array.
void ipc_send_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.