15 #include <sys/socket.h> 20 #include <yajl/yajl_gen.h> 21 #include <yajl/yajl_parse.h> 35 int flags = fcntl(sockfd, F_GETFL, 0);
37 if (fcntl(sockfd, F_SETFL, flags) < 0)
38 err(-1,
"Could not set O_NONBLOCK");
47 const size_t size = strlen(payload);
48 const i3_ipc_header_t
header = {
49 .magic = {
'i',
'3',
'-',
'i',
'p',
'c'},
51 .type = message_type};
52 const size_t header_size =
sizeof(i3_ipc_header_t);
53 const size_t message_size = header_size + size;
120 struct ev_timer *timeout =
scalloc(1,
sizeof(
struct ev_timer));
122 timeout->data = client;
124 ev_set_priority(timeout, EV_MINPRI);
126 }
else if (result > 0) {
148 void ipc_send_event(
const char *event, uint32_t message_type,
const char *payload) {
152 bool interested =
false;
153 for (
int i = 0; i < current->
num_events; i++) {
154 if (strcasecmp(current->
events[i], event) != 0)
187 const unsigned char *payload;
190 y(get_buf, &payload, &length);
191 ipc_send_event(
"shutdown", I3_IPC_EVENT_SHUTDOWN, (
const char *)payload);
207 shutdown(current->
fd, SHUT_RDWR);
220 char *command =
scalloc(message_size + 1, 1);
221 strncpy(command, (
const char *)message, message_size);
222 LOG(
"IPC: received: *%s*\n", command);
223 yajl_gen gen = yajl_gen_alloc(NULL);
233 const unsigned char *reply;
235 yajl_gen_get_buf(gen, &reply, &length);
238 (
const uint8_t *)reply);
259 for (
int i = 0; i < 20; i++) {
262 case XCB_KEY_BUT_MASK_SHIFT:
265 case XCB_KEY_BUT_MASK_LOCK:
268 case XCB_KEY_BUT_MASK_CONTROL:
271 case XCB_KEY_BUT_MASK_MOD_1:
274 case XCB_KEY_BUT_MASK_MOD_2:
277 case XCB_KEY_BUT_MASK_MOD_3:
280 case XCB_KEY_BUT_MASK_MOD_4:
283 case XCB_KEY_BUT_MASK_MOD_5:
286 case XCB_KEY_BUT_MASK_BUTTON_1:
289 case XCB_KEY_BUT_MASK_BUTTON_2:
292 case XCB_KEY_BUT_MASK_BUTTON_3:
295 case XCB_KEY_BUT_MASK_BUTTON_4:
298 case XCB_KEY_BUT_MASK_BUTTON_5:
341 ystr(
"event_state_mask");
350 y(integer, (uintptr_t)con);
363 case CT_FLOATING_CON:
364 ystr(
"floating_con");
385 ystr(
"scratchpad_state");
386 switch (con->scratchpad_state) {
387 case SCRATCHPAD_NONE:
390 case SCRATCHPAD_FRESH:
393 case SCRATCHPAD_CHANGED:
399 if (con->percent == 0.0)
402 y(
double, con->percent);
405 y(
bool, con->urgent);
422 if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
428 switch (con->layout) {
430 DLOG(
"About to dump layout=default, this is a bug in the code.\n");
453 ystr(
"workspace_layout");
454 switch (con->workspace_layout) {
465 DLOG(
"About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
470 ystr(
"last_split_layout");
471 switch (con->layout) {
481 switch (con->border_style) {
493 ystr(
"current_border_width");
494 y(integer, con->current_border_width);
497 dump_rect(gen,
"deco_rect", con->deco_rect);
498 dump_rect(gen,
"window_rect", con->window_rect);
499 dump_rect(gen,
"geometry", con->geometry);
502 if (con->window && con->window->name)
504 else if (con->name != NULL)
509 if (con->title_format != NULL) {
510 ystr(
"title_format");
511 ystr(con->title_format);
514 if (con->type == CT_WORKSPACE) {
516 y(integer, con->num);
521 y(integer, con->window->id);
525 if (con->window && !inplace_restart) {
529 ystr(
"window_properties");
532 #define DUMP_PROPERTY(key, prop_name) \ 534 if (con->window->prop_name != NULL) { \ 536 ystr(con->window->prop_name); \ 544 if (con->window->name != NULL) {
549 ystr(
"transient_for");
550 if (con->window->transient_for == XCB_NONE)
553 y(integer, con->window->transient_for);
561 if (con->type != CT_DOCKAREA || !inplace_restart) {
568 ystr(
"floating_nodes");
570 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
578 y(integer, (uintptr_t)node);
582 ystr(
"fullscreen_mode");
583 y(integer, con->fullscreen_mode);
586 y(
bool, con->sticky);
589 switch (con->floating) {
590 case FLOATING_AUTO_OFF:
593 case FLOATING_AUTO_ON:
596 case FLOATING_USER_OFF:
599 case FLOATING_USER_ON:
613 if (match->
dock != M_DONTCHECK) {
615 y(integer, match->
dock);
616 ystr(
"insert_where");
620 #define DUMP_REGEX(re_name) \ 622 if (match->re_name != NULL) { \ 624 ystr(match->re_name->pattern); \ 637 if (inplace_restart) {
638 if (con->window != NULL) {
641 y(integer, con->window->id);
642 ystr(
"restart_mode");
649 if (inplace_restart && con->window != NULL) {
651 y(integer, con->depth);
673 y(
bool, current->
release == B_UPON_KEYRELEASE);
683 if (strcasecmp(name,
"primary") == 0) {
710 ystr(
"tray_outputs");
721 #define YSTR_IF_SET(name) \ 723 if (config->name) { \ 725 ystr(config->name); \ 729 ystr(
"tray_padding");
735 switch (config->
mode) {
748 ystr(
"hidden_state");
774 ystr(
"separator_symbol");
778 ystr(
"workspace_buttons");
781 ystr(
"strip_workspace_numbers");
784 ystr(
"strip_workspace_name");
787 ystr(
"binding_mode_indicator");
794 #define YSTR_IF_SET(name) \ 796 if (config->colors.name) { \ 798 ystr(config->colors.name); \ 832 setlocale(LC_NUMERIC,
"C");
835 setlocale(LC_NUMERIC,
"");
837 const unsigned char *payload;
839 y(get_buf, &payload, &length);
862 assert(ws->
type == CT_WORKSPACE);
875 y(
bool, ws == focused_ws);
901 const unsigned char *payload;
903 y(get_buf, &payload, &length);
934 y(integer, output->
rect.
x);
936 y(integer, output->
rect.
y);
943 ystr(
"current_workspace");
955 const unsigned char *payload;
957 y(get_buf, &payload, &length);
982 const unsigned char *payload;
984 y(get_buf, &payload, &length);
999 y(integer, MAJOR_VERSION);
1002 y(integer, MINOR_VERSION);
1005 y(integer, PATCH_VERSION);
1007 ystr(
"human_readable");
1010 ystr(
"loaded_config_file_name");
1015 const unsigned char *payload;
1017 y(get_buf, &payload, &length);
1032 if (message_size == 0) {
1040 const unsigned char *payload;
1042 y(get_buf, &payload, &length);
1051 char *bar_id = NULL;
1052 sasprintf(&bar_id,
"%.*s", message_size, message);
1053 LOG(
"IPC: looking for config for bar ID \"%s\"\n", bar_id);
1056 if (strcmp(current->
id, bar_id) != 0)
1077 const unsigned char *payload;
1079 y(get_buf, &payload, &length);
1099 const unsigned char *payload;
1101 y(get_buf, &payload, &length);
1115 DLOG(
"should add subscription to extra %p, sub %.*s\n", client, (
int)len, s);
1123 memcpy(client->
events[event], s, len);
1125 DLOG(
"client is now subscribed to:\n");
1126 for (
int i = 0; i < client->
num_events; i++) {
1146 if (current->
fd != fd)
1153 if (client == NULL) {
1154 ELOG(
"Could not find ipc_client data structure for fd %d\n", fd);
1159 static yajl_callbacks callbacks = {
1163 p =
yalloc(&callbacks, (
void *)client);
1164 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1165 if (stat != yajl_status_ok) {
1167 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1169 ELOG(
"YAJL parse error: %s\n", err);
1170 yajl_free_error(p, err);
1172 const char *reply =
"{\"success\":false}";
1173 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (
const uint8_t *)reply);
1178 const char *reply =
"{\"success\":true}";
1179 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (
const uint8_t *)reply);
1181 if (client->first_tick_sent) {
1185 bool is_tick =
false;
1186 for (
int i = 0; i < client->num_events; i++) {
1187 if (strcmp(client->events[i],
"tick") == 0) {
1196 client->first_tick_sent =
true;
1197 const char *payload =
"{\"first\":true,\"payload\":\"\"}";
1198 ipc_send_message(client->fd, strlen(payload), I3_IPC_EVENT_TICK, (
const uint8_t *)payload);
1214 const unsigned char *payload;
1216 y(get_buf, &payload, &length);
1235 yajl_gen_string(gen, (
unsigned char *)message, message_size);
1239 const unsigned char *payload;
1241 y(get_buf, &payload, &length);
1243 ipc_send_event(
"tick", I3_IPC_EVENT_TICK, (
const char *)payload);
1246 const char *reply =
"{\"success\":true}";
1247 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (
const uint8_t *)reply);
1248 DLOG(
"Sent tick event\n");
1267 if (strcasecmp(state->
last_key,
"rnd") == 0) {
1269 }
else if (strcasecmp(state->
last_key,
"window") == 0) {
1270 state->
window = (xcb_window_t)val;
1280 static yajl_callbacks callbacks = {
1286 memset(&state,
'\0',
sizeof(
struct sync_state));
1287 p =
yalloc(&callbacks, (
void *)&state);
1288 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1289 FREE(state.last_key);
1290 if (stat != yajl_status_ok) {
1292 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1294 ELOG(
"YAJL parse error: %s\n", err);
1295 yajl_free_error(p, err);
1297 const char *reply =
"{\"success\":false}";
1298 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (
const uint8_t *)reply);
1304 DLOG(
"received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
1306 const char *reply =
"{\"success\":true}";
1307 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (
const uint8_t *)reply);
1314 handle_get_workspaces,
1319 handle_get_bar_config,
1321 handle_get_binding_modes,
1338 uint32_t message_type;
1339 uint32_t message_length;
1340 uint8_t *message = NULL;
1342 int ret =
ipc_recv_message(w->fd, &message_type, &message_length, &message);
1346 if (ret == -1 && errno == EAGAIN) {
1353 bool closed =
false;
1356 if (current->
fd != w->fd)
1367 ev_io_stop(EV_A_ w);
1371 DLOG(
"IPC: client disconnected\n");
1376 DLOG(
"Unhandled message type: %d\n", message_type);
1379 h(w->fd, message, 0, message_length, message_type);
1390 char *cmdline = NULL;
1391 #
if defined(__linux__) && defined(SO_PEERCRED)
1392 struct ucred peercred;
1393 socklen_t so_len =
sizeof(peercred);
1394 if (getsockopt(client->
fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1398 sasprintf(&exepath,
"/proc/%d/cmdline", peercred.pid);
1400 int fd = open(exepath, O_RDONLY);
1405 char buf[512] = {
'\0'};
1406 const ssize_t n = read(fd, buf,
sizeof(buf));
1411 for (
char *walk = buf; walk < buf + n - 1; walk++) {
1412 if (*walk ==
'\0') {
1419 ELOG(
"client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->
fd);
1425 ELOG(
"client %p on fd %d timed out, killing\n", client, client->
fd);
1432 DLOG(
"fd %d writeable\n", w->fd);
1437 assert(client->
timeout != NULL);
1449 struct sockaddr_un peer;
1450 socklen_t len =
sizeof(
struct sockaddr_un);
1452 if ((client = accept(w->fd, (
struct sockaddr *)&peer, &len)) < 0) {
1461 (void)fcntl(client, F_SETFD, FD_CLOEXEC);
1465 struct ev_io *
package = scalloc(1, sizeof(struct ev_io));
1467 ev_io_start(EV_A_ package);
1471 package = scalloc(1, sizeof(struct ev_io));
1472 package->data =
new;
1475 DLOG(
"IPC: new client connected on fd %d\n", w->fd);
1478 new->callback = package;
1494 DLOG(
"Creating IPC-socket at %s\n", resolved);
1495 char *copy =
sstrdup(resolved);
1496 const char *dir = dirname(copy);
1504 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1510 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1512 struct sockaddr_un addr;
1513 memset(&addr, 0,
sizeof(
struct sockaddr_un));
1514 addr.sun_family = AF_LOCAL;
1515 strncpy(addr.sun_path, resolved,
sizeof(addr.sun_path) - 1);
1516 if (bind(sockfd, (
struct sockaddr *)&addr,
sizeof(
struct sockaddr_un)) < 0) {
1524 if (listen(sockfd, 5) < 0) {
1539 setlocale(LC_NUMERIC,
"C");
1548 if (current == NULL)
1561 setlocale(LC_NUMERIC,
"");
1574 const unsigned char *payload;
1576 y(get_buf, &payload, &length);
1578 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE, (
const char *)payload);
1588 DLOG(
"Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1589 property, con, (con->
window ? con->
window->
id : XCB_WINDOW_NONE));
1591 setlocale(LC_NUMERIC,
"C");
1604 const unsigned char *payload;
1606 y(get_buf, &payload, &length);
1608 ipc_send_event(
"window", I3_IPC_EVENT_WINDOW, (
const char *)payload);
1610 setlocale(LC_NUMERIC,
"");
1617 DLOG(
"Issue barconfig_update event for id = %s\n", barconfig->
id);
1618 setlocale(LC_NUMERIC,
"C");
1623 const unsigned char *payload;
1625 y(get_buf, &payload, &length);
1627 ipc_send_event(
"barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (
const char *)payload);
1629 setlocale(LC_NUMERIC,
"");
1636 DLOG(
"Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->
symbol, bind->
keycode);
1638 setlocale(LC_NUMERIC,
"C");
1652 const unsigned char *payload;
1654 y(get_buf, &payload, &length);
1656 ipc_send_event(
"binding", I3_IPC_EVENT_BINDING, (
const char *)payload);
1659 setlocale(LC_NUMERIC,
"");
int num_outputs
Number of outputs in the outputs array.
struct outputs_head outputs
#define SLIST_FOREACH(var, head, field)
void(* handler_t)(int, uint8_t *, int, uint32_t, uint32_t)
uint32_t keycode
Keycode to bind.
void * srealloc(void *ptr, size_t size)
Safe-wrapper around realloc which exits if realloc returns NULL (meaning that there is no more memory...
#define YSTR_IF_SET(name)
static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents)
char * current_socketpath
static int _sync_json_int(void *extra, long long val)
struct barconfig_head barconfigs
bool strip_workspace_name
Strip workspace name? Configuration option is 'strip_workspace_name yes'.
char * symbol
Symbol the user specified in configfile, if any.
char * command
Command, like in command mode.
#define TAILQ_HEAD(name, type)
static void dump_bar_config(yajl_gen gen, Barconfig *config)
#define TAILQ_EMPTY(head)
void * scalloc(size_t num, size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
An Output is a physical output on your graphics driver.
A struct that contains useful information about the result of a command as a whole (e...
int mkdirp(const char *path, mode_t mode)
Emulates mkdir -p (creates any missing folders)
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
static void dump_event_state_mask(yajl_gen gen, Binding *bind)
static void dump_bar_bindings(yajl_gen gen, Barconfig *config)
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
bool path_exists(const char *path)
Checks if the given path exists by calling stat().
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
struct bindings_head * bindings
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".
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...
#define yalloc(callbacks, client)
The configuration file can contain multiple sets of bindings.
Rect rect
x, y, width, height
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
Defines a mouse command to be executed instead of the default behavior when clicking on the non-statu...
enum Match::@17 insert_where
bool verbose
Enable verbose mode? Useful for debugging purposes.
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
void ipc_send_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.
#define TAILQ_REMOVE(head, elm, field)
static void set_nonblock(int sockfd)
#define TAILQ_FIRST(head)
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
Con * con
Pointer to the Con which represents this output.
enum Barconfig::@10 hidden_state
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
static int add_subscription(void *extra, const unsigned char *s, ylength len)
Stores a rectangle, for example the size of a window, the child window etc.
void command_result_free(CommandResult *result)
Frees a CommandResult.
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
bool hide_binding_mode_indicator
Hide mode button? Configuration option is 'binding_mode_indicator no' but we invert the bool for the ...
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
char ** outputs
Outputs on which this bar should show up on.
bool release
If true, the command will be executed after the button is released.
#define DUMP_PROPERTY(key, prop_name)
char * current_configpath
static void dump_binding(yajl_gen gen, Binding *bind)
#define DUMP_REGEX(re_name)
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
bool con_is_split(Con *con)
Returns true if a container should be considered split.
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
enum Barconfig::@9 mode
Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mo...
Con * output_get_content(Con *output)
Returns the output container below the given output container.
struct ev_timer * timeout
void ipc_set_kill_timeout(ev_tstamp new)
Set the maximum duration that we allow for a connection with an unwriteable socket.
static ev_tstamp kill_timeout
uint32_t modifier
Bar modifier (to show bar when in hide mode).
void ipc_shutdown(shutdown_reason_t reason)
Calls shutdown() on each socket and closes it.
static void ipc_push_pending(ipc_client *client)
bool strip_workspace_numbers
Strip workspace numbers? Configuration option is 'strip_workspace_numbers yes'.
static void ipc_send_shutdown_event(shutdown_reason_t reason)
shutdown_reason_t
Calls to ipc_shutdown() should provide a reason for the shutdown.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
struct all_cons_head all_cons
char * command
The command which is to be executed for this button.
Holds the status bar configuration (i3bar).
CommandResult * parse_command(const char *input, yajl_gen gen)
Parses and executes the given command.
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...
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old)
Generates a json workspace event.
A 'Con' represents everything from the X11 root window down to a single X11 window.
ssize_t writeall_nonblock(int fd, const void *buf, size_t count)
Like writeall, but instead of retrying upon EAGAIN (returned when a write would block), the function stops and returns the total number of bytes written so far.
static int _sync_json_key(void *extra, const unsigned char *val, size_t len)
const char * i3string_as_utf8(i3String *str)
Returns the UTF-8 encoded version of the i3String.
bool hide_workspace_buttons
Hide workspace buttons? Configuration option is 'workspace_buttons no' but we invert the bool to get ...
void sync_respond(xcb_window_t window, uint32_t rnd)
const char * i3_version
Git commit identifier, from version.c.
static void dump_rect(yajl_gen gen, const char *name, Rect r)
static char * canonicalize_output_name(char *name)
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
char * separator_symbol
A custom separator to use instead of a vertical line.
struct ev_loop * main_loop
int input_code
The button to be used (e.g., 1 for "button1").
enum Barconfig::@11 position
Bar position (bottom by default).
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...
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
static void append_payload(ipc_client *client, uint32_t message_type, const char *payload)
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...
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...
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...
static void ipc_client_timeout(EV_P_ ev_timer *w, int revents)
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
#define TAILQ_HEAD_INITIALIZER(head)
#define TAILQ_INSERT_TAIL(head, elm, field)
#define TAILQ_FOREACH(var, head, field)
static i3_shmlog_header * header
static void free_ipc_client(ipc_client *client)
i3_event_state_mask_t event_state_mask
Bitmask which is applied against event->state for KeyPress and KeyRelease events to determine whether...
char * id
Automatically generated ID for this bar config.