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