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