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