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