Set a minimum value for the screenEdgeWarpTime for 25ms.
[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 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         /* minimum value of 25 for this property, when it is 1 and you hit the
450            edge it basically never stops */
451         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
452             config_mouse_screenedgetime = 25;
453     }
454
455     n = parse_find_node("context", node);
456     while (n) {
457         if (!parse_attr_string("name", n, &contextstr))
458             goto next_n;
459         nbut = parse_find_node("mousebind", n->children);
460         while (nbut) {
461             if (!parse_attr_string("button", nbut, &buttonstr))
462                 goto next_nbut;
463             if (parse_attr_contains("press", nbut, "action")) {
464                 mact = OB_MOUSE_ACTION_PRESS;
465             } else if (parse_attr_contains("release", nbut, "action")) {
466                 mact = OB_MOUSE_ACTION_RELEASE;
467             } else if (parse_attr_contains("click", nbut, "action")) {
468                 mact = OB_MOUSE_ACTION_CLICK;
469             } else if (parse_attr_contains("doubleclick", nbut,"action")) {
470                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
471             } else if (parse_attr_contains("drag", nbut, "action")) {
472                 mact = OB_MOUSE_ACTION_MOTION;
473             } else
474                 goto next_nbut;
475             nact = parse_find_node("action", nbut->children);
476             while (nact) {
477                 ObActionsAct *action;
478
479                 if ((action = actions_parse(i, doc, nact)))
480                     mouse_bind(buttonstr, contextstr, mact, action);
481                 nact = parse_find_node("action", nact->next);
482             }
483             g_free(buttonstr);
484         next_nbut:
485             nbut = parse_find_node("mousebind", nbut->next);
486         }
487         g_free(contextstr);
488     next_n:
489         n = parse_find_node("context", n->next);
490     }
491 }
492
493 static void parse_focus(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
494                         gpointer data)
495 {
496     xmlNodePtr n;
497
498     node = node->children;
499
500     if ((n = parse_find_node("focusNew", node)))
501         config_focus_new = parse_bool(doc, n);
502     if ((n = parse_find_node("followMouse", node)))
503         config_focus_follow = parse_bool(doc, n);
504     if ((n = parse_find_node("focusDelay", node)))
505         config_focus_delay = parse_int(doc, n);
506     if ((n = parse_find_node("raiseOnFocus", node)))
507         config_focus_raise = parse_bool(doc, n);
508     if ((n = parse_find_node("focusLast", node)))
509         config_focus_last = parse_bool(doc, n);
510     if ((n = parse_find_node("underMouse", node)))
511         config_focus_under_mouse = parse_bool(doc, n);
512 }
513
514 static void parse_placement(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
515                             gpointer data)
516 {
517     xmlNodePtr n;
518
519     node = node->children;
520
521     if ((n = parse_find_node("policy", node)))
522         if (parse_contains("UnderMouse", doc, n))
523             config_place_policy = OB_PLACE_POLICY_MOUSE;
524     if ((n = parse_find_node("center", node)))
525         config_place_center = parse_bool(doc, n);
526     if ((n = parse_find_node("monitor", node))) {
527         if (parse_contains("active", doc, n))
528             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
529         else if (parse_contains("mouse", doc, n))
530             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
531     }
532 }
533
534 static void parse_margins(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
535                           gpointer data)
536 {
537     xmlNodePtr n;
538
539     node = node->children;
540
541     if ((n = parse_find_node("top", node)))
542         config_margins.top = MAX(0, parse_int(doc, n));
543     if ((n = parse_find_node("left", node)))
544         config_margins.left = MAX(0, parse_int(doc, n));
545     if ((n = parse_find_node("right", node)))
546         config_margins.right = MAX(0, parse_int(doc, n));
547     if ((n = parse_find_node("bottom", node)))
548         config_margins.bottom = MAX(0, parse_int(doc, n));
549 }
550
551 static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
552                         gpointer data)
553 {
554     xmlNodePtr n;
555
556     node = node->children;
557
558     if ((n = parse_find_node("name", node))) {
559         gchar *c;
560
561         g_free(config_theme);
562         c = parse_string(doc, n);
563         config_theme = parse_expand_tilde(c);
564         g_free(c);
565     }
566     if ((n = parse_find_node("titleLayout", node))) {
567         gchar *c, *d;
568
569         g_free(config_title_layout);
570         config_title_layout = parse_string(doc, n);
571
572         /* replace duplicates with spaces */
573         for (c = config_title_layout; *c != '\0'; ++c)
574             for (d = c+1; *d != '\0'; ++d)
575                 if (*c == *d) *d = ' ';
576     }
577     if ((n = parse_find_node("keepBorder", node)))
578         config_theme_keepborder = parse_bool(doc, n);
579     if ((n = parse_find_node("animateIconify", node)))
580         config_animate_iconify = parse_bool(doc, n);
581
582     n = parse_find_node("font", node);
583     while (n) {
584         xmlNodePtr   fnode;
585         RrFont     **font;
586         gchar       *name = g_strdup(RrDefaultFontFamily);
587         gint         size = RrDefaultFontSize;
588         RrFontWeight weight = RrDefaultFontWeight;
589         RrFontSlant  slant = RrDefaultFontSlant;
590
591         if (parse_attr_contains("ActiveWindow", n, "place"))
592             font = &config_font_activewindow;
593         else if (parse_attr_contains("InactiveWindow", n, "place"))
594             font = &config_font_inactivewindow;
595         else if (parse_attr_contains("MenuHeader", n, "place"))
596             font = &config_font_menutitle;
597         else if (parse_attr_contains("MenuItem", n, "place"))
598             font = &config_font_menuitem;
599         else if (parse_attr_contains("OnScreenDisplay", n, "place"))
600             font = &config_font_osd;
601         else
602             goto next_font;
603
604         if ((fnode = parse_find_node("name", n->children))) {
605             g_free(name);
606             name = parse_string(doc, fnode);
607         }
608         if ((fnode = parse_find_node("size", n->children))) {
609             int s = parse_int(doc, fnode);
610             if (s > 0) size = s;
611         }
612         if ((fnode = parse_find_node("weight", n->children))) {
613             gchar *w = parse_string(doc, fnode);
614             if (!g_ascii_strcasecmp(w, "Bold"))
615                 weight = RR_FONTWEIGHT_BOLD;
616             g_free(w);
617         }
618         if ((fnode = parse_find_node("slant", n->children))) {
619             gchar *s = parse_string(doc, fnode);
620             if (!g_ascii_strcasecmp(s, "Italic"))
621                 slant = RR_FONTSLANT_ITALIC;
622             if (!g_ascii_strcasecmp(s, "Oblique"))
623                 slant = RR_FONTSLANT_OBLIQUE;
624             g_free(s);
625         }
626
627         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
628         g_free(name);
629     next_font:
630         n = parse_find_node("font", n->next);
631     }
632 }
633
634 static void parse_desktops(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
635                            gpointer data)
636 {
637     xmlNodePtr n;
638
639     node = node->children;
640
641     if ((n = parse_find_node("number", node))) {
642         gint d = parse_int(doc, n);
643         if (d > 0)
644             config_desktops_num = (unsigned) d;
645     }
646     if ((n = parse_find_node("firstdesk", node))) {
647         gint d = parse_int(doc, n);
648         if (d > 0)
649             config_screen_firstdesk = (unsigned) d;
650     }
651     if ((n = parse_find_node("names", node))) {
652         GSList *it;
653         xmlNodePtr nname;
654
655         for (it = config_desktops_names; it; it = it->next)
656             g_free(it->data);
657         g_slist_free(config_desktops_names);
658         config_desktops_names = NULL;
659
660         nname = parse_find_node("name", n->children);
661         while (nname) {
662             config_desktops_names = g_slist_append(config_desktops_names,
663                                                    parse_string(doc, nname));
664             nname = parse_find_node("name", nname->next);
665         }
666     }
667     if ((n = parse_find_node("popupTime", node)))
668         config_desktop_popup_time = parse_int(doc, n);
669 }
670
671 static void parse_resize(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
672                          gpointer data)
673 {
674     xmlNodePtr n;
675
676     node = node->children;
677
678     if ((n = parse_find_node("drawContents", node)))
679         config_resize_redraw = parse_bool(doc, n);
680     if ((n = parse_find_node("popupShow", node))) {
681         config_resize_popup_show = parse_int(doc, n);
682         if (parse_contains("Always", doc, n))
683             config_resize_popup_show = 2;
684         else if (parse_contains("Never", doc, n))
685             config_resize_popup_show = 0;
686         else if (parse_contains("Nonpixel", doc, n))
687             config_resize_popup_show = 1;
688     }
689     if ((n = parse_find_node("popupPosition", node))) {
690         if (parse_contains("Top", doc, n))
691             config_resize_popup_pos = OB_RESIZE_POS_TOP;
692         else if (parse_contains("Center", doc, n))
693             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
694         else if (parse_contains("Fixed", doc, n)) {
695             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
696
697             if ((n = parse_find_node("popupFixedPosition", node))) {
698                 xmlNodePtr n2;
699
700                 if ((n2 = parse_find_node("x", n->children)))
701                     config_parse_gravity_coord(doc, n2,
702                                                &config_resize_popup_fixed.x);
703                 if ((n2 = parse_find_node("y", n->children)))
704                     config_parse_gravity_coord(doc, n2,
705                                                &config_resize_popup_fixed.y);
706
707                 config_resize_popup_fixed.x.pos =
708                     MAX(config_resize_popup_fixed.x.pos, 0);
709                 config_resize_popup_fixed.y.pos =
710                     MAX(config_resize_popup_fixed.y.pos, 0);
711             }
712         }
713     }
714 }
715
716 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
717                        gpointer data)
718 {
719     xmlNodePtr n;
720
721     node = node->children;
722
723     if ((n = parse_find_node("position", node))) {
724         if (parse_contains("TopLeft", doc, n))
725             config_dock_floating = FALSE,
726             config_dock_pos = OB_DIRECTION_NORTHWEST;
727         else if (parse_contains("Top", doc, n))
728             config_dock_floating = FALSE,
729             config_dock_pos = OB_DIRECTION_NORTH;
730         else if (parse_contains("TopRight", doc, n))
731             config_dock_floating = FALSE,
732             config_dock_pos = OB_DIRECTION_NORTHEAST;
733         else if (parse_contains("Right", doc, n))
734             config_dock_floating = FALSE,
735             config_dock_pos = OB_DIRECTION_EAST;
736         else if (parse_contains("BottomRight", doc, n))
737             config_dock_floating = FALSE,
738             config_dock_pos = OB_DIRECTION_SOUTHEAST;
739         else if (parse_contains("Bottom", doc, n))
740             config_dock_floating = FALSE,
741             config_dock_pos = OB_DIRECTION_SOUTH;
742         else if (parse_contains("BottomLeft", doc, n))
743             config_dock_floating = FALSE,
744             config_dock_pos = OB_DIRECTION_SOUTHWEST;
745         else if (parse_contains("Left", doc, n))
746             config_dock_floating = FALSE,
747             config_dock_pos = OB_DIRECTION_WEST;
748         else if (parse_contains("Floating", doc, n))
749             config_dock_floating = TRUE;
750     }
751     if (config_dock_floating) {
752         if ((n = parse_find_node("floatingX", node)))
753             config_dock_x = parse_int(doc, n);
754         if ((n = parse_find_node("floatingY", node)))
755             config_dock_y = parse_int(doc, n);
756     } else {
757         if ((n = parse_find_node("noStrut", node)))
758             config_dock_nostrut = parse_bool(doc, n);
759     }
760     if ((n = parse_find_node("stacking", node))) {
761         if (parse_contains("above", doc, n))
762             config_dock_layer = OB_STACKING_LAYER_ABOVE;
763         else if (parse_contains("normal", doc, n))
764             config_dock_layer = OB_STACKING_LAYER_NORMAL;
765         else if (parse_contains("below", doc, n))
766             config_dock_layer = OB_STACKING_LAYER_BELOW;
767     }
768     if ((n = parse_find_node("direction", node))) {
769         if (parse_contains("horizontal", doc, n))
770             config_dock_orient = OB_ORIENTATION_HORZ;
771         else if (parse_contains("vertical", doc, n))
772             config_dock_orient = OB_ORIENTATION_VERT;
773     }
774     if ((n = parse_find_node("autoHide", node)))
775         config_dock_hide = parse_bool(doc, n);
776     if ((n = parse_find_node("hideDelay", node)))
777         config_dock_hide_delay = parse_int(doc, n);
778     if ((n = parse_find_node("showDelay", node)))
779         config_dock_show_delay = parse_int(doc, n);
780     if ((n = parse_find_node("moveButton", node))) {
781         gchar *str = parse_string(doc, n);
782         guint b, s;
783         if (translate_button(str, &s, &b)) {
784             config_dock_app_move_button = b;
785             config_dock_app_move_modifiers = s;
786         } else {
787             g_message(_("Invalid button \"%s\" specified in config file"), str);
788         }
789         g_free(str);
790     }
791 }
792
793 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
794                        gpointer data)
795 {
796     xmlNodePtr n;
797     node = node->children;
798
799     if ((n = parse_find_node("hideDelay", node)))
800         config_menu_hide_delay = parse_int(doc, n);
801     if ((n = parse_find_node("middle", node)))
802         config_menu_middle = parse_bool(doc, n);
803     if ((n = parse_find_node("submenuShowDelay", node)))
804         config_submenu_show_delay = parse_int(doc, n);
805     if ((n = parse_find_node("applicationIcons", node)))
806         config_menu_client_list_icons = parse_bool(doc, n);
807     if ((n = parse_find_node("manageDesktops", node)))
808         config_menu_manage_desktops = parse_bool(doc, n);
809
810     while ((node = parse_find_node("file", node))) {
811             gchar *c = parse_string(doc, node);
812             config_menu_files = g_slist_append(config_menu_files,
813                                                parse_expand_tilde(c));
814             g_free(c);
815             node = node->next;
816     }
817 }
818
819 static void parse_resistance(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
820                              gpointer data)
821 {
822     xmlNodePtr n;
823
824     node = node->children;
825     if ((n = parse_find_node("strength", node)))
826         config_resist_win = parse_int(doc, n);
827     if ((n = parse_find_node("screen_edge_strength", node)))
828         config_resist_edge = parse_int(doc, n);
829 }
830
831 typedef struct
832 {
833     const gchar *key;
834     const gchar *actname;
835 } ObDefKeyBind;
836
837 static void bind_default_keyboard(void)
838 {
839     ObDefKeyBind *it;
840     ObDefKeyBind binds[] = {
841         { "A-Tab", "NextWindow" },
842         { "S-A-Tab", "PreviousWindow" },
843         { "A-F4", "Close" },
844         { NULL, NULL }
845     };
846     for (it = binds; it->key; ++it) {
847         GList *l = g_list_append(NULL, g_strdup(it->key));
848         keyboard_bind(l, actions_parse_string(it->actname));
849     }
850 }
851
852 typedef struct
853 {
854     const gchar *button;
855     const gchar *context;
856     const ObMouseAction mact;
857     const gchar *actname;
858 } ObDefMouseBind;
859
860 static void bind_default_mouse(void)
861 {
862     ObDefMouseBind *it;
863     ObDefMouseBind binds[] = {
864         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
865         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
866         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
867         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
868         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
869         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
870         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
871         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
872         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
873         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
874         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
875         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
876         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
877         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
878         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
879         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
880         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
881         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
882         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
883         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
884         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
885         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
886         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
887         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
888         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
889         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
890         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
891         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
892         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
893         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
894         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
895         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
896         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
897         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
898         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
899         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
900         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
901         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
902         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
903         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
904         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
905         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
906         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
907         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
908         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
909         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
910         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
911         { NULL, NULL, 0, NULL }
912     };
913
914     for (it = binds; it->button; ++it)
915         mouse_bind(it->button, it->context, it->mact,
916                    actions_parse_string(it->actname));
917 }
918
919 void config_startup(ObParseInst *i)
920 {
921     config_focus_new = TRUE;
922     config_focus_follow = FALSE;
923     config_focus_delay = 0;
924     config_focus_raise = FALSE;
925     config_focus_last = TRUE;
926     config_focus_under_mouse = FALSE;
927
928     parse_register(i, "focus", parse_focus, NULL);
929
930     config_place_policy = OB_PLACE_POLICY_SMART;
931     config_place_center = TRUE;
932     config_place_monitor = OB_PLACE_MONITOR_ANY;
933
934     parse_register(i, "placement", parse_placement, NULL);
935
936     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
937
938     parse_register(i, "margins", parse_margins, NULL);
939
940     config_theme = NULL;
941
942     config_animate_iconify = TRUE;
943     config_title_layout = g_strdup("NLIMC");
944     config_theme_keepborder = TRUE;
945
946     config_font_activewindow = NULL;
947     config_font_inactivewindow = NULL;
948     config_font_menuitem = NULL;
949     config_font_menutitle = NULL;
950
951     parse_register(i, "theme", parse_theme, NULL);
952
953     config_desktops_num = 4;
954     config_screen_firstdesk = 1;
955     config_desktops_names = NULL;
956     config_desktop_popup_time = 875;
957
958     parse_register(i, "desktops", parse_desktops, NULL);
959
960     config_resize_redraw = TRUE;
961     config_resize_popup_show = 1; /* nonpixel increments */
962     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
963     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
964     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
965
966     parse_register(i, "resize", parse_resize, NULL);
967
968     config_dock_layer = OB_STACKING_LAYER_ABOVE;
969     config_dock_pos = OB_DIRECTION_NORTHEAST;
970     config_dock_floating = FALSE;
971     config_dock_nostrut = FALSE;
972     config_dock_x = 0;
973     config_dock_y = 0;
974     config_dock_orient = OB_ORIENTATION_VERT;
975     config_dock_hide = FALSE;
976     config_dock_hide_delay = 300;
977     config_dock_show_delay = 300;
978     config_dock_app_move_button = 2; /* middle */
979     config_dock_app_move_modifiers = 0;
980
981     parse_register(i, "dock", parse_dock, NULL);
982
983     translate_key("C-g", &config_keyboard_reset_state,
984                   &config_keyboard_reset_keycode);
985
986     bind_default_keyboard();
987
988     parse_register(i, "keyboard", parse_keyboard, NULL);
989
990     config_mouse_threshold = 8;
991     config_mouse_dclicktime = 200;
992     config_mouse_screenedgetime = 400;
993
994     bind_default_mouse();
995
996     parse_register(i, "mouse", parse_mouse, NULL);
997
998     config_resist_win = 10;
999     config_resist_edge = 20;
1000
1001     parse_register(i, "resistance", parse_resistance, NULL);
1002
1003     config_menu_hide_delay = 250;
1004     config_menu_middle = FALSE;
1005     config_submenu_show_delay = 0;
1006     config_menu_client_list_icons = TRUE;
1007     config_menu_manage_desktops = TRUE;
1008     config_menu_files = NULL;
1009
1010     parse_register(i, "menu", parse_menu, NULL);
1011
1012     config_per_app_settings = NULL;
1013
1014     parse_register(i, "applications", parse_per_app_settings, NULL);
1015 }
1016
1017 void config_shutdown(void)
1018 {
1019     GSList *it;
1020
1021     g_free(config_theme);
1022
1023     g_free(config_title_layout);
1024
1025     RrFontClose(config_font_activewindow);
1026     RrFontClose(config_font_inactivewindow);
1027     RrFontClose(config_font_menuitem);
1028     RrFontClose(config_font_menutitle);
1029     RrFontClose(config_font_osd);
1030
1031     for (it = config_desktops_names; it; it = g_slist_next(it))
1032         g_free(it->data);
1033     g_slist_free(config_desktops_names);
1034
1035     for (it = config_menu_files; it; it = g_slist_next(it))
1036         g_free(it->data);
1037     g_slist_free(config_menu_files);
1038
1039     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1040         ObAppSettings *itd = (ObAppSettings *)it->data;
1041         if (itd->name)  g_pattern_spec_free(itd->name);
1042         if (itd->role)  g_pattern_spec_free(itd->role);
1043         if (itd->class) g_pattern_spec_free(itd->class);
1044         g_free(it->data);
1045     }
1046     g_slist_free(config_per_app_settings);
1047 }