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);
36 if (flags & O_NONBLOCK) {
40 if (fcntl(sockfd, F_SETFL, flags) < 0)
41 err(-1,
"Could not set O_NONBLOCK");
83 struct ev_timer *timeout =
scalloc(1,
sizeof(
struct ev_timer));
85 timeout->data = client;
87 ev_set_priority(timeout, EV_MINPRI);
89 }
else if (result > 0) {
113 const i3_ipc_header_t
header = {
114 .magic = {
'i',
'3',
'-',
'i',
'p',
'c'},
116 .type = message_type};
117 const size_t header_size =
sizeof(i3_ipc_header_t);
118 const size_t message_size = header_size + size;
132 if (client->
fd != exempt_fd) {
133 DLOG(
"Disconnecting client on fd %d\n", client->
fd);
148 for (
int i = 0; i < client->
num_events; i++) {
161 void ipc_send_event(
const char *event, uint32_t message_type,
const char *payload) {
164 for (
int i = 0; i < current->
num_events; i++) {
165 if (strcasecmp(current->
events[i], event) == 0) {
190 const unsigned char *payload;
193 y(get_buf, &payload, &length);
194 ipc_send_event(
"shutdown", I3_IPC_EVENT_SHUTDOWN, (
const char *)payload);
212 if (current->
fd != exempt_fd) {
213 shutdown(current->
fd, SHUT_RDWR);
227 char *command =
sstrndup((
const char *)message, message_size);
228 LOG(
"IPC: received: *%s*\n", command);
229 yajl_gen gen = yajl_gen_alloc(NULL);
239 const unsigned char *reply;
241 yajl_gen_get_buf(gen, &reply, &length);
244 (
const uint8_t *)reply);
265 for (
int i = 0; i < 20; i++) {
268 case XCB_KEY_BUT_MASK_SHIFT:
271 case XCB_KEY_BUT_MASK_LOCK:
274 case XCB_KEY_BUT_MASK_CONTROL:
277 case XCB_KEY_BUT_MASK_MOD_1:
280 case XCB_KEY_BUT_MASK_MOD_2:
283 case XCB_KEY_BUT_MASK_MOD_3:
286 case XCB_KEY_BUT_MASK_MOD_4:
289 case XCB_KEY_BUT_MASK_MOD_5:
292 case XCB_KEY_BUT_MASK_BUTTON_1:
295 case XCB_KEY_BUT_MASK_BUTTON_2:
298 case XCB_KEY_BUT_MASK_BUTTON_3:
301 case XCB_KEY_BUT_MASK_BUTTON_4:
304 case XCB_KEY_BUT_MASK_BUTTON_5:
347 ystr(
"event_state_mask");
356 y(integer, (uintptr_t)con);
369 case CT_FLOATING_CON:
370 ystr(
"floating_con");
391 ystr(
"scratchpad_state");
392 switch (con->scratchpad_state) {
393 case SCRATCHPAD_NONE:
396 case SCRATCHPAD_FRESH:
399 case SCRATCHPAD_CHANGED:
405 if (con->percent == 0.0)
408 y(
double, con->percent);
411 y(
bool, con->urgent);
428 if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
434 switch (con->layout) {
436 DLOG(
"About to dump layout=default, this is a bug in the code.\n");
459 ystr(
"workspace_layout");
460 switch (con->workspace_layout) {
471 DLOG(
"About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
476 ystr(
"last_split_layout");
477 switch (con->layout) {
487 switch (con->border_style) {
499 ystr(
"current_border_width");
500 y(integer, con->current_border_width);
503 dump_rect(gen,
"deco_rect", con->deco_rect);
504 dump_rect(gen,
"window_rect", con->window_rect);
505 dump_rect(gen,
"geometry", con->geometry);
508 if (con->window && con->window->name)
510 else if (con->name != NULL)
515 if (con->title_format != NULL) {
516 ystr(
"title_format");
517 ystr(con->title_format);
520 if (con->type == CT_WORKSPACE) {
522 y(integer, con->num);
527 y(integer, con->window->id);
533 if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NORMAL) {
535 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DOCK) {
537 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DIALOG) {
539 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_UTILITY) {
541 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLBAR) {
543 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_SPLASH) {
545 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_MENU) {
547 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) {
548 ystr(
"dropdown_menu");
549 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_POPUP_MENU) {
551 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLTIP) {
553 }
else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NOTIFICATION) {
554 ystr(
"notification");
561 if (con->window && !inplace_restart) {
565 ystr(
"window_properties");
568 #define DUMP_PROPERTY(key, prop_name) \
570 if (con->window->prop_name != NULL) { \
572 ystr(con->window->prop_name); \
580 if (con->window->name != NULL) {
585 ystr(
"transient_for");
586 if (con->window->transient_for == XCB_NONE)
589 y(integer, con->window->transient_for);
597 if (con->type != CT_DOCKAREA || !inplace_restart) {
604 ystr(
"floating_nodes");
606 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
614 y(integer, (uintptr_t)node);
618 ystr(
"fullscreen_mode");
619 y(integer, con->fullscreen_mode);
622 y(
bool, con->sticky);
625 switch (con->floating) {
626 case FLOATING_AUTO_OFF:
629 case FLOATING_AUTO_ON:
632 case FLOATING_USER_OFF:
635 case FLOATING_USER_ON:
649 if (match->
dock != M_DONTCHECK) {
651 y(integer, match->
dock);
652 ystr(
"insert_where");
656 #define DUMP_REGEX(re_name) \
658 if (match->re_name != NULL) { \
660 ystr(match->re_name->pattern); \
673 if (inplace_restart) {
674 if (con->window != NULL) {
677 y(integer, con->window->id);
678 ystr(
"restart_mode");
685 if (inplace_restart && con->window != NULL) {
687 y(integer, con->depth);
691 ystr(
"previous_workspace_name");
714 y(
bool, current->
release == B_UPON_KEYRELEASE);
724 if (strcasecmp(name,
"primary") == 0) {
737 if (
config->num_outputs > 0) {
740 for (
int c = 0; c <
config->num_outputs; c++) {
751 ystr(
"tray_outputs");
762 #define YSTR_IF_SET(name) \
764 if (config->name) { \
766 ystr(config->name); \
770 ystr(
"tray_padding");
771 y(integer,
config->tray_padding);
789 ystr(
"hidden_state");
790 switch (
config->hidden_state) {
806 if (
config->position == P_BOTTOM)
814 if (
config->separator_symbol) {
815 ystr(
"separator_symbol");
819 ystr(
"workspace_buttons");
820 y(
bool, !
config->hide_workspace_buttons);
822 ystr(
"workspace_min_width");
823 y(integer,
config->workspace_min_width);
825 ystr(
"strip_workspace_numbers");
826 y(
bool,
config->strip_workspace_numbers);
828 ystr(
"strip_workspace_name");
829 y(
bool,
config->strip_workspace_name);
831 ystr(
"binding_mode_indicator");
832 y(
bool, !
config->hide_binding_mode_indicator);
838 #define YSTR_IF_SET(name) \
840 if (config->colors.name) { \
842 ystr(config->colors.name); \
876 setlocale(LC_NUMERIC,
"C");
879 setlocale(LC_NUMERIC,
"");
881 const unsigned char *payload;
883 y(get_buf, &payload, &length);
906 assert(ws->
type == CT_WORKSPACE);
910 y(integer, (uintptr_t)ws);
922 y(
bool, ws == focused_ws);
927 y(integer, ws->rect.x);
929 y(integer, ws->rect.y);
931 y(integer, ws->rect.width);
933 y(integer, ws->rect.height);
948 const unsigned char *payload;
950 y(get_buf, &payload, &length);
985 y(integer,
output->rect.width);
987 y(integer,
output->rect.height);
990 ystr(
"current_workspace");
1002 const unsigned char *payload;
1004 y(get_buf, &payload, &length);
1029 const unsigned char *payload;
1031 y(get_buf, &payload, &length);
1046 y(integer, MAJOR_VERSION);
1049 y(integer, MINOR_VERSION);
1052 y(integer, PATCH_VERSION);
1054 ystr(
"human_readable");
1057 ystr(
"loaded_config_file_name");
1062 const unsigned char *payload;
1064 y(get_buf, &payload, &length);
1079 if (message_size == 0) {
1087 const unsigned char *payload;
1089 y(get_buf, &payload, &length);
1098 char *bar_id = NULL;
1099 sasprintf(&bar_id,
"%.*s", message_size, message);
1100 LOG(
"IPC: looking for config for bar ID \"%s\"\n", bar_id);
1103 if (strcmp(current->
id, bar_id) != 0)
1124 const unsigned char *payload;
1126 y(get_buf, &payload, &length);
1146 const unsigned char *payload;
1148 y(get_buf, &payload, &length);
1162 DLOG(
"should add subscription to extra %p, sub %.*s\n", client, (
int)len, s);
1170 memcpy(client->
events[event], s, len);
1172 DLOG(
"client is now subscribed to:\n");
1173 for (
int i = 0; i < client->
num_events; i++) {
1191 static yajl_callbacks callbacks = {
1195 p =
yalloc(&callbacks, (
void *)client);
1196 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1197 if (stat != yajl_status_ok) {
1199 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1201 ELOG(
"YAJL parse error: %s\n", err);
1202 yajl_free_error(p, err);
1204 const char *reply =
"{\"success\":false}";
1210 const char *reply =
"{\"success\":true}";
1213 if (client->first_tick_sent) {
1217 bool is_tick =
false;
1218 for (
int i = 0; i < client->num_events; i++) {
1219 if (strcmp(client->events[i],
"tick") == 0) {
1228 client->first_tick_sent =
true;
1229 const char *payload =
"{\"first\":true,\"payload\":\"\"}";
1246 const unsigned char *payload;
1248 y(get_buf, &payload, &length);
1267 yajl_gen_string(gen, (
unsigned char *)message, message_size);
1271 const unsigned char *payload;
1273 y(get_buf, &payload, &length);
1275 ipc_send_event(
"tick", I3_IPC_EVENT_TICK, (
const char *)payload);
1278 const char *reply =
"{\"success\":true}";
1280 DLOG(
"Sent tick event\n");
1293 memcpy(
state->last_key, val, len);
1299 if (strcasecmp(
state->last_key,
"rnd") == 0) {
1301 }
else if (strcasecmp(
state->last_key,
"window") == 0) {
1302 state->window = (xcb_window_t)val;
1312 static yajl_callbacks callbacks = {
1320 stat = yajl_parse(p, (
const unsigned char *)message, message_size);
1322 if (stat != yajl_status_ok) {
1324 err = yajl_get_error(p,
true, (
const unsigned char *)message,
1326 ELOG(
"YAJL parse error: %s\n", err);
1327 yajl_free_error(p, err);
1329 const char *reply =
"{\"success\":false}";
1336 DLOG(
"received IPC sync request (rnd = %d, window = 0x%08x)\n",
state.rnd,
state.window);
1338 const char *reply =
"{\"success\":true}";
1346 handle_get_workspaces,
1351 handle_get_bar_config,
1353 handle_get_binding_modes,
1370 uint32_t message_type;
1371 uint32_t message_length;
1372 uint8_t *message = NULL;
1374 assert(client->
fd == w->fd);
1376 int ret =
ipc_recv_message(w->fd, &message_type, &message_length, &message);
1380 if (ret == -1 && errno == EAGAIN) {
1393 DLOG(
"Unhandled message type: %d\n", message_type);
1396 h(client, message, 0, message_length, message_type);
1407 char *cmdline = NULL;
1408 #
if defined(__linux__) && defined(SO_PEERCRED)
1409 struct ucred peercred;
1410 socklen_t so_len =
sizeof(peercred);
1411 if (getsockopt(client->
fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1415 sasprintf(&exepath,
"/proc/%d/cmdline", peercred.pid);
1417 int fd = open(exepath, O_RDONLY);
1422 char buf[512] = {
'\0'};
1423 const ssize_t n = read(fd, buf,
sizeof(buf));
1428 for (
char *walk = buf; walk < buf + n - 1; walk++) {
1429 if (*walk ==
'\0') {
1436 ELOG(
"client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->
fd);
1442 ELOG(
"client %p on fd %d timed out, killing\n", client, client->
fd);
1449 DLOG(
"fd %d writeable\n", w->fd);
1454 assert(client->
timeout != NULL);
1466 struct sockaddr_un peer;
1467 socklen_t len =
sizeof(
struct sockaddr_un);
1469 if ((fd = accept(w->fd, (
struct sockaddr *)&peer, &len)) < 0) {
1470 if (errno != EINTR) {
1477 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1505 DLOG(
"IPC: new client connected on fd %d\n", fd);
1521 DLOG(
"Creating IPC-socket at %s\n", resolved);
1522 char *copy =
sstrdup(resolved);
1523 const char *dir = dirname(copy);
1531 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1537 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1539 struct sockaddr_un addr;
1540 memset(&addr, 0,
sizeof(
struct sockaddr_un));
1541 addr.sun_family = AF_LOCAL;
1542 strncpy(addr.sun_path, resolved,
sizeof(addr.sun_path) - 1);
1543 if (bind(sockfd, (
struct sockaddr *)&addr,
sizeof(
struct sockaddr_un)) < 0) {
1551 if (listen(sockfd, 5) < 0) {
1566 setlocale(LC_NUMERIC,
"C");
1575 if (current == NULL)
1588 setlocale(LC_NUMERIC,
"");
1601 const unsigned char *payload;
1603 y(get_buf, &payload, &length);
1605 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE, (
const char *)payload);
1615 DLOG(
"Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1616 property, con, (con->
window ? con->
window->
id : XCB_WINDOW_NONE));
1618 setlocale(LC_NUMERIC,
"C");
1631 const unsigned char *payload;
1633 y(get_buf, &payload, &length);
1635 ipc_send_event(
"window", I3_IPC_EVENT_WINDOW, (
const char *)payload);
1637 setlocale(LC_NUMERIC,
"");
1644 DLOG(
"Issue barconfig_update event for id = %s\n", barconfig->
id);
1645 setlocale(LC_NUMERIC,
"C");
1650 const unsigned char *payload;
1652 y(get_buf, &payload, &length);
1654 ipc_send_event(
"barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (
const char *)payload);
1656 setlocale(LC_NUMERIC,
"");
1663 DLOG(
"Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->
symbol, bind->
keycode);
1665 setlocale(LC_NUMERIC,
"C");
1679 const unsigned char *payload;
1681 y(get_buf, &payload, &length);
1683 ipc_send_event(
"binding", I3_IPC_EVENT_BINDING, (
const char *)payload);
1686 setlocale(LC_NUMERIC,
"");
1693 DLOG(
"ipc_confirm_restart(fd %d)\n", client->
fd);
1694 static const char *reply =
"[{\"success\":true}]";
1696 client, strlen(reply), I3_IPC_REPLY_TYPE_COMMAND,
1697 (
const uint8_t *)reply);