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