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