19#include <sys/socket.h>
23#include <yajl/yajl_gen.h>
24#include <yajl/yajl_parse.h>
33static ev_tstamp kill_timeout = 10.0;
69 struct ev_timer *timeout =
scalloc(1,
sizeof(
struct ev_timer));
71 timeout->data = client;
73 ev_set_priority(timeout, EV_MINPRI);
75 }
else if (result > 0) {
79 ev_timer_set(client->
timeout, kill_timeout, 0.0);
99 const i3_ipc_header_t
header = {
100 .magic = {
'i',
'3',
'-',
'i',
'p',
'c'},
102 .type = message_type};
103 const size_t header_size =
sizeof(i3_ipc_header_t);
104 const size_t message_size = header_size + size;
118 if (client->
fd != exempt_fd) {
119 DLOG(
"Disconnecting client on fd %d\n", client->
fd);
134 for (
int i = 0; i < client->
num_events; i++) {
147void ipc_send_event(
const char *event, uint32_t message_type,
const char *payload) {
150 for (
int i = 0; i < current->
num_events; i++) {
151 if (strcasecmp(current->
events[i], event) == 0) {
176 const unsigned char *payload;
179 y(get_buf, &payload, &length);
180 ipc_send_event(
"shutdown", I3_IPC_EVENT_SHUTDOWN, (
const char *)payload);
198 if (current->
fd != exempt_fd) {
199 shutdown(current->
fd, SHUT_RDWR);
212 char *command =
sstrndup((
const char *)message, message_size);
213 LOG(
"IPC: received: *%.4000s*\n", command);
214 yajl_gen gen = yajl_gen_alloc(NULL);
225 const unsigned char *reply;
227 yajl_gen_get_buf(gen, &reply, &length);
230 (
const uint8_t *)reply);
239 y(integer, (int32_t)r.
x);
241 y(integer, (int32_t)r.
y);
258 y(integer, gaps.
top);
261 y(integer, gaps.
top);
267 y(integer, gaps.
left);
273 for (
int i = 0; i < 20; i++) {
276 case XCB_KEY_BUT_MASK_SHIFT:
279 case XCB_KEY_BUT_MASK_LOCK:
282 case XCB_KEY_BUT_MASK_CONTROL:
285 case XCB_KEY_BUT_MASK_MOD_1:
288 case XCB_KEY_BUT_MASK_MOD_2:
291 case XCB_KEY_BUT_MASK_MOD_3:
294 case XCB_KEY_BUT_MASK_MOD_4:
297 case XCB_KEY_BUT_MASK_MOD_5:
300 case XCB_KEY_BUT_MASK_BUTTON_1:
303 case XCB_KEY_BUT_MASK_BUTTON_2:
306 case XCB_KEY_BUT_MASK_BUTTON_3:
309 case XCB_KEY_BUT_MASK_BUTTON_4:
312 case XCB_KEY_BUT_MASK_BUTTON_5:
342 if (bind->
symbol == NULL) {
356 ystr(
"event_state_mask");
365 y(integer, (uintptr_t)con);
378 case CT_FLOATING_CON:
379 ystr(
"floating_con");
401 ystr(
"scratchpad_state");
403 case SCRATCHPAD_NONE:
406 case SCRATCHPAD_FRESH:
409 case SCRATCHPAD_CHANGED:
435 if (con->
type != CT_ROOT && con->
type != CT_OUTPUT) {
443 DLOG(
"About to dump layout=default, this is a bug in the code.\n");
466 ystr(
"workspace_layout");
478 DLOG(
"About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->
workspace_layout);
483 ystr(
"last_split_layout");
506 ystr(
"current_border_width");
514 dump_rect(gen,
"deco_rect", simulated_deco_rect);
525 }
else if (con->
name != NULL) {
532 ystr(
"title_format");
536 ystr(
"window_icon_padding");
539 if (con->
type == CT_WORKSPACE) {
541 y(integer, con->
num);
570 ystr(
"dropdown_menu");
576 ystr(
"notification");
584 if (con->
window && !inplace_restart) {
588 ystr(
"window_properties");
591#define DUMP_PROPERTY(key, prop_name) \
593 if (con->window->prop_name != NULL) { \
595 ystr(con->window->prop_name); \
609 ystr(
"transient_for");
622 if (con->
type != CT_DOCKAREA || !inplace_restart) {
629 ystr(
"floating_nodes");
631 TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
639 y(integer, (uintptr_t)node);
643 ystr(
"fullscreen_mode");
651 case FLOATING_AUTO_OFF:
654 case FLOATING_AUTO_ON:
657 case FLOATING_USER_OFF:
660 case FLOATING_USER_ON:
675 if (match->
dock != M_DONTCHECK) {
677 y(integer, match->
dock);
678 ystr(
"insert_where");
682#define DUMP_REGEX(re_name) \
684 if (match->re_name != NULL) { \
686 ystr(match->re_name->pattern); \
700 if (inplace_restart) {
701 if (con->
window != NULL) {
705 ystr(
"restart_mode");
712 if (inplace_restart && con->
window != NULL) {
718 ystr(
"previous_workspace_name");
742 y(
bool, current->
release == B_UPON_KEYRELEASE);
752 if (strcasecmp(name,
"primary") == 0 || strcasecmp(name,
"nonprimary") == 0) {
765 if (
config->num_outputs > 0) {
768 for (
int c = 0; c <
config->num_outputs; c++) {
779 ystr(
"tray_outputs");
790#define YSTR_IF_SET(name) \
792 if (config->name) { \
794 ystr(config->name); \
798 ystr(
"tray_padding");
799 y(integer,
config->tray_padding);
817 ystr(
"hidden_state");
818 switch (
config->hidden_state) {
834 if (
config->position == P_BOTTOM) {
846 y(integer,
config->bar_height);
851 if (
config->separator_symbol) {
852 ystr(
"separator_symbol");
856 ystr(
"workspace_buttons");
857 y(
bool, !
config->hide_workspace_buttons);
859 ystr(
"workspace_min_width");
860 y(integer,
config->workspace_min_width);
862 ystr(
"strip_workspace_numbers");
863 y(
bool,
config->strip_workspace_numbers);
865 ystr(
"strip_workspace_name");
866 y(
bool,
config->strip_workspace_name);
868 ystr(
"binding_mode_indicator");
869 y(
bool, !
config->hide_binding_mode_indicator);
875#define YSTR_IF_SET(name) \
877 if (config->colors.name) { \
879 ystr(config->colors.name); \
913 setlocale(LC_NUMERIC,
"C");
916 setlocale(LC_NUMERIC,
"");
918 const unsigned char *payload;
920 y(get_buf, &payload, &length);
944 assert(ws->
type == CT_WORKSPACE);
948 y(integer, (uintptr_t)ws);
960 y(
bool, ws == focused_ws);
965 y(integer, ws->rect.x);
967 y(integer, ws->rect.y);
969 y(integer, ws->rect.width);
971 y(integer, ws->rect.height);
986 const unsigned char *payload;
988 y(get_buf, &payload, &length);
1023 y(integer,
output->rect.width);
1025 y(integer,
output->rect.height);
1028 ystr(
"current_workspace");
1041 const unsigned char *payload;
1043 y(get_buf, &payload, &length);
1068 const unsigned char *payload;
1070 y(get_buf, &payload, &length);
1085 y(integer, MAJOR_VERSION);
1088 y(integer, MINOR_VERSION);
1091 y(integer, PATCH_VERSION);
1093 ystr(
"human_readable");
1096 ystr(
"loaded_config_file_name");
1099 ystr(
"included_config_file_names");
1112 const unsigned char *payload;
1114 y(get_buf, &payload, &length);
1129 if (message_size == 0) {
1137 const unsigned char *payload;
1139 y(get_buf, &payload, &length);
1148 char *bar_id = NULL;
1149 sasprintf(&bar_id,
"%.*s", message_size, message);
1150 LOG(
"IPC: looking for config for bar ID \"%s\"\n", bar_id);
1153 if (strcmp(current->
id, bar_id) != 0) {
1175 const unsigned char *payload;
1177 y(get_buf, &payload, &length);
1197 const unsigned char *payload;
1199 y(get_buf, &payload, &length);
1213 DLOG(
"should add subscription to extra %p, sub %.*s\n", client, (
int)len, s);
1221 memcpy(client->
events[event], s, len);
1223 DLOG(
"client is now subscribed to:\n");
1224 for (
int i = 0; i < client->
num_events; i++) {
1242 static yajl_callbacks callbacks = {
1246 p =
yalloc(&callbacks, (
void *)client);
1247 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1248 if (stat != yajl_status_ok) {
1250 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1252 ELOG(
"YAJL parse error: %s\n", err);
1253 yajl_free_error(p, err);
1255 const char *reply =
"{\"success\":false}";
1261 const char *reply =
"{\"success\":true}";
1264 if (client->first_tick_sent) {
1268 bool is_tick =
false;
1269 for (
int i = 0; i < client->num_events; i++) {
1270 if (strcmp(client->events[i],
"tick") == 0) {
1279 client->first_tick_sent =
true;
1280 const char *payload =
"{\"first\":true,\"payload\":\"\"}";
1296 ystr(
"included_configs");
1302 ystr(
"raw_contents");
1304 ystr(
"variable_replaced_contents");
1312 const unsigned char *payload;
1314 y(get_buf, &payload, &length);
1333 yajl_gen_string(gen, (
unsigned char *)message, message_size);
1337 const unsigned char *payload;
1339 y(get_buf, &payload, &length);
1341 ipc_send_event(
"tick", I3_IPC_EVENT_TICK, (
const char *)payload);
1344 const char *reply =
"{\"success\":true}";
1346 DLOG(
"Sent tick event\n");
1359 memcpy(
state->last_key, val, len);
1365 if (strcasecmp(
state->last_key,
"rnd") == 0) {
1367 }
else if (strcasecmp(
state->last_key,
"window") == 0) {
1368 state->window = (xcb_window_t)val;
1378 static yajl_callbacks callbacks = {
1386 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1388 if (stat != yajl_status_ok) {
1390 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1392 ELOG(
"YAJL parse error: %s\n", err);
1393 yajl_free_error(p, err);
1395 const char *reply =
"{\"success\":false}";
1402 DLOG(
"received IPC sync request (rnd = %d, window = 0x%08x)\n",
state.rnd,
state.window);
1404 const char *reply =
"{\"success\":true}";
1418 const unsigned char *payload;
1420 y(get_buf, &payload, &length);
1430 handle_get_workspaces,
1435 handle_get_bar_config,
1437 handle_get_binding_modes,
1441 handle_get_binding_state,
1455 uint32_t message_type;
1456 uint32_t message_length;
1457 uint8_t *message = NULL;
1459 assert(client->
fd == w->fd);
1461 int ret =
ipc_recv_message(w->fd, &message_type, &message_length, &message);
1465 if (ret == -1 && errno == EAGAIN) {
1478 DLOG(
"Unhandled message type: %d\n", message_type);
1481 h(client, message, 0, message_length, message_type);
1492 char *cmdline = NULL;
1493#if defined(__linux__) && defined(SO_PEERCRED)
1494 struct ucred peercred;
1495 socklen_t so_len =
sizeof(peercred);
1496 if (getsockopt(client->
fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1500 sasprintf(&exepath,
"/proc/%d/cmdline", peercred.pid);
1502 int fd = open(exepath, O_RDONLY);
1507 char buf[512] = {
'\0'};
1508 const ssize_t n = read(fd, buf,
sizeof(buf));
1513 for (
char *walk = buf; walk < buf + n - 1; walk++) {
1514 if (*walk ==
'\0') {
1521 ELOG(
"client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->
fd);
1527 ELOG(
"client %p on fd %d timed out, killing\n", client, client->
fd);
1534 DLOG(
"fd %d writeable\n", w->fd);
1539 assert(client->
timeout != NULL);
1551 struct sockaddr_un peer;
1552 socklen_t len =
sizeof(
struct sockaddr_un);
1554 if ((fd = accept(w->fd, (
struct sockaddr *)&peer, &len)) < 0) {
1555 if (errno != EINTR) {
1562 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1590 DLOG(
"IPC: new client connected on fd %d\n", fd);
1600 setlocale(LC_NUMERIC,
"C");
1609 if (current == NULL) {
1624 setlocale(LC_NUMERIC,
"");
1637 const unsigned char *payload;
1639 y(get_buf, &payload, &length);
1641 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE, (
const char *)payload);
1651 DLOG(
"Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1652 property, con, (con->
window ? con->
window->
id : XCB_WINDOW_NONE));
1654 setlocale(LC_NUMERIC,
"C");
1667 const unsigned char *payload;
1669 y(get_buf, &payload, &length);
1671 ipc_send_event(
"window", I3_IPC_EVENT_WINDOW, (
const char *)payload);
1673 setlocale(LC_NUMERIC,
"");
1680 DLOG(
"Issue barconfig_update event for id = %s\n", barconfig->
id);
1681 setlocale(LC_NUMERIC,
"C");
1686 const unsigned char *payload;
1688 y(get_buf, &payload, &length);
1690 ipc_send_event(
"barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (
const char *)payload);
1692 setlocale(LC_NUMERIC,
"");
1699 DLOG(
"Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->
symbol, bind->
keycode);
1701 setlocale(LC_NUMERIC,
"C");
1711 if (modename == NULL) {
1722 const unsigned char *payload;
1724 y(get_buf, &payload, &length);
1726 ipc_send_event(
"binding", I3_IPC_EVENT_BINDING, (
const char *)payload);
1729 setlocale(LC_NUMERIC,
"");
1736 DLOG(
"ipc_confirm_restart(fd %d)\n", client->
fd);
1737 static const char *reply =
"[{\"success\":true}]";
1739 client, strlen(reply), I3_IPC_REPLY_TYPE_COMMAND,
1740 (
const uint8_t *)reply);
CommandResult * parse_command(const char *input, yajl_gen gen, ipc_client *client)
Parses and executes the given command.
void command_result_free(CommandResult *result)
Frees a CommandResult.
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
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.
bool con_draw_decoration_into_frame(Con *con)
Returns whether the window decoration (title bar) should be drawn into the X11 frame window of this c...
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on.
struct includedfiles_head included_files
struct barconfig_head barconfigs
char * current_configpath
struct pending_marks * marks
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
struct outputs_head outputs
void sync_respond(xcb_window_t window, uint32_t rnd)
struct all_cons_head all_cons
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
const char * i3_version
Git commit identifier, from version.c.
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
char * previous_workspace_name
Stores a copy of the name of the last used workspace for the workspace back-and-forth switching.
static void free_ipc_client(ipc_client *client, int exempt_fd)
static int _sync_json_int(void *extra, long long val)
static void dump_event_state_mask(yajl_gen gen, Binding *bind)
static void ipc_send_shutdown_event(shutdown_reason_t reason)
static void dump_bar_config(yajl_gen gen, Barconfig *config)
#define DUMP_REGEX(re_name)
static void dump_rect(yajl_gen gen, const char *name, Rect r)
static int _sync_json_key(void *extra, const unsigned char *val, size_t len)
static void ipc_client_timeout(EV_P_ ev_timer *w, int revents)
static void dump_binding(yajl_gen gen, Binding *bind)
void ipc_confirm_restart(ipc_client *client)
Sends a restart reply to the IPC client on the specified fd.
ipc_client * ipc_new_client_on_fd(EV_P_ int fd)
ipc_new_client_on_fd() only sets up the event handler for activity on the new connection and inserts ...
char * current_socketpath
void ipc_send_binding_event(const char *event_type, Binding *bind, const char *modename)
For the binding events, we send the serialized binding struct.
static void dump_bar_bindings(yajl_gen gen, Barconfig *config)
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
void ipc_shutdown(shutdown_reason_t reason, int exempt_fd)
Calls shutdown() on each socket and closes it.
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
static void ipc_send_client_message(ipc_client *client, size_t size, const uint32_t message_type, const uint8_t *payload)
#define YSTR_IF_SET(name)
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...
static void dump_gaps(yajl_gen gen, const char *name, gaps_t gaps)
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_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.
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.
static int add_subscription(void *extra, const unsigned char *s, ylength len)
static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents)
static char * canonicalize_output_name(char *name)
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,...
static void ipc_push_pending(ipc_client *client)
#define DUMP_PROPERTY(key, prop_name)
static i3_shmlog_header * header
const char * current_binding_mode
struct ev_loop * main_loop
struct bindings_head * bindings
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),...
void set_nonblock(int sockfd)
Puts the given socket file descriptor into non-blocking mode or dies if setting O_NONBLOCK failed.
const char * i3string_as_utf8(i3String *str)
Returns the UTF-8 encoded version of the i3String.
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...
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...
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...
char * sstrndup(const char *str, size_t size)
Safe-wrapper around strndup which exits if strndup returns NULL (meaning that there is no more memory...
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 SLIST_FOREACH(var, head, field)
#define TAILQ_FOREACH(var, head, field)
#define TAILQ_HEAD(name, type)
#define TAILQ_INSERT_TAIL(head, elm, field)
#define TAILQ_FIRST(head)
#define TAILQ_REMOVE(head, elm, field)
#define TAILQ_HEAD_INITIALIZER(head)
#define TAILQ_EMPTY(head)
#define yalloc(callbacks, client)
#define IPC_HANDLER(name)
void ipc_set_kill_timeout(ev_tstamp new)
Set the maximum duration that we allow for a connection with an unwriteable socket.
void(* handler_t)(ipc_client *, uint8_t *, int, uint32_t, uint32_t)
shutdown_reason_t
Calls to ipc_shutdown() should provide a reason for the shutdown.
@ SHUTDOWN_REASON_RESTART
A struct that contains useful information about the result of a command as a whole (e....
List entry struct for an included file.
char * variable_replaced_contents
The configuration file can contain multiple sets of bindings.
Holds the status bar configuration (i3bar).
char * id
Automatically generated ID for this bar config.
Defines a mouse command to be executed instead of the default behavior when clicking on the non-statu...
bool release
If true, the command will be executed after the button is released.
int input_code
The button to be used (e.g., 1 for "button1").
char * command
The command which is to be executed for this button.
Stores a rectangle, for example the size of a window, the child window etc.
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
char * command
Command, like in command mode.
uint32_t keycode
Keycode to bind.
char * symbol
Symbol the user specified in configfile, if any.
i3_event_state_mask_t event_state_mask
Bitmask which is applied against event->state for KeyPress and KeyRelease events to determine whether...
An Output is a physical output on your graphics driver.
i3String * name
The name of the window.
xcb_atom_t window_type
The _NET_WM_WINDOW_TYPE for this window.
xcb_window_t transient_for
A "match" is a data structure which acts like a mask or expression to match certain windows or not.
enum Match::@15 insert_where
A 'Con' represents everything from the X11 root window down to a single X11 window.
enum Con::@20 scratchpad_state
layout_t workspace_layout
gaps_t gaps
Only applicable for containers of type CT_WORKSPACE.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
int window_icon_padding
Whether the window icon should be displayed, and with what padding.
char * title_format
The format with which the window's name should be displayed.
border_style_t border_style
struct Rect geometry
the geometry this window requested when getting mapped
enum Con::@19 floating
floating? (= not in tiling layout) This cannot be simply a bool because we want to keep track of whet...
fullscreen_mode_t fullscreen_mode
struct ev_io * read_callback
struct ev_timer * timeout
struct ev_io * write_callback