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