19 #include <sys/socket.h>
23 #include <yajl/yajl_gen.h>
24 #include <yajl/yajl_parse.h>
33 static 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++) {
147 void 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);
224 const unsigned char *reply;
226 yajl_gen_get_buf(gen, &reply, &length);
229 (
const uint8_t *)reply);
238 y(integer, (int32_t)r.
x);
240 y(integer, (int32_t)r.
y);
250 for (
int i = 0; i < 20; i++) {
253 case XCB_KEY_BUT_MASK_SHIFT:
256 case XCB_KEY_BUT_MASK_LOCK:
259 case XCB_KEY_BUT_MASK_CONTROL:
262 case XCB_KEY_BUT_MASK_MOD_1:
265 case XCB_KEY_BUT_MASK_MOD_2:
268 case XCB_KEY_BUT_MASK_MOD_3:
271 case XCB_KEY_BUT_MASK_MOD_4:
274 case XCB_KEY_BUT_MASK_MOD_5:
277 case XCB_KEY_BUT_MASK_BUTTON_1:
280 case XCB_KEY_BUT_MASK_BUTTON_2:
283 case XCB_KEY_BUT_MASK_BUTTON_3:
286 case XCB_KEY_BUT_MASK_BUTTON_4:
289 case XCB_KEY_BUT_MASK_BUTTON_5:
332 ystr(
"event_state_mask");
341 y(integer, (uintptr_t)con);
354 case CT_FLOATING_CON:
355 ystr(
"floating_con");
376 ystr(
"scratchpad_state");
377 switch (con->scratchpad_state) {
378 case SCRATCHPAD_NONE:
381 case SCRATCHPAD_FRESH:
384 case SCRATCHPAD_CHANGED:
390 if (con->percent == 0.0)
393 y(
double, con->percent);
396 y(
bool, con->urgent);
409 if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
415 switch (con->layout) {
417 DLOG(
"About to dump layout=default, this is a bug in the code.\n");
440 ystr(
"workspace_layout");
441 switch (con->workspace_layout) {
452 DLOG(
"About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
457 ystr(
"last_split_layout");
458 switch (con->layout) {
468 switch (con->border_style) {
480 ystr(
"current_border_width");
481 y(integer, con->current_border_width);
484 dump_rect(gen,
"deco_rect", con->deco_rect);
485 dump_rect(gen,
"window_rect", con->window_rect);
486 dump_rect(gen,
"geometry", con->geometry);
489 if (con->window && con->window->name)
491 else if (con->name != NULL)
496 if (con->title_format != NULL) {
497 ystr(
"title_format");
498 ystr(con->title_format);
501 ystr(
"window_icon_padding");
502 y(integer, con->window_icon_padding);
504 if (con->type == CT_WORKSPACE) {
506 y(integer, con->num);
511 y(integer, con->window->id);
517 if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NORMAL) {
519 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DOCK) {
521 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DIALOG) {
523 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_UTILITY) {
525 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLBAR) {
527 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_SPLASH) {
529 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_MENU) {
531 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) {
532 ystr(
"dropdown_menu");
533 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_POPUP_MENU) {
535 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLTIP) {
537 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NOTIFICATION) {
538 ystr(
"notification");
545 if (con->window && !inplace_restart) {
549 ystr(
"window_properties");
552 #define DUMP_PROPERTY(key, prop_name) \
554 if (con->window->prop_name != NULL) { \
556 ystr(con->window->prop_name); \
565 if (con->window->name != NULL) {
570 ystr(
"transient_for");
571 if (con->window->transient_for == XCB_NONE)
574 y(integer, con->window->transient_for);
582 if (con->type != CT_DOCKAREA || !inplace_restart) {
589 ystr(
"floating_nodes");
591 TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
599 y(integer, (uintptr_t)node);
603 ystr(
"fullscreen_mode");
604 y(integer, con->fullscreen_mode);
607 y(
bool, con->sticky);
610 switch (con->floating) {
611 case FLOATING_AUTO_OFF:
614 case FLOATING_AUTO_ON:
617 case FLOATING_USER_OFF:
620 case FLOATING_USER_ON:
634 if (match->
dock != M_DONTCHECK) {
636 y(integer, match->
dock);
637 ystr(
"insert_where");
641 #define DUMP_REGEX(re_name) \
643 if (match->re_name != NULL) { \
645 ystr(match->re_name->pattern); \
659 if (inplace_restart) {
660 if (con->window != NULL) {
663 y(integer, con->window->id);
664 ystr(
"restart_mode");
671 if (inplace_restart && con->window != NULL) {
673 y(integer, con->depth);
677 ystr(
"previous_workspace_name");
700 y(
bool, current->
release == B_UPON_KEYRELEASE);
710 if (strcasecmp(name,
"primary") == 0) {
723 if (
config->num_outputs > 0) {
726 for (
int c = 0; c <
config->num_outputs; c++) {
737 ystr(
"tray_outputs");
748 #define YSTR_IF_SET(name) \
750 if (config->name) { \
752 ystr(config->name); \
756 ystr(
"tray_padding");
757 y(integer,
config->tray_padding);
775 ystr(
"hidden_state");
776 switch (
config->hidden_state) {
792 if (
config->position == P_BOTTOM)
800 if (
config->separator_symbol) {
801 ystr(
"separator_symbol");
805 ystr(
"workspace_buttons");
806 y(
bool, !
config->hide_workspace_buttons);
808 ystr(
"workspace_min_width");
809 y(integer,
config->workspace_min_width);
811 ystr(
"strip_workspace_numbers");
812 y(
bool,
config->strip_workspace_numbers);
814 ystr(
"strip_workspace_name");
815 y(
bool,
config->strip_workspace_name);
817 ystr(
"binding_mode_indicator");
818 y(
bool, !
config->hide_binding_mode_indicator);
824 #define YSTR_IF_SET(name) \
826 if (config->colors.name) { \
828 ystr(config->colors.name); \
862 setlocale(LC_NUMERIC,
"C");
865 setlocale(LC_NUMERIC,
"");
867 const unsigned char *payload;
869 y(get_buf, &payload, &length);
892 assert(ws->
type == CT_WORKSPACE);
896 y(integer, (uintptr_t)ws);
908 y(
bool, ws == focused_ws);
913 y(integer, ws->rect.x);
915 y(integer, ws->rect.y);
917 y(integer, ws->rect.width);
919 y(integer, ws->rect.height);
934 const unsigned char *payload;
936 y(get_buf, &payload, &length);
971 y(integer,
output->rect.width);
973 y(integer,
output->rect.height);
976 ystr(
"current_workspace");
988 const unsigned char *payload;
990 y(get_buf, &payload, &length);
1015 const unsigned char *payload;
1017 y(get_buf, &payload, &length);
1032 y(integer, MAJOR_VERSION);
1035 y(integer, MINOR_VERSION);
1038 y(integer, PATCH_VERSION);
1040 ystr(
"human_readable");
1043 ystr(
"loaded_config_file_name");
1046 ystr(
"included_config_file_names");
1059 const unsigned char *payload;
1061 y(get_buf, &payload, &length);
1076 if (message_size == 0) {
1084 const unsigned char *payload;
1086 y(get_buf, &payload, &length);
1095 char *bar_id = NULL;
1096 sasprintf(&bar_id,
"%.*s", message_size, message);
1097 LOG(
"IPC: looking for config for bar ID \"%s\"\n", bar_id);
1100 if (strcmp(current->
id, bar_id) != 0)
1121 const unsigned char *payload;
1123 y(get_buf, &payload, &length);
1143 const unsigned char *payload;
1145 y(get_buf, &payload, &length);
1159 DLOG(
"should add subscription to extra %p, sub %.*s\n", client, (
int)len, s);
1167 memcpy(client->
events[event], s, len);
1169 DLOG(
"client is now subscribed to:\n");
1170 for (
int i = 0; i < client->
num_events; i++) {
1188 static yajl_callbacks callbacks = {
1192 p =
yalloc(&callbacks, (
void *)client);
1193 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1194 if (stat != yajl_status_ok) {
1196 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1198 ELOG(
"YAJL parse error: %s\n", err);
1199 yajl_free_error(p, err);
1201 const char *reply =
"{\"success\":false}";
1207 const char *reply =
"{\"success\":true}";
1210 if (client->first_tick_sent) {
1214 bool is_tick =
false;
1215 for (
int i = 0; i < client->num_events; i++) {
1216 if (strcmp(client->events[i],
"tick") == 0) {
1225 client->first_tick_sent =
true;
1226 const char *payload =
"{\"first\":true,\"payload\":\"\"}";
1242 ystr(
"included_configs");
1248 ystr(
"raw_contents");
1250 ystr(
"variable_replaced_contents");
1258 const unsigned char *payload;
1260 y(get_buf, &payload, &length);
1279 yajl_gen_string(gen, (
unsigned char *)message, message_size);
1283 const unsigned char *payload;
1285 y(get_buf, &payload, &length);
1287 ipc_send_event(
"tick", I3_IPC_EVENT_TICK, (
const char *)payload);
1290 const char *reply =
"{\"success\":true}";
1292 DLOG(
"Sent tick event\n");
1305 memcpy(
state->last_key, val, len);
1311 if (strcasecmp(
state->last_key,
"rnd") == 0) {
1313 }
else if (strcasecmp(
state->last_key,
"window") == 0) {
1314 state->window = (xcb_window_t)val;
1324 static yajl_callbacks callbacks = {
1332 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1334 if (stat != yajl_status_ok) {
1336 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1338 ELOG(
"YAJL parse error: %s\n", err);
1339 yajl_free_error(p, err);
1341 const char *reply =
"{\"success\":false}";
1348 DLOG(
"received IPC sync request (rnd = %d, window = 0x%08x)\n",
state.rnd,
state.window);
1350 const char *reply =
"{\"success\":true}";
1364 const unsigned char *payload;
1366 y(get_buf, &payload, &length);
1376 handle_get_workspaces,
1381 handle_get_bar_config,
1383 handle_get_binding_modes,
1387 handle_get_binding_state,
1401 uint32_t message_type;
1402 uint32_t message_length;
1403 uint8_t *message = NULL;
1405 assert(client->
fd == w->fd);
1407 int ret =
ipc_recv_message(w->fd, &message_type, &message_length, &message);
1411 if (ret == -1 && errno == EAGAIN) {
1424 DLOG(
"Unhandled message type: %d\n", message_type);
1427 h(client, message, 0, message_length, message_type);
1438 char *cmdline = NULL;
1439 #
if defined(__linux__) && defined(SO_PEERCRED)
1440 struct ucred peercred;
1441 socklen_t so_len =
sizeof(peercred);
1442 if (getsockopt(client->
fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1446 sasprintf(&exepath,
"/proc/%d/cmdline", peercred.pid);
1448 int fd = open(exepath, O_RDONLY);
1453 char buf[512] = {
'\0'};
1454 const ssize_t n = read(fd, buf,
sizeof(buf));
1459 for (
char *walk = buf; walk < buf + n - 1; walk++) {
1460 if (*walk ==
'\0') {
1467 ELOG(
"client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->
fd);
1473 ELOG(
"client %p on fd %d timed out, killing\n", client, client->
fd);
1480 DLOG(
"fd %d writeable\n", w->fd);
1485 assert(client->
timeout != NULL);
1497 struct sockaddr_un peer;
1498 socklen_t len =
sizeof(
struct sockaddr_un);
1500 if ((fd = accept(w->fd, (
struct sockaddr *)&peer, &len)) < 0) {
1501 if (errno != EINTR) {
1508 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1536 DLOG(
"IPC: new client connected on fd %d\n", fd);
1546 setlocale(LC_NUMERIC,
"C");
1555 if (current == NULL)
1568 setlocale(LC_NUMERIC,
"");
1581 const unsigned char *payload;
1583 y(get_buf, &payload, &length);
1585 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE, (
const char *)payload);
1595 DLOG(
"Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1596 property, con, (con->
window ? con->
window->
id : XCB_WINDOW_NONE));
1598 setlocale(LC_NUMERIC,
"C");
1611 const unsigned char *payload;
1613 y(get_buf, &payload, &length);
1615 ipc_send_event(
"window", I3_IPC_EVENT_WINDOW, (
const char *)payload);
1617 setlocale(LC_NUMERIC,
"");
1624 DLOG(
"Issue barconfig_update event for id = %s\n", barconfig->
id);
1625 setlocale(LC_NUMERIC,
"C");
1630 const unsigned char *payload;
1632 y(get_buf, &payload, &length);
1634 ipc_send_event(
"barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (
const char *)payload);
1636 setlocale(LC_NUMERIC,
"");
1643 DLOG(
"Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->
symbol, bind->
keycode);
1645 setlocale(LC_NUMERIC,
"C");
1659 const unsigned char *payload;
1661 y(get_buf, &payload, &length);
1663 ipc_send_event(
"binding", I3_IPC_EVENT_BINDING, (
const char *)payload);
1666 setlocale(LC_NUMERIC,
"");
1673 DLOG(
"ipc_confirm_restart(fd %d)\n", client->
fd);
1674 static const char *reply =
"[{\"success\":true}]";
1676 client, strlen(reply), I3_IPC_REPLY_TYPE_COMMAND,
1677 (
const uint8_t *)reply);