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