diff --git a/wmfs2/src/barwin.c b/wmfs2/src/barwin.c index 15d6b46..288301c 100755 --- a/wmfs2/src/barwin.c +++ b/wmfs2/src/barwin.c @@ -20,7 +20,7 @@ Barwin* barwin_new(Window parent, int x, int y, int w, int h, Color fg, Color bg, bool entermask) { - Barwin *b; + Barwin *b = (Barwin*)xcalloc(1, sizeof(Barwin)); XSetWindowAttributes at = { .override_redirect = True, @@ -28,8 +28,6 @@ barwin_new(Window parent, int x, int y, int w, int h, Color fg, Color bg, bool e .event_mask = BARWIN_MASK }; - b = (Barwin*)xcalloc(1, sizeof(Barwin)); - if(entermask) at.event_mask |= BARWIN_ENTERMASK; @@ -76,9 +74,6 @@ barwin_remove(Barwin *b) void barwin_resize(Barwin *b, int w, int h) { - if(b->geo.w == w && b->geo.h == h) - return; - /* Frame */ XFreePixmap(W->dpy, b->dr); diff --git a/wmfs2/src/parse.c b/wmfs2/src/parse.c new file mode 100755 index 0000000..337d0bb --- /dev/null +++ b/wmfs2/src/parse.c @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "util.h" + +extern char *__progname; + +enum keyword_t { SEC_START, SEC_END, INCLUDE, WORD, EQUAL, LIST_START, LIST_END, NONE }; + +#ifdef DEBUG +static struct { + const char *name; + enum keyword_t type; +} kw_t_name[] = { + {"SEC_START", SEC_START}, + {"SEC_END", SEC_END}, + {"INCLUDE", INCLUDE}, + {"WORD", WORD}, + {"EQUAL", EQUAL}, + {"LIST_START", LIST_START}, + {"LIST_END", LIST_END}, + {"NONE", NONE}, +}; +#endif + +struct files { + char *name; + struct files *parent; +}; + +struct keyword { + enum keyword_t type; + /* if WORD */ + int line; + struct files *file; + char *name; + struct keyword *next; +}; + +struct state { + bool quote; + bool comment; + char quote_char; +}; + +/* TO REMOVE (use a identifier for config and fallback XDG in api functions) */ +TAILQ_HEAD(, conf_sec) config; +static struct keyword *keywords = NULL; + +static struct keyword * +push_keyword(struct keyword *tail, enum keyword_t type, char *buf, size_t *offset, struct files *file, int line) +{ + struct keyword *kw; +#ifdef DEBUG + int i = 0; +#endif + + if (type == WORD && *offset == 0) + return tail; + + kw = xcalloc(1, sizeof(*kw)); + kw->type = type; + kw->line = line; + kw->file = file; + kw->next = NULL; + + if (*offset != 0) { + buf[*offset] = '\0'; + if (!strcmp(buf, INCLUDE_CMD)) + kw->type = INCLUDE; + else + kw->name = strdup(buf); + *offset = 0; + } + else + kw->name = NULL; + + if (tail) + tail->next = kw; + +#ifdef DEBUG + for (i = 0; kw_t_name[i].type != NONE; i++) { + if (kw_t_name[i].type == kw->type) { + warnx("%s %s %s:%d\n", kw_t_name[i].name, + (kw->name) ? kw->name : "", + kw->file->name, kw->line); + } + } +#endif + + return kw; +} + +static void +syntax(struct keyword *kw, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "%s:", __progname); + + if (kw && kw->file && kw->file->name) + fprintf(stderr, "%s:%d", kw->file->name, kw->line); + + if (kw && kw->name) + fprintf(stderr, ", near '%s'", kw->name); + fprintf(stderr, ": "); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + + +#define PUSH_KEYWORD(type) tail = push_keyword(tail, type, bufname, &j, file, line) +static struct keyword * +parse_keywords(const char *filename) +{ + int fd; + struct stat st; + char *buf; + + struct keyword *head = NULL; + struct keyword *tail = NULL; + struct files *file; + enum keyword_t type; /* keyword type to push */ + struct state s = { False, False, '\0'}; + char *bufname; + char path[PATH_MAX]; + size_t i, j; + int line; + bool error = False; + + + if ((fd = open(filename, O_RDONLY)) == -1 || stat(filename, &st) == -1) { + warn("%s", filename); + return NULL; + } + + if (st.st_size == 0) { + warnx("%s: empty file", filename); + close(fd); + return NULL; + } + + if (!realpath(filename, path)) { + warn("%s", filename); + close(fd); + return NULL; + } + + buf = xmalloc(1, st.st_size+1); + + if (read(fd, buf, st.st_size) == -1) { + warn("%s", filename); + free(buf); + close(fd); + return NULL; + } + + buf[st.st_size] = '\0'; + + file = xcalloc(1, sizeof(*file)); + bufname = xcalloc(BUFSIZ, sizeof(*bufname)); + file->name = strdup(path); + file->parent = NULL; + + for(i = 0, j = 0, line = 1; i < (size_t)st.st_size; i++) { + + if (!head && tail) + head = tail; + + if (buf[i] == '\n' && s.comment == True) { + line++; + s.comment = False; + continue; + } + + if (buf[i] == '#' && s.quote == False) { + s.comment = True; + continue; + } + + if (s.comment == True) + continue; + + if (s.quote == True && buf[i] == s.quote_char) { + /* end of quotted string */ + PUSH_KEYWORD(WORD); + s.quote = False; + continue; + } + + if (s.quote == False) { + if ((buf[i] == '"' || buf[i] == '\'')) { + PUSH_KEYWORD(WORD); + /* begin quotted string */ + s.quote_char = buf[i]; + s.quote = True; + continue; + } + + if (buf[i] == '[') { + PUSH_KEYWORD(WORD); + if (buf[i+1] == '/') { + i +=2; + type = SEC_END; + } + else { + i++; + type = SEC_START; + } + + /* get section name */ + while (buf[i] != ']') { + + if (i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1)) { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = True; + break; + } + + bufname[j++] = buf[i++]; + } + PUSH_KEYWORD(type); + continue; + } + + if (buf[i] == '{') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_START); + continue; + } + + if (buf[i] == '}') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_END); + continue; + } + + if (buf[i] == ',') { + PUSH_KEYWORD(WORD); + continue; + } + + if (buf[i] == '=') { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(EQUAL); + continue; + } + + if (strchr("\t\n ", buf[i])) { + PUSH_KEYWORD(WORD); + + if (buf[i] == '\n') + line++; + + continue; + } + } /* s.quote == False */ + + if (j >= (BUFSIZ - 1)) { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = True; + break; + } + + bufname[j++] = buf[i]; + } + + free(buf); + free(bufname); + close(fd); + warnx("%s read", file->name); + + return (error ? NULL: head); +} + +/* + * return NULL on failure and head->next if + * no config found (of file doesn't exist) + * NOTE to devs: head->name is the file to include + */ +static struct keyword * +include(struct keyword *head) +{ + struct keyword *kw; + struct keyword *tail; + struct files *file; + struct passwd *user; + char *filename = NULL; + char *base = NULL; + + head = head->next; + + if (!head || head->type != WORD) { + syntax(head, "missing filename to include"); + return NULL; + } + + /* replace ~ by user directory */ + if (head->name && head->name[0] == '~') { + if ( (user = getpwuid(getuid())) && user->pw_dir) + xasprintf(&filename, "%s%s", user->pw_dir, head->name+1); + else if (getenv("HOME")) + xasprintf(&filename, "%s%s", getenv("HOME"), head->name+1); + else /* to warning ? */ + filename = head->name; + } + /* relative path from parent file */ + else if (head->name && head->name[0] != '/') { + base = strdup(head->file->name); + xasprintf(&filename, "%s/%s", dirname(base), head->name); + free(base); + } + else + filename = head->name; + + if (!(kw = parse_keywords(filename))) { + warnx("no config found in include file %s", head->name); + + if (filename != head->name) + free(filename); + + return NULL; + } + + kw->file->parent = head->file; + + /* detect circular include */ + for (file = kw->file->parent; file != NULL; file = file->parent) { + if (!strcmp(file->name, kw->file->name)) { + syntax(kw, "circular include of %s", kw->file->name); + + if (filename != head->name) + free(filename); + + return NULL; + } + } + + if (filename != head->name) + free(filename); + + head = head->next; + + if (kw) { + for (tail = kw; tail->next; tail = tail->next); + tail->next = head; + } + + return kw; +} + +static void * +free_opt(struct conf_opt *o) +{ + free(o); + return NULL; +} + +static struct conf_opt * +get_option(struct keyword **head) +{ + struct conf_opt *o; + size_t j = 0; + struct keyword *kw = *head; + + o = xcalloc(1, sizeof(*o)); + o->name = kw->name; + o->used = False; + o->line = kw->line; + o->filename = kw->file->name; + + kw = kw->next; + + if (kw->type != EQUAL) { + syntax(kw, "missing '=' here"); + return free_opt(o); + } + + kw = kw->next; + + if (!kw) { + syntax(kw, "missing value"); + return free_opt(o); + } + + + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_opt(o); + break; + case WORD: + o->val[0] = kw->name; + o->val[1] = NULL; + kw = kw->next; + break; + case LIST_START: + kw = kw->next; + while (kw && kw->type != LIST_END) { + switch (kw->type) { + case WORD: + if (j >= (PARSE_MAX_LIST - 1)) { + syntax(kw, "too much values in list"); + return free_opt(o); + } + o->val[j++] = kw->name; + kw = kw->next; + break; + case INCLUDE: + if (!(kw = include(kw))) + return free_opt(o); + break; + default: + syntax(kw, "declaration into a list"); + return free_opt(o); + break; + } + } + + if (!kw) { + syntax(kw, "list unclosed"); + return free_opt(o); + } + + kw = kw->next; + break; + default: + syntax(kw, "missing value"); + return free_opt(o); + break; + } + + *head = kw; + return o; +} + +static void * +free_sec(struct conf_sec *sec) +{ + struct conf_opt *o; + struct conf_sec *s; + + if (sec) { + while (!SLIST_EMPTY(&sec->optlist)) { + o = SLIST_FIRST(&sec->optlist); + SLIST_REMOVE_HEAD(&sec->optlist, entry); + free_opt(o); + } + while (!TAILQ_EMPTY(&sec->sub)) { + s = TAILQ_FIRST(&sec->sub); + TAILQ_REMOVE(&sec->sub, s, entry); + free_sec(s); + } + free(sec); + } + return NULL; +} + +static struct conf_sec * +get_section(struct keyword **head) +{ + struct conf_sec *s; + struct conf_opt *o; + struct conf_sec *sub; + struct keyword *kw = *head; + + s = xcalloc(1, sizeof(*s)); + s->name = kw->name; + TAILQ_INIT(&s->sub); + SLIST_INIT(&s->optlist); + + kw = kw->next; + + while (kw && kw->type != SEC_END) { + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_sec(s); + break; + case SEC_START: + if (!(sub = get_section(&kw))) + return free_sec(s); + TAILQ_INSERT_TAIL(&s->sub, sub, entry); + s->nsub++; + break; + case WORD: + if (!(o = get_option(&kw))) + return free_sec(s); + SLIST_INSERT_HEAD(&s->optlist, o, entry); + s->nopt++; + break; + default: + syntax(kw, "syntax error"); + return free_sec(s); + break; + } + } + + if (!kw || strcmp(kw->name, s->name)) { + syntax(kw, "missing end section %s", s->name); + return free_sec(s); + } + + kw = kw->next; + *head = kw; + + return s; +} + +int +free_conf(void) +{ + struct conf_sec *s; + struct keyword *kw, *nkw; + struct files **f = NULL; + int i, nf = 0; + + while (!TAILQ_EMPTY(&config)) { + s = TAILQ_FIRST(&config); + TAILQ_REMOVE(&config, s, entry); + free_sec(s); + } + + kw = keywords; + + while (kw) { + nkw = kw->next; + + free(kw->name); + + for (i = 0; i < nf; i++) { + if (f[i] == kw->file) { + if (!(f = realloc(f, sizeof(*f) * (++i)))) + err(EXIT_FAILURE, "realloc"); + f[i-1] = kw->file; + } + } + + kw = nkw; + } + + if (nf > 0) { + for (i = 0; i < nf; i++) { + free(f[i]->name); + free(f[i]); + } + free(f); + } + return -1; +} + +int +get_conf(const char *filename) +{ + struct conf_sec *s; + struct keyword *head, *kw; + + kw = head = parse_keywords(filename); + + if (!head) + return -1; /* TODO ERREUR */ + + keywords = head; + + TAILQ_INIT(&config); + + while (kw) { + switch (kw->type) { + case INCLUDE: + if (!(kw = include(kw))) + return free_conf(); + break; + case SEC_START: + if (!(s = get_section(&kw))) + return free_conf(); + TAILQ_INSERT_TAIL(&config, s, entry); + break; + default: + syntax(kw, "out of any section"); + return free_conf(); + break; + } + } + return 0; +} + diff --git a/wmfs2/src/parse.h b/wmfs2/src/parse.h new file mode 100755 index 0000000..e6db0a6 --- /dev/null +++ b/wmfs2/src/parse.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PARSE_H +#define PARSE_H + +#include "wmfs.h" + +#define INCLUDE_CMD "@include" +#define PARSE_MAX_LIST 32 + +struct conf_opt { + char *name; + char *val[PARSE_MAX_LIST]; + size_t nval; + bool used; + int line; + char *filename; + SLIST_ENTRY(conf_opt) entry; +}; + +struct conf_sec { + char *name; + SLIST_HEAD(, conf_opt) optlist; + TAILQ_HEAD(, conf_sec) sub; + size_t nopt; + size_t nsub; + TAILQ_ENTRY(conf_sec) entry; +}; + +struct opt_type { + long int num; + float fnum; + bool boolean; + char *str; +}; + +/* + * Create config from file + * return -1 on failure + */ +int get_conf(const char *); + +/* + * Print unused option name from section s (and subsections). + * If s == NULL print unused option name for all config struct. + */ +void print_unused(struct conf_sec *s); + +/* + * Free the config struct. + * WARNING: This make all string + * returned by fetch_(opt|section)(_first) unusable. + */ +int free_conf(void); + +/* + * Get all subsection matching the given name on the given + * section. + * If section == NULL, return subsections from root section. + * Return a NULL terminated array. + * Subsections are returned in order as they are in config file + * WARNING : This MUST be free() after use. + */ +struct conf_sec **fetch_section(struct conf_sec *, char *); + +/* + * Get first subsection matching the given name + * on the given section. (first found on the file) + */ +struct conf_sec *fetch_section_first(struct conf_sec *, char *); + +/* + * Count member of a conf_sec ** + */ +size_t fetch_section_count(struct conf_sec **); + +/* + * Return all options matching the given name on the given subsection. + * If none match or section == NULL return opt_type build with the + * given default param. + * WARNING: This MUST be free() after use. + * WARNING: The string member is directly taken from the config struct. + * WARNING: Returned in reverse order as they are in config file. + * (I think the last option MUST overwrite all others) + */ +struct opt_type fetch_opt_first(struct conf_sec *, char *, char *); + +/* + * Get first (last in config file) option matching the given name + * on the given section. + * WARNING: The string member is directly taken from the config struct. + */ +struct opt_type *fetch_opt(struct conf_sec *, char *, char *); + +/* + * Count member of a opt_type * + */ +size_t fetch_opt_count(struct opt_type *); + +#endif /* PARSE_H */ diff --git a/wmfs2/src/parse_api.c b/wmfs2/src/parse_api.c new file mode 100755 index 0000000..de850ff --- /dev/null +++ b/wmfs2/src/parse_api.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include +#include +#include + +#include "wmfs.h" +#include "parse.h" +#include "util.h" + +extern TAILQ_HEAD(, conf_sec) config; + +static const struct opt_type opt_type_null = { 0, 0, False, NULL }; + +static struct opt_type +string_to_opt(char *s) +{ + struct opt_type ret = opt_type_null; + + if (!s || !strlen(s)) + return ret; + + ret.num = strtol(s, (char**)NULL, 10); + ret.fnum = strtod(s, NULL); + + if (!strcmp(s, "true") || !strcmp(s, "True") || + !strcmp(s, "TRUE") || !strcmp(s, "1")) + ret.boolean = True; + else + ret.boolean = False; + + ret.str = s; + + return ret; +} + + +void +print_unused(struct conf_sec *sec) +{ + struct conf_sec *s; + struct conf_opt *o; + + if (!sec) + { + TAILQ_FOREACH(s, &config, entry) + print_unused(s); + return; + } + + SLIST_FOREACH(o, &sec->optlist, entry) + if (o->used == False) + warnx("%s:%d, unused param %s", + o->filename, o->line, o->name); + + TAILQ_FOREACH(s, &sec->sub, entry) + if (!TAILQ_EMPTY(&s->sub)) + print_unused(s); +} + +struct conf_sec ** +fetch_section(struct conf_sec *s, char *name) +{ + struct conf_sec **ret; + struct conf_sec *sec; + size_t i = 0; + + if (!name) + return NULL; + + if (!s) { + ret = xcalloc(2, sizeof(struct conf_sec *)); + TAILQ_FOREACH(sec, &config, entry) + if (!strcmp(sec->name, name)) { + ret[0] = sec; + ret[1] = NULL; + break; + } + } + else { + ret = xcalloc(s->nsub+1, sizeof(struct conf_sec *)); + TAILQ_FOREACH(sec, &s->sub, entry) { + if (!strcmp(sec->name, name) && i < s->nsub) + ret[i++] = sec; + } + ret[i] = NULL; + } + return ret; +} + +struct conf_sec * +fetch_section_first(struct conf_sec *s, char *name) +{ + struct conf_sec *sec, *ret = NULL; + + if (!name) + return NULL; + + if (!s) + { + TAILQ_FOREACH(sec, &config, entry) + if(sec->name && !strcmp(sec->name, name)) { + ret = sec; + break; + } + } + else + { + TAILQ_FOREACH(sec, &s->sub, entry) + if (sec->name && !strcmp(sec->name, name)) { + ret = sec; + break; + } + } + + return ret; +} + +size_t +fetch_section_count(struct conf_sec **s) +{ + size_t ret; + for (ret = 0; s[ret]; ret++); + return ret; +} + +struct opt_type * +fetch_opt(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + struct opt_type *ret; + size_t i = 0; + + if (!name) + return NULL; + + ret = xcalloc(10, sizeof(struct opt_type)); + + if (s) { + SLIST_FOREACH(o, &s->optlist, entry) + if (!strcmp(o->name, name)) { + while (o->val[i]) { + o->used = True; + ret[i] = string_to_opt(o->val[i]); + i++; + } + ret[i] = opt_type_null; + return ret; + } + } + + ret[0] = string_to_opt(dfl); + ret[1] = opt_type_null; + + return ret; +} + +struct opt_type +fetch_opt_first(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + + if (!name) + return opt_type_null; + else if (s) + SLIST_FOREACH(o, &s->optlist, entry) + if (!strcmp(o->name, name)) { + o->used = True; + return string_to_opt(o->val[0]); + } + return string_to_opt(dfl); +} + +size_t +fetch_opt_count(struct opt_type *o) +{ + size_t ret; + for(ret = 0; o[ret].str; ret++); + return ret; +} + + diff --git a/wmfs2/src/util.c b/wmfs2/src/util.c index c7777c2..90c4be0 100755 --- a/wmfs2/src/util.c +++ b/wmfs2/src/util.c @@ -43,6 +43,27 @@ xcalloc(size_t nmemb, size_t size) return ret; } +/** asprintf wrapper + * \param strp target string + * \param fmt format + * \return non zero integer + */ +int +xasprintf(char **strp, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = vasprintf(strp, fmt, args); + va_end(args); + + if (ret == -1) + err(EXIT_FAILURE, "asprintf(%s)", fmt); + + return ret; +} + /** Execute a system command * \param cmd Command * \return child pid diff --git a/wmfs2/src/util.h b/wmfs2/src/util.h index 09f2f10..6faa499 100755 --- a/wmfs2/src/util.h +++ b/wmfs2/src/util.h @@ -23,6 +23,7 @@ void *xmalloc(size_t nmemb, size_t size); void *xcalloc(size_t nmemb, size_t size); +int xasprintf(char **strp, const char *fmt, ...); pid_t spawn(const char *format, ...); #endif /* UTIL_H */ diff --git a/wmfs2/src/wmfs.h b/wmfs2/src/wmfs.h index f568a44..b507c10 100755 --- a/wmfs2/src/wmfs.h +++ b/wmfs2/src/wmfs.h @@ -40,6 +40,7 @@ typedef struct Scr33n Scr33n; typedef struct Tag Tag; typedef struct Client Client; typedef struct Keybind Keybind; +typedef struct Mousebind Mousebind; struct Geo { @@ -98,6 +99,28 @@ struct Keybind SLIST_ENTRY(Keybind) next; }; +struct Mousebind +{ + unsigned int button; + Geo area; + bool use_area; + void (*func)(Uicb); + Uicb cmd; + SLIST_ENTRY(Keybind) next; +}; + +struct Config +{ + /* Misc section */ + struct + { + char *font; + bool focus_follow_mouse; + bool focus_follow_movement; + bool focus_pointer_click; + } misc; +}; + /* Global struct */ struct Wmfs {