Add support for using relative expressions in move and resize actions
[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             name = class = role = title = NULL;
374         }
375
376         app = obt_xml_find_node(app->next, "application");
377     }
378 }
379
380 /*
381
382 <keybind key="C-x">
383   <action name="ChangeDesktop">
384     <desktop>3</desktop>
385   </action>
386 </keybind>
387
388 */
389
390 static void parse_key(xmlNodePtr node, GList *keylist)
391 {
392     gchar *key;
393     xmlNodePtr n;
394     gboolean is_chroot = FALSE;
395
396     if (!obt_xml_attr_string(node, "key", &key))
397         return;
398
399     obt_xml_attr_bool(node, "chroot", &is_chroot);
400
401     keylist = g_list_append(keylist, key);
402
403     if ((n = obt_xml_find_node(node->children, "keybind"))) {
404         while (n) {
405             parse_key(n, keylist);
406             n = obt_xml_find_node(n->next, "keybind");
407         }
408     }
409     else if ((n = obt_xml_find_node(node->children, "action"))) {
410         while (n) {
411             ObActionsAct *action;
412
413             action = actions_parse(n);
414             if (action)
415                 keyboard_bind(keylist, action);
416             n = obt_xml_find_node(n->next, "action");
417         }
418     }
419
420     if (is_chroot)
421         keyboard_chroot(keylist);
422
423     g_free(key);
424     keylist = g_list_delete_link(keylist, g_list_last(keylist));
425 }
426
427 static void parse_keyboard(xmlNodePtr node, gpointer d)
428 {
429     xmlNodePtr n;
430     gchar *key;
431
432     keyboard_unbind_all();
433
434     if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) {
435         key = obt_xml_node_string(n);
436         translate_key(key, &config_keyboard_reset_state,
437                       &config_keyboard_reset_keycode);
438         g_free(key);
439     }
440
441     if ((n = obt_xml_find_node(node->children, "keybind")))
442         while (n) {
443             parse_key(n, NULL);
444             n = obt_xml_find_node(n->next, "keybind");
445         }
446 }
447
448 /*
449
450 <context name="Titlebar">
451   <mousebind button="Left" action="Press">
452     <action name="Raise"></action>
453   </mousebind>
454 </context>
455
456 */
457
458 static void parse_mouse(xmlNodePtr node, gpointer d)
459 {
460     xmlNodePtr n, nbut, nact;
461     gchar *buttonstr;
462     gchar *contextstr;
463     ObMouseAction mact;
464
465     mouse_unbind_all();
466
467     node = node->children;
468
469     if ((n = obt_xml_find_node(node, "dragThreshold")))
470         config_mouse_threshold = obt_xml_node_int(n);
471     if ((n = obt_xml_find_node(node, "doubleClickTime")))
472         config_mouse_dclicktime = obt_xml_node_int(n);
473     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
474         config_mouse_screenedgetime = obt_xml_node_int(n);
475         /* minimum value of 25 for this property, when it is 1 and you hit the
476            edge it basically never stops */
477         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
478             config_mouse_screenedgetime = 25;
479     }
480     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
481         config_mouse_screenedgewarp = obt_xml_node_bool(n);
482
483     n = obt_xml_find_node(node, "context");
484     while (n) {
485         if (!obt_xml_attr_string(n, "name", &contextstr))
486             goto next_n;
487         nbut = obt_xml_find_node(n->children, "mousebind");
488         while (nbut) {
489             if (!obt_xml_attr_string(nbut, "button", &buttonstr))
490                 goto next_nbut;
491             if (obt_xml_attr_contains(nbut, "action", "press")) {
492                 mact = OB_MOUSE_ACTION_PRESS;
493             } else if (obt_xml_attr_contains(nbut, "action", "release")) {
494                 mact = OB_MOUSE_ACTION_RELEASE;
495             } else if (obt_xml_attr_contains(nbut, "action", "click")) {
496                 mact = OB_MOUSE_ACTION_CLICK;
497             } else if (obt_xml_attr_contains(nbut, "action","doubleclick")) {
498                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
499             } else if (obt_xml_attr_contains(nbut, "action", "drag")) {
500                 mact = OB_MOUSE_ACTION_MOTION;
501             } else
502                 goto next_nbut;
503             nact = obt_xml_find_node(nbut->children, "action");
504             while (nact) {
505                 ObActionsAct *action;
506
507                 if ((action = actions_parse(nact)))
508                     mouse_bind(buttonstr, contextstr, mact, action);
509                 nact = obt_xml_find_node(nact->next, "action");
510             }
511             g_free(buttonstr);
512         next_nbut:
513             nbut = obt_xml_find_node(nbut->next, "mousebind");
514         }
515         g_free(contextstr);
516     next_n:
517         n = obt_xml_find_node(n->next, "context");
518     }
519 }
520
521 static void parse_focus(xmlNodePtr node, gpointer d)
522 {
523     xmlNodePtr n;
524
525     node = node->children;
526
527     if ((n = obt_xml_find_node(node, "focusNew")))
528         config_focus_new = obt_xml_node_bool(n);
529     if ((n = obt_xml_find_node(node, "followMouse")))
530         config_focus_follow = obt_xml_node_bool(n);
531     if ((n = obt_xml_find_node(node, "focusDelay")))
532         config_focus_delay = obt_xml_node_int(n);
533     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
534         config_focus_raise = obt_xml_node_bool(n);
535     if ((n = obt_xml_find_node(node, "focusLast")))
536         config_focus_last = obt_xml_node_bool(n);
537     if ((n = obt_xml_find_node(node, "underMouse")))
538         config_focus_under_mouse = obt_xml_node_bool(n);
539     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
540         config_unfocus_leave = obt_xml_node_bool(n);
541 }
542
543 static void parse_placement(xmlNodePtr node, gpointer d)
544 {
545     xmlNodePtr n;
546
547     node = node->children;
548
549     if ((n = obt_xml_find_node(node, "policy")))
550         if (obt_xml_node_contains(n, "UnderMouse"))
551             config_place_policy = OB_PLACE_POLICY_MOUSE;
552     if ((n = obt_xml_find_node(node, "center")))
553         config_place_center = obt_xml_node_bool(n);
554     if ((n = obt_xml_find_node(node, "monitor"))) {
555         if (obt_xml_node_contains(n, "active"))
556             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
557         else if (obt_xml_node_contains(n, "mouse"))
558             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
559     }
560     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
561         config_primary_monitor_index = obt_xml_node_int(n);
562         if (!config_primary_monitor_index) {
563             if (obt_xml_node_contains(n, "mouse"))
564                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
565         }
566     }
567 }
568
569 static void parse_margins(xmlNodePtr node, gpointer d)
570 {
571     xmlNodePtr n;
572
573     node = node->children;
574
575     if ((n = obt_xml_find_node(node, "top")))
576         config_margins.top = MAX(0, obt_xml_node_int(n));
577     if ((n = obt_xml_find_node(node, "left")))
578         config_margins.left = MAX(0, obt_xml_node_int(n));
579     if ((n = obt_xml_find_node(node, "right")))
580         config_margins.right = MAX(0, obt_xml_node_int(n));
581     if ((n = obt_xml_find_node(node, "bottom")))
582         config_margins.bottom = MAX(0, obt_xml_node_int(n));
583 }
584
585 static void parse_theme(xmlNodePtr node, gpointer d)
586 {
587     xmlNodePtr n;
588
589     node = node->children;
590
591     if ((n = obt_xml_find_node(node, "name"))) {
592         gchar *c;
593
594         g_free(config_theme);
595         c = obt_xml_node_string(n);
596         config_theme = obt_paths_expand_tilde(c);
597         g_free(c);
598     }
599     if ((n = obt_xml_find_node(node, "titleLayout"))) {
600         gchar *c, *d;
601
602         g_free(config_title_layout);
603         config_title_layout = obt_xml_node_string(n);
604
605         /* replace duplicates with spaces */
606         for (c = config_title_layout; *c != '\0'; ++c)
607             for (d = c+1; *d != '\0'; ++d)
608                 if (*c == *d) *d = ' ';
609     }
610     if ((n = obt_xml_find_node(node, "keepBorder")))
611         config_theme_keepborder = obt_xml_node_bool(n);
612     if ((n = obt_xml_find_node(node, "animateIconify")))
613         config_animate_iconify = obt_xml_node_bool(n);
614     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
615         config_theme_window_list_icon_size = obt_xml_node_int(n);
616         if (config_theme_window_list_icon_size < 16)
617             config_theme_window_list_icon_size = 16;
618         else if (config_theme_window_list_icon_size > 96)
619             config_theme_window_list_icon_size = 96;
620     }
621
622     n = obt_xml_find_node(node, "font");
623     while (n) {
624         xmlNodePtr   fnode;
625         RrFont     **font;
626         gchar       *name = g_strdup(RrDefaultFontFamily);
627         gint         size = RrDefaultFontSize;
628         RrFontWeight weight = RrDefaultFontWeight;
629         RrFontSlant  slant = RrDefaultFontSlant;
630
631         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
632             font = &config_font_activewindow;
633         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
634             font = &config_font_inactivewindow;
635         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
636             font = &config_font_menutitle;
637         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
638             font = &config_font_menuitem;
639         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
640             font = &config_font_activeosd;
641         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
642             font = &config_font_activeosd;
643         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
644             font = &config_font_inactiveosd;
645         else
646             goto next_font;
647
648         if ((fnode = obt_xml_find_node(n->children, "name"))) {
649             g_free(name);
650             name = obt_xml_node_string(fnode);
651         }
652         if ((fnode = obt_xml_find_node(n->children, "size"))) {
653             int s = obt_xml_node_int(fnode);
654             if (s > 0) size = s;
655         }
656         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
657             gchar *w = obt_xml_node_string(fnode);
658             if (!g_ascii_strcasecmp(w, "Bold"))
659                 weight = RR_FONTWEIGHT_BOLD;
660             g_free(w);
661         }
662         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
663             gchar *s = obt_xml_node_string(fnode);
664             if (!g_ascii_strcasecmp(s, "Italic"))
665                 slant = RR_FONTSLANT_ITALIC;
666             if (!g_ascii_strcasecmp(s, "Oblique"))
667                 slant = RR_FONTSLANT_OBLIQUE;
668             g_free(s);
669         }
670
671         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
672         g_free(name);
673     next_font:
674         n = obt_xml_find_node(n->next, "font");
675     }
676 }
677
678 static void parse_desktops(xmlNodePtr node, gpointer d)
679 {
680     xmlNodePtr n;
681
682     node = node->children;
683
684     if ((n = obt_xml_find_node(node, "number"))) {
685         gint d = obt_xml_node_int(n);
686         if (d > 0)
687             config_desktops_num = (unsigned) d;
688     }
689     if ((n = obt_xml_find_node(node, "firstdesk"))) {
690         gint d = obt_xml_node_int(n);
691         if (d > 0)
692             config_screen_firstdesk = (unsigned) d;
693     }
694     if ((n = obt_xml_find_node(node, "names"))) {
695         GSList *it;
696         xmlNodePtr nname;
697
698         for (it = config_desktops_names; it; it = it->next)
699             g_free(it->data);
700         g_slist_free(config_desktops_names);
701         config_desktops_names = NULL;
702
703         nname = obt_xml_find_node(n->children, "name");
704         while (nname) {
705             config_desktops_names =
706                 g_slist_append(config_desktops_names,
707                                obt_xml_node_string(nname));
708             nname = obt_xml_find_node(nname->next, "name");
709         }
710     }
711     if ((n = obt_xml_find_node(node, "popupTime")))
712         config_desktop_popup_time = obt_xml_node_int(n);
713 }
714
715 static void parse_resize(xmlNodePtr node, gpointer d)
716 {
717     xmlNodePtr n;
718
719     node = node->children;
720
721     if ((n = obt_xml_find_node(node, "drawContents")))
722         config_resize_redraw = obt_xml_node_bool(n);
723     if ((n = obt_xml_find_node(node, "popupShow"))) {
724         config_resize_popup_show = obt_xml_node_int(n);
725         if (obt_xml_node_contains(n, "Always"))
726             config_resize_popup_show = 2;
727         else if (obt_xml_node_contains(n, "Never"))
728             config_resize_popup_show = 0;
729         else if (obt_xml_node_contains(n, "Nonpixel"))
730             config_resize_popup_show = 1;
731     }
732     if ((n = obt_xml_find_node(node, "popupPosition"))) {
733         if (obt_xml_node_contains(n, "Top"))
734             config_resize_popup_pos = OB_RESIZE_POS_TOP;
735         else if (obt_xml_node_contains(n, "Center"))
736             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
737         else if (obt_xml_node_contains(n, "Fixed")) {
738             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
739
740             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
741                 xmlNodePtr n2;
742
743                 if ((n2 = obt_xml_find_node(n->children, "x")))
744                     config_parse_gravity_coord(n2,
745                                                &config_resize_popup_fixed.x);
746                 if ((n2 = obt_xml_find_node(n->children, "y")))
747                     config_parse_gravity_coord(n2,
748                                                &config_resize_popup_fixed.y);
749
750                 config_resize_popup_fixed.x.pos =
751                     MAX(config_resize_popup_fixed.x.pos, 0);
752                 config_resize_popup_fixed.y.pos =
753                     MAX(config_resize_popup_fixed.y.pos, 0);
754             }
755         }
756     }
757 }
758
759 static void parse_dock(xmlNodePtr node, gpointer d)
760 {
761     xmlNodePtr n;
762
763     node = node->children;
764
765     if ((n = obt_xml_find_node(node, "position"))) {
766         if (obt_xml_node_contains(n, "TopLeft"))
767             config_dock_floating = FALSE,
768             config_dock_pos = OB_DIRECTION_NORTHWEST;
769         else if (obt_xml_node_contains(n, "Top"))
770             config_dock_floating = FALSE,
771             config_dock_pos = OB_DIRECTION_NORTH;
772         else if (obt_xml_node_contains(n, "TopRight"))
773             config_dock_floating = FALSE,
774             config_dock_pos = OB_DIRECTION_NORTHEAST;
775         else if (obt_xml_node_contains(n, "Right"))
776             config_dock_floating = FALSE,
777             config_dock_pos = OB_DIRECTION_EAST;
778         else if (obt_xml_node_contains(n, "BottomRight"))
779             config_dock_floating = FALSE,
780             config_dock_pos = OB_DIRECTION_SOUTHEAST;
781         else if (obt_xml_node_contains(n, "Bottom"))
782             config_dock_floating = FALSE,
783             config_dock_pos = OB_DIRECTION_SOUTH;
784         else if (obt_xml_node_contains(n, "BottomLeft"))
785             config_dock_floating = FALSE,
786             config_dock_pos = OB_DIRECTION_SOUTHWEST;
787         else if (obt_xml_node_contains(n, "Left"))
788             config_dock_floating = FALSE,
789             config_dock_pos = OB_DIRECTION_WEST;
790         else if (obt_xml_node_contains(n, "Floating"))
791             config_dock_floating = TRUE;
792     }
793     if (config_dock_floating) {
794         if ((n = obt_xml_find_node(node, "floatingX")))
795             config_dock_x = obt_xml_node_int(n);
796         if ((n = obt_xml_find_node(node, "floatingY")))
797             config_dock_y = obt_xml_node_int(n);
798     } else {
799         if ((n = obt_xml_find_node(node, "noStrut")))
800             config_dock_nostrut = obt_xml_node_bool(n);
801     }
802     if ((n = obt_xml_find_node(node, "stacking"))) {
803         if (obt_xml_node_contains(n, "normal"))
804             config_dock_layer = OB_STACKING_LAYER_NORMAL;
805         else if (obt_xml_node_contains(n, "below"))
806             config_dock_layer = OB_STACKING_LAYER_BELOW;
807         else if (obt_xml_node_contains(n, "above"))
808             config_dock_layer = OB_STACKING_LAYER_ABOVE;
809     }
810     if ((n = obt_xml_find_node(node, "direction"))) {
811         if (obt_xml_node_contains(n, "horizontal"))
812             config_dock_orient = OB_ORIENTATION_HORZ;
813         else if (obt_xml_node_contains(n, "vertical"))
814             config_dock_orient = OB_ORIENTATION_VERT;
815     }
816     if ((n = obt_xml_find_node(node, "autoHide")))
817         config_dock_hide = obt_xml_node_bool(n);
818     if ((n = obt_xml_find_node(node, "hideDelay")))
819         config_dock_hide_delay = obt_xml_node_int(n);
820     if ((n = obt_xml_find_node(node, "showDelay")))
821         config_dock_show_delay = obt_xml_node_int(n);
822     if ((n = obt_xml_find_node(node, "moveButton"))) {
823         gchar *str = obt_xml_node_string(n);
824         guint b, s;
825         if (translate_button(str, &s, &b)) {
826             config_dock_app_move_button = b;
827             config_dock_app_move_modifiers = s;
828         } else {
829             g_message(_("Invalid button \"%s\" specified in config file"), str);
830         }
831         g_free(str);
832     }
833 }
834
835 static void parse_menu(xmlNodePtr node, gpointer d)
836 {
837     xmlNodePtr n;
838     node = node->children;
839
840     if ((n = obt_xml_find_node(node, "hideDelay")))
841         config_menu_hide_delay = obt_xml_node_int(n);
842     if ((n = obt_xml_find_node(node, "middle")))
843         config_menu_middle = obt_xml_node_bool(n);
844     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
845         config_submenu_show_delay = obt_xml_node_int(n);
846     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
847         config_submenu_hide_delay = obt_xml_node_int(n);
848     if ((n = obt_xml_find_node(node, "manageDesktops")))
849         config_menu_manage_desktops = obt_xml_node_bool(n);
850     if ((n = obt_xml_find_node(node, "showIcons"))) {
851         config_menu_show_icons = obt_xml_node_bool(n);
852 #ifndef USE_IMLIB2
853         if (config_menu_show_icons)
854             g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded."));
855 #endif
856     }
857
858     while ((node = obt_xml_find_node(node, "file"))) {
859             gchar *c = obt_xml_node_string(node);
860             config_menu_files = g_slist_append(config_menu_files,
861                                                obt_paths_expand_tilde(c));
862             g_free(c);
863             node = node->next;
864     }
865 }
866
867 static void parse_resistance(xmlNodePtr node, gpointer d)
868 {
869     xmlNodePtr n;
870
871     node = node->children;
872     if ((n = obt_xml_find_node(node, "strength")))
873         config_resist_win = obt_xml_node_int(n);
874     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
875         config_resist_edge = obt_xml_node_int(n);
876 }
877
878 typedef struct
879 {
880     const gchar *key;
881     const gchar *actname;
882 } ObDefKeyBind;
883
884 static void bind_default_keyboard(void)
885 {
886     ObDefKeyBind *it;
887     ObDefKeyBind binds[] = {
888         { "A-Tab", "NextWindow" },
889         { "S-A-Tab", "PreviousWindow" },
890         { "A-F4", "Close" },
891         { NULL, NULL }
892     };
893     for (it = binds; it->key; ++it) {
894         GList *l = g_list_append(NULL, g_strdup(it->key));
895         keyboard_bind(l, actions_parse_string(it->actname));
896     }
897 }
898
899 typedef struct
900 {
901     const gchar *button;
902     const gchar *context;
903     const ObMouseAction mact;
904     const gchar *actname;
905 } ObDefMouseBind;
906
907 static void bind_default_mouse(void)
908 {
909     ObDefMouseBind *it;
910     ObDefMouseBind binds[] = {
911         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
912         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
913         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
914         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
915         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
916         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
917         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
918         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
919         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
920         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
921         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
922         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
923         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
924         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
925         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
926         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
927         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
928         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
929         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
930         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
931         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
932         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
933         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
934         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
935         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
936         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
937         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
938         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
939         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
940         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
941         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
942         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
943         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
944         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
945         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
946         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
947         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
948         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
949         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
950         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
951         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
952         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
953         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
954         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
955         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
956         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
957         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
958         { NULL, NULL, 0, NULL }
959     };
960
961     for (it = binds; it->button; ++it)
962         mouse_bind(it->button, it->context, it->mact,
963                    actions_parse_string(it->actname));
964 }
965
966 void config_startup(ObtXmlInst *i)
967 {
968     config_focus_new = TRUE;
969     config_focus_follow = FALSE;
970     config_focus_delay = 0;
971     config_focus_raise = FALSE;
972     config_focus_last = TRUE;
973     config_focus_under_mouse = FALSE;
974     config_unfocus_leave = FALSE;
975
976     obt_xml_register(i, "focus", parse_focus, NULL);
977
978     config_place_policy = OB_PLACE_POLICY_SMART;
979     config_place_center = TRUE;
980     config_place_monitor = OB_PLACE_MONITOR_ANY;
981
982     config_primary_monitor_index = 1;
983     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
984
985     obt_xml_register(i, "placement", parse_placement, NULL);
986
987     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
988
989     obt_xml_register(i, "margins", parse_margins, NULL);
990
991     config_theme = NULL;
992
993     config_animate_iconify = TRUE;
994     config_title_layout = g_strdup("NLIMC");
995     config_theme_keepborder = TRUE;
996     config_theme_window_list_icon_size = 36;
997
998     config_font_activewindow = NULL;
999     config_font_inactivewindow = NULL;
1000     config_font_menuitem = NULL;
1001     config_font_menutitle = NULL;
1002     config_font_activeosd = NULL;
1003     config_font_inactiveosd = NULL;
1004
1005     obt_xml_register(i, "theme", parse_theme, NULL);
1006
1007     config_desktops_num = 4;
1008     config_screen_firstdesk = 1;
1009     config_desktops_names = NULL;
1010     config_desktop_popup_time = 875;
1011
1012     obt_xml_register(i, "desktops", parse_desktops, NULL);
1013
1014     config_resize_redraw = TRUE;
1015     config_resize_popup_show = 1; /* nonpixel increments */
1016     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1017     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1018     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1019
1020     obt_xml_register(i, "resize", parse_resize, NULL);
1021
1022     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1023     config_dock_pos = OB_DIRECTION_NORTHEAST;
1024     config_dock_floating = FALSE;
1025     config_dock_nostrut = FALSE;
1026     config_dock_x = 0;
1027     config_dock_y = 0;
1028     config_dock_orient = OB_ORIENTATION_VERT;
1029     config_dock_hide = FALSE;
1030     config_dock_hide_delay = 300;
1031     config_dock_show_delay = 300;
1032     config_dock_app_move_button = 2; /* middle */
1033     config_dock_app_move_modifiers = 0;
1034
1035     obt_xml_register(i, "dock", parse_dock, NULL);
1036
1037     translate_key("C-g", &config_keyboard_reset_state,
1038                   &config_keyboard_reset_keycode);
1039
1040     bind_default_keyboard();
1041
1042     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1043
1044     config_mouse_threshold = 8;
1045     config_mouse_dclicktime = 200;
1046     config_mouse_screenedgetime = 400;
1047     config_mouse_screenedgewarp = FALSE;
1048
1049     bind_default_mouse();
1050
1051     obt_xml_register(i, "mouse", parse_mouse, NULL);
1052
1053     config_resist_win = 10;
1054     config_resist_edge = 20;
1055
1056     obt_xml_register(i, "resistance", parse_resistance, NULL);
1057
1058     config_menu_hide_delay = 250;
1059     config_menu_middle = FALSE;
1060     config_submenu_show_delay = 100;
1061     config_submenu_hide_delay = 400;
1062     config_menu_manage_desktops = TRUE;
1063     config_menu_files = NULL;
1064     config_menu_show_icons = TRUE;
1065
1066     obt_xml_register(i, "menu", parse_menu, NULL);
1067
1068     config_per_app_settings = NULL;
1069
1070     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1071 }
1072
1073 void config_shutdown(void)
1074 {
1075     GSList *it;
1076
1077     g_free(config_theme);
1078
1079     g_free(config_title_layout);
1080
1081     RrFontClose(config_font_activewindow);
1082     RrFontClose(config_font_inactivewindow);
1083     RrFontClose(config_font_menuitem);
1084     RrFontClose(config_font_menutitle);
1085     RrFontClose(config_font_activeosd);
1086     RrFontClose(config_font_inactiveosd);
1087
1088     for (it = config_desktops_names; it; it = g_slist_next(it))
1089         g_free(it->data);
1090     g_slist_free(config_desktops_names);
1091
1092     for (it = config_menu_files; it; it = g_slist_next(it))
1093         g_free(it->data);
1094     g_slist_free(config_menu_files);
1095
1096     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1097         ObAppSettings *itd = (ObAppSettings *)it->data;
1098         if (itd->name)  g_pattern_spec_free(itd->name);
1099         if (itd->role)  g_pattern_spec_free(itd->role);
1100         if (itd->title) g_pattern_spec_free(itd->title);
1101         if (itd->class) g_pattern_spec_free(itd->class);
1102         g_slice_free(ObAppSettings, it->data);
1103     }
1104     g_slist_free(config_per_app_settings);
1105 }