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