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