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-2012 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_window_rect;
26 static bool parsing_geometry;
27 static bool parsing_focus;
29 
30 /* This list is used for reordering the focus stack after parsing the 'focus'
31  * array. */
32 struct focus_mapping {
33  int old_id;
34  TAILQ_ENTRY(focus_mapping) focus_mappings;
35 };
36 
37 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
38  TAILQ_HEAD_INITIALIZER(focus_mappings);
39 
40 static int json_start_map(void *ctx) {
41  LOG("start of map, last_key = %s\n", last_key);
42  if (parsing_swallows) {
43  LOG("creating new swallow\n");
44  current_swallow = smalloc(sizeof(Match));
45  match_init(current_swallow);
46  TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
47  } else {
49  if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
50  DLOG("New floating_node\n");
51  Con *ws = con_get_workspace(json_node);
52  json_node = con_new(NULL, NULL);
53  json_node->parent = ws;
54  DLOG("Parent is workspace = %p\n", ws);
55  } else {
56  Con *parent = json_node;
57  json_node = con_new(NULL, NULL);
58  json_node->parent = parent;
59  }
60  }
61  }
62  return 1;
63 }
64 
65 static int json_end_map(void *ctx) {
66  LOG("end of map\n");
68  LOG("attaching\n");
69  con_attach(json_node, json_node->parent, true);
70  json_node = json_node->parent;
71  }
72  if (parsing_rect)
73  parsing_rect = false;
75  parsing_window_rect = false;
76  if (parsing_geometry)
77  parsing_geometry = false;
78  return 1;
79 }
80 
81 static int json_end_array(void *ctx) {
82  LOG("end of array\n");
83  parsing_swallows = false;
84  if (parsing_focus) {
85  /* Clear the list of focus mappings */
86  struct focus_mapping *mapping;
87  TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
88  LOG("focus (reverse) %d\n", mapping->old_id);
89  Con *con;
90  TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
91  if (con->old_id != mapping->old_id)
92  continue;
93  LOG("got it! %p\n", con);
94  /* Move this entry to the top of the focus list. */
95  TAILQ_REMOVE(&(json_node->focus_head), con, focused);
96  TAILQ_INSERT_HEAD(&(json_node->focus_head), con, focused);
97  break;
98  }
99  }
100  while (!TAILQ_EMPTY(&focus_mappings)) {
101  mapping = TAILQ_FIRST(&focus_mappings);
102  TAILQ_REMOVE(&focus_mappings, mapping, focus_mappings);
103  free(mapping);
104  }
105  parsing_focus = false;
106  }
107  return 1;
108 }
109 
110 #if YAJL_MAJOR < 2
111 static int json_key(void *ctx, const unsigned char *val, unsigned int len) {
112 #else
113 static int json_key(void *ctx, const unsigned char *val, size_t len) {
114 #endif
115  LOG("key: %.*s\n", (int)len, val);
116  FREE(last_key);
117  last_key = scalloc((len+1) * sizeof(char));
118  memcpy(last_key, val, len);
119  if (strcasecmp(last_key, "swallows") == 0)
120  parsing_swallows = true;
121 
122  if (strcasecmp(last_key, "rect") == 0)
123  parsing_rect = true;
124 
125  if (strcasecmp(last_key, "window_rect") == 0)
126  parsing_window_rect = true;
127 
128  if (strcasecmp(last_key, "geometry") == 0)
129  parsing_geometry = true;
130 
131  if (strcasecmp(last_key, "focus") == 0)
132  parsing_focus = true;
133 
134  return 1;
135 }
136 
137 #if YAJL_MAJOR >= 2
138 static int json_string(void *ctx, const unsigned char *val, size_t len) {
139 #else
140 static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
141 #endif
142  LOG("string: %.*s for key %s\n", len, val, last_key);
143  if (parsing_swallows) {
144  /* TODO: the other swallowing keys */
145  if (strcasecmp(last_key, "class") == 0) {
146  current_swallow->class = scalloc((len+1) * sizeof(char));
147  memcpy(current_swallow->class, val, len);
148  }
149  LOG("unhandled yet: swallow\n");
150  } else {
151  if (strcasecmp(last_key, "name") == 0) {
152  json_node->name = scalloc((len+1) * sizeof(char));
153  memcpy(json_node->name, val, len);
154  } else if (strcasecmp(last_key, "sticky_group") == 0) {
155  json_node->sticky_group = scalloc((len+1) * sizeof(char));
156  memcpy(json_node->sticky_group, val, len);
157  LOG("sticky_group of this container is %s\n", json_node->sticky_group);
158  } else if (strcasecmp(last_key, "orientation") == 0) {
159  char *buf = NULL;
160  sasprintf(&buf, "%.*s", (int)len, val);
161  if (strcasecmp(buf, "none") == 0)
162  json_node->orientation = NO_ORIENTATION;
163  else if (strcasecmp(buf, "horizontal") == 0)
164  json_node->orientation = HORIZ;
165  else if (strcasecmp(buf, "vertical") == 0)
166  json_node->orientation = VERT;
167  else LOG("Unhandled orientation: %s\n", buf);
168  free(buf);
169  } else if (strcasecmp(last_key, "border") == 0) {
170  char *buf = NULL;
171  sasprintf(&buf, "%.*s", (int)len, val);
172  if (strcasecmp(buf, "none") == 0)
173  json_node->border_style = BS_NONE;
174  else if (strcasecmp(buf, "1pixel") == 0)
175  json_node->border_style = BS_1PIXEL;
176  else if (strcasecmp(buf, "normal") == 0)
177  json_node->border_style = BS_NORMAL;
178  else LOG("Unhandled \"border\": %s\n", buf);
179  free(buf);
180  } else if (strcasecmp(last_key, "layout") == 0) {
181  char *buf = NULL;
182  sasprintf(&buf, "%.*s", (int)len, val);
183  if (strcasecmp(buf, "default") == 0)
184  json_node->layout = L_DEFAULT;
185  else if (strcasecmp(buf, "stacked") == 0)
186  json_node->layout = L_STACKED;
187  else if (strcasecmp(buf, "tabbed") == 0)
188  json_node->layout = L_TABBED;
189  else if (strcasecmp(buf, "dockarea") == 0)
190  json_node->layout = L_DOCKAREA;
191  else if (strcasecmp(buf, "output") == 0)
192  json_node->layout = L_OUTPUT;
193  else LOG("Unhandled \"layout\": %s\n", buf);
194  free(buf);
195  } else if (strcasecmp(last_key, "mark") == 0) {
196  char *buf = NULL;
197  sasprintf(&buf, "%.*s", (int)len, val);
198  json_node->mark = buf;
199  } else if (strcasecmp(last_key, "floating") == 0) {
200  char *buf = NULL;
201  sasprintf(&buf, "%.*s", (int)len, val);
202  if (strcasecmp(buf, "auto_off") == 0)
203  json_node->floating = FLOATING_AUTO_OFF;
204  else if (strcasecmp(buf, "auto_on") == 0)
205  json_node->floating = FLOATING_AUTO_ON;
206  else if (strcasecmp(buf, "user_off") == 0)
207  json_node->floating = FLOATING_USER_OFF;
208  else if (strcasecmp(buf, "user_on") == 0)
209  json_node->floating = FLOATING_USER_ON;
210  free(buf);
211  } else if (strcasecmp(last_key, "scratchpad_state") == 0) {
212  char *buf = NULL;
213  sasprintf(&buf, "%.*s", (int)len, val);
214  if (strcasecmp(buf, "none") == 0)
215  json_node->scratchpad_state = SCRATCHPAD_NONE;
216  else if (strcasecmp(buf, "fresh") == 0)
217  json_node->scratchpad_state = SCRATCHPAD_FRESH;
218  else if (strcasecmp(buf, "changed") == 0)
219  json_node->scratchpad_state = SCRATCHPAD_CHANGED;
220  free(buf);
221  }
222  }
223  return 1;
224 }
225 
226 #if YAJL_MAJOR >= 2
227 static int json_int(void *ctx, long long val) {
228  LOG("int %lld for key %s\n", val, last_key);
229 #else
230 static int json_int(void *ctx, long val) {
231  LOG("int %ld for key %s\n", val, last_key);
232 #endif
233  if (strcasecmp(last_key, "type") == 0)
234  json_node->type = val;
235 
236  if (strcasecmp(last_key, "fullscreen_mode") == 0)
237  json_node->fullscreen_mode = val;
238 
239  if (strcasecmp(last_key, "num") == 0)
240  json_node->num = val;
241 
242  if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
243  json_node->old_id = val;
244 
245  if (parsing_focus) {
246  struct focus_mapping *focus_mapping = scalloc(sizeof(struct focus_mapping));
247  focus_mapping->old_id = val;
248  TAILQ_INSERT_TAIL(&focus_mappings, focus_mapping, focus_mappings);
249  }
250 
252  Rect *r;
253  if (parsing_rect)
254  r = &(json_node->rect);
255  else if (parsing_window_rect)
256  r = &(json_node->window_rect);
257  else r = &(json_node->geometry);
258  if (strcasecmp(last_key, "x") == 0)
259  r->x = val;
260  else if (strcasecmp(last_key, "y") == 0)
261  r->y = val;
262  else if (strcasecmp(last_key, "width") == 0)
263  r->width = val;
264  else if (strcasecmp(last_key, "height") == 0)
265  r->height = val;
266  else printf("WARNING: unknown key %s in rect\n", last_key);
267  printf("rect now: (%d, %d, %d, %d)\n",
268  r->x, r->y, r->width, r->height);
269  }
270  if (parsing_swallows) {
271  if (strcasecmp(last_key, "id") == 0) {
272  current_swallow->id = val;
273  }
274  if (strcasecmp(last_key, "dock") == 0) {
275  current_swallow->dock = val;
276  }
277  if (strcasecmp(last_key, "insert_where") == 0) {
278  current_swallow->insert_where = val;
279  }
280  }
281 
282  return 1;
283 }
284 
285 static int json_bool(void *ctx, int val) {
286  LOG("bool %d for key %s\n", val, last_key);
287  if (strcasecmp(last_key, "focused") == 0 && val) {
288  to_focus = json_node;
289  }
290 
291  if (parsing_swallows) {
292  if (strcasecmp(last_key, "restart_mode") == 0)
293  current_swallow->restart_mode = val;
294  }
295 
296  return 1;
297 }
298 
299 static int json_double(void *ctx, double val) {
300  LOG("double %f for key %s\n", val, last_key);
301  if (strcasecmp(last_key, "percent") == 0) {
302  json_node->percent = val;
303  }
304  return 1;
305 }
306 
307 void tree_append_json(const char *filename) {
308  /* TODO: percent of other windows are not correctly fixed at the moment */
309  FILE *f;
310  if ((f = fopen(filename, "r")) == NULL) {
311  LOG("Cannot open file\n");
312  return;
313  }
314  char *buf = malloc(65535); /* TODO */
315  int n = fread(buf, 1, 65535, f);
316  LOG("read %d bytes\n", n);
317  yajl_gen g;
318  yajl_handle hand;
319  yajl_callbacks callbacks;
320  memset(&callbacks, '\0', sizeof(yajl_callbacks));
321  callbacks.yajl_start_map = json_start_map;
322  callbacks.yajl_end_map = json_end_map;
323  callbacks.yajl_end_array = json_end_array;
324  callbacks.yajl_string = json_string;
325  callbacks.yajl_map_key = json_key;
326  callbacks.yajl_integer = json_int;
327  callbacks.yajl_double = json_double;
328  callbacks.yajl_boolean = json_bool;
329 #if YAJL_MAJOR >= 2
330  g = yajl_gen_alloc(NULL);
331  hand = yajl_alloc(&callbacks, NULL, (void*)g);
332 #else
333  g = yajl_gen_alloc(NULL, NULL);
334  hand = yajl_alloc(&callbacks, NULL, NULL, (void*)g);
335 #endif
336  yajl_status stat;
337  json_node = focused;
338  to_focus = NULL;
339  parsing_rect = false;
340  parsing_window_rect = false;
341  parsing_geometry = false;
342  setlocale(LC_NUMERIC, "C");
343  stat = yajl_parse(hand, (const unsigned char*)buf, n);
344  if (stat != yajl_status_ok)
345  {
346  unsigned char * str = yajl_get_error(hand, 1, (const unsigned char*)buf, n);
347  fprintf(stderr, "%s\n", (const char *) str);
348  yajl_free_error(hand, str);
349  }
350 
351  setlocale(LC_NUMERIC, "");
352 #if YAJL_MAJOR >= 2
353  yajl_complete_parse(hand);
354 #else
355  yajl_parse_complete(hand);
356 #endif
357 
358  fclose(f);
359  if (to_focus)
360  con_focus(to_focus);
361 }