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