Use >= instead of > when comparing timestamps, or we will ignore some events
[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 *keystring, **keys, **key;
393     xmlNodePtr n;
394     gboolean is_chroot = FALSE;
395
396     if (!obt_xml_attr_string(node, "key", &keystring))
397         return;
398
399     obt_xml_attr_bool(node, "chroot", &is_chroot);
400
401     keys = g_strsplit(keystring, " ", 0);
402     for (key = keys; *key; ++key) {
403         keylist = g_list_append(keylist, *key);
404
405         if ((n = obt_xml_find_node(node->children, "keybind"))) {
406             while (n) {
407                 parse_key(n, keylist);
408                 n = obt_xml_find_node(n->next, "keybind");
409             }
410         }
411         else if ((n = obt_xml_find_node(node->children, "action"))) {
412             while (n) {
413                 ObActionsAct *action;
414
415                 action = actions_parse(n);
416                 if (action)
417                     keyboard_bind(keylist, action);
418                 n = obt_xml_find_node(n->next, "action");
419             }
420         }
421
422
423         if (is_chroot)
424             keyboard_chroot(keylist);
425         keylist = g_list_delete_link(keylist, g_list_last(keylist));
426     }
427
428     g_strfreev(keys);
429     g_free(keystring);
430 }
431
432 static void parse_keyboard(xmlNodePtr node, gpointer d)
433 {
434     xmlNodePtr n;
435     gchar *key;
436
437     keyboard_unbind_all();
438
439     if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) {
440         key = obt_xml_node_string(n);
441         translate_key(key, &config_keyboard_reset_state,
442                       &config_keyboard_reset_keycode);
443         g_free(key);
444     }
445
446     if ((n = obt_xml_find_node(node->children, "keybind")))
447         while (n) {
448             parse_key(n, NULL);
449             n = obt_xml_find_node(n->next, "keybind");
450         }
451 }
452
453 /*
454
455 <context name="Titlebar">
456   <mousebind button="Left" action="Press">
457     <action name="Raise"></action>
458   </mousebind>
459 </context>
460
461 */
462
463 static void parse_mouse(xmlNodePtr node, gpointer d)
464 {
465     xmlNodePtr n, nbut, nact;
466     gchar *buttonstr;
467     gchar *cxstr;
468     ObMouseAction mact;
469
470     mouse_unbind_all();
471
472     node = node->children;
473
474     if ((n = obt_xml_find_node(node, "dragThreshold")))
475         config_mouse_threshold = obt_xml_node_int(n);
476     if ((n = obt_xml_find_node(node, "doubleClickTime")))
477         config_mouse_dclicktime = obt_xml_node_int(n);
478     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
479         config_mouse_screenedgetime = obt_xml_node_int(n);
480         /* minimum value of 25 for this property, when it is 1 and you hit the
481            edge it basically never stops */
482         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
483             config_mouse_screenedgetime = 25;
484     }
485     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
486         config_mouse_screenedgewarp = obt_xml_node_bool(n);
487
488     n = obt_xml_find_node(node, "context");
489     while (n) {
490         gchar *modcxstr;
491         ObFrameContext cx;
492
493         if (!obt_xml_attr_string(n, "name", &cxstr))
494             goto next_n;
495
496         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
497         while (frame_next_context_from_string(modcxstr, &cx)) {
498             if (!cx) {
499                 gchar *s = strchr(modcxstr, ' ');
500                 if (s) {
501                     *s = '\0';
502                     g_message(_("Invalid context \"%s\" in mouse binding"),
503                               modcxstr);
504                     *s = ' ';
505                 }
506                 continue;
507             }
508
509             nbut = obt_xml_find_node(n->children, "mousebind");
510             while (nbut) {
511                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
512                     goto next_nbut;
513                 if (obt_xml_attr_contains(nbut, "action", "press"))
514                     mact = OB_MOUSE_ACTION_PRESS;
515                 else if (obt_xml_attr_contains(nbut, "action", "release"))
516                     mact = OB_MOUSE_ACTION_RELEASE;
517                 else if (obt_xml_attr_contains(nbut, "action", "click"))
518                     mact = OB_MOUSE_ACTION_CLICK;
519                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
520                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
521                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
522                     mact = OB_MOUSE_ACTION_MOTION;
523                 else
524                     goto next_nbut;
525
526                 nact = obt_xml_find_node(nbut->children, "action");
527                 while (nact) {
528                     ObActionsAct *action;
529
530                     if ((action = actions_parse(nact)))
531                         mouse_bind(buttonstr, cx, mact, action);
532                     nact = obt_xml_find_node(nact->next, "action");
533                 }
534             g_free(buttonstr);
535             next_nbut:
536             nbut = obt_xml_find_node(nbut->next, "mousebind");
537             }
538         }
539         g_free(modcxstr);
540         g_free(cxstr);
541     next_n:
542         n = obt_xml_find_node(n->next, "context");
543     }
544 }
545
546 static void parse_focus(xmlNodePtr node, gpointer d)
547 {
548     xmlNodePtr n;
549
550     node = node->children;
551
552     if ((n = obt_xml_find_node(node, "focusNew")))
553         config_focus_new = obt_xml_node_bool(n);
554     if ((n = obt_xml_find_node(node, "followMouse")))
555         config_focus_follow = obt_xml_node_bool(n);
556     if ((n = obt_xml_find_node(node, "focusDelay")))
557         config_focus_delay = obt_xml_node_int(n);
558     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
559         config_focus_raise = obt_xml_node_bool(n);
560     if ((n = obt_xml_find_node(node, "focusLast")))
561         config_focus_last = obt_xml_node_bool(n);
562     if ((n = obt_xml_find_node(node, "underMouse")))
563         config_focus_under_mouse = obt_xml_node_bool(n);
564     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
565         config_unfocus_leave = obt_xml_node_bool(n);
566 }
567
568 static void parse_placement(xmlNodePtr node, gpointer d)
569 {
570     xmlNodePtr n;
571
572     node = node->children;
573
574     if ((n = obt_xml_find_node(node, "policy")))
575         if (obt_xml_node_contains(n, "UnderMouse"))
576             config_place_policy = OB_PLACE_POLICY_MOUSE;
577     if ((n = obt_xml_find_node(node, "center")))
578         config_place_center = obt_xml_node_bool(n);
579     if ((n = obt_xml_find_node(node, "monitor"))) {
580         if (obt_xml_node_contains(n, "active"))
581             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
582         else if (obt_xml_node_contains(n, "mouse"))
583             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
584     }
585     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
586         config_primary_monitor_index = obt_xml_node_int(n);
587         if (!config_primary_monitor_index) {
588             if (obt_xml_node_contains(n, "mouse"))
589                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
590         }
591     }
592 }
593
594 static void parse_margins(xmlNodePtr node, gpointer d)
595 {
596     xmlNodePtr n;
597
598     node = node->children;
599
600     if ((n = obt_xml_find_node(node, "top")))
601         config_margins.top = MAX(0, obt_xml_node_int(n));
602     if ((n = obt_xml_find_node(node, "left")))
603         config_margins.left = MAX(0, obt_xml_node_int(n));
604     if ((n = obt_xml_find_node(node, "right")))
605         config_margins.right = MAX(0, obt_xml_node_int(n));
606     if ((n = obt_xml_find_node(node, "bottom")))
607         config_margins.bottom = MAX(0, obt_xml_node_int(n));
608 }
609
610 static void parse_theme(xmlNodePtr node, gpointer d)
611 {
612     xmlNodePtr n;
613
614     node = node->children;
615
616     if ((n = obt_xml_find_node(node, "name"))) {
617         gchar *c;
618
619         g_free(config_theme);
620         c = obt_xml_node_string(n);
621         config_theme = obt_paths_expand_tilde(c);
622         g_free(c);
623     }
624     if ((n = obt_xml_find_node(node, "titleLayout"))) {
625         gchar *c, *d;
626
627         g_free(config_title_layout);
628         config_title_layout = obt_xml_node_string(n);
629
630         /* replace duplicates with spaces */
631         for (c = config_title_layout; *c != '\0'; ++c)
632             for (d = c+1; *d != '\0'; ++d)
633                 if (*c == *d) *d = ' ';
634     }
635     if ((n = obt_xml_find_node(node, "keepBorder")))
636         config_theme_keepborder = obt_xml_node_bool(n);
637     if ((n = obt_xml_find_node(node, "animateIconify")))
638         config_animate_iconify = obt_xml_node_bool(n);
639     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
640         config_theme_window_list_icon_size = obt_xml_node_int(n);
641         if (config_theme_window_list_icon_size < 16)
642             config_theme_window_list_icon_size = 16;
643         else if (config_theme_window_list_icon_size > 96)
644             config_theme_window_list_icon_size = 96;
645     }
646
647     n = obt_xml_find_node(node, "font");
648     while (n) {
649         xmlNodePtr   fnode;
650         RrFont     **font;
651         gchar       *name = g_strdup(RrDefaultFontFamily);
652         gint         size = RrDefaultFontSize;
653         RrFontWeight weight = RrDefaultFontWeight;
654         RrFontSlant  slant = RrDefaultFontSlant;
655
656         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
657             font = &config_font_activewindow;
658         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
659             font = &config_font_inactivewindow;
660         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
661             font = &config_font_menutitle;
662         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
663             font = &config_font_menuitem;
664         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
665             font = &config_font_activeosd;
666         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
667             font = &config_font_activeosd;
668         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
669             font = &config_font_inactiveosd;
670         else
671             goto next_font;
672
673         if ((fnode = obt_xml_find_node(n->children, "name"))) {
674             g_free(name);
675             name = obt_xml_node_string(fnode);
676         }
677         if ((fnode = obt_xml_find_node(n->children, "size"))) {
678             int s = obt_xml_node_int(fnode);
679             if (s > 0) size = s;
680         }
681         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
682             gchar *w = obt_xml_node_string(fnode);
683             if (!g_ascii_strcasecmp(w, "Bold"))
684                 weight = RR_FONTWEIGHT_BOLD;
685             g_free(w);
686         }
687         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
688             gchar *s = obt_xml_node_string(fnode);
689             if (!g_ascii_strcasecmp(s, "Italic"))
690                 slant = RR_FONTSLANT_ITALIC;
691             if (!g_ascii_strcasecmp(s, "Oblique"))
692                 slant = RR_FONTSLANT_OBLIQUE;
693             g_free(s);
694         }
695
696         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
697         g_free(name);
698     next_font:
699         n = obt_xml_find_node(n->next, "font");
700     }
701 }
702
703 static void parse_desktops(xmlNodePtr node, gpointer d)
704 {
705     xmlNodePtr n;
706
707     node = node->children;
708
709     if ((n = obt_xml_find_node(node, "number"))) {
710         gint d = obt_xml_node_int(n);
711         if (d > 0)
712             config_desktops_num = (unsigned) d;
713     }
714     if ((n = obt_xml_find_node(node, "firstdesk"))) {
715         gint d = obt_xml_node_int(n);
716         if (d > 0)
717             config_screen_firstdesk = (unsigned) d;
718     }
719     if ((n = obt_xml_find_node(node, "names"))) {
720         GSList *it;
721         xmlNodePtr nname;
722
723         for (it = config_desktops_names; it; it = it->next)
724             g_free(it->data);
725         g_slist_free(config_desktops_names);
726         config_desktops_names = NULL;
727
728         nname = obt_xml_find_node(n->children, "name");
729         while (nname) {
730             config_desktops_names =
731                 g_slist_append(config_desktops_names,
732                                obt_xml_node_string(nname));
733             nname = obt_xml_find_node(nname->next, "name");
734         }
735     }
736     if ((n = obt_xml_find_node(node, "popupTime")))
737         config_desktop_popup_time = obt_xml_node_int(n);
738 }
739
740 static void parse_resize(xmlNodePtr node, gpointer d)
741 {
742     xmlNodePtr n;
743
744     node = node->children;
745
746     if ((n = obt_xml_find_node(node, "drawContents")))
747         config_resize_redraw = obt_xml_node_bool(n);
748     if ((n = obt_xml_find_node(node, "popupShow"))) {
749         config_resize_popup_show = obt_xml_node_int(n);
750         if (obt_xml_node_contains(n, "Always"))
751             config_resize_popup_show = 2;
752         else if (obt_xml_node_contains(n, "Never"))
753             config_resize_popup_show = 0;
754         else if (obt_xml_node_contains(n, "Nonpixel"))
755             config_resize_popup_show = 1;
756     }
757     if ((n = obt_xml_find_node(node, "popupPosition"))) {
758         if (obt_xml_node_contains(n, "Top"))
759             config_resize_popup_pos = OB_RESIZE_POS_TOP;
760         else if (obt_xml_node_contains(n, "Center"))
761             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
762         else if (obt_xml_node_contains(n, "Fixed")) {
763             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
764
765             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
766                 xmlNodePtr n2;
767
768                 if ((n2 = obt_xml_find_node(n->children, "x")))
769                     config_parse_gravity_coord(n2,
770                                                &config_resize_popup_fixed.x);
771                 if ((n2 = obt_xml_find_node(n->children, "y")))
772                     config_parse_gravity_coord(n2,
773                                                &config_resize_popup_fixed.y);
774
775                 config_resize_popup_fixed.x.pos =
776                     MAX(config_resize_popup_fixed.x.pos, 0);
777                 config_resize_popup_fixed.y.pos =
778                     MAX(config_resize_popup_fixed.y.pos, 0);
779             }
780         }
781     }
782 }
783
784 static void parse_dock(xmlNodePtr node, gpointer d)
785 {
786     xmlNodePtr n;
787
788     node = node->children;
789
790     if ((n = obt_xml_find_node(node, "position"))) {
791         if (obt_xml_node_contains(n, "TopLeft"))
792             config_dock_floating = FALSE,
793             config_dock_pos = OB_DIRECTION_NORTHWEST;
794         else if (obt_xml_node_contains(n, "Top"))
795             config_dock_floating = FALSE,
796             config_dock_pos = OB_DIRECTION_NORTH;
797         else if (obt_xml_node_contains(n, "TopRight"))
798             config_dock_floating = FALSE,
799             config_dock_pos = OB_DIRECTION_NORTHEAST;
800         else if (obt_xml_node_contains(n, "Right"))
801             config_dock_floating = FALSE,
802             config_dock_pos = OB_DIRECTION_EAST;
803         else if (obt_xml_node_contains(n, "BottomRight"))
804             config_dock_floating = FALSE,
805             config_dock_pos = OB_DIRECTION_SOUTHEAST;
806         else if (obt_xml_node_contains(n, "Bottom"))
807             config_dock_floating = FALSE,
808             config_dock_pos = OB_DIRECTION_SOUTH;
809         else if (obt_xml_node_contains(n, "BottomLeft"))
810             config_dock_floating = FALSE,
811             config_dock_pos = OB_DIRECTION_SOUTHWEST;
812         else if (obt_xml_node_contains(n, "Left"))
813             config_dock_floating = FALSE,
814             config_dock_pos = OB_DIRECTION_WEST;
815         else if (obt_xml_node_contains(n, "Floating"))
816             config_dock_floating = TRUE;
817     }
818     if (config_dock_floating) {
819         if ((n = obt_xml_find_node(node, "floatingX")))
820             config_dock_x = obt_xml_node_int(n);
821         if ((n = obt_xml_find_node(node, "floatingY")))
822             config_dock_y = obt_xml_node_int(n);
823     } else {
824         if ((n = obt_xml_find_node(node, "noStrut")))
825             config_dock_nostrut = obt_xml_node_bool(n);
826     }
827     if ((n = obt_xml_find_node(node, "stacking"))) {
828         if (obt_xml_node_contains(n, "normal"))
829             config_dock_layer = OB_STACKING_LAYER_NORMAL;
830         else if (obt_xml_node_contains(n, "below"))
831             config_dock_layer = OB_STACKING_LAYER_BELOW;
832         else if (obt_xml_node_contains(n, "above"))
833             config_dock_layer = OB_STACKING_LAYER_ABOVE;
834     }
835     if ((n = obt_xml_find_node(node, "direction"))) {
836         if (obt_xml_node_contains(n, "horizontal"))
837             config_dock_orient = OB_ORIENTATION_HORZ;
838         else if (obt_xml_node_contains(n, "vertical"))
839             config_dock_orient = OB_ORIENTATION_VERT;
840     }
841     if ((n = obt_xml_find_node(node, "autoHide")))
842         config_dock_hide = obt_xml_node_bool(n);
843     if ((n = obt_xml_find_node(node, "hideDelay")))
844         config_dock_hide_delay = obt_xml_node_int(n);
845     if ((n = obt_xml_find_node(node, "showDelay")))
846         config_dock_show_delay = obt_xml_node_int(n);
847     if ((n = obt_xml_find_node(node, "moveButton"))) {
848         gchar *str = obt_xml_node_string(n);
849         guint b, s;
850         if (translate_button(str, &s, &b)) {
851             config_dock_app_move_button = b;
852             config_dock_app_move_modifiers = s;
853         } else {
854             g_message(_("Invalid button \"%s\" specified in config file"), str);
855         }
856         g_free(str);
857     }
858 }
859
860 static void parse_menu(xmlNodePtr node, gpointer d)
861 {
862     xmlNodePtr n;
863     node = node->children;
864
865     if ((n = obt_xml_find_node(node, "hideDelay")))
866         config_menu_hide_delay = obt_xml_node_int(n);
867     if ((n = obt_xml_find_node(node, "middle")))
868         config_menu_middle = obt_xml_node_bool(n);
869     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
870         config_submenu_show_delay = obt_xml_node_int(n);
871     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
872         config_submenu_hide_delay = obt_xml_node_int(n);
873     if ((n = obt_xml_find_node(node, "manageDesktops")))
874         config_menu_manage_desktops = obt_xml_node_bool(n);
875     if ((n = obt_xml_find_node(node, "showIcons"))) {
876         config_menu_show_icons = obt_xml_node_bool(n);
877 #ifndef USE_IMLIB2
878         if (config_menu_show_icons)
879             g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded."));
880 #endif
881     }
882
883     while ((node = obt_xml_find_node(node, "file"))) {
884             gchar *c = obt_xml_node_string(node);
885             config_menu_files = g_slist_append(config_menu_files,
886                                                obt_paths_expand_tilde(c));
887             g_free(c);
888             node = node->next;
889     }
890 }
891
892 static void parse_resistance(xmlNodePtr node, gpointer d)
893 {
894     xmlNodePtr n;
895
896     node = node->children;
897     if ((n = obt_xml_find_node(node, "strength")))
898         config_resist_win = obt_xml_node_int(n);
899     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
900         config_resist_edge = obt_xml_node_int(n);
901 }
902
903 typedef struct
904 {
905     const gchar *key;
906     const gchar *actname;
907 } ObDefKeyBind;
908
909 static void bind_default_keyboard(void)
910 {
911     ObDefKeyBind *it;
912     ObDefKeyBind binds[] = {
913         { "A-Tab", "NextWindow" },
914         { "S-A-Tab", "PreviousWindow" },
915         { "A-F4", "Close" },
916         { NULL, NULL }
917     };
918     for (it = binds; it->key; ++it) {
919         GList *l = g_list_append(NULL, g_strdup(it->key));
920         keyboard_bind(l, actions_parse_string(it->actname));
921     }
922 }
923
924 typedef struct
925 {
926     const gchar *button;
927     const gchar *context;
928     const ObMouseAction mact;
929     const gchar *actname;
930 } ObDefMouseBind;
931
932 static void bind_default_mouse(void)
933 {
934     ObDefMouseBind *it;
935     ObDefMouseBind binds[] = {
936         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
937         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
938         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
939         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
940         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
941         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
942         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
943         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
944         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
945         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
946         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
947         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
948         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
949         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
950         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
951         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
952         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
953         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
954         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
955         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
956         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
957         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
958         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
959         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
960         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
961         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
962         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
963         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
964         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
965         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
966         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
967         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
968         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
969         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
970         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
971         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
972         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
973         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
974         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
975         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
976         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
977         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
978         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
979         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
980         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
981         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
982         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
983         { NULL, NULL, 0, NULL }
984     };
985
986     for (it = binds; it->button; ++it)
987         mouse_bind(it->button, frame_context_from_string(it->context),
988                    it->mact, actions_parse_string(it->actname));
989 }
990
991 void config_startup(ObtXmlInst *i)
992 {
993     config_focus_new = TRUE;
994     config_focus_follow = FALSE;
995     config_focus_delay = 0;
996     config_focus_raise = FALSE;
997     config_focus_last = TRUE;
998     config_focus_under_mouse = FALSE;
999     config_unfocus_leave = FALSE;
1000
1001     obt_xml_register(i, "focus", parse_focus, NULL);
1002
1003     config_place_policy = OB_PLACE_POLICY_SMART;
1004     config_place_center = TRUE;
1005     config_place_monitor = OB_PLACE_MONITOR_ANY;
1006
1007     config_primary_monitor_index = 1;
1008     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1009
1010     obt_xml_register(i, "placement", parse_placement, NULL);
1011
1012     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1013
1014     obt_xml_register(i, "margins", parse_margins, NULL);
1015
1016     config_theme = NULL;
1017
1018     config_animate_iconify = TRUE;
1019     config_title_layout = g_strdup("NLIMC");
1020     config_theme_keepborder = TRUE;
1021     config_theme_window_list_icon_size = 36;
1022
1023     config_font_activewindow = NULL;
1024     config_font_inactivewindow = NULL;
1025     config_font_menuitem = NULL;
1026     config_font_menutitle = NULL;
1027     config_font_activeosd = NULL;
1028     config_font_inactiveosd = NULL;
1029
1030     obt_xml_register(i, "theme", parse_theme, NULL);
1031
1032     config_desktops_num = 4;
1033     config_screen_firstdesk = 1;
1034     config_desktops_names = NULL;
1035     config_desktop_popup_time = 875;
1036
1037     obt_xml_register(i, "desktops", parse_desktops, NULL);
1038
1039     config_resize_redraw = TRUE;
1040     config_resize_popup_show = 1; /* nonpixel increments */
1041     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1042     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1043     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1044
1045     obt_xml_register(i, "resize", parse_resize, NULL);
1046
1047     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1048     config_dock_pos = OB_DIRECTION_NORTHEAST;
1049     config_dock_floating = FALSE;
1050     config_dock_nostrut = FALSE;
1051     config_dock_x = 0;
1052     config_dock_y = 0;
1053     config_dock_orient = OB_ORIENTATION_VERT;
1054     config_dock_hide = FALSE;
1055     config_dock_hide_delay = 300;
1056     config_dock_show_delay = 300;
1057     config_dock_app_move_button = 2; /* middle */
1058     config_dock_app_move_modifiers = 0;
1059
1060     obt_xml_register(i, "dock", parse_dock, NULL);
1061
1062     translate_key("C-g", &config_keyboard_reset_state,
1063                   &config_keyboard_reset_keycode);
1064
1065     bind_default_keyboard();
1066
1067     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1068
1069     config_mouse_threshold = 8;
1070     config_mouse_dclicktime = 200;
1071     config_mouse_screenedgetime = 400;
1072     config_mouse_screenedgewarp = FALSE;
1073
1074     bind_default_mouse();
1075
1076     obt_xml_register(i, "mouse", parse_mouse, NULL);
1077
1078     config_resist_win = 10;
1079     config_resist_edge = 20;
1080
1081     obt_xml_register(i, "resistance", parse_resistance, NULL);
1082
1083     config_menu_hide_delay = 250;
1084     config_menu_middle = FALSE;
1085     config_submenu_show_delay = 100;
1086     config_submenu_hide_delay = 400;
1087     config_menu_manage_desktops = TRUE;
1088     config_menu_files = NULL;
1089     config_menu_show_icons = TRUE;
1090
1091     obt_xml_register(i, "menu", parse_menu, NULL);
1092
1093     config_per_app_settings = NULL;
1094
1095     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1096 }
1097
1098 void config_shutdown(void)
1099 {
1100     GSList *it;
1101
1102     g_free(config_theme);
1103
1104     g_free(config_title_layout);
1105
1106     RrFontClose(config_font_activewindow);
1107     RrFontClose(config_font_inactivewindow);
1108     RrFontClose(config_font_menuitem);
1109     RrFontClose(config_font_menutitle);
1110     RrFontClose(config_font_activeosd);
1111     RrFontClose(config_font_inactiveosd);
1112
1113     for (it = config_desktops_names; it; it = g_slist_next(it))
1114         g_free(it->data);
1115     g_slist_free(config_desktops_names);
1116
1117     for (it = config_menu_files; it; it = g_slist_next(it))
1118         g_free(it->data);
1119     g_slist_free(config_menu_files);
1120
1121     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1122         ObAppSettings *itd = (ObAppSettings *)it->data;
1123         if (itd->name)  g_pattern_spec_free(itd->name);
1124         if (itd->role)  g_pattern_spec_free(itd->role);
1125         if (itd->title) g_pattern_spec_free(itd->title);
1126         if (itd->class) g_pattern_spec_free(itd->class);
1127         g_slice_free(ObAppSettings, it->data);
1128     }
1129     g_slist_free(config_per_app_settings);
1130 }