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