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