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