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