Change some while loops to for loops in config.c
[dana/openbox.git] / openbox / config.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    config.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "config.h"
21 #include "keyboard.h"
22 #include "mouse.h"
23 #include "actions.h"
24 #include "translate.h"
25 #include "client.h"
26 #include "screen.h"
27 #include "openbox.h"
28 #include "gettext.h"
29 #include "obt/paths.h"
30
31 gboolean config_focus_new;
32 gboolean config_focus_follow;
33 guint    config_focus_delay;
34 gboolean config_focus_raise;
35 gboolean config_focus_last;
36 gboolean config_focus_under_mouse;
37 gboolean config_unfocus_leave;
38
39 ObPlacePolicy  config_place_policy;
40 gboolean       config_place_center;
41 ObPlaceMonitor config_place_monitor;
42
43 guint          config_primary_monitor_index;
44 ObPlaceMonitor config_primary_monitor;
45
46 StrutPartial config_margins;
47
48 gchar   *config_theme;
49 gboolean config_theme_keepborder;
50 guint    config_theme_window_list_icon_size;
51
52 gchar   *config_title_layout;
53
54 gboolean config_animate_iconify;
55
56 RrFont *config_font_activewindow;
57 RrFont *config_font_inactivewindow;
58 RrFont *config_font_menuitem;
59 RrFont *config_font_menutitle;
60 RrFont *config_font_activeosd;
61 RrFont *config_font_inactiveosd;
62
63 guint   config_desktops_num;
64 GSList *config_desktops_names;
65 guint   config_screen_firstdesk;
66 guint   config_desktop_popup_time;
67
68 gboolean         config_resize_redraw;
69 gint             config_resize_popup_show;
70 ObResizePopupPos config_resize_popup_pos;
71 GravityPoint     config_resize_popup_fixed;
72
73 ObStackingLayer config_dock_layer;
74 gboolean        config_dock_floating;
75 gboolean        config_dock_nostrut;
76 ObDirection     config_dock_pos;
77 gint            config_dock_x;
78 gint            config_dock_y;
79 ObOrientation   config_dock_orient;
80 gboolean        config_dock_hide;
81 guint           config_dock_hide_delay;
82 guint           config_dock_show_delay;
83 guint           config_dock_app_move_button;
84 guint           config_dock_app_move_modifiers;
85
86 guint    config_keyboard_reset_keycode;
87 guint    config_keyboard_reset_state;
88 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     for (n = obt_xml_find_node(node, "context");
548          n;
549          n = obt_xml_find_node(n->next, "context"))
550     {
551         gchar *modcxstr;
552         ObFrameContext cx;
553
554         if (!obt_xml_attr_string(n, "name", &cxstr))
555             continue;
556
557         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
558         while (frame_next_context_from_string(modcxstr, &cx)) {
559             if (!cx) {
560                 gchar *s = strchr(modcxstr, ' ');
561                 if (s) {
562                     *s = '\0';
563                     g_message(_("Invalid context \"%s\" in mouse binding"),
564                               modcxstr);
565                     *s = ' ';
566                 }
567                 continue;
568             }
569
570             for (nbut = obt_xml_find_node(n->children, "mousebind");
571                  nbut;
572                  nbut = obt_xml_find_node(nbut->next, "mousebind"))
573             {
574
575                 gchar **button, **buttons;
576
577                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
578                     continue;
579                 if (obt_xml_attr_contains(nbut, "action", "press"))
580                     mact = OB_MOUSE_ACTION_PRESS;
581                 else if (obt_xml_attr_contains(nbut, "action", "release"))
582                     mact = OB_MOUSE_ACTION_RELEASE;
583                 else if (obt_xml_attr_contains(nbut, "action", "click"))
584                     mact = OB_MOUSE_ACTION_CLICK;
585                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
586                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
587                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
588                     mact = OB_MOUSE_ACTION_MOTION;
589                 else
590                     continue;
591
592                 buttons = g_strsplit(buttonstr, " ", 0);
593                 for (nact = obt_xml_find_node(nbut->children, "action");
594                      nact;
595                      nact = obt_xml_find_node(nact->next, "action"))
596                 {
597                     ObActionsAct *action;
598
599                     /* actions_parse() creates one ref to the action, but we need
600                      * exactly one ref per binding we use it for. */
601                     if ((action = actions_parse(nact))) {
602                         for (button = buttons; *button; ++button) {
603                             actions_act_ref(action);
604                             mouse_bind(*button, cx, mact, action);
605                         }
606                         actions_act_unref(action);
607                     }
608                 }
609                 g_strfreev(buttons);
610                 g_free(buttonstr);
611             }
612         }
613         g_free(modcxstr);
614         g_free(cxstr);
615     }
616 }
617
618 static void parse_focus(xmlNodePtr node, gpointer d)
619 {
620     xmlNodePtr n;
621
622     node = node->children;
623
624     if ((n = obt_xml_find_node(node, "focusNew")))
625         config_focus_new = obt_xml_node_bool(n);
626     if ((n = obt_xml_find_node(node, "followMouse")))
627         config_focus_follow = obt_xml_node_bool(n);
628     if ((n = obt_xml_find_node(node, "focusDelay")))
629         config_focus_delay = obt_xml_node_int(n);
630     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
631         config_focus_raise = obt_xml_node_bool(n);
632     if ((n = obt_xml_find_node(node, "focusLast")))
633         config_focus_last = obt_xml_node_bool(n);
634     if ((n = obt_xml_find_node(node, "underMouse")))
635         config_focus_under_mouse = obt_xml_node_bool(n);
636     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
637         config_unfocus_leave = obt_xml_node_bool(n);
638 }
639
640 static void parse_placement(xmlNodePtr node, gpointer d)
641 {
642     xmlNodePtr n;
643
644     node = node->children;
645
646     if ((n = obt_xml_find_node(node, "policy"))) {
647         if (obt_xml_node_contains(n, "UnderMouse"))
648             config_place_policy = OB_PLACE_POLICY_MOUSE;
649     }
650     if ((n = obt_xml_find_node(node, "center"))) {
651         config_place_center = obt_xml_node_bool(n);
652     }
653     if ((n = obt_xml_find_node(node, "monitor"))) {
654         if (obt_xml_node_contains(n, "active"))
655             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
656         else if (obt_xml_node_contains(n, "mouse"))
657             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
658         else if (obt_xml_node_contains(n, "any"))
659             config_place_monitor = OB_PLACE_MONITOR_ANY;
660     }
661     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
662         config_primary_monitor_index = obt_xml_node_int(n);
663         if (!config_primary_monitor_index) {
664             if (obt_xml_node_contains(n, "mouse"))
665                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
666         }
667     }
668 }
669
670 static void parse_margins(xmlNodePtr node, gpointer d)
671 {
672     xmlNodePtr n;
673
674     node = node->children;
675
676     if ((n = obt_xml_find_node(node, "top")))
677         config_margins.top = MAX(0, obt_xml_node_int(n));
678     if ((n = obt_xml_find_node(node, "left")))
679         config_margins.left = MAX(0, obt_xml_node_int(n));
680     if ((n = obt_xml_find_node(node, "right")))
681         config_margins.right = MAX(0, obt_xml_node_int(n));
682     if ((n = obt_xml_find_node(node, "bottom")))
683         config_margins.bottom = MAX(0, obt_xml_node_int(n));
684 }
685
686 static void parse_theme(xmlNodePtr node, gpointer d)
687 {
688     xmlNodePtr n;
689
690     node = node->children;
691
692     if ((n = obt_xml_find_node(node, "name"))) {
693         gchar *c;
694
695         g_free(config_theme);
696         c = obt_xml_node_string(n);
697         config_theme = obt_paths_expand_tilde(c);
698         g_free(c);
699     }
700     if ((n = obt_xml_find_node(node, "titleLayout"))) {
701         gchar *c, *d;
702
703         g_free(config_title_layout);
704         config_title_layout = obt_xml_node_string(n);
705
706         /* replace duplicates with spaces */
707         for (c = config_title_layout; *c != '\0'; ++c)
708             for (d = c+1; *d != '\0'; ++d)
709                 if (*c == *d) *d = ' ';
710     }
711     if ((n = obt_xml_find_node(node, "keepBorder")))
712         config_theme_keepborder = obt_xml_node_bool(n);
713     if ((n = obt_xml_find_node(node, "animateIconify")))
714         config_animate_iconify = obt_xml_node_bool(n);
715     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
716         config_theme_window_list_icon_size = obt_xml_node_int(n);
717         if (config_theme_window_list_icon_size < 16)
718             config_theme_window_list_icon_size = 16;
719         else if (config_theme_window_list_icon_size > 96)
720             config_theme_window_list_icon_size = 96;
721     }
722
723     for (n = obt_xml_find_node(node, "font");
724          n;
725          n = obt_xml_find_node(n->next, "font"))
726     {
727         xmlNodePtr   fnode;
728         RrFont     **font;
729         gchar       *name = g_strdup(RrDefaultFontFamily);
730         gint         size = RrDefaultFontSize;
731         RrFontWeight weight = RrDefaultFontWeight;
732         RrFontSlant  slant = RrDefaultFontSlant;
733
734         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
735             font = &config_font_activewindow;
736         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
737             font = &config_font_inactivewindow;
738         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
739             font = &config_font_menutitle;
740         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
741             font = &config_font_menuitem;
742         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
743             font = &config_font_activeosd;
744         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
745             font = &config_font_activeosd;
746         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
747             font = &config_font_inactiveosd;
748         else
749             continue;
750
751         if ((fnode = obt_xml_find_node(n->children, "name"))) {
752             g_free(name);
753             name = obt_xml_node_string(fnode);
754         }
755         if ((fnode = obt_xml_find_node(n->children, "size"))) {
756             int s = obt_xml_node_int(fnode);
757             if (s > 0) size = s;
758         }
759         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
760             gchar *w = obt_xml_node_string(fnode);
761             if (!g_ascii_strcasecmp(w, "Bold"))
762                 weight = RR_FONTWEIGHT_BOLD;
763             g_free(w);
764         }
765         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
766             gchar *s = obt_xml_node_string(fnode);
767             if (!g_ascii_strcasecmp(s, "Italic"))
768                 slant = RR_FONTSLANT_ITALIC;
769             if (!g_ascii_strcasecmp(s, "Oblique"))
770                 slant = RR_FONTSLANT_OBLIQUE;
771             g_free(s);
772         }
773
774         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
775         g_free(name);
776     }
777 }
778
779 static void parse_desktops(xmlNodePtr node, gpointer d)
780 {
781     xmlNodePtr n;
782
783     node = node->children;
784
785     if ((n = obt_xml_find_node(node, "number"))) {
786         gint d = obt_xml_node_int(n);
787         if (d > 0)
788             config_desktops_num = (unsigned) d;
789     }
790     if ((n = obt_xml_find_node(node, "firstdesk"))) {
791         gint d = obt_xml_node_int(n);
792         if (d > 0)
793             config_screen_firstdesk = (unsigned) d;
794     }
795     if ((n = obt_xml_find_node(node, "names"))) {
796         GSList *it;
797         xmlNodePtr nname;
798
799         for (it = config_desktops_names; it; it = it->next)
800             g_free(it->data);
801         g_slist_free(config_desktops_names);
802         config_desktops_names = NULL;
803
804         for (nname = obt_xml_find_node(n->children, "name");
805              nname;
806              nname = obt_xml_find_node(nname->next, "name"))
807         {
808             config_desktops_names =
809                 g_slist_append(config_desktops_names,
810                                obt_xml_node_string(nname));
811         }
812     }
813     if ((n = obt_xml_find_node(node, "popupTime")))
814         config_desktop_popup_time = obt_xml_node_int(n);
815 }
816
817 static void parse_resize(xmlNodePtr node, gpointer d)
818 {
819     xmlNodePtr n;
820
821     node = node->children;
822
823     if ((n = obt_xml_find_node(node, "drawContents")))
824         config_resize_redraw = obt_xml_node_bool(n);
825     if ((n = obt_xml_find_node(node, "popupShow"))) {
826         config_resize_popup_show = obt_xml_node_int(n);
827         if (obt_xml_node_contains(n, "Always"))
828             config_resize_popup_show = 2;
829         else if (obt_xml_node_contains(n, "Never"))
830             config_resize_popup_show = 0;
831         else if (obt_xml_node_contains(n, "Nonpixel"))
832             config_resize_popup_show = 1;
833     }
834     if ((n = obt_xml_find_node(node, "popupPosition"))) {
835         if (obt_xml_node_contains(n, "Top"))
836             config_resize_popup_pos = OB_RESIZE_POS_TOP;
837         else if (obt_xml_node_contains(n, "Center"))
838             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
839         else if (obt_xml_node_contains(n, "Fixed")) {
840             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
841
842             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
843                 xmlNodePtr n2;
844
845                 if ((n2 = obt_xml_find_node(n->children, "x")))
846                     config_parse_gravity_coord(n2,
847                                                &config_resize_popup_fixed.x);
848                 if ((n2 = obt_xml_find_node(n->children, "y")))
849                     config_parse_gravity_coord(n2,
850                                                &config_resize_popup_fixed.y);
851
852                 config_resize_popup_fixed.x.pos =
853                     MAX(config_resize_popup_fixed.x.pos, 0);
854                 config_resize_popup_fixed.y.pos =
855                     MAX(config_resize_popup_fixed.y.pos, 0);
856             }
857         }
858     }
859 }
860
861 static void parse_dock(xmlNodePtr node, gpointer d)
862 {
863     xmlNodePtr n;
864
865     node = node->children;
866
867     if ((n = obt_xml_find_node(node, "position"))) {
868         if (obt_xml_node_contains(n, "TopLeft"))
869             config_dock_floating = FALSE,
870             config_dock_pos = OB_DIRECTION_NORTHWEST;
871         else if (obt_xml_node_contains(n, "Top"))
872             config_dock_floating = FALSE,
873             config_dock_pos = OB_DIRECTION_NORTH;
874         else if (obt_xml_node_contains(n, "TopRight"))
875             config_dock_floating = FALSE,
876             config_dock_pos = OB_DIRECTION_NORTHEAST;
877         else if (obt_xml_node_contains(n, "Right"))
878             config_dock_floating = FALSE,
879             config_dock_pos = OB_DIRECTION_EAST;
880         else if (obt_xml_node_contains(n, "BottomRight"))
881             config_dock_floating = FALSE,
882             config_dock_pos = OB_DIRECTION_SOUTHEAST;
883         else if (obt_xml_node_contains(n, "Bottom"))
884             config_dock_floating = FALSE,
885             config_dock_pos = OB_DIRECTION_SOUTH;
886         else if (obt_xml_node_contains(n, "BottomLeft"))
887             config_dock_floating = FALSE,
888             config_dock_pos = OB_DIRECTION_SOUTHWEST;
889         else if (obt_xml_node_contains(n, "Left"))
890             config_dock_floating = FALSE,
891             config_dock_pos = OB_DIRECTION_WEST;
892         else if (obt_xml_node_contains(n, "Floating"))
893             config_dock_floating = TRUE;
894     }
895     if (config_dock_floating) {
896         if ((n = obt_xml_find_node(node, "floatingX")))
897             config_dock_x = obt_xml_node_int(n);
898         if ((n = obt_xml_find_node(node, "floatingY")))
899             config_dock_y = obt_xml_node_int(n);
900     } else {
901         if ((n = obt_xml_find_node(node, "noStrut")))
902             config_dock_nostrut = obt_xml_node_bool(n);
903     }
904     if ((n = obt_xml_find_node(node, "stacking"))) {
905         if (obt_xml_node_contains(n, "normal"))
906             config_dock_layer = OB_STACKING_LAYER_NORMAL;
907         else if (obt_xml_node_contains(n, "below"))
908             config_dock_layer = OB_STACKING_LAYER_BELOW;
909         else if (obt_xml_node_contains(n, "above"))
910             config_dock_layer = OB_STACKING_LAYER_ABOVE;
911     }
912     if ((n = obt_xml_find_node(node, "direction"))) {
913         if (obt_xml_node_contains(n, "horizontal"))
914             config_dock_orient = OB_ORIENTATION_HORZ;
915         else if (obt_xml_node_contains(n, "vertical"))
916             config_dock_orient = OB_ORIENTATION_VERT;
917     }
918     if ((n = obt_xml_find_node(node, "autoHide")))
919         config_dock_hide = obt_xml_node_bool(n);
920     if ((n = obt_xml_find_node(node, "hideDelay")))
921         config_dock_hide_delay = obt_xml_node_int(n);
922     if ((n = obt_xml_find_node(node, "showDelay")))
923         config_dock_show_delay = obt_xml_node_int(n);
924     if ((n = obt_xml_find_node(node, "moveButton"))) {
925         gchar *str = obt_xml_node_string(n);
926         guint b, s;
927         if (translate_button(str, &s, &b)) {
928             config_dock_app_move_button = b;
929             config_dock_app_move_modifiers = s;
930         } else {
931             g_message(_("Invalid button \"%s\" specified in config file"), str);
932         }
933         g_free(str);
934     }
935 }
936
937 static void parse_menu(xmlNodePtr node, gpointer d)
938 {
939     xmlNodePtr n;
940     node = node->children;
941
942     if ((n = obt_xml_find_node(node, "hideDelay")))
943         config_menu_hide_delay = obt_xml_node_int(n);
944     if ((n = obt_xml_find_node(node, "middle")))
945         config_menu_middle = obt_xml_node_bool(n);
946     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
947         config_submenu_show_delay = obt_xml_node_int(n);
948     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
949         config_submenu_hide_delay = obt_xml_node_int(n);
950     if ((n = obt_xml_find_node(node, "manageDesktops")))
951         config_menu_manage_desktops = obt_xml_node_bool(n);
952     if ((n = obt_xml_find_node(node, "showIcons"))) {
953         config_menu_show_icons = obt_xml_node_bool(n);
954 #if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
955         if (config_menu_show_icons)
956             g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
957 #endif
958     }
959
960     for (node = obt_xml_find_node(node, "file");
961          node;
962          node = obt_xml_find_node(node->next, "file"))
963     {
964             gchar *c = obt_xml_node_string(node);
965             config_menu_files = g_slist_append(config_menu_files,
966                                                obt_paths_expand_tilde(c));
967             g_free(c);
968     }
969 }
970
971 static void parse_resistance(xmlNodePtr node, gpointer d)
972 {
973     xmlNodePtr n;
974
975     node = node->children;
976     if ((n = obt_xml_find_node(node, "strength")))
977         config_resist_win = obt_xml_node_int(n);
978     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
979         config_resist_edge = obt_xml_node_int(n);
980 }
981
982 typedef struct
983 {
984     const gchar *key;
985     const gchar *actname;
986 } ObDefKeyBind;
987
988 static void bind_default_keyboard(void)
989 {
990     ObDefKeyBind *it;
991     ObDefKeyBind binds[] = {
992         { "A-Tab", "NextWindow" },
993         { "S-A-Tab", "PreviousWindow" },
994         { "A-F4", "Close" },
995         { NULL, NULL }
996     };
997     for (it = binds; it->key; ++it) {
998         GList *l = g_list_append(NULL, g_strdup(it->key));
999         keyboard_bind(l, actions_parse_string(it->actname));
1000     }
1001 }
1002
1003 typedef struct
1004 {
1005     const gchar *button;
1006     const gchar *context;
1007     const ObMouseAction mact;
1008     const gchar *actname;
1009 } ObDefMouseBind;
1010
1011 static void bind_default_mouse(void)
1012 {
1013     ObDefMouseBind *it;
1014     ObDefMouseBind binds[] = {
1015         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1016         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1017         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1018         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1019         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1020         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1021         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
1022         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
1023         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1024         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1025         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1026         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1027         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
1028         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
1029         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
1030         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
1031         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
1032         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
1033         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
1034         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
1035         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
1036         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1037         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1038         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1039         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1040         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
1041         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
1042         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
1043         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
1044         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
1045         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
1046         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
1047         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
1048         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
1049         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
1050         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
1051         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1052         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1053         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1054         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1055         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
1056         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
1057         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
1058         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
1059         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
1060         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
1061         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
1062         { NULL, NULL, 0, NULL }
1063     };
1064
1065     for (it = binds; it->button; ++it)
1066         mouse_bind(it->button, frame_context_from_string(it->context),
1067                    it->mact, actions_parse_string(it->actname));
1068 }
1069
1070 void config_startup(ObtXmlInst *i)
1071 {
1072     config_focus_new = TRUE;
1073     config_focus_follow = FALSE;
1074     config_focus_delay = 0;
1075     config_focus_raise = FALSE;
1076     config_focus_last = TRUE;
1077     config_focus_under_mouse = FALSE;
1078     config_unfocus_leave = FALSE;
1079
1080     obt_xml_register(i, "focus", parse_focus, NULL);
1081
1082     config_place_policy = OB_PLACE_POLICY_SMART;
1083     config_place_center = TRUE;
1084     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
1085
1086     config_primary_monitor_index = 1;
1087     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1088
1089     obt_xml_register(i, "placement", parse_placement, NULL);
1090
1091     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1092
1093     obt_xml_register(i, "margins", parse_margins, NULL);
1094
1095     config_theme = NULL;
1096
1097     config_animate_iconify = TRUE;
1098     config_title_layout = g_strdup("NLIMC");
1099     config_theme_keepborder = TRUE;
1100     config_theme_window_list_icon_size = 36;
1101
1102     config_font_activewindow = NULL;
1103     config_font_inactivewindow = NULL;
1104     config_font_menuitem = NULL;
1105     config_font_menutitle = NULL;
1106     config_font_activeosd = NULL;
1107     config_font_inactiveosd = NULL;
1108
1109     obt_xml_register(i, "theme", parse_theme, NULL);
1110
1111     config_desktops_num = 4;
1112     config_screen_firstdesk = 1;
1113     config_desktops_names = NULL;
1114     config_desktop_popup_time = 875;
1115
1116     obt_xml_register(i, "desktops", parse_desktops, NULL);
1117
1118     config_resize_redraw = TRUE;
1119     config_resize_popup_show = 1; /* nonpixel increments */
1120     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1121     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1122     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1123
1124     obt_xml_register(i, "resize", parse_resize, NULL);
1125
1126     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1127     config_dock_pos = OB_DIRECTION_NORTHEAST;
1128     config_dock_floating = FALSE;
1129     config_dock_nostrut = FALSE;
1130     config_dock_x = 0;
1131     config_dock_y = 0;
1132     config_dock_orient = OB_ORIENTATION_VERT;
1133     config_dock_hide = FALSE;
1134     config_dock_hide_delay = 300;
1135     config_dock_show_delay = 300;
1136     config_dock_app_move_button = 2; /* middle */
1137     config_dock_app_move_modifiers = 0;
1138
1139     obt_xml_register(i, "dock", parse_dock, NULL);
1140
1141     translate_key("C-g", &config_keyboard_reset_state,
1142                   &config_keyboard_reset_keycode);
1143     config_keyboard_rebind_on_mapping_notify = TRUE;
1144
1145     bind_default_keyboard();
1146
1147     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1148
1149     config_mouse_threshold = 8;
1150     config_mouse_dclicktime = 500;
1151     config_mouse_screenedgetime = 400;
1152     config_mouse_screenedgewarp = FALSE;
1153
1154     bind_default_mouse();
1155
1156     obt_xml_register(i, "mouse", parse_mouse, NULL);
1157
1158     config_resist_win = 10;
1159     config_resist_edge = 20;
1160
1161     obt_xml_register(i, "resistance", parse_resistance, NULL);
1162
1163     config_menu_hide_delay = 250;
1164     config_menu_middle = FALSE;
1165     config_submenu_show_delay = 100;
1166     config_submenu_hide_delay = 400;
1167     config_menu_manage_desktops = TRUE;
1168     config_menu_files = NULL;
1169     config_menu_show_icons = TRUE;
1170
1171     obt_xml_register(i, "menu", parse_menu, NULL);
1172
1173     config_per_app_settings = NULL;
1174
1175     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1176 }
1177
1178 void config_shutdown(void)
1179 {
1180     GSList *it;
1181
1182     g_free(config_theme);
1183
1184     g_free(config_title_layout);
1185
1186     RrFontClose(config_font_activewindow);
1187     RrFontClose(config_font_inactivewindow);
1188     RrFontClose(config_font_menuitem);
1189     RrFontClose(config_font_menutitle);
1190     RrFontClose(config_font_activeosd);
1191     RrFontClose(config_font_inactiveosd);
1192
1193     for (it = config_desktops_names; it; it = g_slist_next(it))
1194         g_free(it->data);
1195     g_slist_free(config_desktops_names);
1196
1197     for (it = config_menu_files; it; it = g_slist_next(it))
1198         g_free(it->data);
1199     g_slist_free(config_menu_files);
1200
1201     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1202         ObAppSettings *itd = (ObAppSettings *)it->data;
1203         if (itd->name) g_pattern_spec_free(itd->name);
1204         if (itd->role) g_pattern_spec_free(itd->role);
1205         if (itd->title) g_pattern_spec_free(itd->title);
1206         if (itd->class) g_pattern_spec_free(itd->class);
1207         if (itd->group_name) g_pattern_spec_free(itd->group_name);
1208         if (itd->group_class) g_pattern_spec_free(itd->group_class);
1209         g_slice_free(ObAppSettings, it->data);
1210     }
1211     g_slist_free(config_per_app_settings);
1212 }