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