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