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