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