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