i3
load_layout.c
Go to the documentation of this file.
1 #undef I3__FILE__
2 #define I3__FILE__ "load_layout.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * load_layout.c: Restore (parts of) the layout, for example after an inplace
10  * restart.
11  *
12  */
13 #include "all.h"
14 
15 #include <yajl/yajl_common.h>
16 #include <yajl/yajl_gen.h>
17 #include <yajl/yajl_parse.h>
18 #include <yajl/yajl_version.h>
19 
20 /* TODO: refactor the whole parsing thing */
21 
22 static char *last_key;
23 static Con *json_node;
24 static Con *to_focus;
25 static bool parsing_swallows;
26 static bool parsing_rect;
27 static bool parsing_deco_rect;
28 static bool parsing_window_rect;
29 static bool parsing_geometry;
30 static bool parsing_focus;
31 static bool parsing_marks;
33 static bool swallow_is_empty;
34 
35 /* This list is used for reordering the focus stack after parsing the 'focus'
36  * array. */
37 struct focus_mapping {
38  int old_id;
39  TAILQ_ENTRY(focus_mapping) focus_mappings;
40 };
41 
42 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
43  TAILQ_HEAD_INITIALIZER(focus_mappings);
44 
45 static int json_start_map(void *ctx) {
46  LOG("start of map, last_key = %s\n", last_key);
47  if (parsing_swallows) {
48  LOG("creating new swallow\n");
49  current_swallow = smalloc(sizeof(Match));
50  match_init(current_swallow);
51  TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
52  swallow_is_empty = true;
53  } else {
55  if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
56  DLOG("New floating_node\n");
57  Con *ws = con_get_workspace(json_node);
58  json_node = con_new_skeleton(NULL, NULL);
59  json_node->name = NULL;
60  json_node->parent = ws;
61  DLOG("Parent is workspace = %p\n", ws);
62  } else {
63  Con *parent = json_node;
64  json_node = con_new_skeleton(NULL, NULL);
65  json_node->name = NULL;
66  json_node->parent = parent;
67  }
68  }
69  }
70  return 1;
71 }
72 
73 static int json_end_map(void *ctx) {
74  LOG("end of map\n");
76  /* Set a few default values to simplify manually crafted layout files. */
77  if (json_node->layout == L_DEFAULT) {
78  DLOG("Setting layout = L_SPLITH\n");
79  json_node->layout = L_SPLITH;
80  }
81 
82  /* Sanity check: swallow criteria don’t make any sense on a split
83  * container. */
84  if (con_is_split(json_node) > 0 && !TAILQ_EMPTY(&(json_node->swallow_head))) {
85  DLOG("sanity check: removing swallows specification from split container\n");
86  while (!TAILQ_EMPTY(&(json_node->swallow_head))) {
87  Match *match = TAILQ_FIRST(&(json_node->swallow_head));
88  TAILQ_REMOVE(&(json_node->swallow_head), match, matches);
89  match_free(match);
90  free(match);
91  }
92  }
93 
94  if (json_node->type == CT_WORKSPACE) {
95  /* Ensure the workspace has a name. */
96  DLOG("Attaching workspace. name = %s\n", json_node->name);
97  if (json_node->name == NULL || strcmp(json_node->name, "") == 0) {
98  json_node->name = sstrdup("unnamed");
99  }
100 
101  /* Prevent name clashes when appending a workspace, e.g. when the
102  * user tries to restore a workspace called “1” but already has a
103  * workspace called “1”. */
104  Con *output;
105  Con *workspace = NULL;
106  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
107  GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
108  char *base = sstrdup(json_node->name);
109  int cnt = 1;
110  while (workspace != NULL) {
111  FREE(json_node->name);
112  sasprintf(&(json_node->name), "%s_%d", base, cnt++);
113  workspace = NULL;
114  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
115  GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
116  }
117  free(base);
118 
119  /* Set num accordingly so that i3bar will properly sort it. */
120  json_node->num = ws_name_to_number(json_node->name);
121  }
122 
123  // When appending JSON layout files that only contain the workspace
124  // _contents_, we might not have an upfront signal that the
125  // container we’re currently parsing is a floating container (like
126  // the “floating_nodes” key of the workspace container itself).
127  // That’s why we make sure the con is attached at the right place
128  // in the hierarchy in case it’s floating.
129  if (json_node->type == CT_FLOATING_CON) {
130  DLOG("fixing parent which currently is %p / %s\n", json_node->parent, json_node->parent->name);
131  json_node->parent = con_get_workspace(json_node->parent);
132 
133  // Also set a size if none was supplied, otherwise the placeholder
134  // window cannot be created as X11 requests with width=0 or
135  // height=0 are invalid.
136  const Rect zero = {0, 0, 0, 0};
137  if (memcmp(&(json_node->rect), &zero, sizeof(Rect)) == 0) {
138  DLOG("Geometry not set, combining children\n");
139  Con *child;
140  TAILQ_FOREACH(child, &(json_node->nodes_head), nodes) {
141  DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
142  json_node->rect.width += child->geometry.width;
143  json_node->rect.height = max(json_node->rect.height, child->geometry.height);
144  }
145  }
146 
147  floating_check_size(json_node);
148  }
149 
150  LOG("attaching\n");
151  con_attach(json_node, json_node->parent, true);
152  LOG("Creating window\n");
153  x_con_init(json_node, json_node->depth);
154  json_node = json_node->parent;
155  }
156 
158  /* We parsed an empty swallow definition. This is an invalid layout
159  * definition, hence we reject it. */
160  ELOG("Layout file is invalid: found an empty swallow definition.\n");
161  return 0;
162  }
163 
164  parsing_rect = false;
165  parsing_deco_rect = false;
166  parsing_window_rect = false;
167  parsing_geometry = false;
168  return 1;
169 }
170 
171 static int json_end_array(void *ctx) {
172  LOG("end of array\n");
174  con_fix_percent(json_node);
175  }
176  if (parsing_swallows) {
177  parsing_swallows = false;
178  }
179  if (parsing_marks) {
180  parsing_marks = false;
181  }
182 
183  if (parsing_focus) {
184  /* Clear the list of focus mappings */
185  struct focus_mapping *mapping;
186  TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
187  LOG("focus (reverse) %d\n", mapping->old_id);
188  Con *con;
189  TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
190  if (con->old_id != mapping->old_id)
191  continue;
192  LOG("got it! %p\n", con);
193  /* Move this entry to the top of the focus list. */
194  TAILQ_REMOVE(&(json_node->focus_head), con, focused);
195  TAILQ_INSERT_HEAD(&(json_node->focus_head), con, focused);
196  break;
197  }
198  }
199  while (!TAILQ_EMPTY(&focus_mappings)) {
200  mapping = TAILQ_FIRST(&focus_mappings);
201  TAILQ_REMOVE(&focus_mappings, mapping, focus_mappings);
202  free(mapping);
203  }
204  parsing_focus = false;
205  }
206  return 1;
207 }
208 
209 static int json_key(void *ctx, const unsigned char *val, size_t len) {
210  LOG("key: %.*s\n", (int)len, val);
211  FREE(last_key);
212  last_key = scalloc(len + 1, 1);
213  memcpy(last_key, val, len);
214  if (strcasecmp(last_key, "swallows") == 0)
215  parsing_swallows = true;
216 
217  if (strcasecmp(last_key, "rect") == 0)
218  parsing_rect = true;
219 
220  if (strcasecmp(last_key, "deco_rect") == 0)
221  parsing_deco_rect = true;
222 
223  if (strcasecmp(last_key, "window_rect") == 0)
224  parsing_window_rect = true;
225 
226  if (strcasecmp(last_key, "geometry") == 0)
227  parsing_geometry = true;
228 
229  if (strcasecmp(last_key, "focus") == 0)
230  parsing_focus = true;
231 
232  if (strcasecmp(last_key, "marks") == 0)
233  parsing_marks = true;
234 
235  return 1;
236 }
237 
238 static int json_string(void *ctx, const unsigned char *val, size_t len) {
239  LOG("string: %.*s for key %s\n", (int)len, val, last_key);
240  if (parsing_swallows) {
241  char *sval;
242  sasprintf(&sval, "%.*s", len, val);
243  if (strcasecmp(last_key, "class") == 0) {
244  current_swallow->class = regex_new(sval);
245  swallow_is_empty = false;
246  } else if (strcasecmp(last_key, "instance") == 0) {
247  current_swallow->instance = regex_new(sval);
248  swallow_is_empty = false;
249  } else if (strcasecmp(last_key, "window_role") == 0) {
250  current_swallow->window_role = regex_new(sval);
251  swallow_is_empty = false;
252  } else if (strcasecmp(last_key, "title") == 0) {
253  current_swallow->title = regex_new(sval);
254  swallow_is_empty = false;
255  } else {
256  ELOG("swallow key %s unknown\n", last_key);
257  }
258  free(sval);
259  } else if (parsing_marks) {
260  char *mark;
261  sasprintf(&mark, "%.*s", (int)len, val);
262 
263  con_mark(json_node, mark, MM_ADD);
264  } else {
265  if (strcasecmp(last_key, "name") == 0) {
266  json_node->name = scalloc(len + 1, 1);
267  memcpy(json_node->name, val, len);
268  } else if (strcasecmp(last_key, "title_format") == 0) {
269  json_node->title_format = scalloc(len + 1, 1);
270  memcpy(json_node->title_format, val, len);
271  } else if (strcasecmp(last_key, "sticky_group") == 0) {
272  json_node->sticky_group = scalloc(len + 1, 1);
273  memcpy(json_node->sticky_group, val, len);
274  LOG("sticky_group of this container is %s\n", json_node->sticky_group);
275  } else if (strcasecmp(last_key, "orientation") == 0) {
276  /* Upgrade path from older versions of i3 (doing an inplace restart
277  * to a newer version):
278  * "orientation" is dumped before "layout". Therefore, we store
279  * whether the orientation was horizontal or vertical in the
280  * last_split_layout. When we then encounter layout == "default",
281  * we will use the last_split_layout as layout instead. */
282  char *buf = NULL;
283  sasprintf(&buf, "%.*s", (int)len, val);
284  if (strcasecmp(buf, "none") == 0 ||
285  strcasecmp(buf, "horizontal") == 0)
286  json_node->last_split_layout = L_SPLITH;
287  else if (strcasecmp(buf, "vertical") == 0)
288  json_node->last_split_layout = L_SPLITV;
289  else
290  LOG("Unhandled orientation: %s\n", buf);
291  free(buf);
292  } else if (strcasecmp(last_key, "border") == 0) {
293  char *buf = NULL;
294  sasprintf(&buf, "%.*s", (int)len, val);
295  if (strcasecmp(buf, "none") == 0)
296  json_node->border_style = BS_NONE;
297  else if (strcasecmp(buf, "1pixel") == 0) {
298  json_node->border_style = BS_PIXEL;
299  json_node->current_border_width = 1;
300  } else if (strcasecmp(buf, "pixel") == 0)
301  json_node->border_style = BS_PIXEL;
302  else if (strcasecmp(buf, "normal") == 0)
303  json_node->border_style = BS_NORMAL;
304  else
305  LOG("Unhandled \"border\": %s\n", buf);
306  free(buf);
307  } else if (strcasecmp(last_key, "type") == 0) {
308  char *buf = NULL;
309  sasprintf(&buf, "%.*s", (int)len, val);
310  if (strcasecmp(buf, "root") == 0)
311  json_node->type = CT_ROOT;
312  else if (strcasecmp(buf, "output") == 0)
313  json_node->type = CT_OUTPUT;
314  else if (strcasecmp(buf, "con") == 0)
315  json_node->type = CT_CON;
316  else if (strcasecmp(buf, "floating_con") == 0)
317  json_node->type = CT_FLOATING_CON;
318  else if (strcasecmp(buf, "workspace") == 0)
319  json_node->type = CT_WORKSPACE;
320  else if (strcasecmp(buf, "dockarea") == 0)
321  json_node->type = CT_DOCKAREA;
322  else
323  LOG("Unhandled \"type\": %s\n", buf);
324  free(buf);
325  } else if (strcasecmp(last_key, "layout") == 0) {
326  char *buf = NULL;
327  sasprintf(&buf, "%.*s", (int)len, val);
328  if (strcasecmp(buf, "default") == 0)
329  /* This set above when we read "orientation". */
330  json_node->layout = json_node->last_split_layout;
331  else if (strcasecmp(buf, "stacked") == 0)
332  json_node->layout = L_STACKED;
333  else if (strcasecmp(buf, "tabbed") == 0)
334  json_node->layout = L_TABBED;
335  else if (strcasecmp(buf, "dockarea") == 0)
336  json_node->layout = L_DOCKAREA;
337  else if (strcasecmp(buf, "output") == 0)
338  json_node->layout = L_OUTPUT;
339  else if (strcasecmp(buf, "splith") == 0)
340  json_node->layout = L_SPLITH;
341  else if (strcasecmp(buf, "splitv") == 0)
342  json_node->layout = L_SPLITV;
343  else
344  LOG("Unhandled \"layout\": %s\n", buf);
345  free(buf);
346  } else if (strcasecmp(last_key, "workspace_layout") == 0) {
347  char *buf = NULL;
348  sasprintf(&buf, "%.*s", (int)len, val);
349  if (strcasecmp(buf, "default") == 0)
350  json_node->workspace_layout = L_DEFAULT;
351  else if (strcasecmp(buf, "stacked") == 0)
352  json_node->workspace_layout = L_STACKED;
353  else if (strcasecmp(buf, "tabbed") == 0)
354  json_node->workspace_layout = L_TABBED;
355  else
356  LOG("Unhandled \"workspace_layout\": %s\n", buf);
357  free(buf);
358  } else if (strcasecmp(last_key, "last_split_layout") == 0) {
359  char *buf = NULL;
360  sasprintf(&buf, "%.*s", (int)len, val);
361  if (strcasecmp(buf, "splith") == 0)
362  json_node->last_split_layout = L_SPLITH;
363  else if (strcasecmp(buf, "splitv") == 0)
364  json_node->last_split_layout = L_SPLITV;
365  else
366  LOG("Unhandled \"last_splitlayout\": %s\n", buf);
367  free(buf);
368  } else if (strcasecmp(last_key, "mark") == 0) {
369  DLOG("Found deprecated key \"mark\".\n");
370 
371  char *buf = NULL;
372  sasprintf(&buf, "%.*s", (int)len, val);
373 
374  con_mark(json_node, buf, MM_REPLACE);
375  } else if (strcasecmp(last_key, "floating") == 0) {
376  char *buf = NULL;
377  sasprintf(&buf, "%.*s", (int)len, val);
378  if (strcasecmp(buf, "auto_off") == 0)
379  json_node->floating = FLOATING_AUTO_OFF;
380  else if (strcasecmp(buf, "auto_on") == 0)
381  json_node->floating = FLOATING_AUTO_ON;
382  else if (strcasecmp(buf, "user_off") == 0)
383  json_node->floating = FLOATING_USER_OFF;
384  else if (strcasecmp(buf, "user_on") == 0)
385  json_node->floating = FLOATING_USER_ON;
386  free(buf);
387  } else if (strcasecmp(last_key, "scratchpad_state") == 0) {
388  char *buf = NULL;
389  sasprintf(&buf, "%.*s", (int)len, val);
390  if (strcasecmp(buf, "none") == 0)
391  json_node->scratchpad_state = SCRATCHPAD_NONE;
392  else if (strcasecmp(buf, "fresh") == 0)
393  json_node->scratchpad_state = SCRATCHPAD_FRESH;
394  else if (strcasecmp(buf, "changed") == 0)
395  json_node->scratchpad_state = SCRATCHPAD_CHANGED;
396  free(buf);
397  }
398  }
399  return 1;
400 }
401 
402 static int json_int(void *ctx, long long val) {
403  LOG("int %lld for key %s\n", val, last_key);
404  /* For backwards compatibility with i3 < 4.8 */
405  if (strcasecmp(last_key, "type") == 0)
406  json_node->type = val;
407 
408  if (strcasecmp(last_key, "fullscreen_mode") == 0)
409  json_node->fullscreen_mode = val;
410 
411  if (strcasecmp(last_key, "num") == 0)
412  json_node->num = val;
413 
414  if (strcasecmp(last_key, "current_border_width") == 0)
415  json_node->current_border_width = val;
416 
417  if (strcasecmp(last_key, "depth") == 0)
418  json_node->depth = val;
419 
420  if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
421  json_node->old_id = val;
422 
423  if (parsing_focus) {
424  struct focus_mapping *focus_mapping = scalloc(1, sizeof(struct focus_mapping));
425  focus_mapping->old_id = val;
426  TAILQ_INSERT_TAIL(&focus_mappings, focus_mapping, focus_mappings);
427  }
428 
430  Rect *r;
431  if (parsing_rect)
432  r = &(json_node->rect);
433  else if (parsing_window_rect)
434  r = &(json_node->window_rect);
435  else
436  r = &(json_node->geometry);
437  if (strcasecmp(last_key, "x") == 0)
438  r->x = val;
439  else if (strcasecmp(last_key, "y") == 0)
440  r->y = val;
441  else if (strcasecmp(last_key, "width") == 0)
442  r->width = val;
443  else if (strcasecmp(last_key, "height") == 0)
444  r->height = val;
445  else
446  ELOG("WARNING: unknown key %s in rect\n", last_key);
447  DLOG("rect now: (%d, %d, %d, %d)\n",
448  r->x, r->y, r->width, r->height);
449  }
450  if (parsing_swallows) {
451  if (strcasecmp(last_key, "id") == 0) {
452  current_swallow->id = val;
453  swallow_is_empty = false;
454  }
455  if (strcasecmp(last_key, "dock") == 0) {
456  current_swallow->dock = val;
457  swallow_is_empty = false;
458  }
459  if (strcasecmp(last_key, "insert_where") == 0) {
460  current_swallow->insert_where = val;
461  swallow_is_empty = false;
462  }
463  }
464 
465  return 1;
466 }
467 
468 static int json_bool(void *ctx, int val) {
469  LOG("bool %d for key %s\n", val, last_key);
470  if (strcasecmp(last_key, "focused") == 0 && val) {
471  to_focus = json_node;
472  }
473 
474  if (strcasecmp(last_key, "sticky") == 0)
475  json_node->sticky = val;
476 
477  if (parsing_swallows) {
478  if (strcasecmp(last_key, "restart_mode") == 0) {
479  current_swallow->restart_mode = val;
480  swallow_is_empty = false;
481  }
482  }
483 
484  return 1;
485 }
486 
487 static int json_double(void *ctx, double val) {
488  LOG("double %f for key %s\n", val, last_key);
489  if (strcasecmp(last_key, "percent") == 0) {
490  json_node->percent = val;
491  }
492  return 1;
493 }
494 
496 static int content_level;
497 
499  content_level++;
500  return 1;
501 }
502 
504  content_level--;
505  return 1;
506 }
507 
508 static int json_determine_content_string(void *ctx, const unsigned char *val, size_t len) {
509  if (strcasecmp(last_key, "type") != 0 || content_level > 1)
510  return 1;
511 
512  DLOG("string = %.*s, last_key = %s\n", (int)len, val, last_key);
513  if (strncasecmp((const char *)val, "workspace", len) == 0)
515  return 0;
516 }
517 
518 /* Parses the given JSON file until it encounters the first “type” property to
519  * determine whether the file contains workspaces or regular containers, which
520  * is important to know when deciding where (and how) to append the contents.
521  * */
522 json_content_t json_determine_content(const char *filename) {
523  FILE *f;
524  if ((f = fopen(filename, "r")) == NULL) {
525  ELOG("Cannot open file \"%s\"\n", filename);
526  return JSON_CONTENT_UNKNOWN;
527  }
528  struct stat stbuf;
529  if (fstat(fileno(f), &stbuf) != 0) {
530  ELOG("Cannot fstat() \"%s\"\n", filename);
531  fclose(f);
532  return JSON_CONTENT_UNKNOWN;
533  }
534  char *buf = smalloc(stbuf.st_size);
535  int n = fread(buf, 1, stbuf.st_size, f);
536  if (n != stbuf.st_size) {
537  ELOG("File \"%s\" could not be read entirely, not loading.\n", filename);
538  fclose(f);
539  return JSON_CONTENT_UNKNOWN;
540  }
541  DLOG("read %d bytes\n", n);
542  // We default to JSON_CONTENT_CON because it is legal to not include
543  // “"type": "con"” in the JSON files for better readability.
545  content_level = 0;
546  yajl_gen g;
547  yajl_handle hand;
548  static yajl_callbacks callbacks = {
549  .yajl_string = json_determine_content_string,
550  .yajl_map_key = json_key,
551  .yajl_start_array = json_determine_content_deeper,
552  .yajl_start_map = json_determine_content_deeper,
553  .yajl_end_map = json_determine_content_shallower,
554  .yajl_end_array = json_determine_content_shallower,
555  };
556  g = yajl_gen_alloc(NULL);
557  hand = yajl_alloc(&callbacks, NULL, (void *)g);
558  /* Allowing comments allows for more user-friendly layout files. */
559  yajl_config(hand, yajl_allow_comments, true);
560  /* Allow multiple values, i.e. multiple nodes to attach */
561  yajl_config(hand, yajl_allow_multiple_values, true);
562  yajl_status stat;
563  setlocale(LC_NUMERIC, "C");
564  stat = yajl_parse(hand, (const unsigned char *)buf, n);
565  if (stat != yajl_status_ok && stat != yajl_status_client_canceled) {
566  unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n);
567  ELOG("JSON parsing error: %s\n", str);
568  yajl_free_error(hand, str);
569  }
570 
571  setlocale(LC_NUMERIC, "");
572  yajl_complete_parse(hand);
573 
574  fclose(f);
575 
576  return content_result;
577 }
578 
579 void tree_append_json(Con *con, const char *filename, char **errormsg) {
580  FILE *f;
581  if ((f = fopen(filename, "r")) == NULL) {
582  ELOG("Cannot open file \"%s\"\n", filename);
583  return;
584  }
585  struct stat stbuf;
586  if (fstat(fileno(f), &stbuf) != 0) {
587  ELOG("Cannot fstat() \"%s\"\n", filename);
588  fclose(f);
589  return;
590  }
591  char *buf = smalloc(stbuf.st_size);
592  int n = fread(buf, 1, stbuf.st_size, f);
593  if (n != stbuf.st_size) {
594  ELOG("File \"%s\" could not be read entirely, not loading.\n", filename);
595  fclose(f);
596  return;
597  }
598  DLOG("read %d bytes\n", n);
599  yajl_gen g;
600  yajl_handle hand;
601  static yajl_callbacks callbacks = {
602  .yajl_boolean = json_bool,
603  .yajl_integer = json_int,
604  .yajl_double = json_double,
605  .yajl_string = json_string,
606  .yajl_start_map = json_start_map,
607  .yajl_map_key = json_key,
608  .yajl_end_map = json_end_map,
609  .yajl_end_array = json_end_array,
610  };
611  g = yajl_gen_alloc(NULL);
612  hand = yajl_alloc(&callbacks, NULL, (void *)g);
613  /* Allowing comments allows for more user-friendly layout files. */
614  yajl_config(hand, yajl_allow_comments, true);
615  /* Allow multiple values, i.e. multiple nodes to attach */
616  yajl_config(hand, yajl_allow_multiple_values, true);
617  yajl_status stat;
618  json_node = con;
619  to_focus = NULL;
620  parsing_swallows = false;
621  parsing_rect = false;
622  parsing_deco_rect = false;
623  parsing_window_rect = false;
624  parsing_geometry = false;
625  parsing_focus = false;
626  parsing_marks = false;
627  setlocale(LC_NUMERIC, "C");
628  stat = yajl_parse(hand, (const unsigned char *)buf, n);
629  if (stat != yajl_status_ok) {
630  unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n);
631  ELOG("JSON parsing error: %s\n", str);
632  if (errormsg != NULL)
633  *errormsg = sstrdup((const char *)str);
634  yajl_free_error(hand, str);
635  }
636 
637  /* In case not all containers were restored, we need to fix the
638  * percentages, otherwise i3 will crash immediately when rendering the
639  * next time. */
640  con_fix_percent(con);
641 
642  setlocale(LC_NUMERIC, "");
643  yajl_complete_parse(hand);
644  yajl_free(hand);
645  yajl_gen_free(g);
646 
647  fclose(f);
648  free(buf);
649  if (to_focus)
650  con_focus(to_focus);
651 }
uint32_t height
Definition: data.h:145
uint32_t x
Definition: data.h:142
uint16_t depth
Definition: data.h:688
enum Con::@22 scratchpad_state
Definition: data.h:61
xcb_window_t id
Definition: data.h:463
void con_mark(Con *con, const char *mark, mark_mode_t mode)
Assigns a mark to the container.
Definition: con.c:602
char * title_format
The format with which the window&#39;s name should be displayed.
Definition: data.h:593
Definition: data.h:63
enum Match::@15 dock
#define TAILQ_ENTRY(type)
Definition: queue.h:327
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:574
void x_con_init(Con *con, uint16_t depth)
Initializes the X11 part for the given container.
Definition: x.c:97
char * name
Definition: data.h:590
static int json_end_map(void *ctx)
Definition: load_layout.c:73
Definition: data.h:87
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:198
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
#define TAILQ_EMPTY(head)
Definition: queue.h:344
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:18
void tree_append_json(Con *con, const char *filename, char **errormsg)
Definition: load_layout.c:579
enum Con::@21 floating
floating? (= not in tiling layout) This cannot be simply a bool because we want to keep track of whet...
Definition: data.h:79
void match_init(Match *match)
Definition: match.c:28
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:347
void match_free(Match *match)
Frees the given match.
Definition: match.c:254
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Definition: con.c:174
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...
fullscreen_mode_t fullscreen_mode
Definition: data.h:627
Definition: data.h:85
struct regex * title
Definition: data.h:443
int current_border_width
Definition: data.h:609
static int json_bool(void *ctx, int val)
Definition: load_layout.c:468
static bool parsing_swallows
Definition: load_layout.c:25
uint32_t y
Definition: data.h:143
static int json_end_array(void *ctx)
Definition: load_layout.c:171
Definition: data.h:86
static json_content_t content_result
Definition: load_layout.c:495
struct Con * croot
Definition: tree.c:14
struct regex * class
Definition: data.h:445
#define ELOG(fmt,...)
Definition: libi3.h:93
double percent
Definition: data.h:605
uint32_t width
Definition: data.h:144
#define GREP_FIRST(dest, head, condition)
Definition: util.h:39
bool restart_mode
Definition: data.h:487
#define LOG(fmt,...)
Definition: libi3.h:88
static int content_level
Definition: load_layout.c:496
struct Rect geometry
the geometry this window requested when getting mapped
Definition: data.h:588
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:402
Definition: data.h:91
struct Rect rect
Definition: data.h:580
static int json_determine_content_deeper(void *ctx)
Definition: load_layout.c:498
static xcb_cursor_context_t * ctx
Definition: xcursor.c:19
static int json_key(void *ctx, const unsigned char *val, size_t len)
Definition: load_layout.c:209
#define TAILQ_HEAD(name, type)
Definition: queue.h:318
static bool swallow_is_empty
Definition: load_layout.c:33
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition: queue.h:352
static Con * to_focus
Definition: load_layout.c:24
int old_id
Definition: data.h:685
static bool parsing_deco_rect
Definition: load_layout.c:27
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:366
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:141
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:373
static int json_double(void *ctx, double val)
Definition: load_layout.c:487
json_content_t json_determine_content(const char *filename)
Definition: load_layout.c:522
static bool parsing_marks
Definition: load_layout.c:31
struct regex * window_role
Definition: data.h:448
struct Rect window_rect
Definition: data.h:583
static Con * json_node
Definition: load_layout.c:23
enum Match::@17 insert_where
#define FREE(pointer)
Definition: util.h:48
#define DLOG(fmt,...)
Definition: libi3.h:98
static int json_determine_content_string(void *ctx, const unsigned char *val, size_t len)
Definition: load_layout.c:508
A &#39;Con&#39; represents everything from the X11 root window down to a single X11 window.
Definition: data.h:544
json_content_t
Definition: load_layout.h:13
#define TAILQ_FIRST(head)
Definition: queue.h:336
static int json_int(void *ctx, long long val)
Definition: load_layout.c:402
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 con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
Definition: con.c:736
bool con_is_split(Con *con)
Returns true if a container should be considered split.
Definition: con.c:281
static bool parsing_window_rect
Definition: load_layout.c:28
enum Con::@20 type
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:376
bool sticky
Definition: data.h:632
layout_t layout
Definition: data.h:648
Con * con_new_skeleton(Con *parent, i3Window *window)
Create a new container (and attach it to the given parent, if not NULL).
Definition: con.c:38
border_style_t border_style
Definition: data.h:649
Definition: data.h:89
int max(int a, int b)
Definition: util.c:33
struct regex * instance
Definition: data.h:446
static bool parsing_focus
Definition: load_layout.c:30
static int json_string(void *ctx, const unsigned char *val, size_t len)
Definition: load_layout.c:238
struct regex * regex_new(const char *pattern)
Creates a new &#39;regex&#39; struct containing the given pattern and a PCRE compiled regular expression...
Definition: regex.c:24
char * sticky_group
Definition: data.h:598
long ws_name_to_number(const char *name)
Parses the workspace name as a number.
Definition: util.c:76
void floating_check_size(Con *floating_con)
Called when a floating window is created or resized.
Definition: floating.c:66
A &quot;match&quot; is a data structure which acts like a mask or expression to match certain windows or not...
Definition: data.h:439
Con * focused
Definition: tree.c:15
Definition: data.h:90
layout_t workspace_layout
Definition: data.h:648
struct Match * current_swallow
Definition: load_layout.c:32
struct Con * parent
Definition: data.h:576
Definition: data.h:62
static char * last_key
Definition: load_layout.c:22
layout_t last_split_layout
Definition: data.h:648
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
static bool parsing_rect
Definition: load_layout.c:26
static int json_determine_content_shallower(void *ctx)
Definition: load_layout.c:503
static bool parsing_geometry
Definition: load_layout.c:29