mwfact is now configurable per tag
[awesome.git] / config.c
blob2ed61e5b5a64946cd774fda2f8d35e42396b1b8b
1 /*
2 * config.c - configuration management
4 * Copyright © 2007 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /**
23 * \defgroup ui_callback
26 #include <confuse.h>
27 #include <X11/keysym.h>
29 #include "util.h"
30 #include "awesome.h"
31 #include "screen.h"
32 #include "draw.h"
33 #include "event.h"
34 #include "tag.h"
35 #include "statusbar.h"
36 #include "layout.h"
37 #include "layouts/tile.h"
38 #include "layouts/floating.h"
39 #include "layouts/max.h"
41 #define AWESOME_CONFIG_FILE ".awesomerc"
43 static XColor initxcolor(Display *, int, const char *);
44 static unsigned int get_numlockmask(Display *);
46 /** Link a name to a key symbol */
47 typedef struct
49 const char *name;
50 KeySym keysym;
51 } KeyMod;
53 /** List of available UI bindable callbacks and functions */
54 const NameFuncLink UicbList[] = {
55 /* util.c */
56 {"spawn", uicb_spawn},
57 {"exec", uicb_exec},
58 /* client.c */
59 {"killclient", uicb_killclient},
60 {"moveresize", uicb_moveresize},
61 {"settrans", uicb_settrans},
62 {"setborder", uicb_setborder},
63 {"swapnext", uicb_swapnext},
64 {"swapprev", uicb_swapprev},
65 /* tag.c */
66 {"tag", uicb_tag},
67 {"togglefloating", uicb_togglefloating},
68 {"toggleview", uicb_toggleview},
69 {"toggletag", uicb_toggletag},
70 {"view", uicb_view},
71 {"view_tag_prev_selected", uicb_tag_prev_selected},
72 {"view_tag_previous", uicb_tag_viewprev},
73 {"view_tag_next", uicb_tag_viewnext},
74 /* layout.c */
75 {"setlayout", uicb_setlayout},
76 {"focusnext", uicb_focusnext},
77 {"focusprev", uicb_focusprev},
78 {"togglemax", uicb_togglemax},
79 {"toggleverticalmax", uicb_toggleverticalmax},
80 {"togglehorizontalmax", uicb_togglehorizontalmax},
81 {"zoom", uicb_zoom},
82 /* layouts/tile.c */
83 {"setmwfact", uicb_setmwfact},
84 {"setnmaster", uicb_setnmaster},
85 {"setncol", uicb_setncol},
86 /* screen.c */
87 {"focusnextscreen", uicb_focusnextscreen},
88 {"focusprevscreen", uicb_focusprevscreen},
89 {"movetoscreen", uicb_movetoscreen},
90 /* awesome.c */
91 {"quit", uicb_quit},
92 /* statusbar.c */
93 {"togglebar", uicb_togglebar},
94 /* config.c */
95 {"reloadconfig", uicb_reloadconfig},
96 {"setstatustext", uicb_setstatustext},
97 {NULL, NULL}
100 /** List of keyname and corresponding X11 mask codes */
101 static const KeyMod KeyModList[] =
103 {"Shift", ShiftMask},
104 {"Lock", LockMask},
105 {"Control", ControlMask},
106 {"Mod1", Mod1Mask},
107 {"Mod2", Mod2Mask},
108 {"Mod3", Mod3Mask},
109 {"Mod4", Mod4Mask},
110 {"Mod5", Mod5Mask},
111 {NULL, 0}
114 /** List of available layouts and link between name and functions */
115 static const NameFuncLink LayoutsList[] =
117 {"tile", layout_tile},
118 {"tileleft", layout_tileleft},
119 {"max", layout_max},
120 {"floating", layout_floating},
121 {NULL, NULL}
124 /** Lookup for a key mask from its name
125 * \param keyname Key name
126 * \return Key mask or 0 if not found
128 static KeySym
129 key_mask_lookup(const char *keyname)
131 int i;
133 if(keyname)
134 for(i = 0; KeyModList[i].name; i++)
135 if(!a_strcmp(keyname, KeyModList[i].name))
136 return KeyModList[i].keysym;
138 return 0;
141 /** Parse configuration file and initialize some stuff
142 * \param disp Display ref
143 * \param scr Screen number
145 void
146 parse_config(const char *confpatharg, awesome_config *awesomeconf)
148 static cfg_opt_t general_opts[] =
150 CFG_INT((char *) "border", 1, CFGF_NONE),
151 CFG_INT((char *) "snap", 8, CFGF_NONE),
152 CFG_BOOL((char *) "resize_hints", cfg_false, CFGF_NONE),
153 CFG_INT((char *) "opacity_unfocused", 100, CFGF_NONE),
154 CFG_BOOL((char *) "focus_move_pointer", cfg_false, CFGF_NONE),
155 CFG_BOOL((char *) "allow_lower_floats", cfg_false, CFGF_NONE),
156 CFG_STR((char *) "font", (char *) "mono-12", CFGF_NONE),
157 CFG_END()
159 static cfg_opt_t colors_opts[] =
161 CFG_STR((char *) "normal_border", (char *) "#111111", CFGF_NONE),
162 CFG_STR((char *) "normal_bg", (char *) "#111111", CFGF_NONE),
163 CFG_STR((char *) "normal_fg", (char *) "#eeeeee", CFGF_NONE),
164 CFG_STR((char *) "focus_border", (char *) "#6666ff", CFGF_NONE),
165 CFG_STR((char *) "focus_bg", (char *) "#6666ff", CFGF_NONE),
166 CFG_STR((char *) "focus_fg", (char *) "#ffffff", CFGF_NONE),
167 CFG_STR((char *) "tab_border", (char *) "#ff0000", CFGF_NONE),
168 CFG_END()
170 static cfg_opt_t statusbar_opts[] =
172 CFG_STR((char *) "position", (char *) "top", CFGF_NONE),
173 CFG_END()
175 static cfg_opt_t tag_opts[] =
177 CFG_STR((char *) "layout", (char *) "tile", CFGF_NONE),
178 CFG_FLOAT((char *) "mwfact", 0.5, CFGF_NONE),
179 CFG_END()
181 static cfg_opt_t tags_opts[] =
183 CFG_SEC((char *) "tag", tag_opts, CFGF_TITLE | CFGF_MULTI),
184 CFG_END()
186 static cfg_opt_t layout_opts[] =
188 CFG_STR((char *) "symbol", (char *) "???", CFGF_NONE),
189 CFG_END()
191 static cfg_opt_t layouts_opts[] =
193 CFG_SEC((char *) "layout", layout_opts, CFGF_TITLE | CFGF_MULTI),
194 CFG_INT((char *) "nmaster", 1, CFGF_NONE),
195 CFG_INT((char *) "ncol", 1, CFGF_NONE),
196 CFG_END()
198 static cfg_opt_t screen_opts[] =
200 CFG_SEC((char *) "statusbar", statusbar_opts, CFGF_NONE),
201 CFG_SEC((char *) "tags", tags_opts, CFGF_NONE),
203 static cfg_opt_t rule_opts[] =
205 CFG_STR((char *) "name", (char *) "", CFGF_NONE),
206 CFG_STR((char *) "tags", (char *) "", CFGF_NONE),
207 CFG_BOOL((char *) "float", cfg_false, CFGF_NONE),
208 CFG_END()
210 static cfg_opt_t rules_opts[] =
212 CFG_SEC((char *) "rule", rule_opts, CFGF_MULTI),
213 CFG_END()
215 static cfg_opt_t key_opts[] =
217 CFG_STR_LIST((char *) "modkey", (char *) "{Mod4}", CFGF_NONE),
218 CFG_STR((char *) "key", (char *) "None", CFGF_NONE),
219 CFG_STR((char *) "command", (char *) "", CFGF_NONE),
220 CFG_STR((char *) "arg", NULL, CFGF_NONE),
221 CFG_END()
223 static cfg_opt_t keys_opts[] =
225 CFG_STR((char *) "modkey", (char *) "Mod4", CFGF_NONE),
226 CFG_SEC((char *) "key", key_opts, CFGF_MULTI),
227 CFG_END()
229 static cfg_opt_t opts[] =
231 CFG_SEC((char *) "general", general_opts, CFGF_NONE),
232 CFG_SEC((char *) "colors", colors_opts, CFGF_NONE),
233 CFG_SEC((char *) "screen", screen_opts, CFGF_TITLE | CFGF_MULTI),
234 CFG_SEC((char *) "layouts", layouts_opts, CFGF_NONE),
235 CFG_SEC((char *) "rules", rules_opts, CFGF_NONE),
236 CFG_SEC((char *) "keys", keys_opts, CFGF_NONE),
237 CFG_END()
239 cfg_t *cfg, *cfg_general, *cfg_colors, *cfg_screen, *cfg_statusbar,
240 *cfg_tags, *cfg_layouts, *cfg_rules, *cfg_keys, *cfgsectmp;
241 int i = 0, k = 0, ret;
242 unsigned int j = 0;
243 const char *tmp, *homedir;
244 char *confpath, buf[2];
245 KeySym tmp_key;
246 ssize_t confpath_len;
248 if(confpatharg)
249 confpath = a_strdup(confpatharg);
250 else
252 homedir = getenv("HOME");
253 confpath_len = a_strlen(homedir) + a_strlen(AWESOME_CONFIG_FILE) + 2;
254 confpath = p_new(char, confpath_len);
255 a_strcpy(confpath, confpath_len, homedir);
256 a_strcat(confpath, confpath_len, "/");
257 a_strcat(confpath, confpath_len, AWESOME_CONFIG_FILE);
260 awesomeconf->configpath = a_strdup(confpath);
262 a_strcpy(awesomeconf->statustext, sizeof(awesomeconf->statustext), "awesome-" VERSION " (" RELEASE ")");
264 awesomeconf->phys_screen = get_phys_screen(awesomeconf->display, awesomeconf->screen);
266 cfg = cfg_init(opts, CFGF_NONE);
268 ret = cfg_parse(cfg, confpath);
269 if(ret == CFG_FILE_ERROR)
270 perror("awesome: parsing configuration file failed");
271 else if(ret == CFG_PARSE_ERROR)
272 cfg_error(cfg, "awesome: parsing configuration file %s failed.\n", confpath);
274 /* get the right screen section */
275 snprintf(buf, sizeof(buf), "%d", awesomeconf->screen);
276 cfg_screen = cfg_gettsec(cfg, "screen", buf);
277 if(!cfg_screen)
278 cfg_screen = cfg_getsec(cfg, "screen");
280 /* get screen specific sections */
281 cfg_statusbar = cfg_getsec(cfg_screen, "statusbar");
282 cfg_tags = cfg_getsec(cfg_screen, "tags");
284 /* get general sections */
285 cfg_general = cfg_getsec(cfg, "general");
286 cfg_colors = cfg_getsec(cfg, "colors");
287 cfg_layouts = cfg_getsec(cfg, "layouts");
288 cfg_rules = cfg_getsec(cfg, "rules");
289 cfg_keys = cfg_getsec(cfg, "keys");
291 /* General section */
293 awesomeconf->borderpx = cfg_getint(cfg_general, "border");
294 awesomeconf->snap = cfg_getint(cfg_general, "snap");
295 awesomeconf->resize_hints = cfg_getbool(cfg_general, "resize_hints");
296 awesomeconf->opacity_unfocused = cfg_getint(cfg_general, "opacity_unfocused");
297 awesomeconf->focus_move_pointer = cfg_getbool(cfg_general, "focus_move_pointer");
298 awesomeconf->allow_lower_floats = cfg_getbool(cfg_general, "allow_lower_floats");
299 awesomeconf->font = XftFontOpenName(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_general, "font"));
300 if(!awesomeconf->font)
301 eprint("awesome: cannot init font\n");
303 /* Colors */
304 awesomeconf->colors_normal[ColBorder] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "normal_border"));
305 awesomeconf->colors_normal[ColBG] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "normal_bg"));
306 awesomeconf->colors_normal[ColFG] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "normal_fg"));
307 awesomeconf->colors_selected[ColBorder] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_border"));
308 awesomeconf->colors_selected[ColBG] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_bg"));
309 awesomeconf->colors_selected[ColFG] = initxcolor(awesomeconf->display, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_fg"));
311 /* Statusbar */
312 tmp = cfg_getstr(cfg_statusbar, "position");
314 if(tmp && !a_strncmp(tmp, "off", 6))
315 awesomeconf->statusbar.dposition = BarOff;
316 else if(tmp && !a_strncmp(tmp, "bottom", 6))
317 awesomeconf->statusbar.dposition = BarBot;
318 else
319 awesomeconf->statusbar.dposition = BarTop;
321 awesomeconf->statusbar.position = awesomeconf->statusbar.dposition;
323 /* Layouts */
325 awesomeconf->nlayouts = cfg_size(cfg_layouts, "layout");
326 awesomeconf->layouts = p_new(Layout, awesomeconf->nlayouts);
327 for(i = 0; i < awesomeconf->nlayouts; i++)
329 cfgsectmp = cfg_getnsec(cfg_layouts, "layout", i);
330 awesomeconf->layouts[i].arrange = name_func_lookup(cfg_title(cfgsectmp), LayoutsList);
331 if(!awesomeconf->layouts[i].arrange)
333 fprintf(stderr, "awesome: unknown layout #%d in configuration file\n", i);
334 awesomeconf->layouts[i].symbol = NULL;
335 continue;
337 awesomeconf->layouts[i].symbol = a_strdup(cfg_getstr(cfgsectmp, "symbol"));
340 awesomeconf->nmaster = cfg_getint(cfg_layouts, "nmaster");
341 awesomeconf->ncol = cfg_getint(cfg_layouts, "ncol");
343 if(!awesomeconf->nlayouts)
344 eprint("awesome: fatal: no default layout available\n");
346 /* Rules */
348 awesomeconf->nrules = cfg_size(cfg_rules, "rule");
349 awesomeconf->rules = p_new(Rule, awesomeconf->nrules);
350 for(i = 0; i < awesomeconf->nrules; i++)
352 cfgsectmp = cfg_getnsec(cfg_rules, "rule", i);
353 awesomeconf->rules[i].prop = a_strdup(cfg_getstr(cfgsectmp, "name"));
354 awesomeconf->rules[i].tags = a_strdup(cfg_getstr(cfgsectmp, "tags"));
355 if(!a_strlen(awesomeconf->rules[i].tags))
356 awesomeconf->rules[i].tags = NULL;
357 awesomeconf->rules[i].isfloating = cfg_getbool(cfgsectmp, "float");
360 compileregs(awesomeconf->rules, awesomeconf->nrules);
362 /* Tags */
364 awesomeconf->ntags = cfg_size(cfg_tags, "tag");
365 awesomeconf->tags = p_new(Tag, awesomeconf->ntags);
366 for(i = 0; i < awesomeconf->ntags; i++)
368 cfgsectmp = cfg_getnsec(cfg_tags, "tag", i);
369 awesomeconf->tags[i].name = a_strdup(cfg_title(cfgsectmp));
370 awesomeconf->tags[i].selected = False;
371 awesomeconf->tags[i].was_selected = False;
372 tmp = cfg_getstr(cfgsectmp, "layout");
373 for(k = 0; k < awesomeconf->nlayouts; k++)
374 if(awesomeconf->layouts[k].arrange == name_func_lookup(tmp, LayoutsList))
375 break;
376 if(k == awesomeconf->nlayouts)
377 k = 0;
378 awesomeconf->tags[i].layout = &awesomeconf->layouts[k];
379 awesomeconf->tags[i].mwfact = cfg_getfloat(cfgsectmp, "mwfact");
383 if(!awesomeconf->ntags)
384 eprint("awesome: fatal: no tags found in configuration file\n");
386 /* select first tag by default */
387 awesomeconf->tags[0].selected = True;
388 awesomeconf->tags[0].was_selected = True;
390 /* Keys */
391 tmp_key = key_mask_lookup(cfg_getstr(cfg_keys, "modkey"));
392 awesomeconf->modkey = tmp_key ? tmp_key : Mod4Mask;
393 awesomeconf->numlockmask = get_numlockmask(awesomeconf->display);
395 awesomeconf->nkeys = cfg_size(cfg_keys, "key");
396 awesomeconf->keys = p_new(Key, awesomeconf->nkeys);
397 for(i = 0; i < awesomeconf->nkeys; i++)
399 cfgsectmp = cfg_getnsec(cfg_keys, "key", i);
400 for(j = 0; j < cfg_size(cfgsectmp, "modkey"); j++)
401 awesomeconf->keys[i].mod |= key_mask_lookup(cfg_getnstr(cfgsectmp, "modkey", j));
402 awesomeconf->keys[i].keysym = XStringToKeysym(cfg_getstr(cfgsectmp, "key"));
403 awesomeconf->keys[i].func = name_func_lookup(cfg_getstr(cfgsectmp, "command"), UicbList);
404 awesomeconf->keys[i].arg = a_strdup(cfg_getstr(cfgsectmp, "arg"));
407 /* Free! Like a river! */
408 cfg_free(cfg);
409 p_delete(&confpath);
412 static unsigned int
413 get_numlockmask(Display *disp)
415 XModifierKeymap *modmap;
416 unsigned int mask = 0;
417 int i, j;
419 modmap = XGetModifierMapping(disp);
420 for(i = 0; i < 8; i++)
421 for(j = 0; j < modmap->max_keypermod; j++)
422 if(modmap->modifiermap[i * modmap->max_keypermod + j]
423 == XKeysymToKeycode(disp, XK_Num_Lock))
424 mask = (1 << i);
426 XFreeModifiermap(modmap);
428 return mask;
431 /** Initialize color from X side
432 * \param colorstr Color code
433 * \param disp Display ref
434 * \param scr Screen number
435 * \return XColor pixel
437 static XColor
438 initxcolor(Display *disp, int scr, const char *colstr)
440 XColor color;
441 if(!XAllocNamedColor(disp, DefaultColormap(disp, scr), colstr, &color, &color))
442 die("awesome: error, cannot allocate color '%s'\n", colstr);
443 return color;
446 void
447 uicb_reloadconfig(awesome_config *awesomeconf,
448 const char *arg __attribute__ ((unused)))
450 int i, j, tag, screen, screen_count = get_screen_count(awesomeconf->display);
451 awesome_config *awesomeconf_first = &awesomeconf[-awesomeconf->screen];
452 int *old_ntags, old_c_ntags, new_c_ntags, **mapping;
453 char ***savetagnames;
454 Client ***savetagclientsel;
455 char *configpath = a_strdup(awesomeconf_first->configpath);
456 Bool ***savetagselected;
457 Bool *old_c_tags;
458 Client *c, *clients;
460 /* Save tag information */
461 savetagnames = p_new(char **, screen_count);
462 savetagclientsel = p_new(Client **, screen_count);
463 savetagselected = p_new(Bool **, screen_count);
464 clients = *awesomeconf_first->clients;
465 for (screen = 0; screen < screen_count; screen ++)
467 savetagnames[screen] = p_new(char *, awesomeconf_first[screen].ntags);
468 savetagclientsel[screen] = p_new(Client *, awesomeconf_first[screen].ntags);
469 savetagselected[screen] = p_new(Bool *, awesomeconf_first[screen].ntags);
470 for (tag = 0; tag < awesomeconf_first[screen].ntags; tag++)
472 savetagnames[screen][tag] = a_strdup(awesomeconf_first[screen].tags[tag].name);
473 savetagclientsel[screen][tag] = awesomeconf_first[screen].tags[tag].client_sel;
474 savetagselected[screen][tag] = p_new(Bool, 2);
475 savetagselected[screen][tag][0] = awesomeconf_first[screen].tags[tag].selected;
476 savetagselected[screen][tag][1] = awesomeconf_first[screen].tags[tag].was_selected;
479 old_ntags = p_new(int, screen_count);
480 for (screen = 0; screen < screen_count; screen ++)
481 old_ntags[screen] = awesomeconf_first[screen].ntags;
483 mapping = p_new(int*, screen_count);
484 for(screen = 0; screen < screen_count; screen++)
486 /* Cleanup screens and reload their config. */
487 cleanup_screen(&awesomeconf_first[screen]);
488 setup_screen(&awesomeconf_first[screen], configpath);
490 /* Compute a mapping of tags between the old and new config, based on
491 * tag names. */
492 mapping[screen] = p_new(int, awesomeconf_first[screen].ntags);
493 for (i = 0; i < awesomeconf_first[screen].ntags; i ++)
495 mapping[screen][i] = -1;
496 for (j = 0; j < old_ntags[screen]; j ++)
497 if (!a_strcmp(savetagnames[screen][j], awesomeconf_first[screen].tags[i].name))
499 mapping[screen][i] = j;
500 break;
504 /* Reinitialize the tags' client lists and selected client. */
505 *awesomeconf_first[screen].clients = clients;
506 for (tag = 0; tag < awesomeconf_first[screen].ntags; tag++)
507 if (mapping[screen][tag] >= 0)
509 awesomeconf_first[screen].tags[tag].client_sel = savetagclientsel[screen][mapping[screen][tag]];
510 awesomeconf_first[screen].tags[tag].selected = savetagselected[screen][mapping[screen][tag]][0];
511 awesomeconf_first[screen].tags[tag].was_selected = savetagselected[screen][mapping[screen][tag]][1];
513 drawstatusbar(&awesomeconf_first[screen]);
516 /* Reinitialize the 'tags' array of each client.
517 * Clients are assigned to the tags of the same name as in the previous
518 * awesomerc, or to tag #1 otherwise. */
519 for (c = *awesomeconf_first->clients; c; c = c->next)
521 old_c_ntags = old_ntags[c->screen];
522 new_c_ntags = awesomeconf_first[c->screen].ntags;
524 old_c_tags = c->tags;
525 c->tags = p_new(Bool, new_c_ntags);
526 for (i = 0; i < new_c_ntags; i ++)
527 if (mapping[c->screen][i] >= 0)
528 c->tags[i] = old_c_tags[mapping[c->screen][i]];
529 p_delete(&old_c_tags);
531 for (i = 0; i < new_c_ntags && c->tags[i] == 0; i++) {}
532 if (i == new_c_ntags)
533 c->tags[0] = 1;
535 saveprops(c, awesomeconf_first[c->screen].ntags);
536 if (!loadprops(c, awesomeconf_first[c->screen].ntags))
537 applyrules(c, awesomeconf_first);
540 /* Cleanup after ourselves */
541 for(screen = 0; screen < screen_count; screen++)
543 for(i = 0; i < old_ntags[screen]; i++)
545 p_delete(&savetagnames[screen][i]);
546 p_delete(&savetagselected[screen][i]);
548 p_delete(&savetagselected[screen]);
549 p_delete(&savetagnames[screen]);
550 p_delete(&mapping[screen]);
551 p_delete(&savetagclientsel[screen]);
553 p_delete(&mapping);
554 p_delete(&savetagselected);
555 p_delete(&savetagnames);
556 p_delete(&old_ntags);
557 p_delete(&savetagclientsel);
558 p_delete(&configpath);
559 for (screen = 0; screen < screen_count; screen ++)
560 arrange(&awesomeconf_first[screen]);
563 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99