]> icculus.org git repositories - dana/openbox.git/blob - openbox/config_parser.c
wip: Add config_parser.c which will provide a nice means to specify config variables...
[dana/openbox.git] / openbox / config_parser.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    config_parser.c for the Openbox window manager
4    Copyright (c) 2011        Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "config_parser.h"
20 #include "config.h"
21 #include "geom.h"
22 #include "obt/paths.h"
23 #include "obt/xml.h"
24
25 typedef struct {
26     gchar *name;
27     ObConfigValue *def;
28     ObConfigValue *value;
29     ObConfigValueDataType type;
30     ObConfigValueDataPtr data;
31     const ObConfigValueEnum *enum_choices;
32     gchar* (*string_modify_func)(const gchar *s);
33 } ObConfigParserEntity;
34
35 static GHashTable *entities;
36
37 static void entity_free(ObConfigParserEntity *e)
38 {
39     g_free(e->name);
40     config_value_unref(e->def);
41     config_value_unref(e->value);
42     e->data.pointer = NULL; /* if it was holding a pointer, then NULL it */
43     g_slice_free(ObConfigParserEntity, e);
44 }
45
46 void config_parser_startup(gboolean reconfig)
47 {
48     if (reconfig) return;
49
50     entities = g_hash_table_new_full(
51         g_str_hash, g_str_equal, NULL, (GDestroyNotify)entity_free);
52 }
53
54 void config_parser_shutdown(gboolean reconfig)
55 {
56     if (reconfig) return;
57
58     g_hash_table_unref(entities);
59     entities = NULL;
60 }
61
62 static void copy_value(ObConfigParserEntity *e)
63 {
64     gboolean b;
65
66     if (e->string_modify_func && config_value_is_string(e->value)) {
67         gchar *s = e->string_modify_func(config_value_string(e->value));
68         if (s) {
69             config_value_unref(e->value);
70             e->value = config_value_new_string_steal(s);
71         }
72     }
73
74     switch (e->type) {
75     case OB_CONFIG_VALUE_ENUM:
76         b = config_value_copy_ptr(e->value, e->type, e->data, e->enum_choices);
77         break;
78     default:
79         b = config_value_copy_ptr(e->value, e->type, e->data, NULL);
80         break;
81     }
82
83     /* replace the bad value with the default (if it's not the default) */
84     if (!b && e->value != e->def) {
85         config_value_unref(e->value);
86         e->value = e->def;
87         config_value_ref(e->value);
88         copy_value(e);
89     }
90 }
91
92 static ObConfigParserEntity* add(const gchar *name, ObConfigValue *def,
93                                  ObConfigValueDataType type,
94                                  ObConfigValueDataPtr v)
95 {
96     ObConfigParserEntity *e;
97
98     g_return_val_if_fail(def != NULL, NULL);
99     g_return_val_if_fail(g_hash_table_lookup(entities, name) != NULL, NULL);
100
101     e = g_slice_new(ObConfigParserEntity);
102     e->name = g_strdup(name);
103     e->value = def;
104     e->def = def;
105     config_value_ref(def);
106     config_value_ref(def);
107     e->type = type;
108     e->data = v;
109     g_hash_table_replace(entities, e->name, e);
110
111     copy_value(e);
112     return e;
113 }
114
115 void config_parser_bool(const gchar *name, const gchar *def, gboolean *v)
116 {
117     ObConfigValue *cv = config_value_new_string(def);
118     add(name, cv, OB_CONFIG_VALUE_BOOLEAN, (ObConfigValueDataPtr)v);
119     config_value_unref(cv);
120 }
121
122 void config_parser_int(const gchar *name, const gchar *def, gint *v)
123 {
124     ObConfigValue *cv = config_value_new_string(def);
125     add(name, cv, OB_CONFIG_VALUE_INTEGER, (ObConfigValueDataPtr)v);
126     config_value_unref(cv);
127 }
128
129 void config_parser_string(const gchar *name, const gchar *def, const gchar **v)
130 {
131     ObConfigValue *cv = config_value_new_string(def);
132     add(name, cv, OB_CONFIG_VALUE_STRING, (ObConfigValueDataPtr)v);
133     config_value_unref(cv);
134 }
135
136 void config_parser_enum(const gchar *name, const gchar *def, guint *v,
137                         const ObConfigValueEnum choices[])
138 {
139     ObConfigValue *cv;
140     ObConfigParserEntity *e;
141
142     cv = config_value_new_string(def);
143     e = add(name, cv, OB_CONFIG_VALUE_ENUM, (ObConfigValueDataPtr)v);
144     config_value_unref(cv);
145     e->enum_choices = choices;
146 }
147
148 void config_parser_list(const gchar *name, GList *def, const GList **v)
149 {
150     ObConfigValue *cv = config_value_new_list(def);
151     add(name, cv, OB_CONFIG_VALUE_LIST, (ObConfigValueDataPtr)v);
152     config_value_unref(cv);
153 }
154
155 gchar *modify_uniq(const gchar *s)
156 {
157     gchar *c, *d, *e;
158     gchar *str = g_strdup(s);
159     /* look at each char c.
160        scan ahead and look at each char d past c.
161        if d == c, then move everything past d up one position, destroying d
162     */
163     for (c = str; *c != '\0'; ++c)
164         for (d = c+1; *d != '\0'; ++d)
165             if (*c == *d)
166                 for (e = d; *e != '\0'; ++e)
167                     *e = *(e+1);
168     return str;
169 }
170
171 void config_parser_string_uniq(const gchar *name, const gchar *def,
172                                const gchar **v)
173 {
174     ObConfigValue *cv;
175     ObConfigParserEntity *e;
176
177     cv = config_value_new_string(def);
178     e = add(name, cv, OB_CONFIG_VALUE_STRING, (ObConfigValueDataPtr)v);
179     config_value_unref(cv);
180     e->string_modify_func = modify_uniq;
181 }
182
183 gchar *modify_path(const gchar *s)
184 {
185     return obt_paths_expand_tilde(s);
186 }
187
188 void config_parser_string_path(const gchar *name, const gchar *def,
189                                const gchar **v)
190 {
191     ObConfigValue *cv;
192     ObConfigParserEntity *e;
193
194     cv = config_value_new_string(def);
195     e = add(name, cv, OB_CONFIG_VALUE_STRING, (ObConfigValueDataPtr)v);
196     config_value_unref(cv);
197     e->string_modify_func = modify_path;
198 }
199 void config_parser_key(const gchar *name, const gchar *def,
200                        const gchar **v)
201 {
202     ObConfigValue *cv;
203     ObConfigParserEntity *e;
204
205     cv = config_value_new_string(def);
206     e = add(name, cv, OB_CONFIG_VALUE_KEY, (ObConfigValueDataPtr)v);
207     config_value_unref(cv);
208 }
209
210
211 #if 0
212
213 void config_parser_options(void)
214 {
215     ObtXmlInst *i = obt_xml_instance_new();
216     ObtPaths *paths;
217     gchar *fpath, *dpath;
218     xmlNodePtr root, n;
219
220     paths = obt_paths_new();
221     dpath = g_build_filename(obt_paths_cache_home(paths), "openbox", NULL);
222     fpath = g_build_filename(dpath, "config", NULL);
223
224     obt_paths_mkdir_path(dpath, 0777);
225     if (!obt_xml_load_file(i, fpath, "openbox_config"))
226         obt_xml_new_file(i, "openbox_config");
227     root = obt_xml_root(i);
228
229     n = obt_xml_tree_get_node("focus");
230     config_focus_new = read_bool(n, "focusnew", TRUE);
231     config_focus_follow = read_bool(n, "followmouse", FALSE);
232     config_focus_delay = read_int(n, "focusdelay", 0);
233     config_focus_raise = read_bool(n, "raiseonfocus", FALSE);
234     config_focus_last = read_bool(n, "focuslast", TRUE);
235     config_focus_under_mouse = read_bool(n, "undermouse", FALSE);
236     config_unfocus_leave = read_bool(n, "unfocusonleave", FALSE);
237
238     n = obt_xml_tree_get_node("placement");
239     config_place_policy = read_enum(
240         n, "policy",
241         {
242             {"smart", OB_PLACE_POLICY_SMART},
243             {"undermouse", OB_PLACE_POLICY_MOUSE},
244             {0, 0},
245         }
246         "smart");
247     config_place_center = read_bool(n "center", TRUE);
248     config_place_monitor = read_enum(
249         n, "monitor",
250         {
251             {"any", OB_PLACE_MONITOR_ANY},
252             {"active", OB_PLACE_MONITOR_ACTIVE},
253             {"mouse", OB_PLACE_MONITOR_MOUSE},
254             {"primary", OB_PLACE_MONITOR_PRIMARY},
255             {0, 0}
256         },
257         "primary");
258     config_primary_monitor_index = read_int(n, "primarymonitor" 1);
259     if (!config_primary_monitor_index)
260         config_primary_monitor = read_enum(
261             n, "primarymonitor",
262             {
263                 {"active", OB_PLACE_MONITOR_ACTIVE},
264                 {"mouse", OB_PLACE_MONITOR_MOUSE},
265                 {0, 0}
266             },
267             "active");
268
269     n = obt_xml_tree_get_node("margins");
270     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
271     config_margins.top = MAX(0, read_int(n, "top", 0));
272     config_margins.left = MAX(0, read_int(n, "left", 0));
273     config_margins.bottom = MAX(0, read_int(n, "bottom", 0));
274     config_margins.right = MAX(0, read_int(n, "right", 0));
275
276     n = obt_xml_tree_get_node("theme");
277     config_theme = read_path(n, "name", NULL);
278     config_animate_iconify = read_bool(n, "animateiconify", TRUE);
279     config_title_layout = read_string_uniq(n, "titlelayout", "NLIMC");
280     config_theme_keepborder = read_bool(n, "keepborder", TRUE);
281     config_theme_window_list_icon_size = read_int(n, "windowlisticonsize", 36);
282     config_font_activewindow = read_font(n, "font:place=activewindow");
283     config_font_inactivewindow = read_font(n, "font:place=inactivewindow");
284     config_font_menuitem = read_font(n, "font:place=menuitem");
285     config_font_menutitle = read_font(n, "font:place=menuheader");
286     config_font_activeosd = read_font(n, "font:place=activeonscreendisplay");
287     config_font_inactiveosd =
288         read_font(n, "font:place=inactiveonscreendisplay");
289
290     n = obt_xml_tree_get_node("desktops");
291     config_desktops_num = MAX(1, read_int(n, "number", 4));
292     config_screen_firstdesk = 1; /* XXX remove me */
293     config_desktop_popup_time = MAX(0, read_int(n, "popuptime", 875));
294     config_desktops_names = NULL; /* XXX remove me */
295
296     n = obt_xml_tree_get_node("resize");
297     config_resize_redraw = read_bool(n, "drawcontents", TRUE);
298     config_resize_popup_show = read_enum(
299         n, "popupshow",
300         {
301             {"never", 0},
302             {"nonpixel", 1},
303             {"always", 2},
304             {0, 0}
305         },
306         "nonpixel");
307     config_resize_popup_pos = read_enum(
308         n, "popupposition",
309         {
310             {"top", OB_RESIZE_POS_TOP},
311             {"center", OB_RESIZE_POS_CENTER},
312             {"fixed", OB_RESIZE_POS_FIXED},
313         },
314         "center");
315     if (config_resize_popup_pos == OB_RESIZE_POS_FIXED)
316         config_resize_popup_fixed =
317             read_gravity_point(n, "popupfixedposition",
318                                0, 0, FALSE, FALSE, 0, 0, FALSE, FALSE);
319
320     n = obt_xml_tree_get_node("dock");
321     config_dock_pos = read_enum(
322         n, "position",
323         {
324             {"topleft", OB_DIRECTION_NORTHWEST},
325             {"top", OB_DIRECTION_NORTH},
326             {"topright", OB_DIRECTION_NORTHEAST},
327             {"right", OB_DIRECTION_EAST},
328             {"bottomright", OB_DIRECTION_SOUTHEAST},
329             {"bottom", OB_DIRECTION_SOUTH},
330             {"bottomleft", OB_DIRECTION_SOUTHWEST},
331             {"left", OB_DIRECTION_WEST},
332             {"floating", (guint)-1},
333             {0, 0}
334         },
335         "topright");
336     config_dock_floating = (config_dock_pos == (guint)-1);
337     config_dock_layer = read_enum(
338         n, "stacking",
339         {
340             {"normal", OB_STACKING_LAYER_NORMAL},
341             {"above", OB_STACKING_LAYER_ABOVE},
342             {"below", OB_STACKING_LAYER_BELOW},
343             {0, 0}
344         },
345         "above");
346     config_dock_nostrut = read_bool(n, "nostrut", FALSE);
347     config_dock_x = read_int(n, "floatingx", 0);
348     config_dock_y = read_int(n, "floatingx", y);
349     config_dock_orient = read_enum(
350         n, "direction",
351         {
352             {"horizontal", OB_ORIENTATION_HORZ},
353             {"vertical", OB_ORIENTATION_VERT},
354             {0, 0}
355         },
356         "vertical");
357     config_dock_hide = read_bool(n, "autohide", FALSE);
358     config_dock_hide_delay = read_int(n, "hidedelay", 300);
359     config_dock_show_delay = read_int(n, "showdelay", 300);
360     read_button(n, "movebutton", "2",
361                 &config_dock_app_move_button,
362                 &config_dock_app_move_modifiers);
363
364     n = obt_xml_tree_get_node("keyboard");
365     read_key(n, "chainquitkey", "C-g",
366              &config_keyboard_reset_keycode,
367              &config_keyboard_reset_state);
368
369     n = obt_xml_tree_get_node("mouse");
370     config_mouse_threshold = read_int(n, "dragthreshold", 8);
371     config_mouse_dclicktime = read_int(n, "doubleclicktime", 200);
372     config_mouse_screenedgetime = read_int(n, "screenedgewarptime", 400);
373     /* minimum value of 25 for this property, when it is 1 and you hit the
374        edge it basically never stops */
375     if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
376         config_mouse_screenedgetime = 25;
377     config_mouse_screenedgewarp = read_bool(n, "screenedgewarpmouse", FALSE);
378
379     n = obt_xml_tree_get_node("resistance");
380     config_resist_win = read_int(n, "strength", 10);
381     config_resist_edge = read_int(n, "screen_edge_strength", 20);
382
383     n = obt_xml_tree_get_node("menu");
384     config_menu_hide_delay = read_int(n, "hidedelay", 250);
385     config_menu_middle = read_bool(n, "middle", FALSE);
386     config_submenu_show_delay = read_int(n, "submenushowdelay", 100);
387     config_submenu_hide_delay = read_int(n, "submenuhidedelay", 400);
388     config_menu_manage_desktops = read_bool(n, "managedesktops", TRUE);
389     config_menu_show_icons = read_bool(n, "showicons", TRUE);
390 #ifndef USE_IMLIB2
391     if (config_menu_show_icons)
392         g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded."));
393 #endif
394
395     config_menu_files = NULL; /* XXX remove me */
396 }
397
398
399
400 RrFont *read_font(xmlNodePtr n, const gchar *path)
401 {
402     RrFont *font;
403     gchar *name;
404     gint size;
405     RrFontWeight weight;
406     RrFontSlant slant;
407
408     n = obt_xml_tree_get_node(n, path);
409
410     name = read_string(n, "name", RrDefaultFontFamily);
411     size = read_int(n, "size", RrDefaultFontSize);
412     weight = read_enum(n, "weight",
413                        {
414                            {"normal", RR_FONTWEIGHT_NORMAL},
415                            {"bold", RR_FONTWEIGHT_BOLD},
416                            {0, 0}
417                        },
418                        "normal");
419     slant = read_enum(n, "slant",
420                       {
421                           {"normal", RR_FONTSLANT_NORMAL},
422                           {"italic", RR_FONTSLANT_ITALIC},
423                           {"oblique", RR_FONTSLANT_OBLIQUE},
424                       },
425                       "normal");
426
427     font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
428     g_free(name);
429     return font;
430 }
431 #endif