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