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