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