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