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