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