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