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