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