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