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