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