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