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