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