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