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