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