i3
ipc.c
Go to the documentation of this file.
1/*
2 * vim:ts=4:sw=4:expandtab
3 *
4 * i3 - an improved dynamic tiling window manager
5 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
6 *
7 * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
8 *
9 */
10
11#include "all.h"
12#include "yajl_utils.h"
13
14#include <ev.h>
15#include <fcntl.h>
16#include <libgen.h>
17#include <locale.h>
18#include <stdint.h>
19#include <sys/socket.h>
20#include <sys/un.h>
21#include <unistd.h>
22
23#include <yajl/yajl_gen.h>
24#include <yajl/yajl_parse.h>
25
26char *current_socketpath = NULL;
27
28TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
29
30static void ipc_client_timeout(EV_P_ ev_timer *w, int revents);
31static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents);
32
33static ev_tstamp kill_timeout = 10.0;
34
35void ipc_set_kill_timeout(ev_tstamp new) {
36 kill_timeout = new;
37}
38
39/*
40 * Try to write the contents of the pending buffer to the client's subscription
41 * socket. Will set, reset or clear the timeout and io write callbacks depending
42 * on the result of the write operation.
43 *
44 */
45static void ipc_push_pending(ipc_client *client) {
46 const ssize_t result = writeall_nonblock(client->fd, client->buffer, client->buffer_size);
47 if (result < 0) {
48 return;
49 }
50
51 if ((size_t)result == client->buffer_size) {
52 /* Everything was written successfully: clear the timer and stop the io
53 * callback. */
54 FREE(client->buffer);
55 client->buffer_size = 0;
56 if (client->timeout) {
57 ev_timer_stop(main_loop, client->timeout);
58 FREE(client->timeout);
59 }
60 ev_io_stop(main_loop, client->write_callback);
61 return;
62 }
63
64 /* Otherwise, make sure that the io callback is enabled and create a new
65 * timer if needed. */
66 ev_io_start(main_loop, client->write_callback);
67
68 if (!client->timeout) {
69 struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer));
70 ev_timer_init(timeout, ipc_client_timeout, kill_timeout, 0.);
71 timeout->data = client;
72 client->timeout = timeout;
73 ev_set_priority(timeout, EV_MINPRI);
74 ev_timer_start(main_loop, client->timeout);
75 } else if (result > 0) {
76 /* Keep the old timeout when nothing is written. Otherwise, we would
77 * keep a dead connection by continuously renewing its timeouts. */
78 ev_timer_stop(main_loop, client->timeout);
79 ev_timer_set(client->timeout, kill_timeout, 0.0);
80 ev_timer_start(main_loop, client->timeout);
81 }
82 if (result == 0) {
83 return;
84 }
85
86 /* Shift the buffer to the left and reduce the allocated space. */
87 client->buffer_size -= (size_t)result;
88 memmove(client->buffer, client->buffer + result, client->buffer_size);
89 client->buffer = srealloc(client->buffer, client->buffer_size);
90}
91
92/*
93 * Given a message and a message type, create the corresponding header, merge it
94 * with the message and append it to the given client's output buffer. Also,
95 * send the message if the client's buffer was empty.
96 *
97 */
98static void ipc_send_client_message(ipc_client *client, size_t size, const uint32_t message_type, const uint8_t *payload) {
99 const i3_ipc_header_t header = {
100 .magic = {'i', '3', '-', 'i', 'p', 'c'},
101 .size = size,
102 .type = message_type};
103 const size_t header_size = sizeof(i3_ipc_header_t);
104 const size_t message_size = header_size + size;
105
106 const bool push_now = (client->buffer_size == 0);
107 client->buffer = srealloc(client->buffer, client->buffer_size + message_size);
108 memcpy(client->buffer + client->buffer_size, ((void *)&header), header_size);
109 memcpy(client->buffer + client->buffer_size + header_size, payload, size);
110 client->buffer_size += message_size;
111
112 if (push_now) {
113 ipc_push_pending(client);
114 }
115}
116
117static void free_ipc_client(ipc_client *client, int exempt_fd) {
118 if (client->fd != exempt_fd) {
119 DLOG("Disconnecting client on fd %d\n", client->fd);
120 close(client->fd);
121 }
122
123 ev_io_stop(main_loop, client->read_callback);
124 FREE(client->read_callback);
125 ev_io_stop(main_loop, client->write_callback);
126 FREE(client->write_callback);
127 if (client->timeout) {
128 ev_timer_stop(main_loop, client->timeout);
129 FREE(client->timeout);
130 }
131
132 free(client->buffer);
133
134 for (int i = 0; i < client->num_events; i++) {
135 free(client->events[i]);
136 }
137 free(client->events);
138 TAILQ_REMOVE(&all_clients, client, clients);
139 free(client);
140}
141
142/*
143 * Sends the specified event to all IPC clients which are currently connected
144 * and subscribed to this kind of event.
145 *
146 */
147void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
148 ipc_client *current;
149 TAILQ_FOREACH (current, &all_clients, clients) {
150 for (int i = 0; i < current->num_events; i++) {
151 if (strcasecmp(current->events[i], event) == 0) {
152 ipc_send_client_message(current, strlen(payload), message_type, (uint8_t *)payload);
153 break;
154 }
155 }
156 }
157}
158
159/*
160 * For shutdown events, we send the reason for the shutdown.
161 */
163 yajl_gen gen = ygenalloc();
164 y(map_open);
165
166 ystr("change");
167
168 if (reason == SHUTDOWN_REASON_RESTART) {
169 ystr("restart");
170 } else if (reason == SHUTDOWN_REASON_EXIT) {
171 ystr("exit");
172 }
173
174 y(map_close);
175
176 const unsigned char *payload;
177 ylength length;
178
179 y(get_buf, &payload, &length);
180 ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
181
182 y(free);
183}
184
185/*
186 * Calls shutdown() on each socket and closes it. This function is to be called
187 * when exiting or restarting only!
188 *
189 * exempt_fd is never closed. Set to -1 to close all fds.
190 *
191 */
192void ipc_shutdown(shutdown_reason_t reason, int exempt_fd) {
194
195 ipc_client *current;
196 while (!TAILQ_EMPTY(&all_clients)) {
197 current = TAILQ_FIRST(&all_clients);
198 if (current->fd != exempt_fd) {
199 shutdown(current->fd, SHUT_RDWR);
200 }
201 free_ipc_client(current, exempt_fd);
202 }
203}
204
205/*
206 * Executes the given command.
207 *
208 */
209IPC_HANDLER(run_command) {
210 /* To get a properly terminated buffer, we copy
211 * message_size bytes out of the buffer */
212 char *command = sstrndup((const char *)message, message_size);
213 LOG("IPC: received: *%.4000s*\n", command);
214 yajl_gen gen = yajl_gen_alloc(NULL);
215
216 CommandResult *result = parse_command(command, gen, client);
217 free(command);
218
219 if (result->needs_tree_render)
220 tree_render();
221
222 command_result_free(result);
223
224 const unsigned char *reply;
225 ylength length;
226 yajl_gen_get_buf(gen, &reply, &length);
227
228 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_COMMAND,
229 (const uint8_t *)reply);
230
231 yajl_gen_free(gen);
232}
233
234static void dump_rect(yajl_gen gen, const char *name, Rect r) {
235 ystr(name);
236 y(map_open);
237 ystr("x");
238 y(integer, (int32_t)r.x);
239 ystr("y");
240 y(integer, (int32_t)r.y);
241 ystr("width");
242 y(integer, r.width);
243 ystr("height");
244 y(integer, r.height);
245 y(map_close);
246}
247
248static void dump_event_state_mask(yajl_gen gen, Binding *bind) {
249 y(array_open);
250 for (int i = 0; i < 20; i++) {
251 if (bind->event_state_mask & (1 << i)) {
252 switch (1 << i) {
253 case XCB_KEY_BUT_MASK_SHIFT:
254 ystr("shift");
255 break;
256 case XCB_KEY_BUT_MASK_LOCK:
257 ystr("lock");
258 break;
259 case XCB_KEY_BUT_MASK_CONTROL:
260 ystr("ctrl");
261 break;
262 case XCB_KEY_BUT_MASK_MOD_1:
263 ystr("Mod1");
264 break;
265 case XCB_KEY_BUT_MASK_MOD_2:
266 ystr("Mod2");
267 break;
268 case XCB_KEY_BUT_MASK_MOD_3:
269 ystr("Mod3");
270 break;
271 case XCB_KEY_BUT_MASK_MOD_4:
272 ystr("Mod4");
273 break;
274 case XCB_KEY_BUT_MASK_MOD_5:
275 ystr("Mod5");
276 break;
277 case XCB_KEY_BUT_MASK_BUTTON_1:
278 ystr("Button1");
279 break;
280 case XCB_KEY_BUT_MASK_BUTTON_2:
281 ystr("Button2");
282 break;
283 case XCB_KEY_BUT_MASK_BUTTON_3:
284 ystr("Button3");
285 break;
286 case XCB_KEY_BUT_MASK_BUTTON_4:
287 ystr("Button4");
288 break;
289 case XCB_KEY_BUT_MASK_BUTTON_5:
290 ystr("Button5");
291 break;
292 case (I3_XKB_GROUP_MASK_1 << 16):
293 ystr("Group1");
294 break;
295 case (I3_XKB_GROUP_MASK_2 << 16):
296 ystr("Group2");
297 break;
298 case (I3_XKB_GROUP_MASK_3 << 16):
299 ystr("Group3");
300 break;
301 case (I3_XKB_GROUP_MASK_4 << 16):
302 ystr("Group4");
303 break;
304 }
305 }
306 }
307 y(array_close);
308}
309
310static void dump_binding(yajl_gen gen, Binding *bind) {
311 y(map_open);
312 ystr("input_code");
313 y(integer, bind->keycode);
314
315 ystr("input_type");
316 ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
317
318 ystr("symbol");
319 if (bind->symbol == NULL)
320 y(null);
321 else
322 ystr(bind->symbol);
323
324 ystr("command");
325 ystr(bind->command);
326
327 // This key is only provided for compatibility, new programs should use
328 // event_state_mask instead.
329 ystr("mods");
330 dump_event_state_mask(gen, bind);
331
332 ystr("event_state_mask");
333 dump_event_state_mask(gen, bind);
334
335 y(map_close);
336}
337
338void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
339 y(map_open);
340 ystr("id");
341 y(integer, (uintptr_t)con);
342
343 ystr("type");
344 switch (con->type) {
345 case CT_ROOT:
346 ystr("root");
347 break;
348 case CT_OUTPUT:
349 ystr("output");
350 break;
351 case CT_CON:
352 ystr("con");
353 break;
354 case CT_FLOATING_CON:
355 ystr("floating_con");
356 break;
357 case CT_WORKSPACE:
358 ystr("workspace");
359 break;
360 case CT_DOCKAREA:
361 ystr("dockarea");
362 break;
363 }
364
365 /* provided for backwards compatibility only. */
366 ystr("orientation");
367 if (!con_is_split(con))
368 ystr("none");
369 else {
370 if (con_orientation(con) == HORIZ)
371 ystr("horizontal");
372 else
373 ystr("vertical");
374 }
375
376 ystr("scratchpad_state");
377 switch (con->scratchpad_state) {
378 case SCRATCHPAD_NONE:
379 ystr("none");
380 break;
381 case SCRATCHPAD_FRESH:
382 ystr("fresh");
383 break;
384 case SCRATCHPAD_CHANGED:
385 ystr("changed");
386 break;
387 }
388
389 ystr("percent");
390 if (con->percent == 0.0)
391 y(null);
392 else
393 y(double, con->percent);
394
395 ystr("urgent");
396 y(bool, con->urgent);
397
398 ystr("marks");
399 y(array_open);
400 mark_t *mark;
401 TAILQ_FOREACH (mark, &(con->marks_head), marks) {
402 ystr(mark->name);
403 }
404 y(array_close);
405
406 ystr("focused");
407 y(bool, (con == focused));
408
409 if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
410 ystr("output");
411 ystr(con_get_output(con)->name);
412 }
413
414 ystr("layout");
415 switch (con->layout) {
416 case L_DEFAULT:
417 DLOG("About to dump layout=default, this is a bug in the code.\n");
418 assert(false);
419 break;
420 case L_SPLITV:
421 ystr("splitv");
422 break;
423 case L_SPLITH:
424 ystr("splith");
425 break;
426 case L_STACKED:
427 ystr("stacked");
428 break;
429 case L_TABBED:
430 ystr("tabbed");
431 break;
432 case L_DOCKAREA:
433 ystr("dockarea");
434 break;
435 case L_OUTPUT:
436 ystr("output");
437 break;
438 }
439
440 ystr("workspace_layout");
441 switch (con->workspace_layout) {
442 case L_DEFAULT:
443 ystr("default");
444 break;
445 case L_STACKED:
446 ystr("stacked");
447 break;
448 case L_TABBED:
449 ystr("tabbed");
450 break;
451 default:
452 DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
453 assert(false);
454 break;
455 }
456
457 ystr("last_split_layout");
458 switch (con->layout) {
459 case L_SPLITV:
460 ystr("splitv");
461 break;
462 default:
463 ystr("splith");
464 break;
465 }
466
467 ystr("border");
468 switch (con->border_style) {
469 case BS_NORMAL:
470 ystr("normal");
471 break;
472 case BS_NONE:
473 ystr("none");
474 break;
475 case BS_PIXEL:
476 ystr("pixel");
477 break;
478 }
479
480 ystr("current_border_width");
481 y(integer, con->current_border_width);
482
483 dump_rect(gen, "rect", con->rect);
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);
487
488 ystr("name");
489 if (con->window && con->window->name)
491 else if (con->name != NULL)
492 ystr(con->name);
493 else
494 y(null);
495
496 if (con->title_format != NULL) {
497 ystr("title_format");
498 ystr(con->title_format);
499 }
500
501 ystr("window_icon_padding");
502 y(integer, con->window_icon_padding);
503
504 if (con->type == CT_WORKSPACE) {
505 ystr("num");
506 y(integer, con->num);
507 }
508
509 ystr("window");
510 if (con->window)
511 y(integer, con->window->id);
512 else
513 y(null);
514
515 ystr("window_type");
516 if (con->window) {
517 if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NORMAL) {
518 ystr("normal");
519 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DOCK) {
520 ystr("dock");
521 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DIALOG) {
522 ystr("dialog");
523 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_UTILITY) {
524 ystr("utility");
525 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLBAR) {
526 ystr("toolbar");
527 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_SPLASH) {
528 ystr("splash");
529 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_MENU) {
530 ystr("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) {
534 ystr("popup_menu");
535 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLTIP) {
536 ystr("tooltip");
537 } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NOTIFICATION) {
538 ystr("notification");
539 } else {
540 ystr("unknown");
541 }
542 } else
543 y(null);
544
545 if (con->window && !inplace_restart) {
546 /* Window properties are useless to preserve when restarting because
547 * they will be queried again anyway. However, for i3-save-tree(1),
548 * they are very useful and save i3-save-tree dealing with X11. */
549 ystr("window_properties");
550 y(map_open);
551
552#define DUMP_PROPERTY(key, prop_name) \
553 do { \
554 if (con->window->prop_name != NULL) { \
555 ystr(key); \
556 ystr(con->window->prop_name); \
557 } \
558 } while (0)
559
560 DUMP_PROPERTY("class", class_class);
561 DUMP_PROPERTY("instance", class_instance);
562 DUMP_PROPERTY("window_role", role);
563 DUMP_PROPERTY("machine", machine);
564
565 if (con->window->name != NULL) {
566 ystr("title");
568 }
569
570 ystr("transient_for");
571 if (con->window->transient_for == XCB_NONE)
572 y(null);
573 else
574 y(integer, con->window->transient_for);
575
576 y(map_close);
577 }
578
579 ystr("nodes");
580 y(array_open);
581 Con *node;
582 if (con->type != CT_DOCKAREA || !inplace_restart) {
583 TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
584 dump_node(gen, node, inplace_restart);
585 }
586 }
587 y(array_close);
588
589 ystr("floating_nodes");
590 y(array_open);
591 TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
592 dump_node(gen, node, inplace_restart);
593 }
594 y(array_close);
595
596 ystr("focus");
597 y(array_open);
598 TAILQ_FOREACH (node, &(con->focus_head), focused) {
599 y(integer, (uintptr_t)node);
600 }
601 y(array_close);
602
603 ystr("fullscreen_mode");
604 y(integer, con->fullscreen_mode);
605
606 ystr("sticky");
607 y(bool, con->sticky);
608
609 ystr("floating");
610 switch (con->floating) {
611 case FLOATING_AUTO_OFF:
612 ystr("auto_off");
613 break;
614 case FLOATING_AUTO_ON:
615 ystr("auto_on");
616 break;
617 case FLOATING_USER_OFF:
618 ystr("user_off");
619 break;
620 case FLOATING_USER_ON:
621 ystr("user_on");
622 break;
623 }
624
625 ystr("swallows");
626 y(array_open);
627 Match *match;
628 TAILQ_FOREACH (match, &(con->swallow_head), matches) {
629 /* We will generate a new restart_mode match specification after this
630 * loop, so skip this one. */
631 if (match->restart_mode)
632 continue;
633 y(map_open);
634 if (match->dock != M_DONTCHECK) {
635 ystr("dock");
636 y(integer, match->dock);
637 ystr("insert_where");
638 y(integer, match->insert_where);
639 }
640
641#define DUMP_REGEX(re_name) \
642 do { \
643 if (match->re_name != NULL) { \
644 ystr(#re_name); \
645 ystr(match->re_name->pattern); \
646 } \
647 } while (0)
648
649 DUMP_REGEX(class);
650 DUMP_REGEX(instance);
651 DUMP_REGEX(window_role);
652 DUMP_REGEX(title);
653 DUMP_REGEX(machine);
654
655#undef DUMP_REGEX
656 y(map_close);
657 }
658
659 if (inplace_restart) {
660 if (con->window != NULL) {
661 y(map_open);
662 ystr("id");
663 y(integer, con->window->id);
664 ystr("restart_mode");
665 y(bool, true);
666 y(map_close);
667 }
668 }
669 y(array_close);
670
671 if (inplace_restart && con->window != NULL) {
672 ystr("depth");
673 y(integer, con->depth);
674 }
675
676 if (inplace_restart && con->type == CT_ROOT && previous_workspace_name) {
677 ystr("previous_workspace_name");
679 }
680
681 y(map_close);
682}
683
684static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
685 if (TAILQ_EMPTY(&(config->bar_bindings)))
686 return;
687
688 ystr("bindings");
689 y(array_open);
690
691 struct Barbinding *current;
692 TAILQ_FOREACH (current, &(config->bar_bindings), bindings) {
693 y(map_open);
694
695 ystr("input_code");
696 y(integer, current->input_code);
697 ystr("command");
698 ystr(current->command);
699 ystr("release");
700 y(bool, current->release == B_UPON_KEYRELEASE);
701
702 y(map_close);
703 }
704
705 y(array_close);
706}
707
708static char *canonicalize_output_name(char *name) {
709 /* Do not canonicalize special output names. */
710 if (strcasecmp(name, "primary") == 0) {
711 return name;
712 }
713 Output *output = get_output_by_name(name, false);
714 return output ? output_primary_name(output) : name;
715}
716
717static void dump_bar_config(yajl_gen gen, Barconfig *config) {
718 y(map_open);
719
720 ystr("id");
721 ystr(config->id);
722
723 if (config->num_outputs > 0) {
724 ystr("outputs");
725 y(array_open);
726 for (int c = 0; c < config->num_outputs; c++) {
727 /* Convert monitor names (RandR ≥ 1.5) or output names
728 * (RandR < 1.5) into monitor names. This way, existing
729 * configs which use output names transparently keep
730 * working. */
732 }
733 y(array_close);
734 }
735
736 if (!TAILQ_EMPTY(&(config->tray_outputs))) {
737 ystr("tray_outputs");
738 y(array_open);
739
740 struct tray_output_t *tray_output;
741 TAILQ_FOREACH (tray_output, &(config->tray_outputs), tray_outputs) {
742 ystr(canonicalize_output_name(tray_output->output));
743 }
744
745 y(array_close);
746 }
747
748#define YSTR_IF_SET(name) \
749 do { \
750 if (config->name) { \
751 ystr(#name); \
752 ystr(config->name); \
753 } \
754 } while (0)
755
756 ystr("tray_padding");
757 y(integer, config->tray_padding);
758
759 YSTR_IF_SET(socket_path);
760
761 ystr("mode");
762 switch (config->mode) {
763 case M_HIDE:
764 ystr("hide");
765 break;
766 case M_INVISIBLE:
767 ystr("invisible");
768 break;
769 case M_DOCK:
770 default:
771 ystr("dock");
772 break;
773 }
774
775 ystr("hidden_state");
776 switch (config->hidden_state) {
777 case S_SHOW:
778 ystr("show");
779 break;
780 case S_HIDE:
781 default:
782 ystr("hide");
783 break;
784 }
785
786 ystr("modifier");
787 y(integer, config->modifier);
788
790
791 ystr("position");
792 if (config->position == P_BOTTOM)
793 ystr("bottom");
794 else
795 ystr("top");
796
797 YSTR_IF_SET(status_command);
798 YSTR_IF_SET(font);
799
800 if (config->separator_symbol) {
801 ystr("separator_symbol");
802 ystr(config->separator_symbol);
803 }
804
805 ystr("workspace_buttons");
806 y(bool, !config->hide_workspace_buttons);
807
808 ystr("workspace_min_width");
809 y(integer, config->workspace_min_width);
810
811 ystr("strip_workspace_numbers");
812 y(bool, config->strip_workspace_numbers);
813
814 ystr("strip_workspace_name");
815 y(bool, config->strip_workspace_name);
816
817 ystr("binding_mode_indicator");
818 y(bool, !config->hide_binding_mode_indicator);
819
820 ystr("verbose");
821 y(bool, config->verbose);
822
823#undef YSTR_IF_SET
824#define YSTR_IF_SET(name) \
825 do { \
826 if (config->colors.name) { \
827 ystr(#name); \
828 ystr(config->colors.name); \
829 } \
830 } while (0)
831
832 ystr("colors");
833 y(map_open);
834 YSTR_IF_SET(background);
835 YSTR_IF_SET(statusline);
836 YSTR_IF_SET(separator);
837 YSTR_IF_SET(focused_background);
838 YSTR_IF_SET(focused_statusline);
839 YSTR_IF_SET(focused_separator);
840 YSTR_IF_SET(focused_workspace_border);
841 YSTR_IF_SET(focused_workspace_bg);
842 YSTR_IF_SET(focused_workspace_text);
843 YSTR_IF_SET(active_workspace_border);
844 YSTR_IF_SET(active_workspace_bg);
845 YSTR_IF_SET(active_workspace_text);
846 YSTR_IF_SET(inactive_workspace_border);
847 YSTR_IF_SET(inactive_workspace_bg);
848 YSTR_IF_SET(inactive_workspace_text);
849 YSTR_IF_SET(urgent_workspace_border);
850 YSTR_IF_SET(urgent_workspace_bg);
851 YSTR_IF_SET(urgent_workspace_text);
852 YSTR_IF_SET(binding_mode_border);
853 YSTR_IF_SET(binding_mode_bg);
854 YSTR_IF_SET(binding_mode_text);
855 y(map_close);
856
857 y(map_close);
858#undef YSTR_IF_SET
859}
860
862 setlocale(LC_NUMERIC, "C");
863 yajl_gen gen = ygenalloc();
864 dump_node(gen, croot, false);
865 setlocale(LC_NUMERIC, "");
866
867 const unsigned char *payload;
868 ylength length;
869 y(get_buf, &payload, &length);
870
871 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_TREE, payload);
872 y(free);
873}
874
875/*
876 * Formats the reply message for a GET_WORKSPACES request and sends it to the
877 * client
878 *
879 */
880IPC_HANDLER(get_workspaces) {
881 yajl_gen gen = ygenalloc();
882 y(array_open);
883
884 Con *focused_ws = con_get_workspace(focused);
885
886 Con *output;
887 TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
889 continue;
890 Con *ws;
891 TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
892 assert(ws->type == CT_WORKSPACE);
893 y(map_open);
894
895 ystr("id");
896 y(integer, (uintptr_t)ws);
897
898 ystr("num");
899 y(integer, ws->num);
900
901 ystr("name");
902 ystr(ws->name);
903
904 ystr("visible");
905 y(bool, workspace_is_visible(ws));
906
907 ystr("focused");
908 y(bool, ws == focused_ws);
909
910 ystr("rect");
911 y(map_open);
912 ystr("x");
913 y(integer, ws->rect.x);
914 ystr("y");
915 y(integer, ws->rect.y);
916 ystr("width");
917 y(integer, ws->rect.width);
918 ystr("height");
919 y(integer, ws->rect.height);
920 y(map_close);
921
922 ystr("output");
923 ystr(output->name);
924
925 ystr("urgent");
926 y(bool, ws->urgent);
927
928 y(map_close);
929 }
930 }
931
932 y(array_close);
933
934 const unsigned char *payload;
935 ylength length;
936 y(get_buf, &payload, &length);
937
938 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
939 y(free);
940}
941
942/*
943 * Formats the reply message for a GET_OUTPUTS request and sends it to the
944 * client
945 *
946 */
947IPC_HANDLER(get_outputs) {
948 yajl_gen gen = ygenalloc();
949 y(array_open);
950
951 Output *output;
953 y(map_open);
954
955 ystr("name");
957
958 ystr("active");
959 y(bool, output->active);
960
961 ystr("primary");
962 y(bool, output->primary);
963
964 ystr("rect");
965 y(map_open);
966 ystr("x");
967 y(integer, output->rect.x);
968 ystr("y");
969 y(integer, output->rect.y);
970 ystr("width");
971 y(integer, output->rect.width);
972 ystr("height");
973 y(integer, output->rect.height);
974 y(map_close);
975
976 ystr("current_workspace");
977 Con *ws = NULL;
978 if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
979 ystr(ws->name);
980 else
981 y(null);
982
983 y(map_close);
984 }
985
986 y(array_close);
987
988 const unsigned char *payload;
989 ylength length;
990 y(get_buf, &payload, &length);
991
992 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
993 y(free);
994}
995
996/*
997 * Formats the reply message for a GET_MARKS request and sends it to the
998 * client
999 *
1000 */
1001IPC_HANDLER(get_marks) {
1002 yajl_gen gen = ygenalloc();
1003 y(array_open);
1004
1005 Con *con;
1007 mark_t *mark;
1008 TAILQ_FOREACH (mark, &(con->marks_head), marks) {
1009 ystr(mark->name);
1010 }
1011 }
1012
1013 y(array_close);
1014
1015 const unsigned char *payload;
1016 ylength length;
1017 y(get_buf, &payload, &length);
1018
1019 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_MARKS, payload);
1020 y(free);
1021}
1022
1023/*
1024 * Returns the version of i3
1025 *
1026 */
1027IPC_HANDLER(get_version) {
1028 yajl_gen gen = ygenalloc();
1029 y(map_open);
1030
1031 ystr("major");
1032 y(integer, MAJOR_VERSION);
1033
1034 ystr("minor");
1035 y(integer, MINOR_VERSION);
1036
1037 ystr("patch");
1038 y(integer, PATCH_VERSION);
1039
1040 ystr("human_readable");
1042
1043 ystr("loaded_config_file_name");
1045
1046 ystr("included_config_file_names");
1047 y(array_open);
1048 IncludedFile *file;
1049 TAILQ_FOREACH (file, &included_files, files) {
1050 if (file == TAILQ_FIRST(&included_files)) {
1051 /* Skip the first file, which is current_configpath. */
1052 continue;
1053 }
1054 ystr(file->path);
1055 }
1056 y(array_close);
1057 y(map_close);
1058
1059 const unsigned char *payload;
1060 ylength length;
1061 y(get_buf, &payload, &length);
1062
1063 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_VERSION, payload);
1064 y(free);
1065}
1066
1067/*
1068 * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
1069 * client.
1070 *
1071 */
1072IPC_HANDLER(get_bar_config) {
1073 yajl_gen gen = ygenalloc();
1074
1075 /* If no ID was passed, we return a JSON array with all IDs */
1076 if (message_size == 0) {
1077 y(array_open);
1078 Barconfig *current;
1079 TAILQ_FOREACH (current, &barconfigs, configs) {
1080 ystr(current->id);
1081 }
1082 y(array_close);
1083
1084 const unsigned char *payload;
1085 ylength length;
1086 y(get_buf, &payload, &length);
1087
1088 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1089 y(free);
1090 return;
1091 }
1092
1093 /* To get a properly terminated buffer, we copy
1094 * message_size bytes out of the buffer */
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);
1098 Barconfig *current, *config = NULL;
1099 TAILQ_FOREACH (current, &barconfigs, configs) {
1100 if (strcmp(current->id, bar_id) != 0)
1101 continue;
1102
1103 config = current;
1104 break;
1105 }
1106 free(bar_id);
1107
1108 if (!config) {
1109 /* If we did not find a config for the given ID, the reply will contain
1110 * a null 'id' field. */
1111 y(map_open);
1112
1113 ystr("id");
1114 y(null);
1115
1116 y(map_close);
1117 } else {
1118 dump_bar_config(gen, config);
1119 }
1120
1121 const unsigned char *payload;
1122 ylength length;
1123 y(get_buf, &payload, &length);
1124
1125 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1126 y(free);
1127}
1128
1129/*
1130 * Returns a list of configured binding modes
1131 *
1132 */
1133IPC_HANDLER(get_binding_modes) {
1134 yajl_gen gen = ygenalloc();
1135
1136 y(array_open);
1137 struct Mode *mode;
1138 SLIST_FOREACH (mode, &modes, modes) {
1139 ystr(mode->name);
1140 }
1141 y(array_close);
1142
1143 const unsigned char *payload;
1144 ylength length;
1145 y(get_buf, &payload, &length);
1146
1147 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload);
1148 y(free);
1149}
1150
1151/*
1152 * Callback for the YAJL parser (will be called when a string is parsed).
1153 *
1154 */
1155static int add_subscription(void *extra, const unsigned char *s,
1156 ylength len) {
1157 ipc_client *client = extra;
1158
1159 DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
1160 int event = client->num_events;
1161
1162 client->num_events++;
1163 client->events = srealloc(client->events, client->num_events * sizeof(char *));
1164 /* We copy the string because it is not null-terminated and strndup()
1165 * is missing on some BSD systems */
1166 client->events[event] = scalloc(len + 1, 1);
1167 memcpy(client->events[event], s, len);
1168
1169 DLOG("client is now subscribed to:\n");
1170 for (int i = 0; i < client->num_events; i++) {
1171 DLOG("event %s\n", client->events[i]);
1172 }
1173 DLOG("(done)\n");
1174
1175 return 1;
1176}
1177
1178/*
1179 * Subscribes this connection to the event types which were given as a JSON
1180 * serialized array in the payload field of the message.
1181 *
1182 */
1183IPC_HANDLER(subscribe) {
1184 yajl_handle p;
1185 yajl_status stat;
1186
1187 /* Setup the JSON parser */
1188 static yajl_callbacks callbacks = {
1189 .yajl_string = add_subscription,
1190 };
1191
1192 p = yalloc(&callbacks, (void *)client);
1193 stat = yajl_parse(p, (const unsigned char *)message, message_size);
1194 if (stat != yajl_status_ok) {
1195 unsigned char *err;
1196 err = yajl_get_error(p, true, (const unsigned char *)message,
1197 message_size);
1198 ELOG("YAJL parse error: %s\n", err);
1199 yajl_free_error(p, err);
1200
1201 const char *reply = "{\"success\":false}";
1202 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1203 yajl_free(p);
1204 return;
1205 }
1206 yajl_free(p);
1207 const char *reply = "{\"success\":true}";
1208 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1209
1210 if (client->first_tick_sent) {
1211 return;
1212 }
1213
1214 bool is_tick = false;
1215 for (int i = 0; i < client->num_events; i++) {
1216 if (strcmp(client->events[i], "tick") == 0) {
1217 is_tick = true;
1218 break;
1219 }
1220 }
1221 if (!is_tick) {
1222 return;
1223 }
1224
1225 client->first_tick_sent = true;
1226 const char *payload = "{\"first\":true,\"payload\":\"\"}";
1227 ipc_send_client_message(client, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
1228}
1229
1230/*
1231 * Returns the raw last loaded i3 configuration file contents.
1232 */
1233IPC_HANDLER(get_config) {
1234 yajl_gen gen = ygenalloc();
1235
1236 y(map_open);
1237
1238 ystr("config");
1240 ystr(file->raw_contents);
1241
1242 ystr("included_configs");
1243 y(array_open);
1244 TAILQ_FOREACH (file, &included_files, files) {
1245 y(map_open);
1246 ystr("path");
1247 ystr(file->path);
1248 ystr("raw_contents");
1249 ystr(file->raw_contents);
1250 ystr("variable_replaced_contents");
1252 y(map_close);
1253 }
1254 y(array_close);
1255
1256 y(map_close);
1257
1258 const unsigned char *payload;
1259 ylength length;
1260 y(get_buf, &payload, &length);
1261
1262 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
1263 y(free);
1264}
1265
1266/*
1267 * Sends the tick event from the message payload to subscribers. Establishes a
1268 * synchronization point in event-related tests.
1269 */
1270IPC_HANDLER(send_tick) {
1271 yajl_gen gen = ygenalloc();
1272
1273 y(map_open);
1274
1275 ystr("first");
1276 y(bool, false);
1277
1278 ystr("payload");
1279 yajl_gen_string(gen, (unsigned char *)message, message_size);
1280
1281 y(map_close);
1282
1283 const unsigned char *payload;
1284 ylength length;
1285 y(get_buf, &payload, &length);
1286
1287 ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
1288 y(free);
1289
1290 const char *reply = "{\"success\":true}";
1291 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
1292 DLOG("Sent tick event\n");
1293}
1294
1297 uint32_t rnd;
1298 xcb_window_t window;
1299};
1300
1301static int _sync_json_key(void *extra, const unsigned char *val, size_t len) {
1302 struct sync_state *state = extra;
1303 FREE(state->last_key);
1304 state->last_key = scalloc(len + 1, 1);
1305 memcpy(state->last_key, val, len);
1306 return 1;
1307}
1308
1309static int _sync_json_int(void *extra, long long val) {
1310 struct sync_state *state = extra;
1311 if (strcasecmp(state->last_key, "rnd") == 0) {
1312 state->rnd = val;
1313 } else if (strcasecmp(state->last_key, "window") == 0) {
1314 state->window = (xcb_window_t)val;
1315 }
1316 return 1;
1317}
1318
1320 yajl_handle p;
1321 yajl_status stat;
1322
1323 /* Setup the JSON parser */
1324 static yajl_callbacks callbacks = {
1325 .yajl_map_key = _sync_json_key,
1326 .yajl_integer = _sync_json_int,
1327 };
1328
1329 struct sync_state state;
1330 memset(&state, '\0', sizeof(struct sync_state));
1331 p = yalloc(&callbacks, (void *)&state);
1332 stat = yajl_parse(p, (const unsigned char *)message, message_size);
1333 FREE(state.last_key);
1334 if (stat != yajl_status_ok) {
1335 unsigned char *err;
1336 err = yajl_get_error(p, true, (const unsigned char *)message,
1337 message_size);
1338 ELOG("YAJL parse error: %s\n", err);
1339 yajl_free_error(p, err);
1340
1341 const char *reply = "{\"success\":false}";
1342 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1343 yajl_free(p);
1344 return;
1345 }
1346 yajl_free(p);
1347
1348 DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
1349 sync_respond(state.window, state.rnd);
1350 const char *reply = "{\"success\":true}";
1351 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1352}
1353
1354IPC_HANDLER(get_binding_state) {
1355 yajl_gen gen = ygenalloc();
1356
1357 y(map_open);
1358
1359 ystr("name");
1361
1362 y(map_close);
1363
1364 const unsigned char *payload;
1365 ylength length;
1366 y(get_buf, &payload, &length);
1367
1368 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_GET_BINDING_STATE, payload);
1369 y(free);
1370}
1371
1372/* The index of each callback function corresponds to the numeric
1373 * value of the message type (see include/i3/ipc.h) */
1375 handle_run_command,
1376 handle_get_workspaces,
1377 handle_subscribe,
1378 handle_get_outputs,
1379 handle_tree,
1380 handle_get_marks,
1381 handle_get_bar_config,
1382 handle_get_version,
1383 handle_get_binding_modes,
1384 handle_get_config,
1385 handle_send_tick,
1386 handle_sync,
1387 handle_get_binding_state,
1388};
1389
1390/*
1391 * Handler for activity on a client connection, receives a message from a
1392 * client.
1393 *
1394 * For now, the maximum message size is 2048. I’m not sure for what the
1395 * IPC interface will be used in the future, thus I’m not implementing a
1396 * mechanism for arbitrarily long messages, as it seems like overkill
1397 * at the moment.
1398 *
1399 */
1400static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
1401 uint32_t message_type;
1402 uint32_t message_length;
1403 uint8_t *message = NULL;
1404 ipc_client *client = (ipc_client *)w->data;
1405 assert(client->fd == w->fd);
1406
1407 int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
1408 /* EOF or other error */
1409 if (ret < 0) {
1410 /* Was this a spurious read? See ev(3) */
1411 if (ret == -1 && errno == EAGAIN) {
1412 FREE(message);
1413 return;
1414 }
1415
1416 /* If not, there was some kind of error. We don’t bother and close the
1417 * connection. Delete the client from the list of clients. */
1418 free_ipc_client(client, -1);
1419 FREE(message);
1420 return;
1421 }
1422
1423 if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
1424 DLOG("Unhandled message type: %d\n", message_type);
1425 else {
1426 handler_t h = handlers[message_type];
1427 h(client, message, 0, message_length, message_type);
1428 }
1429
1430 FREE(message);
1431}
1432
1433static void ipc_client_timeout(EV_P_ ev_timer *w, int revents) {
1434 /* No need to be polite and check for writeability, the other callback would
1435 * have been called by now. */
1436 ipc_client *client = (ipc_client *)w->data;
1437
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) {
1443 goto end;
1444 }
1445 char *exepath;
1446 sasprintf(&exepath, "/proc/%d/cmdline", peercred.pid);
1447
1448 int fd = open(exepath, O_RDONLY);
1449 free(exepath);
1450 if (fd == -1) {
1451 goto end;
1452 }
1453 char buf[512] = {'\0'}; /* cut off cmdline for the error message. */
1454 const ssize_t n = read(fd, buf, sizeof(buf));
1455 close(fd);
1456 if (n < 0) {
1457 goto end;
1458 }
1459 for (char *walk = buf; walk < buf + n - 1; walk++) {
1460 if (*walk == '\0') {
1461 *walk = ' ';
1462 }
1463 }
1464 cmdline = buf;
1465
1466 if (cmdline) {
1467 ELOG("client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->fd);
1468 }
1469
1470end:
1471#endif
1472 if (!cmdline) {
1473 ELOG("client %p on fd %d timed out, killing\n", client, client->fd);
1474 }
1475
1476 free_ipc_client(client, -1);
1477}
1478
1479static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents) {
1480 DLOG("fd %d writeable\n", w->fd);
1481 ipc_client *client = (ipc_client *)w->data;
1482
1483 /* If this callback is called then there should be a corresponding active
1484 * timer. */
1485 assert(client->timeout != NULL);
1486 ipc_push_pending(client);
1487}
1488
1489/*
1490 * Handler for activity on the listening socket, meaning that a new client
1491 * has just connected and we should accept() him. Sets up the event handler
1492 * for activity on the new connection and inserts the file descriptor into
1493 * the list of clients.
1494 *
1495 */
1496void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
1497 struct sockaddr_un peer;
1498 socklen_t len = sizeof(struct sockaddr_un);
1499 int fd;
1500 if ((fd = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
1501 if (errno != EINTR) {
1502 perror("accept()");
1503 }
1504 return;
1505 }
1506
1507 /* Close this file descriptor on exec() */
1508 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1509
1510 ipc_new_client_on_fd(EV_A_ fd);
1511}
1512
1513/*
1514 * ipc_new_client_on_fd() only sets up the event handler
1515 * for activity on the new connection and inserts the file descriptor into
1516 * the list of clients.
1517 *
1518 * This variant is useful for the inherited IPC connection when restarting.
1519 *
1520 */
1522 set_nonblock(fd);
1523
1524 ipc_client *client = scalloc(1, sizeof(ipc_client));
1525 client->fd = fd;
1526
1527 client->read_callback = scalloc(1, sizeof(struct ev_io));
1528 client->read_callback->data = client;
1529 ev_io_init(client->read_callback, ipc_receive_message, fd, EV_READ);
1530 ev_io_start(EV_A_ client->read_callback);
1531
1532 client->write_callback = scalloc(1, sizeof(struct ev_io));
1533 client->write_callback->data = client;
1534 ev_io_init(client->write_callback, ipc_socket_writeable_cb, fd, EV_WRITE);
1535
1536 DLOG("IPC: new client connected on fd %d\n", fd);
1537 TAILQ_INSERT_TAIL(&all_clients, client, clients);
1538 return client;
1539}
1540
1541/*
1542 * Generates a json workspace event. Returns a dynamically allocated yajl
1543 * generator. Free with yajl_gen_free().
1544 */
1545yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
1546 setlocale(LC_NUMERIC, "C");
1547 yajl_gen gen = ygenalloc();
1548
1549 y(map_open);
1550
1551 ystr("change");
1552 ystr(change);
1553
1554 ystr("current");
1555 if (current == NULL)
1556 y(null);
1557 else
1558 dump_node(gen, current, false);
1559
1560 ystr("old");
1561 if (old == NULL)
1562 y(null);
1563 else
1564 dump_node(gen, old, false);
1565
1566 y(map_close);
1567
1568 setlocale(LC_NUMERIC, "");
1569
1570 return gen;
1571}
1572
1573/*
1574 * For the workspace events we send, along with the usual "change" field, also
1575 * the workspace container in "current". For focus events, we send the
1576 * previously focused workspace in "old".
1577 */
1578void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
1579 yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
1580
1581 const unsigned char *payload;
1582 ylength length;
1583 y(get_buf, &payload, &length);
1584
1585 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1586
1587 y(free);
1588}
1589
1590/*
1591 * For the window events we send, along the usual "change" field,
1592 * also the window container, in "container".
1593 */
1594void ipc_send_window_event(const char *property, Con *con) {
1595 DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1596 property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
1597
1598 setlocale(LC_NUMERIC, "C");
1599 yajl_gen gen = ygenalloc();
1600
1601 y(map_open);
1602
1603 ystr("change");
1604 ystr(property);
1605
1606 ystr("container");
1607 dump_node(gen, con, false);
1608
1609 y(map_close);
1610
1611 const unsigned char *payload;
1612 ylength length;
1613 y(get_buf, &payload, &length);
1614
1615 ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
1616 y(free);
1617 setlocale(LC_NUMERIC, "");
1618}
1619
1620/*
1621 * For the barconfig update events, we send the serialized barconfig.
1622 */
1624 DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
1625 setlocale(LC_NUMERIC, "C");
1626 yajl_gen gen = ygenalloc();
1627
1628 dump_bar_config(gen, barconfig);
1629
1630 const unsigned char *payload;
1631 ylength length;
1632 y(get_buf, &payload, &length);
1633
1634 ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
1635 y(free);
1636 setlocale(LC_NUMERIC, "");
1637}
1638
1639/*
1640 * For the binding events, we send the serialized binding struct.
1641 */
1642void ipc_send_binding_event(const char *event_type, Binding *bind) {
1643 DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
1644
1645 setlocale(LC_NUMERIC, "C");
1646
1647 yajl_gen gen = ygenalloc();
1648
1649 y(map_open);
1650
1651 ystr("change");
1652 ystr(event_type);
1653
1654 ystr("binding");
1655 dump_binding(gen, bind);
1656
1657 y(map_close);
1658
1659 const unsigned char *payload;
1660 ylength length;
1661 y(get_buf, &payload, &length);
1662
1663 ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
1664
1665 y(free);
1666 setlocale(LC_NUMERIC, "");
1667}
1668
1669/*
1670 * Sends a restart reply to the IPC client on the specified fd.
1671 */
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);
1678 ipc_push_pending(client);
1679}
#define y(x,...)
Definition: commands.c:18
#define ystr(str)
Definition: commands.c:19
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.
static cmdp_state state
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
Definition: con.c:525
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
Definition: con.c:1514
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:477
bool con_is_split(Con *con)
Returns true if a container should be considered split.
Definition: con.c:385
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Definition: con.c:588
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on.
Definition: con.c:463
struct includedfiles_head included_files
Definition: config.c:22
Config config
Definition: config.c:19
struct barconfig_head barconfigs
Definition: config.c:21
struct modes_head modes
Definition: config.c:20
char * current_configpath
Definition: config.c:18
static void free_ipc_client(ipc_client *client, int exempt_fd)
Definition: ipc.c:117
static int _sync_json_int(void *extra, long long val)
Definition: ipc.c:1309
static void dump_event_state_mask(yajl_gen gen, Binding *bind)
Definition: ipc.c:248
handler_t handlers[13]
Definition: ipc.c:1374
static void ipc_send_shutdown_event(shutdown_reason_t reason)
Definition: ipc.c:162
static void dump_bar_config(yajl_gen gen, Barconfig *config)
Definition: ipc.c:717
#define DUMP_REGEX(re_name)
static void dump_rect(yajl_gen gen, const char *name, Rect r)
Definition: ipc.c:234
static int _sync_json_key(void *extra, const unsigned char *val, size_t len)
Definition: ipc.c:1301
IPC_HANDLER(run_command)
Definition: ipc.c:209
static void ipc_client_timeout(EV_P_ ev_timer *w, int revents)
Definition: ipc.c:1433
static void dump_binding(yajl_gen gen, Binding *bind)
Definition: ipc.c:310
void ipc_confirm_restart(ipc_client *client)
Sends a restart reply to the IPC client on the specified fd.
Definition: ipc.c:1672
TAILQ_HEAD(ipc_client_head, ipc_client)
Definition: ipc.c:28
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 ...
Definition: ipc.c:1521
char * current_socketpath
Definition: ipc.c:26
static void dump_bar_bindings(yajl_gen gen, Barconfig *config)
Definition: ipc.c:684
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
Definition: ipc.c:1400
void ipc_shutdown(shutdown_reason_t reason, int exempt_fd)
Calls shutdown() on each socket and closes it.
Definition: ipc.c:192
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
Definition: ipc.c:338
static void ipc_send_client_message(ipc_client *client, size_t size, const uint32_t message_type, const uint8_t *payload)
Definition: ipc.c:98
#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...
Definition: ipc.c:1578
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
Definition: ipc.c:1642
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...
Definition: ipc.c:1496
void ipc_send_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.
Definition: ipc.c:1623
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...
Definition: ipc.c:147
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old)
Generates a json workspace event.
Definition: ipc.c:1545
static int add_subscription(void *extra, const unsigned char *s, ylength len)
Definition: ipc.c:1155
static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents)
Definition: ipc.c:1479
static char * canonicalize_output_name(char *name)
Definition: ipc.c:708
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,...
Definition: ipc.c:1594
static void ipc_push_pending(ipc_client *client)
Definition: ipc.c:45
#define DUMP_PROPERTY(key, prop_name)
struct pending_marks * marks
static i3_shmlog_header * header
Definition: log.c:53
const char * current_binding_mode
Definition: main.c:88
struct ev_loop * main_loop
Definition: main.c:79
struct bindings_head * bindings
Definition: main.c:87
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
Definition: output.c:53
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:16
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
Definition: randr.c:50
struct outputs_head outputs
Definition: randr.c:22
void sync_respond(xcb_window_t window, uint32_t rnd)
Definition: sync.c:12
struct Con * focused
Definition: tree.c:13
struct Con * croot
Definition: tree.c:12
struct all_cons_head all_cons
Definition: tree.c:15
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
Definition: tree.c:451
const char * i3_version
Git commit identifier, from version.c.
Definition: version.c:13
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
Definition: workspace.c:306
char * previous_workspace_name
Stores a copy of the name of the last used workspace for the workspace back-and-forth switching.
Definition: workspace.c:19
@ I3_XKB_GROUP_MASK_2
Definition: data.h:117
@ I3_XKB_GROUP_MASK_3
Definition: data.h:118
@ I3_XKB_GROUP_MASK_4
Definition: data.h:119
@ I3_XKB_GROUP_MASK_1
Definition: data.h:116
@ L_STACKED
Definition: data.h:95
@ L_TABBED
Definition: data.h:96
@ L_DOCKAREA
Definition: data.h:97
@ L_OUTPUT
Definition: data.h:98
@ L_SPLITH
Definition: data.h:100
@ L_SPLITV
Definition: data.h:99
@ L_DEFAULT
Definition: data.h:94
@ HORIZ
Definition: data.h:60
@ CF_OUTPUT
Definition: data.h:601
@ BS_NONE
Definition: data.h:65
@ BS_PIXEL
Definition: data.h:66
@ BS_NORMAL
Definition: data.h:64
@ B_KEYBOARD
Definition: data.h:107
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)
Definition: ipc.h:56
shutdown_reason_t
Calls to ipc_shutdown() should provide a reason for the shutdown.
Definition: ipc.h:93
@ SHUTDOWN_REASON_RESTART
Definition: ipc.h:94
@ SHUTDOWN_REASON_EXIT
Definition: ipc.h:95
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),...
#define DLOG(fmt,...)
Definition: libi3.h:105
#define LOG(fmt,...)
Definition: libi3.h:95
void set_nonblock(int sockfd)
Puts the given socket file descriptor into non-blocking mode or dies if setting O_NONBLOCK failed.
#define ELOG(fmt,...)
Definition: libi3.h:100
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)
Definition: queue.h:114
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:347
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:376
#define TAILQ_FIRST(head)
Definition: queue.h:336
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:402
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define TAILQ_EMPTY(head)
Definition: queue.h:344
#define FREE(pointer)
Definition: util.h:47
#define yalloc(callbacks, client)
Definition: yajl_utils.h:23
size_t ylength
Definition: yajl_utils.h:24
#define ygenalloc()
Definition: yajl_utils.h:22
char * last_key
Definition: ipc.c:1296
xcb_window_t window
Definition: ipc.c:1298
uint32_t rnd
Definition: ipc.c:1297
A struct that contains useful information about the result of a command as a whole (e....
List entry struct for an included file.
Definition: configuration.h:78
char * variable_replaced_contents
Definition: configuration.h:81
char * raw_contents
Definition: configuration.h:80
The configuration file can contain multiple sets of bindings.
Definition: configuration.h:92
char * name
Definition: configuration.h:93
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.
Definition: data.h:158
uint32_t height
Definition: data.h:162
uint32_t x
Definition: data.h:159
uint32_t y
Definition: data.h:160
uint32_t width
Definition: data.h:161
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
Definition: data.h:277
char * command
Command, like in command mode.
Definition: data.h:328
uint32_t keycode
Keycode to bind.
Definition: data.h:310
char * symbol
Symbol the user specified in configfile, if any.
Definition: data.h:320
i3_event_state_mask_t event_state_mask
Bitmask which is applied against event->state for KeyPress and KeyRelease events to determine whether...
Definition: data.h:315
input_type_t input_type
Definition: data.h:280
An Output is a physical output on your graphics driver.
Definition: data.h:362
i3String * name
The name of the window.
Definition: data.h:412
xcb_window_t id
Definition: data.h:396
xcb_atom_t window_type
The _NET_WM_WINDOW_TYPE for this window.
Definition: data.h:436
xcb_window_t transient_for
Definition: data.h:401
A "match" is a data structure which acts like a mask or expression to match certain windows or not.
Definition: data.h:500
bool restart_mode
Definition: data.h:554
enum Match::@15 insert_where
enum Match::@13 dock
Definition: data.h:604
char * name
Definition: data.h:605
A 'Con' represents everything from the X11 root window down to a single X11 window.
Definition: data.h:614
enum Con::@20 scratchpad_state
struct Rect deco_rect
Definition: data.h:656
enum Con::@18 type
layout_t workspace_layout
Definition: data.h:723
double percent
Definition: data.h:680
struct Rect rect
Definition: data.h:650
int current_border_width
Definition: data.h:684
bool sticky
Definition: data.h:707
layout_t layout
Definition: data.h:723
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:644
struct Rect window_rect
Definition: data.h:653
int window_icon_padding
Whether the window icon should be displayed, and with what padding.
Definition: data.h:668
struct Window * window
Definition: data.h:686
char * title_format
The format with which the window's name should be displayed.
Definition: data.h:663
border_style_t border_style
Definition: data.h:724
char * name
Definition: data.h:660
struct Rect geometry
the geometry this window requested when getting mapped
Definition: data.h:658
uint16_t depth
Definition: data.h:763
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
Definition: data.h:702
bool urgent
Definition: data.h:619
Definition: ipc.h:26
char ** events
Definition: ipc.h:31
int num_events
Definition: ipc.h:30
size_t buffer_size
Definition: ipc.h:41
struct ev_io * read_callback
Definition: ipc.h:37
struct ev_timer * timeout
Definition: ipc.h:39
int fd
Definition: ipc.h:27
uint8_t * buffer
Definition: ipc.h:40
struct ev_io * write_callback
Definition: ipc.h:38
uint32_t size
Definition: shmlog.h:31