]> icculus.org git repositories - manmower/openbox.git/blob - openbox/config.c
let you specify the resize popup to be in a fixed place
[manmower/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 gboolean      config_place_active;
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 gint    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 gboolean         config_resize_four_corners;
65 gint             config_resize_popup_show;
66 ObResizePopupPos config_resize_popup_pos;
67 gboolean         config_resize_popup_x_center;
68 gboolean         config_resize_popup_y_center;
69 gboolean         config_resize_popup_x_opposite;
70 gboolean         config_resize_popup_y_opposite;
71 gint             config_resize_popup_x;
72 gint             config_resize_popup_y;
73
74 ObStackingLayer config_dock_layer;
75 gboolean        config_dock_floating;
76 gboolean        config_dock_nostrut;
77 ObDirection     config_dock_pos;
78 gint            config_dock_x;
79 gint            config_dock_y;
80 ObOrientation   config_dock_orient;
81 gboolean        config_dock_hide;
82 guint           config_dock_hide_delay;
83 guint           config_dock_show_delay;
84 guint           config_dock_app_move_button;
85 guint           config_dock_app_move_modifiers;
86
87 guint config_keyboard_reset_keycode;
88 guint config_keyboard_reset_state;
89
90 gint config_mouse_threshold;
91 gint config_mouse_dclicktime;
92 gint config_mouse_screenedgetime;
93
94 guint    config_menu_hide_delay;
95 gboolean config_menu_middle;
96 guint    config_submenu_show_delay;
97 gboolean config_menu_client_list_icons;
98
99 GSList *config_menu_files;
100
101 gint     config_resist_win;
102 gint     config_resist_edge;
103
104 GSList *config_per_app_settings;
105
106 ObAppSettings* config_create_app_settings(void)
107 {
108     ObAppSettings *settings = g_new0(ObAppSettings, 1);
109     settings->decor = -1;
110     settings->shade = -1;
111     settings->monitor = -1;
112     settings->focus = -1;
113     settings->desktop = 0;
114     settings->layer = -2;
115     settings->iconic = -1;
116     settings->skip_pager = -1;
117     settings->skip_taskbar = -1;
118     settings->fullscreen = -1;
119     settings->max_horz = -1;
120     settings->max_vert = -1;
121     return settings;
122 }
123
124 #define copy_if(setting, default) \
125   if (src->setting != default) dst->setting = src->setting
126 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
127                                            ObAppSettings *dst)
128 {
129     g_assert(src != NULL);
130     g_assert(dst != NULL);
131
132     copy_if(decor, -1);
133     copy_if(shade, -1);
134     copy_if(focus, -1);
135     copy_if(desktop, 0);
136     copy_if(layer, -2);
137     copy_if(iconic, -1);
138     copy_if(skip_pager, -1);
139     copy_if(skip_taskbar, -1);
140     copy_if(fullscreen, -1);
141     copy_if(max_horz, -1);
142     copy_if(max_vert, -1);
143
144     if (src->pos_given) {
145         dst->pos_given = TRUE;
146         dst->center_x = src->center_x;
147         dst->center_y = src->center_y;
148         dst->opposite_x = src->opposite_x;
149         dst->opposite_y = src->opposite_y;
150         dst->position.x = src->position.x;
151         dst->position.y = src->position.y;
152         dst->monitor = src->monitor;
153     }
154 }
155
156 /*
157   <applications>
158     <application name="aterm">
159       <decor>false</decor>
160     </application>
161     <application name="Rhythmbox">
162       <layer>above</layer>
163       <position>
164         <x>700</x>
165         <y>0</y>
166         <monitor>1</monitor>
167       </position>
168       .. there is a lot more settings available
169     </application>
170   </applications>
171 */
172
173 /* Manages settings for individual applications.
174    Some notes: monitor is the screen number in a multi monitor
175    (Xinerama) setup (starting from 0) or mouse, meaning the
176    monitor the pointer is on. Default: mouse.
177    Layer can be three values, above (Always on top), below
178    (Always on bottom) and everything else (normal behaviour).
179    Positions can be an integer value or center, which will
180    center the window in the specified axis. Position is within
181    the monitor, so <position><x>center</x></position><monitor>2</monitor>
182    will center the window on the second monitor.
183 */
184 static void parse_per_app_settings(ObParseInst *inst, xmlDocPtr doc,
185                                    xmlNodePtr node, gpointer data)
186 {
187     xmlNodePtr app = parse_find_node("application", node->children);
188     gchar *name = NULL, *class = NULL, *role = NULL;
189     gboolean name_set, class_set;
190     gboolean x_pos_given;
191
192     while (app) {
193         name_set = class_set = x_pos_given = FALSE;
194
195         class_set = parse_attr_string("class", app, &class);
196         name_set = parse_attr_string("name", app, &name);
197         if (class_set || name_set) {
198             xmlNodePtr n, c;
199             ObAppSettings *settings = config_create_app_settings();;
200
201             if (name_set)
202                 settings->name = g_pattern_spec_new(name);
203
204             if (class_set)
205                 settings->class = g_pattern_spec_new(class);
206
207             if (parse_attr_string("role", app, &role))
208                 settings->role = g_pattern_spec_new(role);
209
210             if ((n = parse_find_node("decor", app->children)))
211                 if (!parse_contains("default", doc, n))
212                     settings->decor = parse_bool(doc, n);
213
214             if ((n = parse_find_node("shade", app->children)))
215                 if (!parse_contains("default", doc, n))
216                     settings->shade = parse_bool(doc, n);
217
218             if ((n = parse_find_node("position", app->children))) {
219                 if ((c = parse_find_node("x", n->children)))
220                     if (!parse_contains("default", doc, c)) {
221                         gchar *s = parse_string(doc, c);
222                         if (!g_ascii_strcasecmp(s, "center")) {
223                             settings->center_x = TRUE;
224                             x_pos_given = TRUE;
225                         } else {
226                             if (s[0] == '-')
227                                 settings->opposite_x = TRUE;
228                             if (s[0] == '-' || s[0] == '+')
229                                 settings->position.x = atoi(s+1);
230                             else
231                                 settings->position.x = atoi(s);
232                             x_pos_given = TRUE;
233                         }
234                         g_free(s);
235                     }
236
237                 if (x_pos_given && (c = parse_find_node("y", n->children)))
238                     if (!parse_contains("default", doc, c)) {
239                         gchar *s = parse_string(doc, c);
240                         if (!g_ascii_strcasecmp(s, "center")) {
241                             settings->center_y = TRUE;
242                             settings->pos_given = TRUE;
243                         } else {
244                             if (s[0] == '-')
245                                 settings->opposite_y = TRUE;
246                             if (s[0] == '-' || s[0] == '+')
247                                 settings->position.y = atoi(s+1);
248                             else
249                                 settings->position.y = atoi(s);
250                             settings->pos_given = TRUE;
251                         }
252                         g_free(s);
253                     }
254
255                 if (settings->pos_given &&
256                     (c = parse_find_node("monitor", n->children)))
257                     if (!parse_contains("default", doc, c)) {
258                         gchar *s = parse_string(doc, c);
259                         if (!g_ascii_strcasecmp(s, "mouse"))
260                             settings->monitor = 0;
261                         else
262                             settings->monitor = parse_int(doc, c) + 1;
263                         g_free(s);
264                     }
265             }
266
267             if ((n = parse_find_node("focus", app->children)))
268                 if (!parse_contains("default", doc, n))
269                     settings->focus = parse_bool(doc, n);
270
271             if ((n = parse_find_node("desktop", app->children))) {
272                 if (!parse_contains("default", doc, n)) {
273                     gchar *s = parse_string(doc, n);
274                     if (!g_ascii_strcasecmp(s, "all"))
275                         settings->desktop = DESKTOP_ALL;
276                     else {
277                         gint i = parse_int(doc, n);
278                         if (i > 0)
279                             settings->desktop = i;
280                     }
281                     g_free(s);
282                 }
283             }
284
285             if ((n = parse_find_node("layer", app->children)))
286                 if (!parse_contains("default", doc, n)) {
287                     gchar *s = parse_string(doc, n);
288                     if (!g_ascii_strcasecmp(s, "above"))
289                         settings->layer = 1;
290                     else if (!g_ascii_strcasecmp(s, "below"))
291                         settings->layer = -1;
292                     else
293                         settings->layer = 0;
294                     g_free(s);
295                 }
296
297             if ((n = parse_find_node("iconic", app->children)))
298                 if (!parse_contains("default", doc, n))
299                     settings->iconic = parse_bool(doc, n);
300
301             if ((n = parse_find_node("skip_pager", app->children)))
302                 if (!parse_contains("default", doc, n))
303                     settings->skip_pager = parse_bool(doc, n);
304
305             if ((n = parse_find_node("skip_taskbar", app->children)))
306                 if (!parse_contains("default", doc, n))
307                     settings->skip_taskbar = parse_bool(doc, n);
308
309             if ((n = parse_find_node("fullscreen", app->children)))
310                 if (!parse_contains("default", doc, n))
311                     settings->fullscreen = parse_bool(doc, n);
312
313             if ((n = parse_find_node("maximized", app->children)))
314                 if (!parse_contains("default", doc, n)) {
315                     gchar *s = parse_string(doc, n);
316                     if (!g_ascii_strcasecmp(s, "horizontal")) {
317                         settings->max_horz = TRUE;
318                         settings->max_vert = FALSE;
319                     } else if (!g_ascii_strcasecmp(s, "vertical")) {
320                         settings->max_horz = FALSE;
321                         settings->max_vert = TRUE;
322                     } else
323                         settings->max_horz = settings->max_vert =
324                             parse_bool(doc, n);
325                     g_free(s);
326                 }
327
328             config_per_app_settings = g_slist_append(config_per_app_settings,
329                                               (gpointer) settings);
330             g_free(name);
331             g_free(class);
332             g_free(role);
333             name = class = role = NULL;
334         }
335
336         app = parse_find_node("application", app->next);
337     }
338 }
339
340 /*
341
342 <keybind key="C-x">
343   <action name="ChangeDesktop">
344     <desktop>3</desktop>
345   </action>
346 </keybind>
347
348 */
349
350 static void parse_key(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
351                       GList *keylist)
352 {
353     gchar *key;
354     xmlNodePtr n;
355     gboolean is_chroot = FALSE;
356
357     if (!parse_attr_string("key", node, &key))
358         return;
359
360     parse_attr_bool("chroot", node, &is_chroot);
361
362     keylist = g_list_append(keylist, key);
363
364     if ((n = parse_find_node("keybind", node->children))) {
365         while (n) {
366             parse_key(i, doc, n, keylist);
367             n = parse_find_node("keybind", n->next);
368         }
369     }
370     else if ((n = parse_find_node("action", node->children))) {
371         while (n) {
372             ObActionsAct *action;
373
374             action = actions_parse(i, doc, n);
375             if (action)
376                 keyboard_bind(keylist, action);
377             n = parse_find_node("action", n->next);
378         }
379     }
380
381     if (is_chroot)
382         keyboard_chroot(keylist);
383
384     g_free(key);
385     keylist = g_list_delete_link(keylist, g_list_last(keylist));
386 }
387
388 static void parse_keyboard(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
389                            gpointer data)
390 {
391     xmlNodePtr n;
392     gchar *key;
393
394     keyboard_unbind_all();
395
396     if ((n = parse_find_node("chainQuitKey", node->children))) {
397         key = parse_string(doc, n);
398         translate_key(key, &config_keyboard_reset_state,
399                       &config_keyboard_reset_keycode);
400         g_free(key);
401     }
402
403     if ((n = parse_find_node("keybind", node->children)))
404         while (n) {
405             parse_key(i, doc, n, NULL);
406             n = parse_find_node("keybind", n->next);
407         }
408 }
409
410 /*
411
412 <context name="Titlebar">
413   <mousebind button="Left" action="Press">
414     <action name="Raise"></action>
415   </mousebind>
416 </context>
417
418 */
419
420 static void parse_mouse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
421                         gpointer data)
422 {
423     xmlNodePtr n, nbut, nact;
424     gchar *buttonstr;
425     gchar *contextstr;
426     ObMouseAction mact;
427
428     mouse_unbind_all();
429
430     node = node->children;
431
432     if ((n = parse_find_node("dragThreshold", node)))
433         config_mouse_threshold = parse_int(doc, n);
434     if ((n = parse_find_node("doubleClickTime", node)))
435         config_mouse_dclicktime = parse_int(doc, n);
436     if ((n = parse_find_node("screenEdgeWarpTime", node)))
437         config_mouse_screenedgetime = parse_int(doc, n);
438
439     n = parse_find_node("context", node);
440     while (n) {
441         if (!parse_attr_string("name", n, &contextstr))
442             goto next_n;
443         nbut = parse_find_node("mousebind", n->children);
444         while (nbut) {
445             if (!parse_attr_string("button", nbut, &buttonstr))
446                 goto next_nbut;
447             if (parse_attr_contains("press", nbut, "action")) {
448                 mact = OB_MOUSE_ACTION_PRESS;
449             } else if (parse_attr_contains("release", nbut, "action")) {
450                 mact = OB_MOUSE_ACTION_RELEASE;
451             } else if (parse_attr_contains("click", nbut, "action")) {
452                 mact = OB_MOUSE_ACTION_CLICK;
453             } else if (parse_attr_contains("doubleclick", nbut,"action")) {
454                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
455             } else if (parse_attr_contains("drag", nbut, "action")) {
456                 mact = OB_MOUSE_ACTION_MOTION;
457             } else
458                 goto next_nbut;
459             nact = parse_find_node("action", nbut->children);
460             while (nact) {
461                 ObActionsAct *action;
462
463                 if ((action = actions_parse(i, doc, nact)))
464                     mouse_bind(buttonstr, contextstr, mact, action);
465                 nact = parse_find_node("action", nact->next);
466             }
467             g_free(buttonstr);
468         next_nbut:
469             nbut = parse_find_node("mousebind", nbut->next);
470         }
471         g_free(contextstr);
472     next_n:
473         n = parse_find_node("context", n->next);
474     }
475 }
476
477 static void parse_focus(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
478                         gpointer data)
479 {
480     xmlNodePtr n;
481
482     node = node->children;
483
484     if ((n = parse_find_node("focusNew", node)))
485         config_focus_new = parse_bool(doc, n);
486     if ((n = parse_find_node("followMouse", node)))
487         config_focus_follow = parse_bool(doc, n);
488     if ((n = parse_find_node("focusDelay", node)))
489         config_focus_delay = parse_int(doc, n);
490     if ((n = parse_find_node("raiseOnFocus", node)))
491         config_focus_raise = parse_bool(doc, n);
492     if ((n = parse_find_node("focusLast", node)))
493         config_focus_last = parse_bool(doc, n);
494     if ((n = parse_find_node("underMouse", node)))
495         config_focus_under_mouse = parse_bool(doc, n);
496 }
497
498 static void parse_placement(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
499                             gpointer data)
500 {
501     xmlNodePtr n;
502
503     node = node->children;
504
505     if ((n = parse_find_node("policy", node)))
506         if (parse_contains("UnderMouse", doc, n))
507             config_place_policy = OB_PLACE_POLICY_MOUSE;
508     if ((n = parse_find_node("center", node)))
509         config_place_center = parse_bool(doc, n);
510     if ((n = parse_find_node("active", node)))
511         config_place_active = parse_bool(doc, n);
512 }
513
514 static void parse_margins(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("top", node)))
522         config_margins.top = MAX(0, parse_int(doc, n));
523     if ((n = parse_find_node("left", node)))
524         config_margins.left = MAX(0, parse_int(doc, n));
525     if ((n = parse_find_node("right", node)))
526         config_margins.right = MAX(0, parse_int(doc, n));
527     if ((n = parse_find_node("bottom", node)))
528         config_margins.bottom = MAX(0, parse_int(doc, n));
529 }
530
531 static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
532                         gpointer data)
533 {
534     xmlNodePtr n;
535
536     node = node->children;
537
538     if ((n = parse_find_node("name", node))) {
539         gchar *c;
540
541         g_free(config_theme);
542         c = parse_string(doc, n);
543         config_theme = parse_expand_tilde(c);
544         g_free(c);
545     }
546     if ((n = parse_find_node("titleLayout", node))) {
547         gchar *c, *d;
548
549         g_free(config_title_layout);
550         config_title_layout = parse_string(doc, n);
551
552         /* replace duplicates with spaces */
553         for (c = config_title_layout; *c != '\0'; ++c)
554             for (d = c+1; *d != '\0'; ++d)
555                 if (*c == *d) *d = ' ';
556     }
557     if ((n = parse_find_node("keepBorder", node)))
558         config_theme_keepborder = parse_bool(doc, n);
559     if ((n = parse_find_node("animateIconify", node)))
560         config_animate_iconify = parse_bool(doc, n);
561
562     n = parse_find_node("font", node);
563     while (n) {
564         xmlNodePtr   fnode;
565         RrFont     **font;
566         gchar       *name = g_strdup(RrDefaultFontFamily);
567         gint         size = RrDefaultFontSize;
568         RrFontWeight weight = RrDefaultFontWeight;
569         RrFontSlant  slant = RrDefaultFontSlant;
570
571         if (parse_attr_contains("ActiveWindow", n, "place"))
572             font = &config_font_activewindow;
573         else if (parse_attr_contains("InactiveWindow", n, "place"))
574             font = &config_font_inactivewindow;
575         else if (parse_attr_contains("MenuHeader", n, "place"))
576             font = &config_font_menutitle;
577         else if (parse_attr_contains("MenuItem", n, "place"))
578             font = &config_font_menuitem;
579         else if (parse_attr_contains("OnScreenDisplay", n, "place"))
580             font = &config_font_osd;
581         else
582             goto next_font;
583
584         if ((fnode = parse_find_node("name", n->children))) {
585             g_free(name);
586             name = parse_string(doc, fnode);
587         }
588         if ((fnode = parse_find_node("size", n->children))) {
589             int s = parse_int(doc, fnode);
590             if (s > 0) size = s;
591         }
592         if ((fnode = parse_find_node("weight", n->children))) {
593             gchar *w = parse_string(doc, fnode);
594             if (!g_ascii_strcasecmp(w, "Bold"))
595                 weight = RR_FONTWEIGHT_BOLD;
596             g_free(w);
597         }
598         if ((fnode = parse_find_node("slant", n->children))) {
599             gchar *s = parse_string(doc, fnode);
600             if (!g_ascii_strcasecmp(s, "Italic"))
601                 slant = RR_FONTSLANT_ITALIC;
602             if (!g_ascii_strcasecmp(s, "Oblique"))
603                 slant = RR_FONTSLANT_OBLIQUE;
604             g_free(s);
605         }
606
607         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
608         g_free(name);
609     next_font:
610         n = parse_find_node("font", n->next);
611     }
612 }
613
614 static void parse_desktops(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
615                            gpointer data)
616 {
617     xmlNodePtr n;
618
619     node = node->children;
620
621     if ((n = parse_find_node("number", node))) {
622         gint d = parse_int(doc, n);
623         if (d > 0)
624             config_desktops_num = d;
625     }
626     if ((n = parse_find_node("firstdesk", node))) {
627         gint d = parse_int(doc, n);
628         if (d > 0)
629             config_screen_firstdesk = (unsigned) d;
630     }
631     if ((n = parse_find_node("names", node))) {
632         GSList *it;
633         xmlNodePtr nname;
634
635         for (it = config_desktops_names; it; it = it->next)
636             g_free(it->data);
637         g_slist_free(config_desktops_names);
638         config_desktops_names = NULL;
639
640         nname = parse_find_node("name", n->children);
641         while (nname) {
642             config_desktops_names = g_slist_append(config_desktops_names,
643                                                    parse_string(doc, nname));
644             nname = parse_find_node("name", nname->next);
645         }
646     }
647     if ((n = parse_find_node("popupTime", node)))
648         config_desktop_popup_time = parse_int(doc, n);
649 }
650
651 static void parse_resize(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
652                          gpointer data)
653 {
654     xmlNodePtr n;
655
656     node = node->children;
657
658     if ((n = parse_find_node("drawContents", node)))
659         config_resize_redraw = parse_bool(doc, n);
660     if ((n = parse_find_node("popupShow", node))) {
661         config_resize_popup_show = parse_int(doc, n);
662         if (parse_contains("Always", doc, n))
663             config_resize_popup_show = 2;
664         else if (parse_contains("Never", doc, n))
665             config_resize_popup_show = 0;
666         else if (parse_contains("Nonpixel", doc, n))
667             config_resize_popup_show = 1;
668     }
669     if ((n = parse_find_node("popupPosition", node))) {
670         if (parse_contains("Top", doc, n))
671             config_resize_popup_pos = OB_RESIZE_POS_TOP;
672         else if (parse_contains("Center", doc, n))
673             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
674         else if (parse_contains("Fixed", doc, n)) {
675             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
676
677             if ((n = parse_find_node("popupFixedPosition", node))) {
678                 xmlNodePtr n2;
679
680                 if ((n2 = parse_find_node("x", n->children))) {
681                     gchar *s = parse_string(doc, n2);
682                     if (!g_ascii_strcasecmp(s, "center"))
683                         config_resize_popup_x_center = TRUE;
684                     else {
685                         if (s[0] == '-')
686                             config_resize_popup_x_opposite = TRUE;
687                         if (s[0] == '-' || s[0] == '+')
688                             config_resize_popup_x = atoi(s+1);
689                         else
690                             config_resize_popup_x = atoi(s);
691                     }
692                 }
693                 if ((n2 = parse_find_node("y", n->children))) {
694                     gchar *s = parse_string(doc, n2);
695                     if (!g_ascii_strcasecmp(s, "center"))
696                         config_resize_popup_y_center = TRUE;
697                     else {
698                         if (s[0] == '-')
699                             config_resize_popup_y_opposite = TRUE;
700                         if (s[0] == '-' || s[0] == '+')
701                             config_resize_popup_y = atoi(s+1);
702                         else
703                             config_resize_popup_y = atoi(s);
704                     }
705                 }
706                 g_print("X %d %d %d\n", config_resize_popup_x_center, config_resize_popup_x_opposite, config_resize_popup_x);
707                 g_print("Y %d %d %d\n", config_resize_popup_y_center, config_resize_popup_y_opposite, config_resize_popup_y);
708             }
709         }
710     }
711 }
712
713 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
714                        gpointer data)
715 {
716     xmlNodePtr n;
717
718     node = node->children;
719
720     if ((n = parse_find_node("position", node))) {
721         if (parse_contains("TopLeft", doc, n))
722             config_dock_floating = FALSE,
723             config_dock_pos = OB_DIRECTION_NORTHWEST;
724         else if (parse_contains("Top", doc, n))
725             config_dock_floating = FALSE,
726             config_dock_pos = OB_DIRECTION_NORTH;
727         else if (parse_contains("TopRight", doc, n))
728             config_dock_floating = FALSE,
729             config_dock_pos = OB_DIRECTION_NORTHEAST;
730         else if (parse_contains("Right", doc, n))
731             config_dock_floating = FALSE,
732             config_dock_pos = OB_DIRECTION_EAST;
733         else if (parse_contains("BottomRight", doc, n))
734             config_dock_floating = FALSE,
735             config_dock_pos = OB_DIRECTION_SOUTHEAST;
736         else if (parse_contains("Bottom", doc, n))
737             config_dock_floating = FALSE,
738             config_dock_pos = OB_DIRECTION_SOUTH;
739         else if (parse_contains("BottomLeft", doc, n))
740             config_dock_floating = FALSE,
741             config_dock_pos = OB_DIRECTION_SOUTHWEST;
742         else if (parse_contains("Left", doc, n))
743             config_dock_floating = FALSE,
744             config_dock_pos = OB_DIRECTION_WEST;
745         else if (parse_contains("Floating", doc, n))
746             config_dock_floating = TRUE;
747     }
748     if (config_dock_floating) {
749         if ((n = parse_find_node("floatingX", node)))
750             config_dock_x = parse_int(doc, n);
751         if ((n = parse_find_node("floatingY", node)))
752             config_dock_y = parse_int(doc, n);
753     } else {
754         if ((n = parse_find_node("noStrut", node)))
755             config_dock_nostrut = parse_bool(doc, n);
756     }
757     if ((n = parse_find_node("stacking", node))) {
758         if (parse_contains("above", doc, n))
759             config_dock_layer = OB_STACKING_LAYER_ABOVE;
760         else if (parse_contains("normal", doc, n))
761             config_dock_layer = OB_STACKING_LAYER_NORMAL;
762         else if (parse_contains("below", doc, n))
763             config_dock_layer = OB_STACKING_LAYER_BELOW;
764     }
765     if ((n = parse_find_node("direction", node))) {
766         if (parse_contains("horizontal", doc, n))
767             config_dock_orient = OB_ORIENTATION_HORZ;
768         else if (parse_contains("vertical", doc, n))
769             config_dock_orient = OB_ORIENTATION_VERT;
770     }
771     if ((n = parse_find_node("autoHide", node)))
772         config_dock_hide = parse_bool(doc, n);
773     if ((n = parse_find_node("hideDelay", node)))
774         config_dock_hide_delay = parse_int(doc, n);
775     if ((n = parse_find_node("showDelay", node)))
776         config_dock_show_delay = parse_int(doc, n);
777     if ((n = parse_find_node("moveButton", node))) {
778         gchar *str = parse_string(doc, n);
779         guint b, s;
780         if (translate_button(str, &s, &b)) {
781             config_dock_app_move_button = b;
782             config_dock_app_move_modifiers = s;
783         } else {
784             g_message(_("Invalid button '%s' specified in config file"), str);
785         }
786         g_free(str);
787     }
788 }
789
790 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
791                        gpointer data)
792 {
793     xmlNodePtr n;
794     for (node = node->children; node; node = node->next) {
795         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
796             gchar *c;
797
798             c = parse_string(doc, node);
799             config_menu_files = g_slist_append(config_menu_files,
800                                                parse_expand_tilde(c));
801             g_free(c);
802         }
803         if ((n = parse_find_node("hideDelay", node)))
804             config_menu_hide_delay = parse_int(doc, n);
805         if ((n = parse_find_node("middle", node)))
806             config_menu_middle = parse_bool(doc, n);
807         if ((n = parse_find_node("submenuShowDelay", node)))
808             config_submenu_show_delay = parse_int(doc, n);
809         if ((n = parse_find_node("applicationIcons", node)))
810             config_menu_client_list_icons = parse_bool(doc, n);
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_active = FALSE;
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_four_corners = FALSE;
957     config_resize_popup_show = 1; /* nonpixel increments */
958     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
959     config_resize_popup_x_center = FALSE;
960     config_resize_popup_x_opposite = FALSE;
961     config_resize_popup_x = 0;
962     config_resize_popup_y_center = FALSE;
963     config_resize_popup_y_opposite = FALSE;
964     config_resize_popup_y = 0;
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_files = NULL;
1008
1009     parse_register(i, "menu", parse_menu, NULL);
1010
1011     config_per_app_settings = NULL;
1012
1013     parse_register(i, "applications", parse_per_app_settings, NULL);
1014 }
1015
1016 void config_shutdown(void)
1017 {
1018     GSList *it;
1019
1020     g_free(config_theme);
1021
1022     g_free(config_title_layout);
1023
1024     RrFontClose(config_font_activewindow);
1025     RrFontClose(config_font_inactivewindow);
1026     RrFontClose(config_font_menuitem);
1027     RrFontClose(config_font_menutitle);
1028
1029     for (it = config_desktops_names; it; it = g_slist_next(it))
1030         g_free(it->data);
1031     g_slist_free(config_desktops_names);
1032
1033     for (it = config_menu_files; it; it = g_slist_next(it))
1034         g_free(it->data);
1035     g_slist_free(config_menu_files);
1036
1037     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1038         ObAppSettings *itd = (ObAppSettings *)it->data;
1039         if (itd->name)  g_pattern_spec_free(itd->name);
1040         if (itd->role)  g_pattern_spec_free(itd->role);
1041         if (itd->class) g_pattern_spec_free(itd->class);
1042         g_free(it->data);
1043     }
1044     g_slist_free(config_per_app_settings);
1045 }