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