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