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-2012 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_window_rect;
28 static bool parsing_geometry;
29 static bool parsing_focus;
31 
32 /* This list is used for reordering the focus stack after parsing the 'focus'
33  * array. */
34 struct focus_mapping {
35  int old_id;
36  TAILQ_ENTRY(focus_mapping) focus_mappings;
37 };
38 
39 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
40  TAILQ_HEAD_INITIALIZER(focus_mappings);
41 
42 static int json_start_map(void *ctx) {
43  LOG("start of map, last_key = %s\n", last_key);
44  if (parsing_swallows) {
45  LOG("creating new swallow\n");
46  current_swallow = smalloc(sizeof(Match));
47  match_init(current_swallow);
48  TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
49  } else {
51  if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
52  DLOG("New floating_node\n");
53  Con *ws = con_get_workspace(json_node);
54  json_node = con_new_skeleton(NULL, NULL);
55  json_node->parent = ws;
56  DLOG("Parent is workspace = %p\n", ws);
57  } else {
58  Con *parent = json_node;
59  json_node = con_new_skeleton(NULL, NULL);
60  json_node->parent = parent;
61  }
62  }
63  }
64  return 1;
65 }
66 
67 static int json_end_map(void *ctx) {
68  LOG("end of map\n");
70  LOG("attaching\n");
71  con_attach(json_node, json_node->parent, true);
72  LOG("Creating window\n");
73  x_con_init(json_node, json_node->depth);
74  json_node = json_node->parent;
75  }
76  if (parsing_rect)
77  parsing_rect = false;
79  parsing_window_rect = false;
80  if (parsing_geometry)
81  parsing_geometry = false;
82  return 1;
83 }
84 
85 static int json_end_array(void *ctx) {
86  LOG("end of array\n");
87  parsing_swallows = false;
88  if (parsing_focus) {
89  /* Clear the list of focus mappings */
90  struct focus_mapping *mapping;
91  TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
92  LOG("focus (reverse) %d\n", mapping->old_id);
93  Con *con;
94  TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
95  if (con->old_id != mapping->old_id)
96  continue;
97  LOG("got it! %p\n", con);
98  /* Move this entry to the top of the focus list. */
99  TAILQ_REMOVE(&(json_node->focus_head), con, focused);
100  TAILQ_INSERT_HEAD(&(json_node->focus_head), con, focused);
101  break;
102  }
103  }
104  while (!TAILQ_EMPTY(&focus_mappings)) {
105  mapping = TAILQ_FIRST(&focus_mappings);
106  TAILQ_REMOVE(&focus_mappings, mapping, focus_mappings);
107  free(mapping);
108  }
109  parsing_focus = false;
110  }
111  return 1;
112 }
113 
114 #if YAJL_MAJOR < 2
115 static int json_key(void *ctx, const unsigned char *val, unsigned int len) {
116 #else
117 static int json_key(void *ctx, const unsigned char *val, size_t len) {
118 #endif
119  LOG("key: %.*s\n", (int)len, val);
120  FREE(last_key);
121  last_key = scalloc((len+1) * sizeof(char));
122  memcpy(last_key, val, len);
123  if (strcasecmp(last_key, "swallows") == 0)
124  parsing_swallows = true;
125 
126  if (strcasecmp(last_key, "rect") == 0)
127  parsing_rect = true;
128 
129  if (strcasecmp(last_key, "window_rect") == 0)
130  parsing_window_rect = true;
131 
132  if (strcasecmp(last_key, "geometry") == 0)
133  parsing_geometry = true;
134 
135  if (strcasecmp(last_key, "focus") == 0)
136  parsing_focus = true;
137 
138  return 1;
139 }
140 
141 #if YAJL_MAJOR >= 2
142 static int json_string(void *ctx, const unsigned char *val, size_t len) {
143 #else
144 static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
145 #endif
146  LOG("string: %.*s for key %s\n", (int)len, val, last_key);
147  if (parsing_swallows) {
148  /* TODO: the other swallowing keys */
149  if (strcasecmp(last_key, "class") == 0) {
150  current_swallow->class = scalloc((len+1) * sizeof(char));
151  memcpy(current_swallow->class, val, len);
152  }
153  LOG("unhandled yet: swallow\n");
154  } else {
155  if (strcasecmp(last_key, "name") == 0) {
156  json_node->name = scalloc((len+1) * sizeof(char));
157  memcpy(json_node->name, val, len);
158  } else if (strcasecmp(last_key, "sticky_group") == 0) {
159  json_node->sticky_group = scalloc((len+1) * sizeof(char));
160  memcpy(json_node->sticky_group, val, len);
161  LOG("sticky_group of this container is %s\n", json_node->sticky_group);
162  } else if (strcasecmp(last_key, "orientation") == 0) {
163  /* Upgrade path from older versions of i3 (doing an inplace restart
164  * to a newer version):
165  * "orientation" is dumped before "layout". Therefore, we store
166  * whether the orientation was horizontal or vertical in the
167  * last_split_layout. When we then encounter layout == "default",
168  * we will use the last_split_layout as layout instead. */
169  char *buf = NULL;
170  sasprintf(&buf, "%.*s", (int)len, val);
171  if (strcasecmp(buf, "none") == 0 ||
172  strcasecmp(buf, "horizontal") == 0)
173  json_node->last_split_layout = L_SPLITH;
174  else if (strcasecmp(buf, "vertical") == 0)
175  json_node->last_split_layout = L_SPLITV;
176  else LOG("Unhandled orientation: %s\n", buf);
177  free(buf);
178  } else if (strcasecmp(last_key, "border") == 0) {
179  char *buf = NULL;
180  sasprintf(&buf, "%.*s", (int)len, val);
181  if (strcasecmp(buf, "none") == 0)
182  json_node->border_style = BS_NONE;
183  else if (strcasecmp(buf, "1pixel") == 0) {
184  json_node->border_style = BS_PIXEL;
185  json_node->current_border_width = 1;
186  } else if (strcasecmp(buf, "pixel") == 0)
187  json_node->border_style = BS_PIXEL;
188  else if (strcasecmp(buf, "normal") == 0)
189  json_node->border_style = BS_NORMAL;
190  else LOG("Unhandled \"border\": %s\n", buf);
191  free(buf);
192  } else if (strcasecmp(last_key, "layout") == 0) {
193  char *buf = NULL;
194  sasprintf(&buf, "%.*s", (int)len, val);
195  if (strcasecmp(buf, "default") == 0)
196  /* This set above when we read "orientation". */
197  json_node->layout = json_node->last_split_layout;
198  else if (strcasecmp(buf, "stacked") == 0)
199  json_node->layout = L_STACKED;
200  else if (strcasecmp(buf, "tabbed") == 0)
201  json_node->layout = L_TABBED;
202  else if (strcasecmp(buf, "dockarea") == 0)
203  json_node->layout = L_DOCKAREA;
204  else if (strcasecmp(buf, "output") == 0)
205  json_node->layout = L_OUTPUT;
206  else if (strcasecmp(buf, "splith") == 0)
207  json_node->layout = L_SPLITH;
208  else if (strcasecmp(buf, "splitv") == 0)
209  json_node->layout = L_SPLITV;
210  else LOG("Unhandled \"layout\": %s\n", buf);
211  free(buf);
212  } else if (strcasecmp(last_key, "workspace_layout") == 0) {
213  char *buf = NULL;
214  sasprintf(&buf, "%.*s", (int)len, val);
215  if (strcasecmp(buf, "default") == 0)
216  json_node->workspace_layout = L_DEFAULT;
217  else if (strcasecmp(buf, "stacked") == 0)
218  json_node->workspace_layout = L_STACKED;
219  else if (strcasecmp(buf, "tabbed") == 0)
220  json_node->workspace_layout = L_TABBED;
221  else LOG("Unhandled \"workspace_layout\": %s\n", buf);
222  free(buf);
223  } else if (strcasecmp(last_key, "last_split_layout") == 0) {
224  char *buf = NULL;
225  sasprintf(&buf, "%.*s", (int)len, val);
226  if (strcasecmp(buf, "splith") == 0)
227  json_node->last_split_layout = L_SPLITH;
228  else if (strcasecmp(buf, "splitv") == 0)
229  json_node->last_split_layout = L_SPLITV;
230  else LOG("Unhandled \"last_splitlayout\": %s\n", buf);
231  free(buf);
232  } else if (strcasecmp(last_key, "mark") == 0) {
233  char *buf = NULL;
234  sasprintf(&buf, "%.*s", (int)len, val);
235  json_node->mark = buf;
236  } else if (strcasecmp(last_key, "floating") == 0) {
237  char *buf = NULL;
238  sasprintf(&buf, "%.*s", (int)len, val);
239  if (strcasecmp(buf, "auto_off") == 0)
240  json_node->floating = FLOATING_AUTO_OFF;
241  else if (strcasecmp(buf, "auto_on") == 0)
242  json_node->floating = FLOATING_AUTO_ON;
243  else if (strcasecmp(buf, "user_off") == 0)
244  json_node->floating = FLOATING_USER_OFF;
245  else if (strcasecmp(buf, "user_on") == 0)
246  json_node->floating = FLOATING_USER_ON;
247  free(buf);
248  } else if (strcasecmp(last_key, "scratchpad_state") == 0) {
249  char *buf = NULL;
250  sasprintf(&buf, "%.*s", (int)len, val);
251  if (strcasecmp(buf, "none") == 0)
252  json_node->scratchpad_state = SCRATCHPAD_NONE;
253  else if (strcasecmp(buf, "fresh") == 0)
254  json_node->scratchpad_state = SCRATCHPAD_FRESH;
255  else if (strcasecmp(buf, "changed") == 0)
256  json_node->scratchpad_state = SCRATCHPAD_CHANGED;
257  free(buf);
258  }
259  }
260  return 1;
261 }
262 
263 #if YAJL_MAJOR >= 2
264 static int json_int(void *ctx, long long val) {
265  LOG("int %lld for key %s\n", val, last_key);
266 #else
267 static int json_int(void *ctx, long val) {
268  LOG("int %ld for key %s\n", val, last_key);
269 #endif
270  if (strcasecmp(last_key, "type") == 0)
271  json_node->type = val;
272 
273  if (strcasecmp(last_key, "fullscreen_mode") == 0)
274  json_node->fullscreen_mode = val;
275 
276  if (strcasecmp(last_key, "num") == 0)
277  json_node->num = val;
278 
279  if (strcasecmp(last_key, "current_border_width") == 0)
280  json_node->current_border_width = val;
281 
282  if (strcasecmp(last_key, "depth") == 0)
283  json_node->depth = val;
284 
285  if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
286  json_node->old_id = val;
287 
288  if (parsing_focus) {
289  struct focus_mapping *focus_mapping = scalloc(sizeof(struct focus_mapping));
290  focus_mapping->old_id = val;
291  TAILQ_INSERT_TAIL(&focus_mappings, focus_mapping, focus_mappings);
292  }
293 
295  Rect *r;
296  if (parsing_rect)
297  r = &(json_node->rect);
298  else if (parsing_window_rect)
299  r = &(json_node->window_rect);
300  else r = &(json_node->geometry);
301  if (strcasecmp(last_key, "x") == 0)
302  r->x = val;
303  else if (strcasecmp(last_key, "y") == 0)
304  r->y = val;
305  else if (strcasecmp(last_key, "width") == 0)
306  r->width = val;
307  else if (strcasecmp(last_key, "height") == 0)
308  r->height = val;
309  else printf("WARNING: unknown key %s in rect\n", last_key);
310  printf("rect now: (%d, %d, %d, %d)\n",
311  r->x, r->y, r->width, r->height);
312  }
313  if (parsing_swallows) {
314  if (strcasecmp(last_key, "id") == 0) {
315  current_swallow->id = val;
316  }
317  if (strcasecmp(last_key, "dock") == 0) {
318  current_swallow->dock = val;
319  }
320  if (strcasecmp(last_key, "insert_where") == 0) {
321  current_swallow->insert_where = val;
322  }
323  }
324 
325  return 1;
326 }
327 
328 static int json_bool(void *ctx, int val) {
329  LOG("bool %d for key %s\n", val, last_key);
330  if (strcasecmp(last_key, "focused") == 0 && val) {
331  to_focus = json_node;
332  }
333 
334  if (parsing_swallows) {
335  if (strcasecmp(last_key, "restart_mode") == 0)
336  current_swallow->restart_mode = val;
337  }
338 
339  return 1;
340 }
341 
342 static int json_double(void *ctx, double val) {
343  LOG("double %f for key %s\n", val, last_key);
344  if (strcasecmp(last_key, "percent") == 0) {
345  json_node->percent = val;
346  }
347  return 1;
348 }
349 
350 void tree_append_json(const char *filename) {
351  /* TODO: percent of other windows are not correctly fixed at the moment */
352  FILE *f;
353  if ((f = fopen(filename, "r")) == NULL) {
354  LOG("Cannot open file \"%s\"\n", filename);
355  return;
356  }
357  struct stat stbuf;
358  if (fstat(fileno(f), &stbuf) != 0) {
359  LOG("Cannot fstat() the file\n");
360  fclose(f);
361  return;
362  }
363  char *buf = smalloc(stbuf.st_size);
364  int n = fread(buf, 1, stbuf.st_size, f);
365  if (n != stbuf.st_size) {
366  LOG("File \"%s\" could not be read entirely, not loading.\n", filename);
367  fclose(f);
368  return;
369  }
370  LOG("read %d bytes\n", n);
371  yajl_gen g;
372  yajl_handle hand;
373  yajl_callbacks callbacks;
374  memset(&callbacks, '\0', sizeof(yajl_callbacks));
375  callbacks.yajl_start_map = json_start_map;
376  callbacks.yajl_end_map = json_end_map;
377  callbacks.yajl_end_array = json_end_array;
378  callbacks.yajl_string = json_string;
379  callbacks.yajl_map_key = json_key;
380  callbacks.yajl_integer = json_int;
381  callbacks.yajl_double = json_double;
382  callbacks.yajl_boolean = json_bool;
383 #if YAJL_MAJOR >= 2
384  g = yajl_gen_alloc(NULL);
385  hand = yajl_alloc(&callbacks, NULL, (void*)g);
386 #else
387  g = yajl_gen_alloc(NULL, NULL);
388  hand = yajl_alloc(&callbacks, NULL, NULL, (void*)g);
389 #endif
390  yajl_status stat;
391  json_node = focused;
392  to_focus = NULL;
393  parsing_rect = false;
394  parsing_window_rect = false;
395  parsing_geometry = false;
396  setlocale(LC_NUMERIC, "C");
397  stat = yajl_parse(hand, (const unsigned char*)buf, n);
398  if (stat != yajl_status_ok)
399  {
400  unsigned char * str = yajl_get_error(hand, 1, (const unsigned char*)buf, n);
401  fprintf(stderr, "%s\n", (const char *) str);
402  yajl_free_error(hand, str);
403  }
404 
405  setlocale(LC_NUMERIC, "");
406 #if YAJL_MAJOR >= 2
407  yajl_complete_parse(hand);
408 #else
409  yajl_parse_complete(hand);
410 #endif
411 
412  fclose(f);
413  if (to_focus)
414  con_focus(to_focus);
415 }
#define DLOG(fmt,...)
Definition: log.h:28
struct Match * current_swallow
Definition: load_layout.c:30
A &#39;Con&#39; represents everything from the X11 root window down to a single X11 window.
Definition: data.h:457
static bool parsing_focus
Definition: load_layout.c:29
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:363
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:107
uint32_t y
Definition: data.h:109
border_style_t border_style
Definition: data.h:557
static int json_string(void *ctx, const unsigned char *val, unsigned int len)
Definition: load_layout.c:144
void x_con_init(Con *con, uint16_t depth)
Initializes the X11 part for the given container.
Definition: x.c:91
static int json_end_map(void *ctx)
Definition: load_layout.c:67
struct Rect window_rect
Definition: data.h:493
struct Rect rect
Definition: data.h:492
char * sticky_group
Definition: data.h:503
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Definition: con.c:104
#define TAILQ_EMPTY(head)
Definition: queue.h:332
A &quot;match&quot; is a data structure which acts like a mask or expression to match certain windows or not...
Definition: data.h:371
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:353
char * mark
Definition: data.h:506
void * scalloc(size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:308
int current_border_width
Definition: data.h:519
#define FREE(pointer)
Definition: util.h:47
xcb_window_t id
Definition: data.h:390
enum Con::@20 floating
floating? (= not in tiling layout) This cannot be simply a bool because we want to keep track of whet...
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:387
static Con * json_node
Definition: load_layout.c:23
void match_init(Match *match)
Definition: match.c:30
static int json_int(void *ctx, long val)
Definition: load_layout.c:267
static Con * to_focus
Definition: load_layout.c:24
Definition: data.h:91
enum Con::@18 type
static int json_start_map(void *ctx)
Definition: key_press.c:43
#define TAILQ_HEAD(name, type)
Definition: queue.h:306
static int json_double(void *ctx, double val)
Definition: load_layout.c:342
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:50
struct Rect geometry
the geometry this window requested when getting mapped
Definition: data.h:496
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:312
layout_t last_split_layout
Definition: data.h:556
Definition: data.h:90
uint16_t depth
Definition: data.h:596
static bool parsing_swallows
Definition: load_layout.c:25
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...
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition: queue.h:340
static int json_end_array(void *ctx)
Definition: load_layout.c:85
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
enum Match::@15 insert_where
Definition: data.h:88
Definition: data.h:57
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:212
Definition: data.h:87
bool restart_mode
Definition: data.h:410
static char * last_key
Definition: load_layout.c:22
#define TAILQ_FIRST(head)
Definition: queue.h:324
Con * focused
Definition: tree.c:15
int old_id
Definition: data.h:593
layout_t layout
Definition: data.h:556
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:335
void tree_append_json(const char *filename)
Definition: load_layout.c:350
Definition: data.h:92
Definition: data.h:57
static bool parsing_geometry
Definition: load_layout.c:28
#define TAILQ_ENTRY(type)
Definition: queue.h:315
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:488
layout_t workspace_layout
Definition: data.h:556
char * name
Definition: data.h:498
struct regex * class
Definition: data.h:374
struct Con * parent
Definition: data.h:490
uint32_t x
Definition: data.h:108
static int json_bool(void *ctx, int val)
Definition: load_layout.c:328
enum Match::@13 dock
enum Con::@21 scratchpad_state
static bool parsing_rect
Definition: load_layout.c:26
#define LOG(fmt,...)
Definition: libi3.h:76
uint32_t height
Definition: data.h:111
static int json_key(void *ctx, const unsigned char *val, unsigned int len)
Definition: load_layout.c:115
Definition: data.h:57
double percent
Definition: data.h:508
static bool parsing_window_rect
Definition: load_layout.c:27
Definition: data.h:86
enum Con::@19 fullscreen_mode
uint32_t width
Definition: data.h:110