Add keyboard/rebindOnMappingNotify option
[mikachu/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 gboolean config_keyboard_rebind_on_mapping_notify;
89
90 gint     config_mouse_threshold;
91 gint     config_mouse_dclicktime;
92 gint     config_mouse_screenedgetime;
93 gboolean config_mouse_screenedgewarp;
94
95 guint    config_menu_hide_delay;
96 gboolean config_menu_middle;
97 guint    config_submenu_show_delay;
98 guint    config_submenu_hide_delay;
99 gboolean config_menu_manage_desktops;
100 gboolean config_menu_show_icons;
101
102 GSList *config_menu_files;
103
104 gint     config_resist_win;
105 gint     config_resist_edge;
106
107 GSList *config_per_app_settings;
108
109 ObAppSettings* config_create_app_settings(void)
110 {
111     ObAppSettings *settings = g_slice_new0(ObAppSettings);
112     settings->type = -1;
113     settings->decor = -1;
114     settings->shade = -1;
115     settings->monitor_type = OB_PLACE_MONITOR_ANY;
116     settings->monitor = -1;
117     settings->focus = -1;
118     settings->desktop = 0;
119     settings->layer = -2;
120     settings->iconic = -1;
121     settings->skip_pager = -1;
122     settings->skip_taskbar = -1;
123     settings->fullscreen = -1;
124     settings->max_horz = -1;
125     settings->max_vert = -1;
126     return settings;
127 }
128
129 #define copy_if(setting, default) \
130   if (src->setting != default) dst->setting = src->setting
131 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
132                                            ObAppSettings *dst)
133 {
134     g_assert(src != NULL);
135     g_assert(dst != NULL);
136
137     copy_if(type, (ObClientType)-1);
138     copy_if(decor, -1);
139     copy_if(shade, -1);
140     copy_if(monitor_type, OB_PLACE_MONITOR_ANY);
141     copy_if(monitor, -1);
142     copy_if(focus, -1);
143     copy_if(desktop, 0);
144     copy_if(layer, -2);
145     copy_if(iconic, -1);
146     copy_if(skip_pager, -1);
147     copy_if(skip_taskbar, -1);
148     copy_if(fullscreen, -1);
149     copy_if(max_horz, -1);
150     copy_if(max_vert, -1);
151
152     if (src->pos_given) {
153         dst->pos_given = TRUE;
154         dst->pos_force = src->pos_force;
155         dst->position = src->position;
156         /* monitor is copied above */
157     }
158
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 void config_parse_relative_number(gchar *s, gint *num, gint *denom)
166 {
167     *num = strtol(s, &s, 10);
168
169     if (*s == '%') {
170         *denom = 100;
171     } else if (*s == '/') {
172         *denom = atoi(s+1);
173     }
174 }
175
176 void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
177 {
178     gchar *s = obt_xml_node_string(node);
179     if (!g_ascii_strcasecmp(s, "center"))
180         c->center = TRUE;
181     else {
182         gchar *ps = s;
183         if (s[0] == '-')
184             c->opposite = TRUE;
185         if (s[0] == '-' || s[0] == '+')
186             ps++;
187         config_parse_relative_number(ps, &c->pos, &c->denom);
188     }
189     g_free(s);
190 }
191
192 /*
193   <applications>
194     <application name="aterm">
195       <decor>false</decor>
196     </application>
197     <application name="Rhythmbox">
198       <layer>above</layer>
199       <position>
200         <x>700</x>
201         <y>0</y>
202         <monitor>1</monitor>
203       </position>
204       .. there is a lot more settings available
205     </application>
206   </applications>
207 */
208
209 static void parse_single_per_app_settings(xmlNodePtr app,
210                                           ObAppSettings *settings)
211 {
212     xmlNodePtr n, c;
213     gboolean x_pos_given = FALSE;
214
215     if ((n = obt_xml_find_node(app->children, "decor")))
216         if (!obt_xml_node_contains(n, "default"))
217             settings->decor = obt_xml_node_bool(n);
218
219     if ((n = obt_xml_find_node(app->children, "shade")))
220         if (!obt_xml_node_contains(n, "default"))
221             settings->shade = obt_xml_node_bool(n);
222
223     if ((n = obt_xml_find_node(app->children, "position"))) {
224         if ((c = obt_xml_find_node(n->children, "x"))) {
225             if (!obt_xml_node_contains(c, "default")) {
226                 config_parse_gravity_coord(c, &settings->position.x);
227                 x_pos_given = TRUE;
228             }
229         }
230
231         if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) {
232             if (!obt_xml_node_contains(c, "default")) {
233                 config_parse_gravity_coord(c, &settings->position.y);
234                 settings->pos_given = TRUE;
235             }
236         }
237
238         /* monitor can be set without setting x or y */
239         if ((c = obt_xml_find_node(n->children, "monitor"))) {
240             if (!obt_xml_node_contains(c, "default")) {
241                 gchar *s = obt_xml_node_string(c);
242                 if (!g_ascii_strcasecmp(s, "mouse"))
243                     settings->monitor_type = OB_PLACE_MONITOR_MOUSE;
244                 else if (!g_ascii_strcasecmp(s, "active"))
245                     settings->monitor_type = OB_PLACE_MONITOR_ACTIVE;
246                 else if (!g_ascii_strcasecmp(s, "primary"))
247                     settings->monitor_type = OB_PLACE_MONITOR_PRIMARY;
248                 else
249                     settings->monitor = obt_xml_node_int(c);
250                 g_free(s);
251             }
252         }
253
254         obt_xml_attr_bool(n, "force", &settings->pos_force);
255     }
256
257     if ((n = obt_xml_find_node(app->children, "size"))) {
258         if ((c = obt_xml_find_node(n->children, "width"))) {
259             if (!obt_xml_node_contains(c, "default")) {
260                 gchar *s = obt_xml_node_string(c);
261                 config_parse_relative_number(s,
262                                              &settings->width_num,
263                                              &settings->width_denom);
264                 if (settings->width_num <= 0 || settings->width_denom < 0)
265                     settings->width_num = settings->width_denom = 0;
266                 g_free(s);
267             }
268         }
269
270         if ((c = obt_xml_find_node(n->children, "height"))) {
271             if (!obt_xml_node_contains(c, "default")) {
272                 gchar *s = obt_xml_node_string(c);
273                 config_parse_relative_number(s,
274                                              &settings->height_num,
275                                              &settings->height_denom);
276                 if (settings->height_num <= 0 || settings->height_denom < 0)
277                     settings->height_num = settings->height_denom = 0;
278                 g_free(s);
279             }
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     if ((n = obt_xml_find_node(node->children, "rebindOnMappingNotify")))
509         config_keyboard_rebind_on_mapping_notify = obt_xml_node_bool(n);
510 }
511
512 /*
513
514 <context name="Titlebar">
515   <mousebind button="Left" action="Press">
516     <action name="Raise"></action>
517   </mousebind>
518 </context>
519
520 */
521
522 static void parse_mouse(xmlNodePtr node, gpointer d)
523 {
524     xmlNodePtr n, nbut, nact;
525     gchar *buttonstr;
526     gchar *cxstr;
527     ObMouseAction mact;
528
529     mouse_unbind_all();
530
531     node = node->children;
532
533     if ((n = obt_xml_find_node(node, "dragThreshold")))
534         config_mouse_threshold = obt_xml_node_int(n);
535     if ((n = obt_xml_find_node(node, "doubleClickTime")))
536         config_mouse_dclicktime = obt_xml_node_int(n);
537     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
538         config_mouse_screenedgetime = obt_xml_node_int(n);
539         /* minimum value of 25 for this property, when it is 1 and you hit the
540            edge it basically never stops */
541         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
542             config_mouse_screenedgetime = 25;
543     }
544     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
545         config_mouse_screenedgewarp = obt_xml_node_bool(n);
546
547     n = obt_xml_find_node(node, "context");
548     while (n) {
549         gchar *modcxstr;
550         ObFrameContext cx;
551
552         if (!obt_xml_attr_string(n, "name", &cxstr))
553             goto next_n;
554
555         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
556         while (frame_next_context_from_string(modcxstr, &cx)) {
557             if (!cx) {
558                 gchar *s = strchr(modcxstr, ' ');
559                 if (s) {
560                     *s = '\0';
561                     g_message(_("Invalid context \"%s\" in mouse binding"),
562                               modcxstr);
563                     *s = ' ';
564                 }
565                 continue;
566             }
567
568             nbut = obt_xml_find_node(n->children, "mousebind");
569             while (nbut) {
570                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
571                     goto next_nbut;
572                 if (obt_xml_attr_contains(nbut, "action", "press"))
573                     mact = OB_MOUSE_ACTION_PRESS;
574                 else if (obt_xml_attr_contains(nbut, "action", "release"))
575                     mact = OB_MOUSE_ACTION_RELEASE;
576                 else if (obt_xml_attr_contains(nbut, "action", "click"))
577                     mact = OB_MOUSE_ACTION_CLICK;
578                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
579                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
580                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
581                     mact = OB_MOUSE_ACTION_MOTION;
582                 else
583                     goto next_nbut;
584
585                 nact = obt_xml_find_node(nbut->children, "action");
586                 while (nact) {
587                     ObActionsAct *action;
588
589                     if ((action = actions_parse(nact)))
590                         mouse_bind(buttonstr, cx, mact, action);
591                     nact = obt_xml_find_node(nact->next, "action");
592                 }
593             g_free(buttonstr);
594             next_nbut:
595             nbut = obt_xml_find_node(nbut->next, "mousebind");
596             }
597         }
598         g_free(modcxstr);
599         g_free(cxstr);
600     next_n:
601         n = obt_xml_find_node(n->next, "context");
602     }
603 }
604
605 static void parse_focus(xmlNodePtr node, gpointer d)
606 {
607     xmlNodePtr n;
608
609     node = node->children;
610
611     if ((n = obt_xml_find_node(node, "focusNew")))
612         config_focus_new = obt_xml_node_bool(n);
613     if ((n = obt_xml_find_node(node, "followMouse")))
614         config_focus_follow = obt_xml_node_bool(n);
615     if ((n = obt_xml_find_node(node, "focusDelay")))
616         config_focus_delay = obt_xml_node_int(n);
617     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
618         config_focus_raise = obt_xml_node_bool(n);
619     if ((n = obt_xml_find_node(node, "focusLast")))
620         config_focus_last = obt_xml_node_bool(n);
621     if ((n = obt_xml_find_node(node, "underMouse")))
622         config_focus_under_mouse = obt_xml_node_bool(n);
623     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
624         config_unfocus_leave = obt_xml_node_bool(n);
625 }
626
627 static void parse_placement(xmlNodePtr node, gpointer d)
628 {
629     xmlNodePtr n;
630
631     node = node->children;
632
633     if ((n = obt_xml_find_node(node, "policy"))) {
634         if (obt_xml_node_contains(n, "UnderMouse"))
635             config_place_policy = OB_PLACE_POLICY_MOUSE;
636     }
637     if ((n = obt_xml_find_node(node, "center"))) {
638         config_place_center = obt_xml_node_bool(n);
639     }
640     if ((n = obt_xml_find_node(node, "monitor"))) {
641         if (obt_xml_node_contains(n, "active"))
642             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
643         else if (obt_xml_node_contains(n, "mouse"))
644             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
645         else if (obt_xml_node_contains(n, "any"))
646             config_place_monitor = OB_PLACE_MONITOR_ANY;
647     }
648     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
649         config_primary_monitor_index = obt_xml_node_int(n);
650         if (!config_primary_monitor_index) {
651             if (obt_xml_node_contains(n, "mouse"))
652                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
653         }
654     }
655 }
656
657 static void parse_margins(xmlNodePtr node, gpointer d)
658 {
659     xmlNodePtr n;
660
661     node = node->children;
662
663     if ((n = obt_xml_find_node(node, "top")))
664         config_margins.top = MAX(0, obt_xml_node_int(n));
665     if ((n = obt_xml_find_node(node, "left")))
666         config_margins.left = MAX(0, obt_xml_node_int(n));
667     if ((n = obt_xml_find_node(node, "right")))
668         config_margins.right = MAX(0, obt_xml_node_int(n));
669     if ((n = obt_xml_find_node(node, "bottom")))
670         config_margins.bottom = MAX(0, obt_xml_node_int(n));
671 }
672
673 static void parse_theme(xmlNodePtr node, gpointer d)
674 {
675     xmlNodePtr n;
676
677     node = node->children;
678
679     if ((n = obt_xml_find_node(node, "name"))) {
680         gchar *c;
681
682         g_free(config_theme);
683         c = obt_xml_node_string(n);
684         config_theme = obt_paths_expand_tilde(c);
685         g_free(c);
686     }
687     if ((n = obt_xml_find_node(node, "titleLayout"))) {
688         gchar *c, *d;
689
690         g_free(config_title_layout);
691         config_title_layout = obt_xml_node_string(n);
692
693         /* replace duplicates with spaces */
694         for (c = config_title_layout; *c != '\0'; ++c)
695             for (d = c+1; *d != '\0'; ++d)
696                 if (*c == *d) *d = ' ';
697     }
698     if ((n = obt_xml_find_node(node, "keepBorder")))
699         config_theme_keepborder = obt_xml_node_bool(n);
700     if ((n = obt_xml_find_node(node, "animateIconify")))
701         config_animate_iconify = obt_xml_node_bool(n);
702     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
703         config_theme_window_list_icon_size = obt_xml_node_int(n);
704         if (config_theme_window_list_icon_size < 16)
705             config_theme_window_list_icon_size = 16;
706         else if (config_theme_window_list_icon_size > 96)
707             config_theme_window_list_icon_size = 96;
708     }
709
710     n = obt_xml_find_node(node, "font");
711     while (n) {
712         xmlNodePtr   fnode;
713         RrFont     **font;
714         gchar       *name = g_strdup(RrDefaultFontFamily);
715         gint         size = RrDefaultFontSize;
716         RrFontWeight weight = RrDefaultFontWeight;
717         RrFontSlant  slant = RrDefaultFontSlant;
718
719         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
720             font = &config_font_activewindow;
721         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
722             font = &config_font_inactivewindow;
723         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
724             font = &config_font_menutitle;
725         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
726             font = &config_font_menuitem;
727         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
728             font = &config_font_activeosd;
729         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
730             font = &config_font_activeosd;
731         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
732             font = &config_font_inactiveosd;
733         else
734             goto next_font;
735
736         if ((fnode = obt_xml_find_node(n->children, "name"))) {
737             g_free(name);
738             name = obt_xml_node_string(fnode);
739         }
740         if ((fnode = obt_xml_find_node(n->children, "size"))) {
741             int s = obt_xml_node_int(fnode);
742             if (s > 0) size = s;
743         }
744         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
745             gchar *w = obt_xml_node_string(fnode);
746             if (!g_ascii_strcasecmp(w, "Bold"))
747                 weight = RR_FONTWEIGHT_BOLD;
748             g_free(w);
749         }
750         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
751             gchar *s = obt_xml_node_string(fnode);
752             if (!g_ascii_strcasecmp(s, "Italic"))
753                 slant = RR_FONTSLANT_ITALIC;
754             if (!g_ascii_strcasecmp(s, "Oblique"))
755                 slant = RR_FONTSLANT_OBLIQUE;
756             g_free(s);
757         }
758
759         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
760         g_free(name);
761     next_font:
762         n = obt_xml_find_node(n->next, "font");
763     }
764 }
765
766 static void parse_desktops(xmlNodePtr node, gpointer d)
767 {
768     xmlNodePtr n;
769
770     node = node->children;
771
772     if ((n = obt_xml_find_node(node, "number"))) {
773         gint d = obt_xml_node_int(n);
774         if (d > 0)
775             config_desktops_num = (unsigned) d;
776     }
777     if ((n = obt_xml_find_node(node, "firstdesk"))) {
778         gint d = obt_xml_node_int(n);
779         if (d > 0)
780             config_screen_firstdesk = (unsigned) d;
781     }
782     if ((n = obt_xml_find_node(node, "names"))) {
783         GSList *it;
784         xmlNodePtr nname;
785
786         for (it = config_desktops_names; it; it = it->next)
787             g_free(it->data);
788         g_slist_free(config_desktops_names);
789         config_desktops_names = NULL;
790
791         nname = obt_xml_find_node(n->children, "name");
792         while (nname) {
793             config_desktops_names =
794                 g_slist_append(config_desktops_names,
795                                obt_xml_node_string(nname));
796             nname = obt_xml_find_node(nname->next, "name");
797         }
798     }
799     if ((n = obt_xml_find_node(node, "popupTime")))
800         config_desktop_popup_time = obt_xml_node_int(n);
801 }
802
803 static void parse_resize(xmlNodePtr node, gpointer d)
804 {
805     xmlNodePtr n;
806
807     node = node->children;
808
809     if ((n = obt_xml_find_node(node, "drawContents")))
810         config_resize_redraw = obt_xml_node_bool(n);
811     if ((n = obt_xml_find_node(node, "popupShow"))) {
812         config_resize_popup_show = obt_xml_node_int(n);
813         if (obt_xml_node_contains(n, "Always"))
814             config_resize_popup_show = 2;
815         else if (obt_xml_node_contains(n, "Never"))
816             config_resize_popup_show = 0;
817         else if (obt_xml_node_contains(n, "Nonpixel"))
818             config_resize_popup_show = 1;
819     }
820     if ((n = obt_xml_find_node(node, "popupPosition"))) {
821         if (obt_xml_node_contains(n, "Top"))
822             config_resize_popup_pos = OB_RESIZE_POS_TOP;
823         else if (obt_xml_node_contains(n, "Center"))
824             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
825         else if (obt_xml_node_contains(n, "Fixed")) {
826             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
827
828             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
829                 xmlNodePtr n2;
830
831                 if ((n2 = obt_xml_find_node(n->children, "x")))
832                     config_parse_gravity_coord(n2,
833                                                &config_resize_popup_fixed.x);
834                 if ((n2 = obt_xml_find_node(n->children, "y")))
835                     config_parse_gravity_coord(n2,
836                                                &config_resize_popup_fixed.y);
837
838                 config_resize_popup_fixed.x.pos =
839                     MAX(config_resize_popup_fixed.x.pos, 0);
840                 config_resize_popup_fixed.y.pos =
841                     MAX(config_resize_popup_fixed.y.pos, 0);
842             }
843         }
844     }
845 }
846
847 static void parse_dock(xmlNodePtr node, gpointer d)
848 {
849     xmlNodePtr n;
850
851     node = node->children;
852
853     if ((n = obt_xml_find_node(node, "position"))) {
854         if (obt_xml_node_contains(n, "TopLeft"))
855             config_dock_floating = FALSE,
856             config_dock_pos = OB_DIRECTION_NORTHWEST;
857         else if (obt_xml_node_contains(n, "Top"))
858             config_dock_floating = FALSE,
859             config_dock_pos = OB_DIRECTION_NORTH;
860         else if (obt_xml_node_contains(n, "TopRight"))
861             config_dock_floating = FALSE,
862             config_dock_pos = OB_DIRECTION_NORTHEAST;
863         else if (obt_xml_node_contains(n, "Right"))
864             config_dock_floating = FALSE,
865             config_dock_pos = OB_DIRECTION_EAST;
866         else if (obt_xml_node_contains(n, "BottomRight"))
867             config_dock_floating = FALSE,
868             config_dock_pos = OB_DIRECTION_SOUTHEAST;
869         else if (obt_xml_node_contains(n, "Bottom"))
870             config_dock_floating = FALSE,
871             config_dock_pos = OB_DIRECTION_SOUTH;
872         else if (obt_xml_node_contains(n, "BottomLeft"))
873             config_dock_floating = FALSE,
874             config_dock_pos = OB_DIRECTION_SOUTHWEST;
875         else if (obt_xml_node_contains(n, "Left"))
876             config_dock_floating = FALSE,
877             config_dock_pos = OB_DIRECTION_WEST;
878         else if (obt_xml_node_contains(n, "Floating"))
879             config_dock_floating = TRUE;
880     }
881     if (config_dock_floating) {
882         if ((n = obt_xml_find_node(node, "floatingX")))
883             config_dock_x = obt_xml_node_int(n);
884         if ((n = obt_xml_find_node(node, "floatingY")))
885             config_dock_y = obt_xml_node_int(n);
886     } else {
887         if ((n = obt_xml_find_node(node, "noStrut")))
888             config_dock_nostrut = obt_xml_node_bool(n);
889     }
890     if ((n = obt_xml_find_node(node, "stacking"))) {
891         if (obt_xml_node_contains(n, "normal"))
892             config_dock_layer = OB_STACKING_LAYER_NORMAL;
893         else if (obt_xml_node_contains(n, "below"))
894             config_dock_layer = OB_STACKING_LAYER_BELOW;
895         else if (obt_xml_node_contains(n, "above"))
896             config_dock_layer = OB_STACKING_LAYER_ABOVE;
897     }
898     if ((n = obt_xml_find_node(node, "direction"))) {
899         if (obt_xml_node_contains(n, "horizontal"))
900             config_dock_orient = OB_ORIENTATION_HORZ;
901         else if (obt_xml_node_contains(n, "vertical"))
902             config_dock_orient = OB_ORIENTATION_VERT;
903     }
904     if ((n = obt_xml_find_node(node, "autoHide")))
905         config_dock_hide = obt_xml_node_bool(n);
906     if ((n = obt_xml_find_node(node, "hideDelay")))
907         config_dock_hide_delay = obt_xml_node_int(n);
908     if ((n = obt_xml_find_node(node, "showDelay")))
909         config_dock_show_delay = obt_xml_node_int(n);
910     if ((n = obt_xml_find_node(node, "moveButton"))) {
911         gchar *str = obt_xml_node_string(n);
912         guint b, s;
913         if (translate_button(str, &s, &b)) {
914             config_dock_app_move_button = b;
915             config_dock_app_move_modifiers = s;
916         } else {
917             g_message(_("Invalid button \"%s\" specified in config file"), str);
918         }
919         g_free(str);
920     }
921 }
922
923 static void parse_menu(xmlNodePtr node, gpointer d)
924 {
925     xmlNodePtr n;
926     node = node->children;
927
928     if ((n = obt_xml_find_node(node, "hideDelay")))
929         config_menu_hide_delay = obt_xml_node_int(n);
930     if ((n = obt_xml_find_node(node, "middle")))
931         config_menu_middle = obt_xml_node_bool(n);
932     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
933         config_submenu_show_delay = obt_xml_node_int(n);
934     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
935         config_submenu_hide_delay = obt_xml_node_int(n);
936     if ((n = obt_xml_find_node(node, "manageDesktops")))
937         config_menu_manage_desktops = obt_xml_node_bool(n);
938     if ((n = obt_xml_find_node(node, "showIcons"))) {
939         config_menu_show_icons = obt_xml_node_bool(n);
940 #if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
941         if (config_menu_show_icons)
942             g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
943 #endif
944     }
945
946     while ((node = obt_xml_find_node(node, "file"))) {
947             gchar *c = obt_xml_node_string(node);
948             config_menu_files = g_slist_append(config_menu_files,
949                                                obt_paths_expand_tilde(c));
950             g_free(c);
951             node = node->next;
952     }
953 }
954
955 static void parse_resistance(xmlNodePtr node, gpointer d)
956 {
957     xmlNodePtr n;
958
959     node = node->children;
960     if ((n = obt_xml_find_node(node, "strength")))
961         config_resist_win = obt_xml_node_int(n);
962     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
963         config_resist_edge = obt_xml_node_int(n);
964 }
965
966 typedef struct
967 {
968     const gchar *key;
969     const gchar *actname;
970 } ObDefKeyBind;
971
972 static void bind_default_keyboard(void)
973 {
974     ObDefKeyBind *it;
975     ObDefKeyBind binds[] = {
976         { "A-Tab", "NextWindow" },
977         { "S-A-Tab", "PreviousWindow" },
978         { "A-F4", "Close" },
979         { NULL, NULL }
980     };
981     for (it = binds; it->key; ++it) {
982         GList *l = g_list_append(NULL, g_strdup(it->key));
983         keyboard_bind(l, actions_parse_string(it->actname));
984     }
985 }
986
987 typedef struct
988 {
989     const gchar *button;
990     const gchar *context;
991     const ObMouseAction mact;
992     const gchar *actname;
993 } ObDefMouseBind;
994
995 static void bind_default_mouse(void)
996 {
997     ObDefMouseBind *it;
998     ObDefMouseBind binds[] = {
999         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1000         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1001         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1002         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1003         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1004         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1005         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
1006         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
1007         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1008         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1009         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1010         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1011         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
1012         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
1013         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
1014         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
1015         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
1016         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
1017         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
1018         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
1019         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
1020         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1021         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1022         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1023         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1024         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
1025         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
1026         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
1027         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
1028         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
1029         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
1030         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
1031         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
1032         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
1033         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
1034         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
1035         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1036         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1037         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1038         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1039         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
1040         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
1041         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
1042         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
1043         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
1044         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
1045         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
1046         { NULL, NULL, 0, NULL }
1047     };
1048
1049     for (it = binds; it->button; ++it)
1050         mouse_bind(it->button, frame_context_from_string(it->context),
1051                    it->mact, actions_parse_string(it->actname));
1052 }
1053
1054 void config_startup(ObtXmlInst *i)
1055 {
1056     config_focus_new = TRUE;
1057     config_focus_follow = FALSE;
1058     config_focus_delay = 0;
1059     config_focus_raise = FALSE;
1060     config_focus_last = TRUE;
1061     config_focus_under_mouse = FALSE;
1062     config_unfocus_leave = FALSE;
1063
1064     obt_xml_register(i, "focus", parse_focus, NULL);
1065
1066     config_place_policy = OB_PLACE_POLICY_SMART;
1067     config_place_center = TRUE;
1068     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
1069
1070     config_primary_monitor_index = 1;
1071     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1072
1073     obt_xml_register(i, "placement", parse_placement, NULL);
1074
1075     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1076
1077     obt_xml_register(i, "margins", parse_margins, NULL);
1078
1079     config_theme = NULL;
1080
1081     config_animate_iconify = TRUE;
1082     config_title_layout = g_strdup("NLIMC");
1083     config_theme_keepborder = TRUE;
1084     config_theme_window_list_icon_size = 36;
1085
1086     config_font_activewindow = NULL;
1087     config_font_inactivewindow = NULL;
1088     config_font_menuitem = NULL;
1089     config_font_menutitle = NULL;
1090     config_font_activeosd = NULL;
1091     config_font_inactiveosd = NULL;
1092
1093     obt_xml_register(i, "theme", parse_theme, NULL);
1094
1095     config_desktops_num = 4;
1096     config_screen_firstdesk = 1;
1097     config_desktops_names = NULL;
1098     config_desktop_popup_time = 875;
1099
1100     obt_xml_register(i, "desktops", parse_desktops, NULL);
1101
1102     config_resize_redraw = TRUE;
1103     config_resize_popup_show = 1; /* nonpixel increments */
1104     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1105     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1106     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1107
1108     obt_xml_register(i, "resize", parse_resize, NULL);
1109
1110     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1111     config_dock_pos = OB_DIRECTION_NORTHEAST;
1112     config_dock_floating = FALSE;
1113     config_dock_nostrut = FALSE;
1114     config_dock_x = 0;
1115     config_dock_y = 0;
1116     config_dock_orient = OB_ORIENTATION_VERT;
1117     config_dock_hide = FALSE;
1118     config_dock_hide_delay = 300;
1119     config_dock_show_delay = 300;
1120     config_dock_app_move_button = 2; /* middle */
1121     config_dock_app_move_modifiers = 0;
1122
1123     obt_xml_register(i, "dock", parse_dock, NULL);
1124
1125     translate_key("C-g", &config_keyboard_reset_state,
1126                   &config_keyboard_reset_keycode);
1127     config_keyboard_rebind_on_mapping_notify = TRUE;
1128
1129     bind_default_keyboard();
1130
1131     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1132
1133     config_mouse_threshold = 8;
1134     config_mouse_dclicktime = 500;
1135     config_mouse_screenedgetime = 400;
1136     config_mouse_screenedgewarp = FALSE;
1137
1138     bind_default_mouse();
1139
1140     obt_xml_register(i, "mouse", parse_mouse, NULL);
1141
1142     config_resist_win = 10;
1143     config_resist_edge = 20;
1144
1145     obt_xml_register(i, "resistance", parse_resistance, NULL);
1146
1147     config_menu_hide_delay = 250;
1148     config_menu_middle = FALSE;
1149     config_submenu_show_delay = 100;
1150     config_submenu_hide_delay = 400;
1151     config_menu_manage_desktops = TRUE;
1152     config_menu_files = NULL;
1153     config_menu_show_icons = TRUE;
1154
1155     obt_xml_register(i, "menu", parse_menu, NULL);
1156
1157     config_per_app_settings = NULL;
1158
1159     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1160 }
1161
1162 void config_shutdown(void)
1163 {
1164     GSList *it;
1165
1166     g_free(config_theme);
1167
1168     g_free(config_title_layout);
1169
1170     RrFontClose(config_font_activewindow);
1171     RrFontClose(config_font_inactivewindow);
1172     RrFontClose(config_font_menuitem);
1173     RrFontClose(config_font_menutitle);
1174     RrFontClose(config_font_activeosd);
1175     RrFontClose(config_font_inactiveosd);
1176
1177     for (it = config_desktops_names; it; it = g_slist_next(it))
1178         g_free(it->data);
1179     g_slist_free(config_desktops_names);
1180
1181     for (it = config_menu_files; it; it = g_slist_next(it))
1182         g_free(it->data);
1183     g_slist_free(config_menu_files);
1184
1185     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1186         ObAppSettings *itd = (ObAppSettings *)it->data;
1187         if (itd->name) g_pattern_spec_free(itd->name);
1188         if (itd->role) g_pattern_spec_free(itd->role);
1189         if (itd->title) g_pattern_spec_free(itd->title);
1190         if (itd->class) g_pattern_spec_free(itd->class);
1191         if (itd->group_name) g_pattern_spec_free(itd->group_name);
1192         if (itd->group_class) g_pattern_spec_free(itd->group_class);
1193         g_slice_free(ObAppSettings, it->data);
1194     }
1195     g_slist_free(config_per_app_settings);
1196 }