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 #include "all.h"
11 
12 #include "yajl_utils.h"
13 
14 #include <stdint.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <fcntl.h>
18 #include <libgen.h>
19 #include <ev.h>
20 #include <yajl/yajl_gen.h>
21 #include <yajl/yajl_parse.h>
22 
23 char *current_socketpath = NULL;
24 
25 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
26 
27 /*
28  * Puts the given socket file descriptor into non-blocking mode or dies if
29  * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
30  * IPC model because we should by no means block the window manager.
31  *
32  */
33 static void set_nonblock(int sockfd) {
34  int flags = fcntl(sockfd, F_GETFL, 0);
35  flags |= O_NONBLOCK;
36  if (fcntl(sockfd, F_SETFL, flags) < 0)
37  err(-1, "Could not set O_NONBLOCK");
38 }
39 
40 /*
41  * Sends the specified event to all IPC clients which are currently connected
42  * and subscribed to this kind of event.
43  *
44  */
45 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
46  ipc_client *current;
47  TAILQ_FOREACH(current, &all_clients, clients) {
48  /* see if this client is interested in this event */
49  bool interested = false;
50  for (int i = 0; i < current->num_events; i++) {
51  if (strcasecmp(current->events[i], event) != 0)
52  continue;
53  interested = true;
54  break;
55  }
56  if (!interested)
57  continue;
58 
59  ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t *)payload);
60  }
61 }
62 
63 /*
64  * Calls shutdown() on each socket and closes it. This function to be called
65  * when exiting or restarting only!
66  *
67  */
68 void ipc_shutdown(void) {
69  ipc_client *current;
70  while (!TAILQ_EMPTY(&all_clients)) {
71  current = TAILQ_FIRST(&all_clients);
72  shutdown(current->fd, SHUT_RDWR);
73  close(current->fd);
74  for (int i = 0; i < current->num_events; i++)
75  free(current->events[i]);
76  free(current->events);
77  TAILQ_REMOVE(&all_clients, current, clients);
78  free(current);
79  }
80 }
81 
82 /*
83  * Executes the command and returns whether it could be successfully parsed
84  * or not (at the moment, always returns true).
85  *
86  */
87 IPC_HANDLER(command) {
88  /* To get a properly terminated buffer, we copy
89  * message_size bytes out of the buffer */
90  char *command = scalloc(message_size + 1, 1);
91  strncpy(command, (const char *)message, message_size);
92  LOG("IPC: received: *%s*\n", command);
93  yajl_gen gen = yajl_gen_alloc(NULL);
94 
95  CommandResult *result = parse_command((const char *)command, gen);
96  free(command);
97 
98  if (result->needs_tree_render)
99  tree_render();
100 
101  command_result_free(result);
102 
103  const unsigned char *reply;
104  ylength length;
105  yajl_gen_get_buf(gen, &reply, &length);
106 
107  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
108  (const uint8_t *)reply);
109 
110  yajl_gen_free(gen);
111 }
112 
113 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
114  ystr(name);
115  y(map_open);
116  ystr("x");
117  y(integer, r.x);
118  ystr("y");
119  y(integer, r.y);
120  ystr("width");
121  y(integer, r.width);
122  ystr("height");
123  y(integer, r.height);
124  y(map_close);
125 }
126 
127 static void dump_event_state_mask(yajl_gen gen, Binding *bind) {
128  y(array_open);
129  for (int i = 0; i < 20; i++) {
130  if (bind->event_state_mask & (1 << i)) {
131  switch (1 << i) {
132  case XCB_KEY_BUT_MASK_SHIFT:
133  ystr("shift");
134  break;
135  case XCB_KEY_BUT_MASK_LOCK:
136  ystr("lock");
137  break;
138  case XCB_KEY_BUT_MASK_CONTROL:
139  ystr("ctrl");
140  break;
141  case XCB_KEY_BUT_MASK_MOD_1:
142  ystr("Mod1");
143  break;
144  case XCB_KEY_BUT_MASK_MOD_2:
145  ystr("Mod2");
146  break;
147  case XCB_KEY_BUT_MASK_MOD_3:
148  ystr("Mod3");
149  break;
150  case XCB_KEY_BUT_MASK_MOD_4:
151  ystr("Mod4");
152  break;
153  case XCB_KEY_BUT_MASK_MOD_5:
154  ystr("Mod5");
155  break;
156  case XCB_KEY_BUT_MASK_BUTTON_1:
157  ystr("Button1");
158  break;
159  case XCB_KEY_BUT_MASK_BUTTON_2:
160  ystr("Button2");
161  break;
162  case XCB_KEY_BUT_MASK_BUTTON_3:
163  ystr("Button3");
164  break;
165  case XCB_KEY_BUT_MASK_BUTTON_4:
166  ystr("Button4");
167  break;
168  case XCB_KEY_BUT_MASK_BUTTON_5:
169  ystr("Button5");
170  break;
171  case (I3_XKB_GROUP_MASK_1 << 16):
172  ystr("Group1");
173  break;
174  case (I3_XKB_GROUP_MASK_2 << 16):
175  ystr("Group2");
176  break;
177  case (I3_XKB_GROUP_MASK_3 << 16):
178  ystr("Group3");
179  break;
180  case (I3_XKB_GROUP_MASK_4 << 16):
181  ystr("Group4");
182  break;
183  }
184  }
185  }
186  y(array_close);
187 }
188 
189 static void dump_binding(yajl_gen gen, Binding *bind) {
190  y(map_open);
191  ystr("input_code");
192  y(integer, bind->keycode);
193 
194  ystr("input_type");
195  ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
196 
197  ystr("symbol");
198  if (bind->symbol == NULL)
199  y(null);
200  else
201  ystr(bind->symbol);
202 
203  ystr("command");
204  ystr(bind->command);
205 
206  // This key is only provided for compatibility, new programs should use
207  // event_state_mask instead.
208  ystr("mods");
209  dump_event_state_mask(gen, bind);
210 
211  ystr("event_state_mask");
212  dump_event_state_mask(gen, bind);
213 
214  y(map_close);
215 }
216 
217 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
218  y(map_open);
219  ystr("id");
220  y(integer, (uintptr_t)con);
221 
222  ystr("type");
223  switch (con->type) {
224  case CT_ROOT:
225  ystr("root");
226  break;
227  case CT_OUTPUT:
228  ystr("output");
229  break;
230  case CT_CON:
231  ystr("con");
232  break;
233  case CT_FLOATING_CON:
234  ystr("floating_con");
235  break;
236  case CT_WORKSPACE:
237  ystr("workspace");
238  break;
239  case CT_DOCKAREA:
240  ystr("dockarea");
241  break;
242  default:
243  DLOG("About to dump unknown container type=%d. This is a bug.\n", con->type);
244  assert(false);
245  break;
246  }
247 
248  /* provided for backwards compatibility only. */
249  ystr("orientation");
250  if (!con_is_split(con))
251  ystr("none");
252  else {
253  if (con_orientation(con) == HORIZ)
254  ystr("horizontal");
255  else
256  ystr("vertical");
257  }
258 
259  ystr("scratchpad_state");
260  switch (con->scratchpad_state) {
261  case SCRATCHPAD_NONE:
262  ystr("none");
263  break;
264  case SCRATCHPAD_FRESH:
265  ystr("fresh");
266  break;
267  case SCRATCHPAD_CHANGED:
268  ystr("changed");
269  break;
270  }
271 
272  ystr("percent");
273  if (con->percent == 0.0)
274  y(null);
275  else
276  y(double, con->percent);
277 
278  ystr("urgent");
279  y(bool, con->urgent);
280 
281  if (!TAILQ_EMPTY(&(con->marks_head))) {
282  ystr("marks");
283  y(array_open);
284 
285  mark_t *mark;
286  TAILQ_FOREACH(mark, &(con->marks_head), marks) {
287  ystr(mark->name);
288  }
289 
290  y(array_close);
291  }
292 
293  ystr("focused");
294  y(bool, (con == focused));
295 
296  if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
297  ystr("output");
298  ystr(con_get_output(con)->name);
299  }
300 
301  ystr("layout");
302  switch (con->layout) {
303  case L_DEFAULT:
304  DLOG("About to dump layout=default, this is a bug in the code.\n");
305  assert(false);
306  break;
307  case L_SPLITV:
308  ystr("splitv");
309  break;
310  case L_SPLITH:
311  ystr("splith");
312  break;
313  case L_STACKED:
314  ystr("stacked");
315  break;
316  case L_TABBED:
317  ystr("tabbed");
318  break;
319  case L_DOCKAREA:
320  ystr("dockarea");
321  break;
322  case L_OUTPUT:
323  ystr("output");
324  break;
325  }
326 
327  ystr("workspace_layout");
328  switch (con->workspace_layout) {
329  case L_DEFAULT:
330  ystr("default");
331  break;
332  case L_STACKED:
333  ystr("stacked");
334  break;
335  case L_TABBED:
336  ystr("tabbed");
337  break;
338  default:
339  DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
340  assert(false);
341  break;
342  }
343 
344  ystr("last_split_layout");
345  switch (con->layout) {
346  case L_SPLITV:
347  ystr("splitv");
348  break;
349  default:
350  ystr("splith");
351  break;
352  }
353 
354  ystr("border");
355  switch (con->border_style) {
356  case BS_NORMAL:
357  ystr("normal");
358  break;
359  case BS_NONE:
360  ystr("none");
361  break;
362  case BS_PIXEL:
363  ystr("pixel");
364  break;
365  }
366 
367  ystr("current_border_width");
368  y(integer, con->current_border_width);
369 
370  dump_rect(gen, "rect", con->rect);
371  dump_rect(gen, "deco_rect", con->deco_rect);
372  dump_rect(gen, "window_rect", con->window_rect);
373  dump_rect(gen, "geometry", con->geometry);
374 
375  ystr("name");
376  if (con->window && con->window->name)
377  ystr(i3string_as_utf8(con->window->name));
378  else if (con->name != NULL)
379  ystr(con->name);
380  else
381  y(null);
382 
383  if (con->title_format != NULL) {
384  ystr("title_format");
385  ystr(con->title_format);
386  }
387 
388  if (con->type == CT_WORKSPACE) {
389  ystr("num");
390  y(integer, con->num);
391  }
392 
393  ystr("window");
394  if (con->window)
395  y(integer, con->window->id);
396  else
397  y(null);
398 
399  if (con->window && !inplace_restart) {
400  /* Window properties are useless to preserve when restarting because
401  * they will be queried again anyway. However, for i3-save-tree(1),
402  * they are very useful and save i3-save-tree dealing with X11. */
403  ystr("window_properties");
404  y(map_open);
405 
406 #define DUMP_PROPERTY(key, prop_name) \
407  do { \
408  if (con->window->prop_name != NULL) { \
409  ystr(key); \
410  ystr(con->window->prop_name); \
411  } \
412  } while (0)
413 
414  DUMP_PROPERTY("class", class_class);
415  DUMP_PROPERTY("instance", class_instance);
416  DUMP_PROPERTY("window_role", role);
417 
418  if (con->window->name != NULL) {
419  ystr("title");
420  ystr(i3string_as_utf8(con->window->name));
421  }
422 
423  ystr("transient_for");
424  if (con->window->transient_for == XCB_NONE)
425  y(null);
426  else
427  y(integer, con->window->transient_for);
428 
429  y(map_close);
430  }
431 
432  ystr("nodes");
433  y(array_open);
434  Con *node;
435  if (con->type != CT_DOCKAREA || !inplace_restart) {
436  TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
437  dump_node(gen, node, inplace_restart);
438  }
439  }
440  y(array_close);
441 
442  ystr("floating_nodes");
443  y(array_open);
444  TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
445  dump_node(gen, node, inplace_restart);
446  }
447  y(array_close);
448 
449  ystr("focus");
450  y(array_open);
451  TAILQ_FOREACH(node, &(con->focus_head), focused) {
452  y(integer, (uintptr_t)node);
453  }
454  y(array_close);
455 
456  ystr("fullscreen_mode");
457  y(integer, con->fullscreen_mode);
458 
459  ystr("sticky");
460  y(bool, con->sticky);
461 
462  ystr("floating");
463  switch (con->floating) {
464  case FLOATING_AUTO_OFF:
465  ystr("auto_off");
466  break;
467  case FLOATING_AUTO_ON:
468  ystr("auto_on");
469  break;
470  case FLOATING_USER_OFF:
471  ystr("user_off");
472  break;
473  case FLOATING_USER_ON:
474  ystr("user_on");
475  break;
476  }
477 
478  ystr("swallows");
479  y(array_open);
480  Match *match;
481  TAILQ_FOREACH(match, &(con->swallow_head), matches) {
482  /* We will generate a new restart_mode match specification after this
483  * loop, so skip this one. */
484  if (match->restart_mode)
485  continue;
486  y(map_open);
487  if (match->dock != M_DONTCHECK) {
488  ystr("dock");
489  y(integer, match->dock);
490  ystr("insert_where");
491  y(integer, match->insert_where);
492  }
493 
494 #define DUMP_REGEX(re_name) \
495  do { \
496  if (match->re_name != NULL) { \
497  ystr(#re_name); \
498  ystr(match->re_name->pattern); \
499  } \
500  } while (0)
501 
502  DUMP_REGEX(class);
503  DUMP_REGEX(instance);
504  DUMP_REGEX(window_role);
505  DUMP_REGEX(title);
506 
507 #undef DUMP_REGEX
508  y(map_close);
509  }
510 
511  if (inplace_restart) {
512  if (con->window != NULL) {
513  y(map_open);
514  ystr("id");
515  y(integer, con->window->id);
516  ystr("restart_mode");
517  y(bool, true);
518  y(map_close);
519  }
520  }
521  y(array_close);
522 
523  if (inplace_restart && con->window != NULL) {
524  ystr("depth");
525  y(integer, con->depth);
526  }
527 
528  y(map_close);
529 }
530 
531 static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
532  if (TAILQ_EMPTY(&(config->bar_bindings)))
533  return;
534 
535  ystr("bindings");
536  y(array_open);
537 
538  struct Barbinding *current;
539  TAILQ_FOREACH(current, &(config->bar_bindings), bindings) {
540  y(map_open);
541 
542  ystr("input_code");
543  y(integer, current->input_code);
544  ystr("command");
545  ystr(current->command);
546 
547  y(map_close);
548  }
549 
550  y(array_close);
551 }
552 
553 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
554  y(map_open);
555 
556  ystr("id");
557  ystr(config->id);
558 
559  if (config->num_outputs > 0) {
560  ystr("outputs");
561  y(array_open);
562  for (int c = 0; c < config->num_outputs; c++)
563  ystr(config->outputs[c]);
564  y(array_close);
565  }
566 
567  if (!TAILQ_EMPTY(&(config->tray_outputs))) {
568  ystr("tray_outputs");
569  y(array_open);
570 
571  struct tray_output_t *tray_output;
572  TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) {
573  ystr(tray_output->output);
574  }
575 
576  y(array_close);
577  }
578 
579 #define YSTR_IF_SET(name) \
580  do { \
581  if (config->name) { \
582  ystr(#name); \
583  ystr(config->name); \
584  } \
585  } while (0)
586 
587  ystr("tray_padding");
588  y(integer, config->tray_padding);
589 
590  YSTR_IF_SET(socket_path);
591 
592  ystr("mode");
593  switch (config->mode) {
594  case M_HIDE:
595  ystr("hide");
596  break;
597  case M_INVISIBLE:
598  ystr("invisible");
599  break;
600  case M_DOCK:
601  default:
602  ystr("dock");
603  break;
604  }
605 
606  ystr("hidden_state");
607  switch (config->hidden_state) {
608  case S_SHOW:
609  ystr("show");
610  break;
611  case S_HIDE:
612  default:
613  ystr("hide");
614  break;
615  }
616 
617  ystr("modifier");
618  switch (config->modifier) {
619  case M_NONE:
620  ystr("none");
621  break;
622  case M_CONTROL:
623  ystr("ctrl");
624  break;
625  case M_SHIFT:
626  ystr("shift");
627  break;
628  case M_MOD1:
629  ystr("Mod1");
630  break;
631  case M_MOD2:
632  ystr("Mod2");
633  break;
634  case M_MOD3:
635  ystr("Mod3");
636  break;
637  case M_MOD5:
638  ystr("Mod5");
639  break;
640  default:
641  ystr("Mod4");
642  break;
643  }
644 
645  dump_bar_bindings(gen, config);
646 
647  ystr("position");
648  if (config->position == P_BOTTOM)
649  ystr("bottom");
650  else
651  ystr("top");
652 
653  YSTR_IF_SET(status_command);
654  YSTR_IF_SET(font);
655 
656  if (config->separator_symbol) {
657  ystr("separator_symbol");
658  ystr(config->separator_symbol);
659  }
660 
661  ystr("workspace_buttons");
662  y(bool, !config->hide_workspace_buttons);
663 
664  ystr("strip_workspace_numbers");
665  y(bool, config->strip_workspace_numbers);
666 
667  ystr("binding_mode_indicator");
668  y(bool, !config->hide_binding_mode_indicator);
669 
670  ystr("verbose");
671  y(bool, config->verbose);
672 
673 #undef YSTR_IF_SET
674 #define YSTR_IF_SET(name) \
675  do { \
676  if (config->colors.name) { \
677  ystr(#name); \
678  ystr(config->colors.name); \
679  } \
680  } while (0)
681 
682  ystr("colors");
683  y(map_open);
684  YSTR_IF_SET(background);
685  YSTR_IF_SET(statusline);
686  YSTR_IF_SET(separator);
687  YSTR_IF_SET(focused_background);
688  YSTR_IF_SET(focused_statusline);
689  YSTR_IF_SET(focused_separator);
690  YSTR_IF_SET(focused_workspace_border);
691  YSTR_IF_SET(focused_workspace_bg);
692  YSTR_IF_SET(focused_workspace_text);
693  YSTR_IF_SET(active_workspace_border);
694  YSTR_IF_SET(active_workspace_bg);
695  YSTR_IF_SET(active_workspace_text);
696  YSTR_IF_SET(inactive_workspace_border);
697  YSTR_IF_SET(inactive_workspace_bg);
698  YSTR_IF_SET(inactive_workspace_text);
699  YSTR_IF_SET(urgent_workspace_border);
700  YSTR_IF_SET(urgent_workspace_bg);
701  YSTR_IF_SET(urgent_workspace_text);
702  YSTR_IF_SET(binding_mode_border);
703  YSTR_IF_SET(binding_mode_bg);
704  YSTR_IF_SET(binding_mode_text);
705  y(map_close);
706 
707  y(map_close);
708 #undef YSTR_IF_SET
709 }
710 
711 IPC_HANDLER(tree) {
712  setlocale(LC_NUMERIC, "C");
713  yajl_gen gen = ygenalloc();
714  dump_node(gen, croot, false);
715  setlocale(LC_NUMERIC, "");
716 
717  const unsigned char *payload;
718  ylength length;
719  y(get_buf, &payload, &length);
720 
721  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload);
722  y(free);
723 }
724 
725 /*
726  * Formats the reply message for a GET_WORKSPACES request and sends it to the
727  * client
728  *
729  */
730 IPC_HANDLER(get_workspaces) {
731  yajl_gen gen = ygenalloc();
732  y(array_open);
733 
734  Con *focused_ws = con_get_workspace(focused);
735 
736  Con *output;
737  TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
738  if (con_is_internal(output))
739  continue;
740  Con *ws;
741  TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
742  assert(ws->type == CT_WORKSPACE);
743  y(map_open);
744 
745  ystr("num");
746  y(integer, ws->num);
747 
748  ystr("name");
749  ystr(ws->name);
750 
751  ystr("visible");
752  y(bool, workspace_is_visible(ws));
753 
754  ystr("focused");
755  y(bool, ws == focused_ws);
756 
757  ystr("rect");
758  y(map_open);
759  ystr("x");
760  y(integer, ws->rect.x);
761  ystr("y");
762  y(integer, ws->rect.y);
763  ystr("width");
764  y(integer, ws->rect.width);
765  ystr("height");
766  y(integer, ws->rect.height);
767  y(map_close);
768 
769  ystr("output");
770  ystr(output->name);
771 
772  ystr("urgent");
773  y(bool, ws->urgent);
774 
775  y(map_close);
776  }
777  }
778 
779  y(array_close);
780 
781  const unsigned char *payload;
782  ylength length;
783  y(get_buf, &payload, &length);
784 
785  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
786  y(free);
787 }
788 
789 /*
790  * Formats the reply message for a GET_OUTPUTS request and sends it to the
791  * client
792  *
793  */
794 IPC_HANDLER(get_outputs) {
795  yajl_gen gen = ygenalloc();
796  y(array_open);
797 
798  Output *output;
799  TAILQ_FOREACH(output, &outputs, outputs) {
800  y(map_open);
801 
802  ystr("name");
803  ystr(output->name);
804 
805  ystr("active");
806  y(bool, output->active);
807 
808  ystr("primary");
809  y(bool, output->primary);
810 
811  ystr("rect");
812  y(map_open);
813  ystr("x");
814  y(integer, output->rect.x);
815  ystr("y");
816  y(integer, output->rect.y);
817  ystr("width");
818  y(integer, output->rect.width);
819  ystr("height");
820  y(integer, output->rect.height);
821  y(map_close);
822 
823  ystr("current_workspace");
824  Con *ws = NULL;
825  if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
826  ystr(ws->name);
827  else
828  y(null);
829 
830  y(map_close);
831  }
832 
833  y(array_close);
834 
835  const unsigned char *payload;
836  ylength length;
837  y(get_buf, &payload, &length);
838 
839  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
840  y(free);
841 }
842 
843 /*
844  * Formats the reply message for a GET_MARKS request and sends it to the
845  * client
846  *
847  */
848 IPC_HANDLER(get_marks) {
849  yajl_gen gen = ygenalloc();
850  y(array_open);
851 
852  Con *con;
854  mark_t *mark;
855  TAILQ_FOREACH(mark, &(con->marks_head), marks) {
856  ystr(mark->name);
857  }
858  }
859 
860  y(array_close);
861 
862  const unsigned char *payload;
863  ylength length;
864  y(get_buf, &payload, &length);
865 
866  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_MARKS, payload);
867  y(free);
868 }
869 
870 /*
871  * Returns the version of i3
872  *
873  */
874 IPC_HANDLER(get_version) {
875  yajl_gen gen = ygenalloc();
876  y(map_open);
877 
878  ystr("major");
879  y(integer, MAJOR_VERSION);
880 
881  ystr("minor");
882  y(integer, MINOR_VERSION);
883 
884  ystr("patch");
885  y(integer, PATCH_VERSION);
886 
887  ystr("human_readable");
888  ystr(i3_version);
889 
890  ystr("loaded_config_file_name");
892 
893  y(map_close);
894 
895  const unsigned char *payload;
896  ylength length;
897  y(get_buf, &payload, &length);
898 
899  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_VERSION, payload);
900  y(free);
901 }
902 
903 /*
904  * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
905  * client.
906  *
907  */
908 IPC_HANDLER(get_bar_config) {
909  yajl_gen gen = ygenalloc();
910 
911  /* If no ID was passed, we return a JSON array with all IDs */
912  if (message_size == 0) {
913  y(array_open);
914  Barconfig *current;
915  TAILQ_FOREACH(current, &barconfigs, configs) {
916  ystr(current->id);
917  }
918  y(array_close);
919 
920  const unsigned char *payload;
921  ylength length;
922  y(get_buf, &payload, &length);
923 
924  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
925  y(free);
926  return;
927  }
928 
929  /* To get a properly terminated buffer, we copy
930  * message_size bytes out of the buffer */
931  char *bar_id = NULL;
932  sasprintf(&bar_id, "%.*s", message_size, message);
933  LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
934  Barconfig *current, *config = NULL;
935  TAILQ_FOREACH(current, &barconfigs, configs) {
936  if (strcmp(current->id, bar_id) != 0)
937  continue;
938 
939  config = current;
940  break;
941  }
942  free(bar_id);
943 
944  if (!config) {
945  /* If we did not find a config for the given ID, the reply will contain
946  * a null 'id' field. */
947  y(map_open);
948 
949  ystr("id");
950  y(null);
951 
952  y(map_close);
953  } else {
954  dump_bar_config(gen, config);
955  }
956 
957  const unsigned char *payload;
958  ylength length;
959  y(get_buf, &payload, &length);
960 
961  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
962  y(free);
963 }
964 
965 /*
966  * Returns a list of configured binding modes
967  *
968  */
969 IPC_HANDLER(get_binding_modes) {
970  yajl_gen gen = ygenalloc();
971 
972  y(array_open);
973  struct Mode *mode;
974  SLIST_FOREACH(mode, &modes, modes) {
975  ystr(mode->name);
976  }
977  y(array_close);
978 
979  const unsigned char *payload;
980  ylength length;
981  y(get_buf, &payload, &length);
982 
983  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload);
984  y(free);
985 }
986 
987 /*
988  * Callback for the YAJL parser (will be called when a string is parsed).
989  *
990  */
991 static int add_subscription(void *extra, const unsigned char *s,
992  ylength len) {
993  ipc_client *client = extra;
994 
995  DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
996  int event = client->num_events;
997 
998  client->num_events++;
999  client->events = srealloc(client->events, client->num_events * sizeof(char *));
1000  /* We copy the string because it is not null-terminated and strndup()
1001  * is missing on some BSD systems */
1002  client->events[event] = scalloc(len + 1, 1);
1003  memcpy(client->events[event], s, len);
1004 
1005  DLOG("client is now subscribed to:\n");
1006  for (int i = 0; i < client->num_events; i++)
1007  DLOG("event %s\n", client->events[i]);
1008  DLOG("(done)\n");
1009 
1010  return 1;
1011 }
1012 
1013 /*
1014  * Subscribes this connection to the event types which were given as a JSON
1015  * serialized array in the payload field of the message.
1016  *
1017  */
1018 IPC_HANDLER(subscribe) {
1019  yajl_handle p;
1020  yajl_status stat;
1021  ipc_client *current, *client = NULL;
1022 
1023  /* Search the ipc_client structure for this connection */
1024  TAILQ_FOREACH(current, &all_clients, clients) {
1025  if (current->fd != fd)
1026  continue;
1027 
1028  client = current;
1029  break;
1030  }
1031 
1032  if (client == NULL) {
1033  ELOG("Could not find ipc_client data structure for fd %d\n", fd);
1034  return;
1035  }
1036 
1037  /* Setup the JSON parser */
1038  static yajl_callbacks callbacks = {
1039  .yajl_string = add_subscription,
1040  };
1041 
1042  p = yalloc(&callbacks, (void *)client);
1043  stat = yajl_parse(p, (const unsigned char *)message, message_size);
1044  if (stat != yajl_status_ok) {
1045  unsigned char *err;
1046  err = yajl_get_error(p, true, (const unsigned char *)message,
1047  message_size);
1048  ELOG("YAJL parse error: %s\n", err);
1049  yajl_free_error(p, err);
1050 
1051  const char *reply = "{\"success\":false}";
1052  ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1053  yajl_free(p);
1054  return;
1055  }
1056  yajl_free(p);
1057  const char *reply = "{\"success\":true}";
1058  ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1059 }
1060 
1061 /* The index of each callback function corresponds to the numeric
1062  * value of the message type (see include/i3/ipc.h) */
1064  handle_command,
1065  handle_get_workspaces,
1066  handle_subscribe,
1067  handle_get_outputs,
1068  handle_tree,
1069  handle_get_marks,
1070  handle_get_bar_config,
1071  handle_get_version,
1072  handle_get_binding_modes,
1073 };
1074 
1075 /*
1076  * Handler for activity on a client connection, receives a message from a
1077  * client.
1078  *
1079  * For now, the maximum message size is 2048. I’m not sure for what the
1080  * IPC interface will be used in the future, thus I’m not implementing a
1081  * mechanism for arbitrarily long messages, as it seems like overkill
1082  * at the moment.
1083  *
1084  */
1085 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
1086  uint32_t message_type;
1087  uint32_t message_length;
1088  uint8_t *message = NULL;
1089 
1090  int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
1091  /* EOF or other error */
1092  if (ret < 0) {
1093  /* Was this a spurious read? See ev(3) */
1094  if (ret == -1 && errno == EAGAIN) {
1095  FREE(message);
1096  return;
1097  }
1098 
1099  /* If not, there was some kind of error. We don’t bother
1100  * and close the connection */
1101  close(w->fd);
1102 
1103  /* Delete the client from the list of clients */
1104  ipc_client *current;
1105  TAILQ_FOREACH(current, &all_clients, clients) {
1106  if (current->fd != w->fd)
1107  continue;
1108 
1109  for (int i = 0; i < current->num_events; i++)
1110  free(current->events[i]);
1111  free(current->events);
1112  /* We can call TAILQ_REMOVE because we break out of the
1113  * TAILQ_FOREACH afterwards */
1114  TAILQ_REMOVE(&all_clients, current, clients);
1115  free(current);
1116  break;
1117  }
1118 
1119  ev_io_stop(EV_A_ w);
1120  free(w);
1121  FREE(message);
1122 
1123  DLOG("IPC: client disconnected\n");
1124  return;
1125  }
1126 
1127  if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
1128  DLOG("Unhandled message type: %d\n", message_type);
1129  else {
1130  handler_t h = handlers[message_type];
1131  h(w->fd, message, 0, message_length, message_type);
1132  }
1133 
1134  FREE(message);
1135 }
1136 
1137 /*
1138  * Handler for activity on the listening socket, meaning that a new client
1139  * has just connected and we should accept() him. Sets up the event handler
1140  * for activity on the new connection and inserts the file descriptor into
1141  * the list of clients.
1142  *
1143  */
1144 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
1145  struct sockaddr_un peer;
1146  socklen_t len = sizeof(struct sockaddr_un);
1147  int client;
1148  if ((client = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
1149  if (errno == EINTR)
1150  return;
1151  else
1152  perror("accept()");
1153  return;
1154  }
1155 
1156  /* Close this file descriptor on exec() */
1157  (void)fcntl(client, F_SETFD, FD_CLOEXEC);
1158 
1159  set_nonblock(client);
1160 
1161  struct ev_io *package = scalloc(1, sizeof(struct ev_io));
1162  ev_io_init(package, ipc_receive_message, client, EV_READ);
1163  ev_io_start(EV_A_ package);
1164 
1165  DLOG("IPC: new client connected on fd %d\n", w->fd);
1166 
1167  ipc_client *new = scalloc(1, sizeof(ipc_client));
1168  new->fd = client;
1169 
1170  TAILQ_INSERT_TAIL(&all_clients, new, clients);
1171 }
1172 
1173 /*
1174  * Creates the UNIX domain socket at the given path, sets it to non-blocking
1175  * mode, bind()s and listen()s on it.
1176  *
1177  */
1178 int ipc_create_socket(const char *filename) {
1179  int sockfd;
1180 
1182 
1183  char *resolved = resolve_tilde(filename);
1184  DLOG("Creating IPC-socket at %s\n", resolved);
1185  char *copy = sstrdup(resolved);
1186  const char *dir = dirname(copy);
1187  if (!path_exists(dir))
1188  mkdirp(dir, DEFAULT_DIR_MODE);
1189  free(copy);
1190 
1191  /* Unlink the unix domain socket before */
1192  unlink(resolved);
1193 
1194  if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1195  perror("socket()");
1196  free(resolved);
1197  return -1;
1198  }
1199 
1200  (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1201 
1202  struct sockaddr_un addr;
1203  memset(&addr, 0, sizeof(struct sockaddr_un));
1204  addr.sun_family = AF_LOCAL;
1205  strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
1206  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
1207  perror("bind()");
1208  free(resolved);
1209  return -1;
1210  }
1211 
1212  set_nonblock(sockfd);
1213 
1214  if (listen(sockfd, 5) < 0) {
1215  perror("listen()");
1216  free(resolved);
1217  return -1;
1218  }
1219 
1220  current_socketpath = resolved;
1221  return sockfd;
1222 }
1223 
1224 /*
1225  * Generates a json workspace event. Returns a dynamically allocated yajl
1226  * generator. Free with yajl_gen_free().
1227  */
1228 yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
1229  setlocale(LC_NUMERIC, "C");
1230  yajl_gen gen = ygenalloc();
1231 
1232  y(map_open);
1233 
1234  ystr("change");
1235  ystr(change);
1236 
1237  ystr("current");
1238  if (current == NULL)
1239  y(null);
1240  else
1241  dump_node(gen, current, false);
1242 
1243  ystr("old");
1244  if (old == NULL)
1245  y(null);
1246  else
1247  dump_node(gen, old, false);
1248 
1249  y(map_close);
1250 
1251  setlocale(LC_NUMERIC, "");
1252 
1253  return gen;
1254 }
1255 
1256 /*
1257  * For the workspace events we send, along with the usual "change" field, also
1258  * the workspace container in "current". For focus events, we send the
1259  * previously focused workspace in "old".
1260  */
1261 void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
1262  yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
1263 
1264  const unsigned char *payload;
1265  ylength length;
1266  y(get_buf, &payload, &length);
1267 
1268  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1269 
1270  y(free);
1271 }
1272 
1277 void ipc_send_window_event(const char *property, Con *con) {
1278  DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1279  property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
1280 
1281  setlocale(LC_NUMERIC, "C");
1282  yajl_gen gen = ygenalloc();
1283 
1284  y(map_open);
1285 
1286  ystr("change");
1287  ystr(property);
1288 
1289  ystr("container");
1290  dump_node(gen, con, false);
1291 
1292  y(map_close);
1293 
1294  const unsigned char *payload;
1295  ylength length;
1296  y(get_buf, &payload, &length);
1297 
1298  ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
1299  y(free);
1300  setlocale(LC_NUMERIC, "");
1301 }
1302 
1307  DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
1308  setlocale(LC_NUMERIC, "C");
1309  yajl_gen gen = ygenalloc();
1310 
1311  dump_bar_config(gen, barconfig);
1312 
1313  const unsigned char *payload;
1314  ylength length;
1315  y(get_buf, &payload, &length);
1316 
1317  ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
1318  y(free);
1319  setlocale(LC_NUMERIC, "");
1320 }
1321 
1322 /*
1323  * For the binding events, we send the serialized binding struct.
1324  */
1325 void ipc_send_binding_event(const char *event_type, Binding *bind) {
1326  DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
1327 
1328  setlocale(LC_NUMERIC, "C");
1329 
1330  yajl_gen gen = ygenalloc();
1331 
1332  y(map_open);
1333 
1334  ystr("change");
1335  ystr(event_type);
1336 
1337  ystr("binding");
1338  dump_binding(gen, bind);
1339 
1340  y(map_close);
1341 
1342  const unsigned char *payload;
1343  ylength length;
1344  y(get_buf, &payload, &length);
1345 
1346  ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
1347 
1348  y(free);
1349  setlocale(LC_NUMERIC, "");
1350 }
static void dump_bar_bindings(yajl_gen gen, Barconfig *config)
Definition: ipc.c:531
int ipc_send_message(int sockfd, const uint32_t message_size, const uint32_t message_type, const uint8_t *payload)
Formats a message (payload) of the given size and type and sends it to i3 via the given socket file d...
#define FREE(pointer)
Definition: util.h:50
IPC_HANDLER(command)
Definition: ipc.c:87
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 ELOG(fmt,...)
Definition: libi3.h:89
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, in "container".
Definition: ipc.c:1277
struct outputs_head outputs
Definition: randr.c:26
#define DEFAULT_DIR_MODE
Definition: libi3.h:25
char * separator_symbol
A custom separator to use instead of a vertical line.
uint32_t y
Definition: data.h:150
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:588
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
Definition: data.h:268
uint32_t height
Definition: data.h:152
enum Barconfig::@11 position
Bar position (bottom by default).
char * command
The command which is to be executed for this button.
struct barconfig_head barconfigs
Definition: config.c:18
void ipc_send_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.
Definition: ipc.c:1306
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:45
int num_events
Definition: ipc.h:31
uint32_t x
Definition: data.h:149
struct Con * croot
Definition: tree.c:12
char * name
Definition: data.h:549
char * current_socketpath
Definition: ipc.c:23
struct bindings_head * bindings
Definition: main.c:72
char ** outputs
Outputs on which this bar should show up on.
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
Definition: workspace.c:254
An Output is a physical output on your graphics driver.
Definition: data.h:344
int fd
Definition: ipc.h:28
Definition: data.h:60
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
Definition: ipc.c:217
char * id
Automatically generated ID for this bar config.
bool path_exists(const char *path)
Checks if the given path exists by calling stat().
Definition: util.c:150
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...
enum Barconfig::@9 hidden_state
static int add_subscription(void *extra, const unsigned char *s, ylength len)
Definition: ipc.c:991
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:16
#define LOG(fmt,...)
Definition: libi3.h:84
struct Rect rect
Definition: data.h:594
struct modes_head modes
Definition: config.c:17
#define yalloc(callbacks, client)
Definition: yajl_utils.h:23
int tray_padding
enum Match::@17 insert_where
static void dump_binding(yajl_gen gen, Binding *bind)
Definition: ipc.c:189
Definition: data.h:548
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:148
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:376
bool strip_workspace_numbers
Strip workspace numbers? Configuration option is &#39;strip_workspace_numbers yes&#39;.
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
void(* handler_t)(int, uint8_t *, int, uint32_t, uint32_t)
Definition: ipc.h:47
char * command
Command, like in command mode.
Definition: data.h:315
#define DUMP_REGEX(re_name)
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
Rect rect
x, y, width, height
Definition: data.h:365
const char * i3string_as_utf8(i3String *str)
Returns the UTF-8 encoded version of the i3String.
Holds the status bar configuration (i3bar).
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:347
char * name
Definition: configuration.h:81
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:372
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
Definition: con.c:358
bool hide_workspace_buttons
Hide workspace buttons? Configuration option is &#39;workspace_buttons no&#39; but we invert the bool to get ...
#define YSTR_IF_SET(name)
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...
void command_result_free(CommandResult *result)
Frees a CommandResult.
input_type_t input_type
Definition: data.h:271
int mkdirp(const char *path, mode_t mode)
Emulates mkdir -p (creates any missing folders)
#define TAILQ_FIRST(head)
Definition: queue.h:336
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
Definition: con.c:1226
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:490
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
Definition: data.h:350
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...
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
Definition: ipc.c:1325
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
Definition: data.h:453
void ipc_shutdown(void)
Calls shutdown() on each socket and closes it.
Definition: ipc.c:68
char * name
Definition: data.h:604
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:402
static void dump_rect(yajl_gen gen, const char *name, Rect r)
Definition: ipc.c:113
char * current_configpath
Definition: config.c:15
bool con_is_split(Con *con)
Returns true if a container should be considered split.
Definition: con.c:280
int input_code
The button to be used (e.g., 1 for "button1").
Definition: data.h:62
Definition: data.h:98
#define DUMP_PROPERTY(key, prop_name)
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Definition: con.c:466
struct Window * window
Definition: data.h:625
static void dump_event_state_mask(yajl_gen gen, Binding *bind)
Definition: ipc.c:127
Definition: ipc.h:27
Con * con
Pointer to the Con which represents this output.
Definition: data.h:362
char * symbol
Symbol the user specified in configfile, if any.
Definition: data.h:307
xcb_window_t id
Definition: data.h:376
Definition: data.h:97
bool verbose
Enable verbose mode? Useful for debugging purposes.
char ** events
Definition: ipc.h:32
Definition: data.h:63
size_t ylength
Definition: yajl_utils.h:24
A struct that contains useful information about the result of a command as a whole (e...
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old)
Generates a json workspace event.
Definition: ipc.c:1228
struct all_cons_head all_cons
Definition: tree.c:15
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:1144
Defines a mouse command to be executed instead of the default behavior when clicking on the non-statu...
CommandResult * parse_command(const char *input, yajl_gen gen)
Parses and executes the given command.
Definition: data.h:92
int num_outputs
Number of outputs in the outputs array.
#define ygenalloc()
Definition: yajl_utils.h:22
bool urgent
Definition: data.h:563
enum Barconfig::@8 mode
Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mo...
Definition: data.h:96
const char * i3_version
Git commit identifier, from version.c.
Definition: version.c:13
A &#39;Con&#39; represents everything from the X11 root window down to a single X11 window.
Definition: data.h:558
The configuration file can contain multiple sets of bindings.
Definition: configuration.h:80
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:114
Definition: data.h:64
handler_t handlers[9]
Definition: ipc.c:1063
bool hide_binding_mode_indicator
Hide mode button? Configuration option is &#39;binding_mode_indicator no&#39; but we invert the bool for the ...
uint32_t width
Definition: data.h:151
#define DLOG(fmt,...)
Definition: libi3.h:94
#define ystr(str)
Definition: commands.c:24
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
Definition: con.c:420
Definition: data.h:94
char * name
Name of the output.
Definition: data.h:359
TAILQ_HEAD(ipc_client_head, ipc_client)
Definition: ipc.c:25
Definition: data.h:93
#define TAILQ_EMPTY(head)
Definition: queue.h:344
Config config
Definition: config.c:16
uint32_t keycode
Keycode to bind.
Definition: data.h:297
int ipc_create_socket(const char *filename)
Creates the UNIX domain socket at the given path, sets it to non-blocking mode, bind()s and listen()s...
Definition: ipc.c:1178
uint32_t y
Definition: data.h:128
bool primary
Definition: data.h:356
enum Match::@15 dock
bool restart_mode
Definition: data.h:501
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:302
enum Barconfig::@10 modifier
Bar modifier (to show bar when in hide mode).
enum Con::@20 type
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:1261
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
Definition: ipc.c:1085
static void dump_bar_config(yajl_gen gen, Barconfig *config)
Definition: ipc.c:553
Con * focused
Definition: tree.c:13