i3
src/load_layout.c
Go to the documentation of this file.
00001 /*
00002  * vim:ts=4:sw=4:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
00006  *
00007  * load_layout.c: Restore (parts of) the layout, for example after an inplace
00008  *                restart.
00009  *
00010  */
00011 #include "all.h"
00012 
00013 #include <yajl/yajl_common.h>
00014 #include <yajl/yajl_gen.h>
00015 #include <yajl/yajl_parse.h>
00016 #include <yajl/yajl_version.h>
00017 
00018 /* TODO: refactor the whole parsing thing */
00019 
00020 static char *last_key;
00021 static Con *json_node;
00022 static Con *to_focus;
00023 static bool parsing_swallows;
00024 static bool parsing_rect;
00025 static bool parsing_window_rect;
00026 static bool parsing_geometry;
00027 static bool parsing_focus;
00028 struct Match *current_swallow;
00029 
00030 /* This list is used for reordering the focus stack after parsing the 'focus'
00031  * array. */
00032 struct focus_mapping {
00033     int old_id;
00034     TAILQ_ENTRY(focus_mapping) focus_mappings;
00035 };
00036 
00037 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
00038   TAILQ_HEAD_INITIALIZER(focus_mappings);
00039 
00040 static int json_start_map(void *ctx) {
00041     LOG("start of map, last_key = %s\n", last_key);
00042     if (parsing_swallows) {
00043         LOG("creating new swallow\n");
00044         current_swallow = smalloc(sizeof(Match));
00045         match_init(current_swallow);
00046         TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
00047     } else {
00048         if (!parsing_rect && !parsing_window_rect && !parsing_geometry) {
00049             if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
00050                 DLOG("New floating_node\n");
00051                 Con *ws = con_get_workspace(json_node);
00052                 json_node = con_new(NULL, NULL);
00053                 json_node->parent = ws;
00054                 DLOG("Parent is workspace = %p\n", ws);
00055             } else {
00056                 Con *parent = json_node;
00057                 json_node = con_new(NULL, NULL);
00058                 json_node->parent = parent;
00059             }
00060         }
00061     }
00062     return 1;
00063 }
00064 
00065 static int json_end_map(void *ctx) {
00066     LOG("end of map\n");
00067     if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
00068         LOG("attaching\n");
00069         con_attach(json_node, json_node->parent, true);
00070         json_node = json_node->parent;
00071     }
00072     if (parsing_rect)
00073         parsing_rect = false;
00074     if (parsing_window_rect)
00075         parsing_window_rect = false;
00076     if (parsing_geometry)
00077         parsing_geometry = false;
00078     return 1;
00079 }
00080 
00081 static int json_end_array(void *ctx) {
00082     LOG("end of array\n");
00083     parsing_swallows = false;
00084     if (parsing_focus) {
00085         /* Clear the list of focus mappings */
00086         struct focus_mapping *mapping;
00087         TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
00088             LOG("focus (reverse) %d\n", mapping->old_id);
00089             Con *con;
00090             TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
00091                 if (con->old_id != mapping->old_id)
00092                     continue;
00093                 LOG("got it! %p\n", con);
00094                 /* Move this entry to the top of the focus list. */
00095                 TAILQ_REMOVE(&(json_node->focus_head), con, focused);
00096                 TAILQ_INSERT_HEAD(&(json_node->focus_head), con, focused);
00097                 break;
00098             }
00099         }
00100         while (!TAILQ_EMPTY(&focus_mappings)) {
00101             mapping = TAILQ_FIRST(&focus_mappings);
00102             TAILQ_REMOVE(&focus_mappings, mapping, focus_mappings);
00103             free(mapping);
00104         }
00105         parsing_focus = false;
00106     }
00107     return 1;
00108 }
00109 
00110 #if YAJL_MAJOR < 2
00111 static int json_key(void *ctx, const unsigned char *val, unsigned int len) {
00112 #else
00113 static int json_key(void *ctx, const unsigned char *val, size_t len) {
00114 #endif
00115     LOG("key: %.*s\n", (int)len, val);
00116     FREE(last_key);
00117     last_key = scalloc((len+1) * sizeof(char));
00118     memcpy(last_key, val, len);
00119     if (strcasecmp(last_key, "swallows") == 0)
00120         parsing_swallows = true;
00121 
00122     if (strcasecmp(last_key, "rect") == 0)
00123         parsing_rect = true;
00124 
00125     if (strcasecmp(last_key, "window_rect") == 0)
00126         parsing_window_rect = true;
00127 
00128     if (strcasecmp(last_key, "geometry") == 0)
00129         parsing_geometry = true;
00130 
00131     if (strcasecmp(last_key, "focus") == 0)
00132         parsing_focus = true;
00133 
00134     return 1;
00135 }
00136 
00137 #if YAJL_MAJOR >= 2
00138 static int json_string(void *ctx, const unsigned char *val, size_t len) {
00139 #else
00140 static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
00141 #endif
00142     LOG("string: %.*s for key %s\n", len, val, last_key);
00143     if (parsing_swallows) {
00144         /* TODO: the other swallowing keys */
00145         if (strcasecmp(last_key, "class") == 0) {
00146             current_swallow->class = scalloc((len+1) * sizeof(char));
00147             memcpy(current_swallow->class, val, len);
00148         }
00149         LOG("unhandled yet: swallow\n");
00150     } else {
00151         if (strcasecmp(last_key, "name") == 0) {
00152             json_node->name = scalloc((len+1) * sizeof(char));
00153             memcpy(json_node->name, val, len);
00154         } else if (strcasecmp(last_key, "sticky_group") == 0) {
00155             json_node->sticky_group = scalloc((len+1) * sizeof(char));
00156             memcpy(json_node->sticky_group, val, len);
00157             LOG("sticky_group of this container is %s\n", json_node->sticky_group);
00158         } else if (strcasecmp(last_key, "orientation") == 0) {
00159             char *buf = NULL;
00160             sasprintf(&buf, "%.*s", (int)len, val);
00161             if (strcasecmp(buf, "none") == 0)
00162                 json_node->orientation = NO_ORIENTATION;
00163             else if (strcasecmp(buf, "horizontal") == 0)
00164                 json_node->orientation = HORIZ;
00165             else if (strcasecmp(buf, "vertical") == 0)
00166                 json_node->orientation = VERT;
00167             else LOG("Unhandled orientation: %s\n", buf);
00168             free(buf);
00169         } else if (strcasecmp(last_key, "border") == 0) {
00170             char *buf = NULL;
00171             sasprintf(&buf, "%.*s", (int)len, val);
00172             if (strcasecmp(buf, "none") == 0)
00173                 json_node->border_style = BS_NONE;
00174             else if (strcasecmp(buf, "1pixel") == 0)
00175                 json_node->border_style = BS_1PIXEL;
00176             else if (strcasecmp(buf, "normal") == 0)
00177                 json_node->border_style = BS_NORMAL;
00178             else LOG("Unhandled \"border\": %s\n", buf);
00179             free(buf);
00180         } else if (strcasecmp(last_key, "layout") == 0) {
00181             char *buf = NULL;
00182             sasprintf(&buf, "%.*s", (int)len, val);
00183             if (strcasecmp(buf, "default") == 0)
00184                 json_node->layout = L_DEFAULT;
00185             else if (strcasecmp(buf, "stacked") == 0)
00186                 json_node->layout = L_STACKED;
00187             else if (strcasecmp(buf, "tabbed") == 0)
00188                 json_node->layout = L_TABBED;
00189             else if (strcasecmp(buf, "dockarea") == 0)
00190                 json_node->layout = L_DOCKAREA;
00191             else if (strcasecmp(buf, "output") == 0)
00192                 json_node->layout = L_OUTPUT;
00193             else LOG("Unhandled \"layout\": %s\n", buf);
00194             free(buf);
00195         } else if (strcasecmp(last_key, "mark") == 0) {
00196             char *buf = NULL;
00197             sasprintf(&buf, "%.*s", (int)len, val);
00198             json_node->mark = buf;
00199         } else if (strcasecmp(last_key, "floating") == 0) {
00200             char *buf = NULL;
00201             sasprintf(&buf, "%.*s", (int)len, val);
00202             if (strcasecmp(buf, "auto_off") == 0)
00203                 json_node->floating = FLOATING_AUTO_OFF;
00204             else if (strcasecmp(buf, "auto_on") == 0)
00205                 json_node->floating = FLOATING_AUTO_ON;
00206             else if (strcasecmp(buf, "user_off") == 0)
00207                 json_node->floating = FLOATING_USER_OFF;
00208             else if (strcasecmp(buf, "user_on") == 0)
00209                 json_node->floating = FLOATING_USER_ON;
00210             free(buf);
00211         } else if (strcasecmp(last_key, "scratchpad_state") == 0) {
00212             char *buf = NULL;
00213             sasprintf(&buf, "%.*s", (int)len, val);
00214             if (strcasecmp(buf, "none") == 0)
00215                 json_node->scratchpad_state = SCRATCHPAD_NONE;
00216             else if (strcasecmp(buf, "fresh") == 0)
00217                 json_node->scratchpad_state = SCRATCHPAD_FRESH;
00218             else if (strcasecmp(buf, "changed") == 0)
00219                 json_node->scratchpad_state = SCRATCHPAD_CHANGED;
00220             free(buf);
00221         }
00222     }
00223     return 1;
00224 }
00225 
00226 #if YAJL_MAJOR >= 2
00227 static int json_int(void *ctx, long long val) {
00228     LOG("int %lld for key %s\n", val, last_key);
00229 #else
00230 static int json_int(void *ctx, long val) {
00231     LOG("int %ld for key %s\n", val, last_key);
00232 #endif
00233     if (strcasecmp(last_key, "type") == 0)
00234         json_node->type = val;
00235 
00236     if (strcasecmp(last_key, "fullscreen_mode") == 0)
00237         json_node->fullscreen_mode = val;
00238 
00239     if (strcasecmp(last_key, "num") == 0)
00240         json_node->num = val;
00241 
00242     if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
00243         json_node->old_id = val;
00244 
00245     if (parsing_focus) {
00246         struct focus_mapping *focus_mapping = scalloc(sizeof(struct focus_mapping));
00247         focus_mapping->old_id = val;
00248         TAILQ_INSERT_TAIL(&focus_mappings, focus_mapping, focus_mappings);
00249     }
00250 
00251     if (parsing_rect || parsing_window_rect || parsing_geometry) {
00252         Rect *r;
00253         if (parsing_rect)
00254             r = &(json_node->rect);
00255         else if (parsing_window_rect)
00256             r = &(json_node->window_rect);
00257         else r = &(json_node->geometry);
00258         if (strcasecmp(last_key, "x") == 0)
00259             r->x = val;
00260         else if (strcasecmp(last_key, "y") == 0)
00261             r->y = val;
00262         else if (strcasecmp(last_key, "width") == 0)
00263             r->width = val;
00264         else if (strcasecmp(last_key, "height") == 0)
00265             r->height = val;
00266         else printf("WARNING: unknown key %s in rect\n", last_key);
00267         printf("rect now: (%d, %d, %d, %d)\n",
00268                 r->x, r->y, r->width, r->height);
00269     }
00270     if (parsing_swallows) {
00271         if (strcasecmp(last_key, "id") == 0) {
00272             current_swallow->id = val;
00273         }
00274         if (strcasecmp(last_key, "dock") == 0) {
00275             current_swallow->dock = val;
00276         }
00277         if (strcasecmp(last_key, "insert_where") == 0) {
00278             current_swallow->insert_where = val;
00279         }
00280     }
00281 
00282     return 1;
00283 }
00284 
00285 static int json_bool(void *ctx, int val) {
00286     LOG("bool %d for key %s\n", val, last_key);
00287     if (strcasecmp(last_key, "focused") == 0 && val) {
00288         to_focus = json_node;
00289     }
00290 
00291     if (parsing_swallows) {
00292         if (strcasecmp(last_key, "restart_mode") == 0)
00293             current_swallow->restart_mode = val;
00294     }
00295 
00296     return 1;
00297 }
00298 
00299 static int json_double(void *ctx, double val) {
00300     LOG("double %f for key %s\n", val, last_key);
00301     if (strcasecmp(last_key, "percent") == 0) {
00302         json_node->percent = val;
00303     }
00304     return 1;
00305 }
00306 
00307 void tree_append_json(const char *filename) {
00308     /* TODO: percent of other windows are not correctly fixed at the moment */
00309     FILE *f;
00310     if ((f = fopen(filename, "r")) == NULL) {
00311         LOG("Cannot open file\n");
00312         return;
00313     }
00314     char *buf = malloc(65535); /* TODO */
00315     int n = fread(buf, 1, 65535, f);
00316     LOG("read %d bytes\n", n);
00317     yajl_gen g;
00318     yajl_handle hand;
00319     yajl_callbacks callbacks;
00320     memset(&callbacks, '\0', sizeof(yajl_callbacks));
00321     callbacks.yajl_start_map = json_start_map;
00322     callbacks.yajl_end_map = json_end_map;
00323     callbacks.yajl_end_array = json_end_array;
00324     callbacks.yajl_string = json_string;
00325     callbacks.yajl_map_key = json_key;
00326     callbacks.yajl_integer = json_int;
00327     callbacks.yajl_double = json_double;
00328     callbacks.yajl_boolean = json_bool;
00329 #if YAJL_MAJOR >= 2
00330     g = yajl_gen_alloc(NULL);
00331     hand = yajl_alloc(&callbacks, NULL, (void*)g);
00332 #else
00333     g = yajl_gen_alloc(NULL, NULL);
00334     hand = yajl_alloc(&callbacks, NULL, NULL, (void*)g);
00335 #endif
00336     yajl_status stat;
00337     json_node = focused;
00338     to_focus = NULL;
00339     parsing_rect = false;
00340     parsing_window_rect = false;
00341     parsing_geometry = false;
00342     setlocale(LC_NUMERIC, "C");
00343     stat = yajl_parse(hand, (const unsigned char*)buf, n);
00344     if (stat != yajl_status_ok)
00345     {
00346         unsigned char * str = yajl_get_error(hand, 1, (const unsigned char*)buf, n);
00347         fprintf(stderr, "%s\n", (const char *) str);
00348         yajl_free_error(hand, str);
00349     }
00350 
00351     setlocale(LC_NUMERIC, "");
00352 #if YAJL_MAJOR >= 2
00353     yajl_complete_parse(hand);
00354 #else
00355     yajl_parse_complete(hand);
00356 #endif
00357 
00358     fclose(f);
00359     if (to_focus)
00360         con_focus(to_focus);
00361 }