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