Merge branch 'work' into wip/mikabox
[mikachu/openbox.git] / openbox / config.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    config.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "config.h"
21 #include "keyboard.h"
22 #include "mouse.h"
23 #include "actions.h"
24 #include "translate.h"
25 #include "client.h"
26 #include "screen.h"
27 #include "openbox.h"
28 #include "gettext.h"
29 #include "obt/paths.h"
30
31 gboolean config_focus_new;
32 gboolean config_focus_follow;
33 guint    config_focus_delay;
34 gboolean config_focus_raise;
35 gboolean config_focus_last;
36 gboolean config_focus_under_mouse;
37 gboolean config_unfocus_leave;
38 guint config_directional_distance_weight;
39 guint config_directional_angle_weight;
40
41 ObPlacePolicy  config_place_policy;
42 gboolean       config_place_center;
43 ObPlaceMonitor config_place_monitor;
44
45 guint          config_primary_monitor_index;
46 ObPlaceMonitor config_primary_monitor;
47
48 StrutPartial config_margins;
49
50 gchar   *config_theme;
51 gboolean config_theme_keepborder;
52 guint    config_theme_window_list_icon_size;
53
54 gchar   *config_title_layout;
55
56 gboolean config_animate_iconify;
57
58 RrFont *config_font_activewindow;
59 RrFont *config_font_inactivewindow;
60 RrFont *config_font_menuitem;
61 RrFont *config_font_menutitle;
62 RrFont *config_font_activeosd;
63 RrFont *config_font_inactiveosd;
64
65 guint   config_desktops_num;
66 GSList *config_desktops_names;
67 guint   config_screen_firstdesk;
68 guint   config_desktop_popup_time;
69 gint    config_emulate_xinerama;
70
71 gboolean         config_resize_redraw;
72 gint             config_resize_popup_show;
73 ObResizePopupPos config_resize_popup_pos;
74 GravityPoint     config_resize_popup_fixed;
75
76 ObStackingLayer config_dock_layer;
77 gboolean        config_dock_floating;
78 gboolean        config_dock_nostrut;
79 ObDirection     config_dock_pos;
80 gint            config_dock_x;
81 gint            config_dock_y;
82 ObOrientation   config_dock_orient;
83 gboolean        config_dock_hide;
84 guint           config_dock_hide_delay;
85 guint           config_dock_show_delay;
86 guint           config_dock_app_move_button;
87 guint           config_dock_app_move_modifiers;
88
89 guint    config_keyboard_reset_keycode;
90 guint    config_keyboard_reset_state;
91 gboolean config_keyboard_rebind_on_mapping_notify;
92
93 gint     config_mouse_threshold;
94 gint     config_mouse_dclicktime;
95 gint     config_mouse_screenedgetime;
96 gboolean config_mouse_screenedgewarp;
97
98 guint    config_menu_hide_delay;
99 gboolean config_menu_middle;
100 guint    config_submenu_show_delay;
101 guint    config_submenu_hide_delay;
102 gboolean config_menu_manage_desktops;
103 gboolean config_menu_show_icons;
104
105 GSList *config_menu_files;
106
107 gint     config_resist_win;
108 gint     config_resist_edge;
109
110 GSList *config_per_app_settings;
111
112 ObAppSettings* config_create_app_settings(void)
113 {
114     ObAppSettings *settings = g_slice_new0(ObAppSettings);
115     settings->type = -1;
116     settings->decor = -1;
117     settings->shade = -1;
118     settings->monitor_type = OB_PLACE_MONITOR_ANY;
119     settings->monitor = -1;
120     settings->focus = -1;
121     settings->desktop = 0;
122     settings->layer = -2;
123     settings->iconic = -1;
124     settings->skip_pager = -1;
125     settings->skip_taskbar = -1;
126     settings->fullscreen = -1;
127     settings->max_horz = -1;
128     settings->max_vert = -1;
129     return settings;
130 }
131
132 #define copy_if(setting, default) \
133   if (src->setting != default) dst->setting = src->setting
134 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
135                                            ObAppSettings *dst)
136 {
137     g_assert(src != NULL);
138     g_assert(dst != NULL);
139
140     copy_if(type, (ObClientType)-1);
141     copy_if(decor, -1);
142     copy_if(shade, -1);
143     copy_if(monitor_type, OB_PLACE_MONITOR_ANY);
144     copy_if(monitor, -1);
145     copy_if(focus, -1);
146     copy_if(desktop, 0);
147     copy_if(layer, -2);
148     copy_if(iconic, -1);
149     copy_if(skip_pager, -1);
150     copy_if(skip_taskbar, -1);
151     copy_if(fullscreen, -1);
152     copy_if(max_horz, -1);
153     copy_if(max_vert, -1);
154
155     if (src->pos_given) {
156         dst->pos_given = TRUE;
157         dst->pos_force = src->pos_force;
158         dst->position = src->position;
159         /* monitor is copied above */
160     }
161
162     dst->width_num = src->width_num;
163     dst->width_denom = src->width_denom;
164     dst->height_num = src->height_num;
165     dst->height_denom = src->height_denom;
166 }
167
168 void config_parse_relative_number(gchar *s, gint *num, gint *denom)
169 {
170     *num = strtol(s, &s, 10);
171
172     if (*s == '%') {
173         *denom = 100;
174     } else if (*s == '/') {
175         *denom = atoi(s+1);
176     }
177 }
178
179 void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
180 {
181     gchar *s = obt_xml_node_string(node);
182     if (!g_ascii_strcasecmp(s, "center"))
183         c->center = TRUE;
184     else {
185         gchar *ps = s;
186         if (s[0] == '-')
187             c->opposite = TRUE;
188         if (s[0] == '-' || s[0] == '+')
189             ps++;
190         config_parse_relative_number(ps, &c->pos, &c->denom);
191     }
192     g_free(s);
193 }
194
195 /*
196   <applications>
197     <application name="aterm">
198       <decor>false</decor>
199     </application>
200     <application name="Rhythmbox">
201       <layer>above</layer>
202       <position>
203         <x>700</x>
204         <y>0</y>
205         <monitor>1</monitor>
206       </position>
207       .. there is a lot more settings available
208     </application>
209   </applications>
210 */
211
212 static void parse_single_per_app_settings(xmlNodePtr app,
213                                           ObAppSettings *settings)
214 {
215     xmlNodePtr n, c;
216     gboolean x_pos_given = FALSE;
217
218     if ((n = obt_xml_find_node(app->children, "decor")))
219         if (!obt_xml_node_contains(n, "default"))
220             settings->decor = obt_xml_node_bool(n);
221
222     if ((n = obt_xml_find_node(app->children, "shade")))
223         if (!obt_xml_node_contains(n, "default"))
224             settings->shade = obt_xml_node_bool(n);
225
226     if ((n = obt_xml_find_node(app->children, "position"))) {
227         if ((c = obt_xml_find_node(n->children, "x"))) {
228             if (!obt_xml_node_contains(c, "default")) {
229                 config_parse_gravity_coord(c, &settings->position.x);
230                 x_pos_given = TRUE;
231             }
232         }
233
234         if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) {
235             if (!obt_xml_node_contains(c, "default")) {
236                 config_parse_gravity_coord(c, &settings->position.y);
237                 settings->pos_given = TRUE;
238             }
239         }
240
241         /* monitor can be set without setting x or y */
242         if ((c = obt_xml_find_node(n->children, "monitor"))) {
243             if (!obt_xml_node_contains(c, "default")) {
244                 gchar *s = obt_xml_node_string(c);
245                 if (!g_ascii_strcasecmp(s, "mouse"))
246                     settings->monitor_type = OB_PLACE_MONITOR_MOUSE;
247                 else if (!g_ascii_strcasecmp(s, "active"))
248                     settings->monitor_type = OB_PLACE_MONITOR_ACTIVE;
249                 else if (!g_ascii_strcasecmp(s, "primary"))
250                     settings->monitor_type = OB_PLACE_MONITOR_PRIMARY;
251                 else
252                     settings->monitor = obt_xml_node_int(c);
253                 g_free(s);
254             }
255         }
256
257         obt_xml_attr_bool(n, "force", &settings->pos_force);
258     }
259
260     if ((n = obt_xml_find_node(app->children, "size"))) {
261         if ((c = obt_xml_find_node(n->children, "width"))) {
262             if (!obt_xml_node_contains(c, "default")) {
263                 gchar *s = obt_xml_node_string(c);
264                 config_parse_relative_number(s,
265                                              &settings->width_num,
266                                              &settings->width_denom);
267                 if (settings->width_num <= 0 || settings->width_denom < 0)
268                     settings->width_num = settings->width_denom = 0;
269                 g_free(s);
270             }
271         }
272
273         if ((c = obt_xml_find_node(n->children, "height"))) {
274             if (!obt_xml_node_contains(c, "default")) {
275                 gchar *s = obt_xml_node_string(c);
276                 config_parse_relative_number(s,
277                                              &settings->height_num,
278                                              &settings->height_denom);
279                 if (settings->height_num <= 0 || settings->height_denom < 0)
280                     settings->height_num = settings->height_denom = 0;
281                 g_free(s);
282             }
283         }
284     }
285
286     if ((n = obt_xml_find_node(app->children, "focus"))) {
287         if (!obt_xml_node_contains(n, "default"))
288             settings->focus = obt_xml_node_bool(n);
289     }
290
291     if ((n = obt_xml_find_node(app->children, "desktop"))) {
292         if (!obt_xml_node_contains(n, "default")) {
293             gchar *s = obt_xml_node_string(n);
294             if (!g_ascii_strcasecmp(s, "all"))
295                 settings->desktop = DESKTOP_ALL;
296             else {
297                 gint i = obt_xml_node_int(n);
298                 if (i > 0)
299                     settings->desktop = i;
300             }
301             g_free(s);
302         }
303     }
304
305     if ((n = obt_xml_find_node(app->children, "layer"))) {
306         if (!obt_xml_node_contains(n, "default")) {
307             gchar *s = obt_xml_node_string(n);
308             if (!g_ascii_strcasecmp(s, "above"))
309                 settings->layer = 1;
310             else if (!g_ascii_strcasecmp(s, "below"))
311                 settings->layer = -1;
312             else
313                 settings->layer = 0;
314             g_free(s);
315         }
316     }
317
318     if ((n = obt_xml_find_node(app->children, "iconic")))
319         if (!obt_xml_node_contains(n, "default"))
320             settings->iconic = obt_xml_node_bool(n);
321
322     if ((n = obt_xml_find_node(app->children, "skip_pager")))
323         if (!obt_xml_node_contains(n, "default"))
324             settings->skip_pager = obt_xml_node_bool(n);
325
326     if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
327         if (!obt_xml_node_contains(n, "default"))
328             settings->skip_taskbar = obt_xml_node_bool(n);
329
330     if ((n = obt_xml_find_node(app->children, "fullscreen")))
331         if (!obt_xml_node_contains(n, "default"))
332             settings->fullscreen = obt_xml_node_bool(n);
333
334     if ((n = obt_xml_find_node(app->children, "maximized"))) {
335         if (!obt_xml_node_contains(n, "default")) {
336             gchar *s = obt_xml_node_string(n);
337             if (!g_ascii_strcasecmp(s, "horizontal")) {
338                 settings->max_horz = TRUE;
339                 settings->max_vert = FALSE;
340             } else if (!g_ascii_strcasecmp(s, "vertical")) {
341                 settings->max_horz = FALSE;
342                 settings->max_vert = TRUE;
343             } else
344                 settings->max_horz = settings->max_vert =
345                     obt_xml_node_bool(n);
346             g_free(s);
347         }
348     }
349 }
350
351 /* Manages settings for individual applications.
352    Some notes: monitor is the screen number in a multi monitor
353    (Xinerama) setup (starting from 0), or mouse: the monitor the pointer
354    is on, active: the active monitor, primary: the primary monitor.
355    Layer can be three values, above (Always on top), below
356    (Always on bottom) and everything else (normal behaviour).
357    Positions can be an integer value or center, which will
358    center the window in the specified axis. Position is within
359    the monitor, so <position><x>center</x></position><monitor>2</monitor>
360    will center the window on the second monitor.
361 */
362 static void parse_per_app_settings(xmlNodePtr node, gpointer d)
363 {
364     xmlNodePtr app = obt_xml_find_node(node->children, "application");
365     for (; app; app = obt_xml_find_node(app->next, "application")) {
366         ObAppSettings *settings;
367
368         gboolean name_set, class_set, role_set, title_set,
369             type_set, group_name_set, group_class_set;
370         gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
371             *type_str = NULL, *group_name = NULL, *group_class = NULL;
372         ObClientType type;
373
374         class_set = obt_xml_attr_string(app, "class", &class);
375         name_set = obt_xml_attr_string(app, "name", &name);
376         group_class_set = obt_xml_attr_string(app, "groupclass", &group_class);
377         group_name_set = obt_xml_attr_string(app, "groupname", &group_name);
378         type_set = obt_xml_attr_string(app, "type", &type_str);
379         role_set = obt_xml_attr_string(app, "role", &role);
380         title_set = obt_xml_attr_string(app, "title", &title);
381
382         /* validate the type tho */
383         if (type_set) {
384             if (!g_ascii_strcasecmp(type_str, "normal"))
385                 type = OB_CLIENT_TYPE_NORMAL;
386             else if (!g_ascii_strcasecmp(type_str, "dialog"))
387                 type = OB_CLIENT_TYPE_DIALOG;
388             else if (!g_ascii_strcasecmp(type_str, "splash"))
389                 type = OB_CLIENT_TYPE_SPLASH;
390             else if (!g_ascii_strcasecmp(type_str, "utility"))
391                 type = OB_CLIENT_TYPE_UTILITY;
392             else if (!g_ascii_strcasecmp(type_str, "menu"))
393                 type = OB_CLIENT_TYPE_MENU;
394             else if (!g_ascii_strcasecmp(type_str, "toolbar"))
395                 type = OB_CLIENT_TYPE_TOOLBAR;
396             else if (!g_ascii_strcasecmp(type_str, "dock"))
397                 type = OB_CLIENT_TYPE_DOCK;
398             else if (!g_ascii_strcasecmp(type_str, "desktop"))
399                 type = OB_CLIENT_TYPE_DESKTOP;
400             else
401                 type_set = FALSE; /* not valid! */
402         }
403
404         if (!(class_set || name_set || role_set || title_set ||
405               type_set || group_class_set || group_name_set))
406             continue;
407
408         settings = config_create_app_settings();
409
410         if (name_set)
411             settings->name = g_pattern_spec_new(name);
412         if (class_set)
413             settings->class = g_pattern_spec_new(class);
414         if (group_name_set)
415             settings->group_name = g_pattern_spec_new(group_name);
416         if (group_class_set)
417             settings->group_class = g_pattern_spec_new(group_class);
418         if (role_set)
419             settings->role = g_pattern_spec_new(role);
420         if (title_set)
421             settings->title = g_pattern_spec_new(title);
422         if (type_set)
423             settings->type = type;
424
425         g_free(name);
426         g_free(class);
427         g_free(group_name);
428         g_free(group_class);
429         g_free(role);
430         g_free(title);
431         g_free(type_str);
432
433         parse_single_per_app_settings(app, settings);
434         config_per_app_settings = g_slist_append(config_per_app_settings,
435                                                  (gpointer)settings);
436     }
437 }
438
439 /*
440
441 <keybind key="C-x">
442   <action name="ChangeDesktop">
443     <desktop>3</desktop>
444   </action>
445 </keybind>
446
447 */
448
449 static void parse_key(xmlNodePtr node, GList *keylist)
450 {
451     gchar *keystring, **keys, **key;
452     xmlNodePtr n;
453     gboolean is_chroot = FALSE;
454     gboolean grab = TRUE;
455     gboolean repeat = FALSE;
456
457     if (!obt_xml_attr_string(node, "key", &keystring))
458         return;
459
460     obt_xml_attr_bool(node, "chroot", &is_chroot);
461     obt_xml_attr_bool(node, "grab", &grab);
462     obt_xml_attr_bool(node, "repeat", &repeat);
463
464     keys = g_strsplit(keystring, " ", 0);
465     for (key = keys; *key; ++key) {
466         keylist = g_list_append(keylist, *key);
467
468         if ((n = obt_xml_find_node(node->children, "keybind"))) {
469             while (n) {
470                 parse_key(n, keylist);
471                 n = obt_xml_find_node(n->next, "keybind");
472             }
473         }
474         else if ((n = obt_xml_find_node(node->children, "action"))) {
475             while (n) {
476                 ObActionsAct *action;
477
478                 action = actions_parse(n);
479                 if (action)
480                     keyboard_bind(keylist, action, grab, !repeat);
481                 n = obt_xml_find_node(n->next, "action");
482             }
483         }
484
485
486         if (is_chroot)
487             keyboard_chroot(keylist);
488         keylist = g_list_delete_link(keylist, g_list_last(keylist));
489     }
490
491     g_strfreev(keys);
492     g_free(keystring);
493 }
494
495 static void parse_keyboard(xmlNodePtr node, gpointer d)
496 {
497     xmlNodePtr n;
498     gchar *key;
499
500     keyboard_unbind_all();
501
502     if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) {
503         key = obt_xml_node_string(n);
504         translate_key(key, &config_keyboard_reset_state,
505                       &config_keyboard_reset_keycode);
506         g_free(key);
507     }
508
509     if ((n = obt_xml_find_node(node->children, "keybind")))
510         while (n) {
511             parse_key(n, NULL);
512             n = obt_xml_find_node(n->next, "keybind");
513         }
514
515     if ((n = obt_xml_find_node(node->children, "rebindOnMappingNotify")))
516         config_keyboard_rebind_on_mapping_notify = obt_xml_node_bool(n);
517 }
518
519 /*
520
521 <context name="Titlebar">
522   <mousebind button="Left" action="Press">
523     <action name="Raise"></action>
524   </mousebind>
525 </context>
526
527 */
528
529 static void parse_mouse(xmlNodePtr node, gpointer d)
530 {
531     xmlNodePtr n, nbut, nact;
532     gchar *buttonstr;
533     gchar *cxstr;
534     ObMouseAction mact;
535
536     mouse_unbind_all();
537
538     node = node->children;
539
540     if ((n = obt_xml_find_node(node, "dragThreshold")))
541         config_mouse_threshold = obt_xml_node_int(n);
542     if ((n = obt_xml_find_node(node, "doubleClickTime")))
543         config_mouse_dclicktime = obt_xml_node_int(n);
544     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
545         config_mouse_screenedgetime = obt_xml_node_int(n);
546         /* minimum value of 25 for this property, when it is 1 and you hit the
547            edge it basically never stops */
548         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
549             config_mouse_screenedgetime = 25;
550     }
551     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
552         config_mouse_screenedgewarp = obt_xml_node_bool(n);
553
554     for (n = obt_xml_find_node(node, "context");
555          n;
556          n = obt_xml_find_node(n->next, "context"))
557     {
558         gchar *modcxstr;
559         ObFrameContext cx;
560
561         if (!obt_xml_attr_string(n, "name", &cxstr))
562             continue;
563
564         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
565         while (frame_next_context_from_string(modcxstr, &cx)) {
566             if (!cx) {
567                 gchar *s = strchr(modcxstr, ' ');
568                 if (s) {
569                     *s = '\0';
570                     g_message(_("Invalid context \"%s\" in mouse binding"),
571                               modcxstr);
572                     *s = ' ';
573                 }
574                 continue;
575             }
576
577             for (nbut = obt_xml_find_node(n->children, "mousebind");
578                  nbut;
579                  nbut = obt_xml_find_node(nbut->next, "mousebind"))
580             {
581
582                 gchar **button, **buttons;
583
584                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
585                     continue;
586                 if (obt_xml_attr_contains(nbut, "action", "press"))
587                     mact = OB_MOUSE_ACTION_PRESS;
588                 else if (obt_xml_attr_contains(nbut, "action", "release"))
589                     mact = OB_MOUSE_ACTION_RELEASE;
590                 else if (obt_xml_attr_contains(nbut, "action", "click"))
591                     mact = OB_MOUSE_ACTION_CLICK;
592                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
593                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
594                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
595                     mact = OB_MOUSE_ACTION_MOTION;
596                 else
597                     continue;
598
599                 buttons = g_strsplit(buttonstr, " ", 0);
600                 for (nact = obt_xml_find_node(nbut->children, "action");
601                      nact;
602                      nact = obt_xml_find_node(nact->next, "action"))
603                 {
604                     ObActionsAct *action;
605
606                     /* actions_parse() creates one ref to the action, but we need
607                      * exactly one ref per binding we use it for. */
608                     if ((action = actions_parse(nact))) {
609                         for (button = buttons; *button; ++button) {
610                             actions_act_ref(action);
611                             mouse_bind(*button, cx, mact, action);
612                         }
613                         actions_act_unref(action);
614                     }
615                 }
616                 g_strfreev(buttons);
617                 g_free(buttonstr);
618             }
619         }
620         g_free(modcxstr);
621         g_free(cxstr);
622     }
623 }
624
625 static void parse_focus(xmlNodePtr node, gpointer d)
626 {
627     xmlNodePtr n;
628
629     node = node->children;
630
631     if ((n = obt_xml_find_node(node, "focusNew")))
632         config_focus_new = obt_xml_node_bool(n);
633     if ((n = obt_xml_find_node(node, "followMouse")))
634         config_focus_follow = obt_xml_node_bool(n);
635     if ((n = obt_xml_find_node(node, "focusDelay")))
636         config_focus_delay = obt_xml_node_int(n);
637     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
638         config_focus_raise = obt_xml_node_bool(n);
639     if ((n = obt_xml_find_node(node, "focusLast")))
640         config_focus_last = obt_xml_node_bool(n);
641     if ((n = obt_xml_find_node(node, "underMouse")))
642         config_focus_under_mouse = obt_xml_node_bool(n);
643     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
644         config_unfocus_leave = obt_xml_node_bool(n);
645     if ((n = obt_xml_find_node(node, "directionalDistanceWeight")))
646         config_directional_distance_weight = obt_xml_node_int(n);
647     else
648         config_directional_distance_weight = 1U;
649     if ((n = obt_xml_find_node(node, "directionalAngleWeight")))
650         config_directional_angle_weight = obt_xml_node_int(n);
651     else
652         config_directional_angle_weight = 1U;
653 }
654
655 static void parse_placement(xmlNodePtr node, gpointer d)
656 {
657     xmlNodePtr n;
658
659     node = node->children;
660
661     if ((n = obt_xml_find_node(node, "policy"))) {
662         if (obt_xml_node_contains(n, "UnderMouse"))
663             config_place_policy = OB_PLACE_POLICY_MOUSE;
664         if (obt_xml_node_contains(n, "Random"))
665             config_place_policy = OB_PLACE_POLICY_RANDOM;
666     }
667     if ((n = obt_xml_find_node(node, "center"))) {
668         config_place_center = obt_xml_node_bool(n);
669     }
670     if ((n = obt_xml_find_node(node, "monitor"))) {
671         if (obt_xml_node_contains(n, "active"))
672             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
673         else if (obt_xml_node_contains(n, "mouse"))
674             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
675         else if (obt_xml_node_contains(n, "any"))
676             config_place_monitor = OB_PLACE_MONITOR_ANY;
677     }
678     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
679         config_primary_monitor_index = obt_xml_node_int(n);
680         if (!config_primary_monitor_index) {
681             if (obt_xml_node_contains(n, "mouse"))
682                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
683         }
684     }
685 }
686
687 static void parse_margins(xmlNodePtr node, gpointer d)
688 {
689     xmlNodePtr n;
690
691     node = node->children;
692
693     if ((n = obt_xml_find_node(node, "top")))
694         config_margins.top = MAX(0, obt_xml_node_int(n));
695     if ((n = obt_xml_find_node(node, "left")))
696         config_margins.left = MAX(0, obt_xml_node_int(n));
697     if ((n = obt_xml_find_node(node, "right")))
698         config_margins.right = MAX(0, obt_xml_node_int(n));
699     if ((n = obt_xml_find_node(node, "bottom")))
700         config_margins.bottom = MAX(0, obt_xml_node_int(n));
701 }
702
703 static void parse_theme(xmlNodePtr node, gpointer d)
704 {
705     xmlNodePtr n;
706
707     node = node->children;
708
709     if ((n = obt_xml_find_node(node, "name"))) {
710         gchar *c;
711
712         g_free(config_theme);
713         c = obt_xml_node_string(n);
714         config_theme = obt_paths_expand_tilde(c);
715         g_free(c);
716     }
717     if ((n = obt_xml_find_node(node, "titleLayout"))) {
718         gchar *c, *d;
719
720         g_free(config_title_layout);
721         config_title_layout = obt_xml_node_string(n);
722
723         /* replace duplicates with spaces */
724         for (c = config_title_layout; *c != '\0'; ++c)
725             for (d = c+1; *d != '\0'; ++d)
726                 if (*c == *d) *d = ' ';
727     }
728     if ((n = obt_xml_find_node(node, "keepBorder")))
729         config_theme_keepborder = obt_xml_node_bool(n);
730     if ((n = obt_xml_find_node(node, "animateIconify")))
731         config_animate_iconify = obt_xml_node_bool(n);
732     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
733         config_theme_window_list_icon_size = obt_xml_node_int(n);
734         if (config_theme_window_list_icon_size < 16)
735             config_theme_window_list_icon_size = 16;
736         else if (config_theme_window_list_icon_size > 96)
737             config_theme_window_list_icon_size = 96;
738     }
739
740     for (n = obt_xml_find_node(node, "font");
741          n;
742          n = obt_xml_find_node(n->next, "font"))
743     {
744         xmlNodePtr   fnode;
745         RrFont     **font;
746         gchar       *name = g_strdup(RrDefaultFontFamily);
747         gint         size = RrDefaultFontSize;
748         RrFontWeight weight = RrDefaultFontWeight;
749         RrFontSlant  slant = RrDefaultFontSlant;
750
751         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
752             font = &config_font_activewindow;
753         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
754             font = &config_font_inactivewindow;
755         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
756             font = &config_font_menutitle;
757         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
758             font = &config_font_menuitem;
759         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
760             font = &config_font_activeosd;
761         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
762             font = &config_font_activeosd;
763         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
764             font = &config_font_inactiveosd;
765         else
766             continue;
767
768         if ((fnode = obt_xml_find_node(n->children, "name"))) {
769             g_free(name);
770             name = obt_xml_node_string(fnode);
771         }
772         if ((fnode = obt_xml_find_node(n->children, "size"))) {
773             int s = obt_xml_node_int(fnode);
774             if (s > 0) {
775                 size = s;
776                 if (obt_xml_attr_contains(fnode, "type", "absolute"))
777                     size = -size;
778             }
779         }
780         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
781             gchar *w = obt_xml_node_string(fnode);
782             if (!g_ascii_strcasecmp(w, "Bold"))
783                 weight = RR_FONTWEIGHT_BOLD;
784             g_free(w);
785         }
786         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
787             gchar *s = obt_xml_node_string(fnode);
788             if (!g_ascii_strcasecmp(s, "Italic"))
789                 slant = RR_FONTSLANT_ITALIC;
790             if (!g_ascii_strcasecmp(s, "Oblique"))
791                 slant = RR_FONTSLANT_OBLIQUE;
792             g_free(s);
793         }
794
795         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
796         g_free(name);
797
798         if ((fnode = obt_xml_find_node(n->children, "description"))) {
799             gchar *s = obt_xml_node_string(fnode);
800             RrFontDescriptionFromString(*font, s);
801             g_free(s);
802         }
803     }
804 }
805
806 static void parse_desktops(xmlNodePtr node, gpointer d)
807 {
808     xmlNodePtr n;
809
810     node = node->children;
811
812     if ((n = obt_xml_find_node(node, "number"))) {
813         gint d = obt_xml_node_int(n);
814         if (d > 0)
815             config_desktops_num = (unsigned) d;
816     }
817     if ((n = obt_xml_find_node(node, "firstdesk"))) {
818         gint d = obt_xml_node_int(n);
819         if (d > 0)
820             config_screen_firstdesk = (unsigned) d;
821     }
822     if ((n = obt_xml_find_node(node, "emulatexinerama")))
823         config_emulate_xinerama = obt_xml_node_bool(n);
824     if ((n = obt_xml_find_node(node, "names"))) {
825         GSList *it;
826         xmlNodePtr nname;
827
828         for (it = config_desktops_names; it; it = it->next)
829             g_free(it->data);
830         g_slist_free(config_desktops_names);
831         config_desktops_names = NULL;
832
833         for (nname = obt_xml_find_node(n->children, "name");
834              nname;
835              nname = obt_xml_find_node(nname->next, "name"))
836         {
837             config_desktops_names =
838                 g_slist_append(config_desktops_names,
839                                obt_xml_node_string(nname));
840         }
841     }
842     if ((n = obt_xml_find_node(node, "popupTime")))
843         config_desktop_popup_time = obt_xml_node_int(n);
844 }
845
846 static void parse_resize(xmlNodePtr node, gpointer d)
847 {
848     xmlNodePtr n;
849
850     node = node->children;
851
852     if ((n = obt_xml_find_node(node, "drawContents")))
853         config_resize_redraw = obt_xml_node_bool(n);
854     if ((n = obt_xml_find_node(node, "popupShow"))) {
855         config_resize_popup_show = obt_xml_node_int(n);
856         if (obt_xml_node_contains(n, "Always"))
857             config_resize_popup_show = 2;
858         else if (obt_xml_node_contains(n, "Never"))
859             config_resize_popup_show = 0;
860         else if (obt_xml_node_contains(n, "Nonpixel"))
861             config_resize_popup_show = 1;
862     }
863     if ((n = obt_xml_find_node(node, "popupPosition"))) {
864         if (obt_xml_node_contains(n, "Top"))
865             config_resize_popup_pos = OB_RESIZE_POS_TOP;
866         else if (obt_xml_node_contains(n, "Center"))
867             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
868         else if (obt_xml_node_contains(n, "Fixed")) {
869             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
870
871             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
872                 xmlNodePtr n2;
873
874                 if ((n2 = obt_xml_find_node(n->children, "x")))
875                     config_parse_gravity_coord(n2,
876                                                &config_resize_popup_fixed.x);
877                 if ((n2 = obt_xml_find_node(n->children, "y")))
878                     config_parse_gravity_coord(n2,
879                                                &config_resize_popup_fixed.y);
880
881                 config_resize_popup_fixed.x.pos =
882                     MAX(config_resize_popup_fixed.x.pos, 0);
883                 config_resize_popup_fixed.y.pos =
884                     MAX(config_resize_popup_fixed.y.pos, 0);
885             }
886         }
887     }
888 }
889
890 static void parse_dock(xmlNodePtr node, gpointer d)
891 {
892     xmlNodePtr n;
893
894     node = node->children;
895
896     if ((n = obt_xml_find_node(node, "position"))) {
897         if (obt_xml_node_contains(n, "TopLeft"))
898             config_dock_floating = FALSE,
899             config_dock_pos = OB_DIRECTION_NORTHWEST;
900         else if (obt_xml_node_contains(n, "Top"))
901             config_dock_floating = FALSE,
902             config_dock_pos = OB_DIRECTION_NORTH;
903         else if (obt_xml_node_contains(n, "TopRight"))
904             config_dock_floating = FALSE,
905             config_dock_pos = OB_DIRECTION_NORTHEAST;
906         else if (obt_xml_node_contains(n, "Right"))
907             config_dock_floating = FALSE,
908             config_dock_pos = OB_DIRECTION_EAST;
909         else if (obt_xml_node_contains(n, "BottomRight"))
910             config_dock_floating = FALSE,
911             config_dock_pos = OB_DIRECTION_SOUTHEAST;
912         else if (obt_xml_node_contains(n, "Bottom"))
913             config_dock_floating = FALSE,
914             config_dock_pos = OB_DIRECTION_SOUTH;
915         else if (obt_xml_node_contains(n, "BottomLeft"))
916             config_dock_floating = FALSE,
917             config_dock_pos = OB_DIRECTION_SOUTHWEST;
918         else if (obt_xml_node_contains(n, "Left"))
919             config_dock_floating = FALSE,
920             config_dock_pos = OB_DIRECTION_WEST;
921         else if (obt_xml_node_contains(n, "Floating"))
922             config_dock_floating = TRUE;
923     }
924     if (config_dock_floating) {
925         if ((n = obt_xml_find_node(node, "floatingX")))
926             config_dock_x = obt_xml_node_int(n);
927         if ((n = obt_xml_find_node(node, "floatingY")))
928             config_dock_y = obt_xml_node_int(n);
929     } else {
930         if ((n = obt_xml_find_node(node, "noStrut")))
931             config_dock_nostrut = obt_xml_node_bool(n);
932     }
933     if ((n = obt_xml_find_node(node, "stacking"))) {
934         if (obt_xml_node_contains(n, "normal"))
935             config_dock_layer = OB_STACKING_LAYER_NORMAL;
936         else if (obt_xml_node_contains(n, "below"))
937             config_dock_layer = OB_STACKING_LAYER_BELOW;
938         else if (obt_xml_node_contains(n, "above"))
939             config_dock_layer = OB_STACKING_LAYER_ABOVE;
940     }
941     if ((n = obt_xml_find_node(node, "direction"))) {
942         if (obt_xml_node_contains(n, "horizontal"))
943             config_dock_orient = OB_ORIENTATION_HORZ;
944         else if (obt_xml_node_contains(n, "vertical"))
945             config_dock_orient = OB_ORIENTATION_VERT;
946     }
947     if ((n = obt_xml_find_node(node, "autoHide")))
948         config_dock_hide = obt_xml_node_bool(n);
949     if ((n = obt_xml_find_node(node, "hideDelay")))
950         config_dock_hide_delay = obt_xml_node_int(n);
951     if ((n = obt_xml_find_node(node, "showDelay")))
952         config_dock_show_delay = obt_xml_node_int(n);
953     if ((n = obt_xml_find_node(node, "moveButton"))) {
954         gchar *str = obt_xml_node_string(n);
955         guint b = 0, s = 0;
956         if (translate_button(str, &s, &b)) {
957             config_dock_app_move_button = b;
958             config_dock_app_move_modifiers = s;
959         } else {
960             g_message(_("Invalid button \"%s\" specified in config file"), str);
961         }
962         g_free(str);
963     }
964 }
965
966 static void parse_menu(xmlNodePtr node, gpointer d)
967 {
968     xmlNodePtr n;
969     node = node->children;
970
971     if ((n = obt_xml_find_node(node, "hideDelay")))
972         config_menu_hide_delay = obt_xml_node_int(n);
973     if ((n = obt_xml_find_node(node, "middle")))
974         config_menu_middle = obt_xml_node_bool(n);
975     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
976         config_submenu_show_delay = obt_xml_node_int(n);
977     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
978         config_submenu_hide_delay = obt_xml_node_int(n);
979     if ((n = obt_xml_find_node(node, "manageDesktops")))
980         config_menu_manage_desktops = obt_xml_node_bool(n);
981     if ((n = obt_xml_find_node(node, "showIcons"))) {
982         config_menu_show_icons = obt_xml_node_bool(n);
983 #if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
984         if (config_menu_show_icons)
985             g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
986 #endif
987     }
988
989     for (node = obt_xml_find_node(node, "file");
990          node;
991          node = obt_xml_find_node(node->next, "file"))
992     {
993             gchar *c = obt_xml_node_string(node);
994             config_menu_files = g_slist_append(config_menu_files,
995                                                obt_paths_expand_tilde(c));
996             g_free(c);
997     }
998 }
999
1000 static void parse_resistance(xmlNodePtr node, gpointer d)
1001 {
1002     xmlNodePtr n;
1003
1004     node = node->children;
1005     if ((n = obt_xml_find_node(node, "strength")))
1006         config_resist_win = obt_xml_node_int(n);
1007     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
1008         config_resist_edge = obt_xml_node_int(n);
1009 }
1010
1011 typedef struct
1012 {
1013     const gchar *key;
1014     const gchar *actname;
1015 } ObDefKeyBind;
1016
1017 static void bind_default_keyboard(void)
1018 {
1019     ObDefKeyBind *it;
1020     ObDefKeyBind binds[] = {
1021         { "A-Tab", "NextWindow" },
1022         { "S-A-Tab", "PreviousWindow" },
1023         { "A-F4", "Close" },
1024         { NULL, NULL }
1025     };
1026     for (it = binds; it->key; ++it) {
1027         GList *l = g_list_append(NULL, g_strdup(it->key));
1028         keyboard_bind(l, actions_parse_string(it->actname), TRUE, TRUE);
1029     }
1030 }
1031
1032 typedef struct
1033 {
1034     const gchar *button;
1035     const gchar *context;
1036     const ObMouseAction mact;
1037     const gchar *actname;
1038 } ObDefMouseBind;
1039
1040 static void bind_default_mouse(void)
1041 {
1042     ObDefMouseBind *it;
1043     ObDefMouseBind binds[] = {
1044         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1045         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1046         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1047         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1048         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1049         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1050         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
1051         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
1052         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1053         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1054         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1055         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1056         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
1057         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
1058         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
1059         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
1060         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
1061         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
1062         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
1063         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
1064         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
1065         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1066         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1067         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1068         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1069         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
1070         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
1071         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
1072         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
1073         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
1074         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
1075         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
1076         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
1077         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
1078         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
1079         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
1080         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1081         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1082         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1083         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1084         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
1085         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
1086         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
1087         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
1088         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
1089         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
1090         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
1091         { NULL, NULL, 0, NULL }
1092     };
1093
1094     for (it = binds; it->button; ++it)
1095         mouse_bind(it->button, frame_context_from_string(it->context),
1096                    it->mact, actions_parse_string(it->actname));
1097 }
1098
1099 void config_startup(ObtXmlInst *i)
1100 {
1101     config_focus_new = TRUE;
1102     config_focus_follow = FALSE;
1103     config_focus_delay = 0;
1104     config_focus_raise = FALSE;
1105     config_focus_last = TRUE;
1106     config_focus_under_mouse = FALSE;
1107     config_unfocus_leave = FALSE;
1108
1109     obt_xml_register(i, "focus", parse_focus, NULL);
1110
1111     config_place_policy = OB_PLACE_POLICY_SMART;
1112     config_place_center = TRUE;
1113     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
1114
1115     config_primary_monitor_index = 1;
1116     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1117
1118     obt_xml_register(i, "placement", parse_placement, NULL);
1119
1120     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1121
1122     obt_xml_register(i, "margins", parse_margins, NULL);
1123
1124     config_theme = NULL;
1125
1126     config_animate_iconify = TRUE;
1127     config_title_layout = g_strdup("NLIMC");
1128     config_theme_keepborder = TRUE;
1129     config_theme_window_list_icon_size = 36;
1130
1131     config_font_activewindow = NULL;
1132     config_font_inactivewindow = NULL;
1133     config_font_menuitem = NULL;
1134     config_font_menutitle = NULL;
1135     config_font_activeosd = NULL;
1136     config_font_inactiveosd = NULL;
1137
1138     obt_xml_register(i, "theme", parse_theme, NULL);
1139
1140     config_desktops_num = 4;
1141     config_screen_firstdesk = 1;
1142     config_emulate_xinerama = FALSE;
1143     config_desktops_names = NULL;
1144     config_desktop_popup_time = 875;
1145
1146     obt_xml_register(i, "desktops", parse_desktops, NULL);
1147
1148     config_resize_redraw = TRUE;
1149     config_resize_popup_show = 1; /* nonpixel increments */
1150     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1151     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1152     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1153
1154     obt_xml_register(i, "resize", parse_resize, NULL);
1155
1156     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1157     config_dock_pos = OB_DIRECTION_NORTHEAST;
1158     config_dock_floating = FALSE;
1159     config_dock_nostrut = FALSE;
1160     config_dock_x = 0;
1161     config_dock_y = 0;
1162     config_dock_orient = OB_ORIENTATION_VERT;
1163     config_dock_hide = FALSE;
1164     config_dock_hide_delay = 300;
1165     config_dock_show_delay = 300;
1166     config_dock_app_move_button = 2; /* middle */
1167     config_dock_app_move_modifiers = 0;
1168
1169     obt_xml_register(i, "dock", parse_dock, NULL);
1170
1171     translate_key("C-g", &config_keyboard_reset_state,
1172                   &config_keyboard_reset_keycode);
1173     config_keyboard_rebind_on_mapping_notify = TRUE;
1174
1175     bind_default_keyboard();
1176
1177     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1178
1179     config_mouse_threshold = 8;
1180     config_mouse_dclicktime = 500;
1181     config_mouse_screenedgetime = 400;
1182     config_mouse_screenedgewarp = FALSE;
1183
1184     bind_default_mouse();
1185
1186     obt_xml_register(i, "mouse", parse_mouse, NULL);
1187
1188     config_resist_win = 10;
1189     config_resist_edge = 20;
1190
1191     obt_xml_register(i, "resistance", parse_resistance, NULL);
1192
1193     config_menu_hide_delay = 250;
1194     config_menu_middle = FALSE;
1195     config_submenu_show_delay = 100;
1196     config_submenu_hide_delay = 400;
1197     config_menu_manage_desktops = TRUE;
1198     config_menu_files = NULL;
1199     config_menu_show_icons = TRUE;
1200
1201     obt_xml_register(i, "menu", parse_menu, NULL);
1202
1203     config_per_app_settings = NULL;
1204
1205     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1206 }
1207
1208 void config_shutdown(void)
1209 {
1210     GSList *it;
1211
1212     g_free(config_theme);
1213
1214     g_free(config_title_layout);
1215
1216     RrFontClose(config_font_activewindow);
1217     RrFontClose(config_font_inactivewindow);
1218     RrFontClose(config_font_menuitem);
1219     RrFontClose(config_font_menutitle);
1220     RrFontClose(config_font_activeosd);
1221     RrFontClose(config_font_inactiveosd);
1222
1223     for (it = config_desktops_names; it; it = g_slist_next(it))
1224         g_free(it->data);
1225     g_slist_free(config_desktops_names);
1226
1227     for (it = config_menu_files; it; it = g_slist_next(it))
1228         g_free(it->data);
1229     g_slist_free(config_menu_files);
1230
1231     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1232         ObAppSettings *itd = (ObAppSettings *)it->data;
1233         if (itd->name) g_pattern_spec_free(itd->name);
1234         if (itd->role) g_pattern_spec_free(itd->role);
1235         if (itd->title) g_pattern_spec_free(itd->title);
1236         if (itd->class) g_pattern_spec_free(itd->class);
1237         if (itd->group_name) g_pattern_spec_free(itd->group_name);
1238         if (itd->group_class) g_pattern_spec_free(itd->group_class);
1239         g_slice_free(ObAppSettings, it->data);
1240     }
1241     g_slist_free(config_per_app_settings);
1242 }