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