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