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