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