diff --git a/client.c b/client.c index ada6088..01c8833 100644 --- a/client.c +++ b/client.c @@ -71,7 +71,7 @@ Client* window_build_client(Window win) ? 1:0; // detect our own title bars - if (TITLE) + if (settings.title) for_monitors(i, m) for_spots(j) if (m->bars[j] && m->bars[j]->window == c->window) { c->ours = 1; c->manage = 0; break; } @@ -160,9 +160,9 @@ void client_free(Client *c) void client_update_border(Client *c) { XColor color; Colormap map = DefaultColormap(display, DefaultScreen(display)); - char *colorname = c->window == current ? BORDER_FOCUS: (c->urgent ? BORDER_URGENT: BORDER_BLUR); + char *colorname = c->window == current ? settings.border_focus: (c->urgent ? settings.border_urgent: settings.border_blur); XSetWindowBorder(display, c->window, XAllocNamedColor(display, map, colorname, &color, &color) ? color.pixel: None); - XSetWindowBorderWidth(display, c->window, c->full ? 0: BORDER); + XSetWindowBorderWidth(display, c->window, c->full ? 0: settings.border); } int client_send_wm_protocol(Client *c, Atom protocol) @@ -206,8 +206,8 @@ void client_place_spot(Client *c, int spot, int mon, int force) { x += (w - c->attr.width)/2; y += (h - c->attr.height)/2; - w = c->attr.width + BORDER*2; - h = c->attr.height + BORDER*2; + w = c->attr.width + settings.border * 2; + h = c->attr.height + settings.border * 2; } else if (c->full) @@ -234,7 +234,7 @@ void client_place_spot(Client *c, int spot, int mon, int force) w = m->w; } - w -= BORDER*2; h -= BORDER*2; + w -= settings.border * 2; h -= settings.border * 2; int sw = w, sh = h; long sr; XSizeHints size; if (XGetWMNormalHints(display, c->window, &size, &sr)) @@ -261,8 +261,8 @@ void client_place_spot(Client *c, int spot, int mon, int force) if (h < sh) y += (sh-h)/2; // bump onto screen - x = MAX(m->x, MIN(x, m->x + m->w - w - BORDER*2)); - y = MAX(m->y, MIN(y, m->y + m->h - h - BORDER*2)); + x = MAX(m->x, MIN(x, m->x + m->w - w - settings.border * 2)); + y = MAX(m->y, MIN(y, m->y + m->h - h - settings.border * 2)); XMoveResizeWindow(display, c->window, x, y, w, h); } @@ -298,7 +298,7 @@ void client_raise_family(Client *c) client_stack_family(c, &raise); - if (!c->full && TITLE) + if (!c->full && settings.title) { // raise spot's title bar in case some other fullscreen or max v/h window has obscured Monitor *m = &monitors[c->monitor]; diff --git a/config.c b/config.c new file mode 100644 index 0000000..8040b7c --- /dev/null +++ b/config.c @@ -0,0 +1,432 @@ +/* + +MIT/X11 License +Copyright (c) 2014 Sean Pringle + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#define CONFIG_BORDER "border" +#define CONFIG_BORDER_BLUR "border_blur" +#define CONFIG_BORDER_FOCUS "border_focus" +#define CONFIG_BORDER_URGENT "border_urgent" +#define CONFIG_GAP "gap" +#define CONFIG_TITLE "title" +#define CONFIG_TITLE_BLUR "title_blur" +#define CONFIG_TITLE_FOCUS "title_focus" +#define CONFIG_TITLE_ELLIPSIS "title_ellipsis" +#define CONFIG_LAYOUTS "layouts" + +#define CONFIG_UINT_NAMES CONFIG_BORDER "|" CONFIG_GAP "|" CONFIG_TITLE_ELLIPSIS +#define CONFIG_STR_NAMES CONFIG_BORDER_BLUR "|" CONFIG_BORDER_FOCUS "|" CONFIG_BORDER_URGENT "|" CONFIG_TITLE "|" CONFIG_TITLE_BLUR "|" CONFIG_TITLE_FOCUS + +#define CONFIG_ACTIONS "action_move_direction|action_focus_direction|action_move|action_focus|action_close|action_cycle|action_raise_nth|action_command|action_find_or_start|action_move_monitor|action_focus_monitor|action_fullscreen|action_maximize_vert|action_maximize_horz|action_maximize" + +void +rtrim(char *str) +{ + char *end = str + strlen(str); + while (end && --end >= str && isspace(*end)) *end = 0; +} + +void +ltrim(char *str) +{ + while (isspace(*str)) memmove(str, str+1, strlen(str+1)+1); +} + +void +trim(char *str) +{ + ltrim(str); + rtrim(str); +} + +void +configure() +{ + unsigned int i; + FILE *file = NULL; + char tmp[1024]; + const char *home = getenv("HOME"); + + settings.border = BORDER; + settings.border_blur = strdup(BORDER_BLUR); + settings.border_focus = strdup(BORDER_FOCUS); + settings.border_urgent = strdup(BORDER_URGENT); + settings.gap = GAP; + settings.title = TITLE ? strdup(TITLE): NULL; + settings.title_blur = strdup(TITLE_BLUR); + settings.title_focus = strdup(TITLE_FOCUS); + settings.title_ellipsis = TITLE_ELLIPSIS; + + settings.layout_count = sizeof(layouts) / sizeof(Layout); + settings.layouts = calloc(settings.layout_count, sizeof(Layout)); + + for (i = 0; i < settings.layout_count; i++) + memmove(&settings.layouts[i], &layouts[i], sizeof(Layout)); + + settings.binding_count = sizeof(keys) / sizeof(Binding); + settings.bindings = calloc(settings.binding_count, sizeof(Binding)); + + for (i = 0; i < settings.binding_count; i++) + memmove(&settings.bindings[i], &keys[i], sizeof(Binding)); + + snprintf(tmp, sizeof(tmp), "%s/.xoatrc", home); + + if (home && (file = fopen(tmp, "r")) && file) + { + while (fgets(tmp, sizeof(tmp), file)) + { + if (regex_match("^[[:space:]]*(" CONFIG_UINT_NAMES ")[[:space:]]+([0-9]+)", tmp)) + { + unsigned n = strtoul(regex_matches[2], NULL, 10); + + char *names[] = { + CONFIG_BORDER, + CONFIG_GAP, + CONFIG_TITLE_ELLIPSIS, + }; + unsigned int *values[] = { + &settings.border, + &settings.gap, + &settings.title_ellipsis, + }; + for (i = 0; i < sizeof(names) / sizeof(char*); i++) + { + if (strcmp(regex_matches[1], names[i]) == 0) + { + fprintf(stderr, "set [%s] to [%u]\n", names[i], n); + *(values[i]) = n; + break; + } + } + } + else + if (regex_match("^[[:space:]]*(" CONFIG_STR_NAMES ")[[:space:]]+(.+)", tmp)) + { + rtrim(regex_matches[2]); + + char *names[] = { + CONFIG_BORDER_BLUR, + CONFIG_BORDER_FOCUS, + CONFIG_BORDER_URGENT, + CONFIG_TITLE, + CONFIG_TITLE_BLUR, + CONFIG_TITLE_FOCUS, + }; + char **values[] = { + &settings.border_blur, + &settings.border_focus, + &settings.border_urgent, + &settings.title, + &settings.title_blur, + &settings.title_focus, + }; + for (i = 0; i < sizeof(names) / sizeof(char*); i++) + { + if (strcmp(regex_matches[1], names[i]) == 0) + { + fprintf(stderr, "set [%s] to [%s]\n", names[i], regex_matches[2]); + free(*(values[i])); + *(values[i]) = strdup(regex_matches[2]); + break; + } + } + } + else + if (regex_match("^[[:space:]]*(layouts)[[:space:]]+([1-9])", tmp)) + { + unsigned int n = strtoul(regex_matches[2], NULL, 10); + fprintf(stderr, "using [%u] monitor layout%s\n", n, n > 1 ? "s":""); + + free(settings.layouts); + settings.layout_count = n; + settings.layouts = calloc(settings.layout_count, sizeof(Layout)); + + for (i = 0; i < settings.layout_count && i < sizeof(layouts) / sizeof(Layout); i++) + memmove(&settings.layouts[i], &layouts[i], sizeof(Layout)); + } + else + if (regex_match("^[[:space:]]*layout[[:space:]]([0-9])(.+)", tmp)) + { + unsigned int n = strtoul(regex_matches[1], NULL, 10); + if (!have_layout(n)) + { + for (i = settings.layout_count; i < n+1; i++) + { + settings.layout_count++; + settings.layouts = realloc(settings.layouts, sizeof(Layout) * settings.layout_count); + memmove(&settings.layouts[i], &settings.layouts[0], sizeof(Layout)); + fprintf(stderr, "creating layout [%u]\n", i); + } + } + + snprintf(tmp, sizeof(tmp), "%s", regex_matches[2]); + + if (regex_match("^[[:space:]]spot_start[[:space:]]+(smart|current|spot1|spot2|spot3)", tmp)) + { + char *names[] = { + "smart", + "current", + "spot1", + "spot2", + "spot3", + }; + short values[] = { + SMART, + CURRENT, + SPOT1, + SPOT2, + SPOT3, + }; + for (i = 0; i < sizeof(names) / sizeof(char*); i++) + { + if (strcmp(regex_matches[1], names[i]) == 0) + { + fprintf(stderr, "layout [%u] spot_start [%s]\n", n, regex_matches[1]); + settings.layouts[n].spot_start = values[i]; + break; + } + } + } + else + if (regex_match("^[[:space:]]spot1_align[[:space:]]+(left|right)", tmp)) + { + char *names[] = { + "left", + "right", + }; + short values[] = { + LEFT, + RIGHT, + }; + for (i = 0; i < sizeof(names) / sizeof(char*); i++) + { + if (strcmp(regex_matches[1], names[i]) == 0) + { + fprintf(stderr, "layout [%u] spot1_align [%s]\n", n, regex_matches[1]); + settings.layouts[n].spot1_align = values[i]; + break; + } + } + } + else + if (regex_match("^[[:space:]]spot1_width_pct[[:space:]]+([0-9]+)", tmp)) + { + unsigned int w = strtoul(regex_matches[1], NULL, 10); + if (w < 50 || w > 90) + { + fprintf(stderr, "layout [%u] spot1_width_pct out of bounds [%u]!\n", n, w); + continue; + } + fprintf(stderr, "layout [%u] spot1_width_pct [%u%%]\n", n, w); + settings.layouts[n].spot1_width_pct = w; + } + else + if (regex_match("^[[:space:]]spot2_height_pct[[:space:]]+([0-9]+)", tmp)) + { + unsigned int h = strtoul(regex_matches[1], NULL, 10); + if (h < 50 || h > 90) + { + fprintf(stderr, "layout [%u] spot2_height_pct out of bounds [%u]!\n", n, h); + continue; + } + fprintf(stderr, "layout [%u] spot2_height_pct [%u%%]\n", n, h); + settings.layouts[n].spot2_height_pct = h; + } + } + else + if (regex_match("^[[:space:]]*bind[[:space:]]+([^[:space:]]+)[[:space:]]+(" CONFIG_ACTIONS ")(.*)", tmp)) + { + unsigned int modifier = 0; + char *str = regex_matches[1]; + fprintf(stderr, "binding"); + + while (*str) + { + if ((strncmp(str, "Mod1" , 4)) == 0) + { + modifier |= Mod1Mask; + fprintf(stderr, " [Mod1]"); + str += 4; + } + else + if ((strncmp(str, "Mod2" , 4)) == 0) + { + modifier |= Mod2Mask; + fprintf(stderr, " [Mod2]"); + str += 4; + } + else + if ((strncmp(str, "Mod3" , 4)) == 0) + { + modifier |= Mod3Mask; + fprintf(stderr, " [Mod3]"); + str += 4; + } + else + if ((strncmp(str, "Mod4" , 4)) == 0) + { + modifier |= Mod4Mask; + fprintf(stderr, " [Mod4]"); + str += 4; + } + else + if ((strncmp(str, "Mod5" , 4)) == 0) + { + modifier |= Mod5Mask; + fprintf(stderr, " [Mod5]"); + str += 4; + } + else + if ((strncmp(str, "Shift", 5)) == 0) + { + modifier |= ShiftMask; + fprintf(stderr, " [Shift]"); + str += 5; + } + else + { + break; + } + while (*str == '+') str++; + } + + if (modifier == 0) + { + fprintf(stderr, " AnyModifier"); + modifier = AnyModifier; + } + + if (!*str) + { + fprintf(stderr, " malformed key binding [%s]\n", regex_matches[1]); + continue; + } + + KeySym keysym = XStringToKeysym(str); + + if (keysym == NoSymbol) + { + fprintf(stderr, " unknown key symbol [%s]\n", str); + continue; + } + + fprintf(stderr, " [%s]", str); + + int num = 0; + char *data = NULL; + void *action = NULL; + trim(regex_matches[3]); + + char *names[] = { + "action_move", + "action_focus", + "action_move_direction", + "action_focus_direction", + "action_close", + "action_cycle", + "action_raise_nth", + "action_command", + "action_find_or_start", + "action_move_monitor", + "action_focus_monitor", + "action_fullscreen", + "action_maximize_vert", + "action_maximize_horz", + "action_maximize", + }; + void *actions[] = { + action_move, + action_focus, + action_move_direction, + action_focus_direction, + action_close, + action_cycle, + action_raise_nth, + action_command, + action_find_or_start, + action_move_monitor, + action_focus_monitor, + action_fullscreen, + action_maximize_vert, + action_maximize_horz, + action_maximize, + }; + for (i = 0; i < sizeof(names) / sizeof(char*); i++) + { + if (strcmp(names[i], regex_matches[2]) == 0) + { + action = actions[i]; + fprintf(stderr, " to [%s %s]\n", regex_matches[2], regex_matches[3]); + break; + } + } + if ((0 == strcmp(regex_matches[2], "action_move")) || 0 == strcmp(regex_matches[2], "action_focus")) + { + if (strcmp(regex_matches[3], "spot1") == 0) num = SPOT1; + if (strcmp(regex_matches[3], "spot2") == 0) num = SPOT2; + if (strcmp(regex_matches[3], "spot3") == 0) num = SPOT3; + } + else + if (0 == strcmp(regex_matches[2], "action_move_direction") || 0 == strcmp(regex_matches[2], "action_focus_direction")) + { + if (strcmp(regex_matches[3], "left" ) == 0) num = LEFT; + if (strcmp(regex_matches[3], "right") == 0) num = RIGHT; + if (strcmp(regex_matches[3], "up" ) == 0) num = UP; + if (strcmp(regex_matches[3], "down" ) == 0) num = DOWN; + } + else + if (0 == strcmp(regex_matches[2], "action_command") || 0 == strcmp(regex_matches[2], "action_find_or_start")) + { + data = regex_matches[3]; + } + else + if (0 == strcmp(regex_matches[2], "action_raise_nth") || 0 == strcmp(regex_matches[2], "action_move_monitor") || 0 == strcmp(regex_matches[2], "action_focus_monitor")) + { + num = strtol(regex_matches[3], NULL, 10); + } + for (i = 0; i < settings.binding_count; i++) + { + if (settings.bindings[i].mod == modifier && settings.bindings[i].key == keysym) + { + settings.bindings[i].act = action; + settings.bindings[i].data = data; + settings.bindings[i].num = num; + break; + } + } + if (i == settings.binding_count) + { + settings.binding_count++; + settings.bindings = realloc(settings.bindings, sizeof(Binding) * settings.binding_count); + settings.bindings[i].mod = modifier; + settings.bindings[i].key = keysym; + settings.bindings[i].act = action; + settings.bindings[i].data = data; + settings.bindings[i].num = num; + } + } + } + fclose(file); + } +} diff --git a/config.h b/config.h index d814342..e5af70d 100644 --- a/config.h +++ b/config.h @@ -64,8 +64,9 @@ Layout layouts[] = { // action_fullscreen // action_maximize_vert // action_maximize_horz +// action_maximize -// If you use "AnyModifier" place those keys at the end of the array. +// If using "AnyModifier" place those keys at the end of the array. Binding keys[] = { // Change focus to a spot by direction. @@ -86,17 +87,6 @@ Binding keys[] = { // Cycle through all windows in the current spot. { .mod = Mod4Mask, .key = XK_grave, .act = action_cycle }, - // Raise nth window in the current spot. - { .mod = Mod4Mask, .key = XK_1, .act = action_raise_nth, .num = 1 }, - { .mod = Mod4Mask, .key = XK_2, .act = action_raise_nth, .num = 2 }, - { .mod = Mod4Mask, .key = XK_3, .act = action_raise_nth, .num = 3 }, - { .mod = Mod4Mask, .key = XK_4, .act = action_raise_nth, .num = 4 }, - { .mod = Mod4Mask, .key = XK_5, .act = action_raise_nth, .num = 5 }, - { .mod = Mod4Mask, .key = XK_6, .act = action_raise_nth, .num = 6 }, - { .mod = Mod4Mask, .key = XK_7, .act = action_raise_nth, .num = 7 }, - { .mod = Mod4Mask, .key = XK_8, .act = action_raise_nth, .num = 8 }, - { .mod = Mod4Mask, .key = XK_9, .act = action_raise_nth, .num = 9 }, - // Gracefully close the current window. { .mod = Mod4Mask, .key = XK_Escape, .act = action_close }, @@ -106,21 +96,9 @@ Binding keys[] = { { .mod = Mod4Mask, .key = XK_h, .act = action_maximize_horz }, { .mod = Mod4Mask, .key = XK_m, .act = action_maximize }, - // Switch focus between monitors. - { .mod = Mod4Mask, .key = XK_Next, .act = action_focus_monitor, .num = -1 }, - { .mod = Mod4Mask, .key = XK_Prior, .act = action_focus_monitor, .num = +1 }, - - // Move windows between monitors. - { .mod = ShiftMask|Mod4Mask, .key = XK_Next, .act = action_move_monitor, .num = -1 }, - { .mod = ShiftMask|Mod4Mask, .key = XK_Prior, .act = action_move_monitor, .num = +1 }, - // Launcher { .mod = Mod4Mask, .key = XK_x, .act = action_command, .data = "dmenu_run" }, - // Find or start apps by WM_CLASS (lower case match). - // Only works for apps that use some form of their binary name as their class... - { .mod = AnyModifier, .key = XK_F1, .act = action_find_or_start, .data = "xterm" }, - { .mod = AnyModifier, .key = XK_F2, .act = action_find_or_start, .data = "firefox" }, - { .mod = AnyModifier, .key = XK_F3, .act = action_find_or_start, .data = "pcmanfm" }, - { .mod = AnyModifier, .key = XK_F4, .act = action_find_or_start, .data = "sublime-text" }, + // Example + // { .mod = AnyModifier, .key = XK_F1, .act = action_find_or_start, .data = "xterm" }, }; \ No newline at end of file diff --git a/event.c b/event.c index f2d9309..13bd6c9 100644 --- a/event.c +++ b/event.c @@ -50,7 +50,7 @@ void configure_request(XEvent *ev) if (e->value_mask & CWWidth) wc.width = e->width; if (e->value_mask & CWHeight) wc.height = e->height; if (e->value_mask & CWStackMode) wc.stack_mode = e->detail; - if (e->value_mask & CWBorderWidth) wc.border_width = BORDER; + if (e->value_mask & CWBorderWidth) wc.border_width = settings.border; XConfigureWindow(display, c->window, e->value_mask, &wc); } client_free(c); @@ -75,7 +75,7 @@ void map_request(XEvent *e) { c->monitor = current_mon; Monitor *m = &monitors[c->monitor]; - int spot = have_layout(c->monitor) ? layouts[c->monitor].spot_start: SMART; + int spot = have_layout(c->monitor) ? settings.layouts[c->monitor].spot_start: SMART; if (spot == CURRENT) { @@ -129,9 +129,9 @@ void key_press(XEvent *ev) while (XCheckTypedEvent(display, KeyPress, ev)); Binding *bind = NULL; - for (int i = 0; i < sizeof(keys)/sizeof(Binding) && !bind; i++) - if (keys[i].key == key && (keys[i].mod == AnyModifier || keys[i].mod == state)) - bind = &keys[i]; + for (int i = 0; i < settings.binding_count && !bind; i++) + if (settings.bindings[i].key == key && (settings.bindings[i].mod == AnyModifier || settings.bindings[i].mod == state)) + bind = &settings.bindings[i]; if (bind && bind->act) { diff --git a/regex.c b/regex.c new file mode 100644 index 0000000..96ffefc --- /dev/null +++ b/regex.c @@ -0,0 +1,174 @@ +/* + +MIT/X11 License +Copyright (c) 2014 Sean Pringle + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#define REGEX_CACHE 8 +#define REGEX_GROUPS 8 + +typedef struct { + unsigned int hash; + char *pattern; + regex_t regex; + unsigned int used; +} RegexCache; + +RegexCache regex_cache[REGEX_CACHE]; + +regex_t* +regex(const char *pattern) +{ + int i = 0; + unsigned int hash = 0; + regex_t *re = NULL; + RegexCache *rc = NULL; + + while (pattern[i]) + { + hash = 33 * hash + pattern[i++]; + } + + // have we compiled this regex before? + for (i = 0; i < REGEX_CACHE; i++) + { + rc = ®ex_cache[i]; + if (rc->hash == hash && rc->pattern && !strcmp(pattern, rc->pattern)) + { + re = &rc->regex; + rc->used++; + break; + } + } + if (!re) + { + int slot = 0; + // look for a free cache slot + for (i = 0; i < REGEX_CACHE; i++) + { + if (!regex_cache[i].pattern) + { + slot = i; + break; + } + } + if (regex_cache[slot].pattern) + { + // choose the least used + for (i = 0; i < REGEX_CACHE; i++) + { + if (regex_cache[i].used < regex_cache[slot].used) + { + slot = i; + break; + } + } + } + + rc = ®ex_cache[slot]; + + if (rc->pattern) + { + regfree(&rc->regex); + free(rc->pattern); + } + + rc->used = 0; + rc->hash = 0; + rc->pattern = NULL; + + re = &rc->regex; + + if (regcomp(re, pattern, REG_EXTENDED) != 0) + { + fprintf(stderr, "regex compile failure: %s", pattern); + re = NULL; + } + if (re) + { + rc->pattern = strdup(pattern); + rc->hash = hash; + rc->used = 1; + } + } + return re; +} + +void +flush_regex_cache() +{ + for (int i = 0; i < REGEX_CACHE; i++) + { + RegexCache *rc = ®ex_cache[i]; + if (rc->pattern) + { + regfree(&rc->regex); + free(rc->pattern); + rc->used = 0; + rc->hash = 0; + rc->pattern = NULL; + } + } +} + +char *regex_matches[REGEX_GROUPS]; + +int +regex_match(const char *pattern, const char *subject) +{ + int r = 0; regmatch_t pmatch[REGEX_GROUPS]; + regex_t *re = regex(pattern); + if (re && subject && (r = regexec(re, subject, REGEX_GROUPS, pmatch, 0) == 0)) + { + for (int i = 0; i < REGEX_GROUPS && pmatch[i].rm_so != (size_t)-1; i++) + { + free(regex_matches[i]); + int len = pmatch[i].rm_eo - pmatch[i].rm_so; + regex_matches[i] = malloc(len + 1); + strncpy(regex_matches[i], subject + pmatch[i].rm_so, len); + regex_matches[i][len] = 0; + } + } + return r; +} + +int +regex_split(char *pattern, char **subject) +{ + int r = 0; regmatch_t pmatch; + regex_t *re = regex(pattern); + if (re && *subject) + { + r = regexec(re, *subject, 1, &pmatch, 0) == 0 ?-1:0; + if (r) + { + memset(*subject + pmatch.rm_so, 0, pmatch.rm_eo - pmatch.rm_so); + *subject += pmatch.rm_eo; + } + else + { + *subject += strlen(*subject); + } + } + return r; +} diff --git a/setup.c b/setup.c index 638017a..dc120cc 100644 --- a/setup.c +++ b/setup.c @@ -32,6 +32,7 @@ typedef struct { void setup() { int i, j; Client *c; Monitor *m; + int screen_w = WidthOfScreen(DefaultScreenOfDisplay(display)); int screen_h = HeightOfScreen(DefaultScreenOfDisplay(display)); @@ -107,9 +108,9 @@ void setup() int x = m->x, y = m->y, w = m->w, h = m->h; // determine spot layout for this monitor - int spot1_align = have_layout(i) ? layouts[i].spot1_align : LEFT ; - int spot1_width_pct = have_layout(i) ? layouts[i].spot1_width_pct : 66; - int spot2_height_pct = have_layout(i) ? layouts[i].spot2_height_pct : 66; + int spot1_align = have_layout(i) ? settings.layouts[i].spot1_align : LEFT ; + int spot1_width_pct = have_layout(i) ? settings.layouts[i].spot1_width_pct : 66; + int spot2_height_pct = have_layout(i) ? settings.layouts[i].spot2_height_pct : 66; // monitor rotated? if (m->w < m->h) @@ -176,14 +177,14 @@ void setup() XFreeModifiermap(modmap); // process config.h key bindings - for (i = 0; i < sizeof(keys)/sizeof(Binding); i++) + for (i = 0; i < settings.binding_count; i++) { - XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod, root, True, GrabModeAsync, GrabModeAsync); - if (keys[i].mod == AnyModifier) continue; + XGrabKey(display, XKeysymToKeycode(display, settings.bindings[i].key), settings.bindings[i].mod, root, True, GrabModeAsync, GrabModeAsync); + if (settings.bindings[i].mod == AnyModifier) continue; - XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|LockMask, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|LockMask|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(display, XKeysymToKeycode(display, settings.bindings[i].key), settings.bindings[i].mod|LockMask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(display, XKeysymToKeycode(display, settings.bindings[i].key), settings.bindings[i].mod|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(display, XKeysymToKeycode(display, settings.bindings[i].key), settings.bindings[i].mod|LockMask|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); } // we grab buttons to do click-to-focus. all clicks get passed through to apps. @@ -191,13 +192,13 @@ void setup() XGrabButton(display, Button3, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); // create title bars - if (TITLE) + if (settings.title) { STACK_FREE(&windows); for_monitors(i, m) for_spots(j) { m->bars[j] = textbox_create(root, TB_AUTOHEIGHT|TB_LEFT, m->spots[j].x, m->spots[j].y, m->spots[j].w, 0, - TITLE, TITLE_BLUR, BORDER_BLUR, NULL, NULL); + settings.title, settings.title_blur, settings.border_blur, NULL, NULL); XSelectInput(display, m->bars[j]->window, ExposureMask); m->spots[j].y += m->bars[j]->h; diff --git a/spot.c b/spot.c index 56e6504..8e262d0 100644 --- a/spot.c +++ b/spot.c @@ -50,24 +50,24 @@ void spot_update_bar(int spot, int mon) name = strdup(tmp); if (name) { - if (TITLE_ELLIPSIS > 0 && strlen(name) > TITLE_ELLIPSIS) + if (settings.title_ellipsis > 0 && strlen(name) > settings.title_ellipsis) { name = realloc(name, strlen(name)+4); - strcpy(name+TITLE_ELLIPSIS, "..."); + strcpy(name+settings.title_ellipsis, "..."); } len += snprintf(title+len, MAX(0, SPOT_BUFF-len), " [%d] %s ", n++, name); free(name); } if (tmp) XFree(tmp); } - if (TITLE) + if (settings.title) { if (c && !c->full && *title && m->bars[spot]) { int focus = c->window == current || (spot == current_spot && mon == current_mon); - char *color = focus && c->window == current ? TITLE_FOCUS : TITLE_BLUR; - char *border = focus && c->window == current ? BORDER_FOCUS: BORDER_BLUR; - textbox_font(m->bars[spot], TITLE, color, border); + char *color = focus && c->window == current ? settings.title_focus : settings.title_blur; + char *border = focus && c->window == current ? settings.border_focus: settings.border_blur; + textbox_font(m->bars[spot], settings.title, color, border); textbox_text(m->bars[spot], title); textbox_draw(m->bars[spot]); textbox_show(m->bars[spot]); @@ -81,7 +81,7 @@ void spot_update_bar(int spot, int mon) void update_bars() { int i, j; Monitor *m; - if (TITLE) for_monitors(i, m) for_spots(j) + if (settings.title) for_monitors(i, m) for_spots(j) spot_update_bar(j, i); } @@ -115,7 +115,7 @@ Window spot_try_focus_top_window(int spot, int mon, Window except) int spot_choose_by_direction(int spot, int mon, int dir) { Monitor *m = &monitors[mon]; - int spot1_align = have_layout(mon) ? layouts[mon].spot1_align : LEFT ; + int spot1_align = have_layout(mon) ? settings.layouts[mon].spot1_align : LEFT ; if (m->w < m->h) // rotated? { diff --git a/xoat.c b/xoat.c index 6201405..ad25da9 100644 --- a/xoat.c +++ b/xoat.c @@ -45,6 +45,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -53,6 +54,7 @@ Display *display; #include "atom.c" #include "textbox.c" +#include "regex.c" #define STACK 64 #define MONITORS 3 @@ -98,7 +100,25 @@ typedef struct _Layout { short spot_start, spot1_align, spot1_width_pct, spot2_height_pct; } Layout; -#define have_layout(i) (sizeof(layouts) / sizeof(Layout) > (i)) +typedef struct _Settings { + unsigned int border; + char *border_blur; + char *border_focus; + char *border_urgent; + unsigned int gap; + char *title; + char *title_blur; + char *title_focus; + unsigned int title_ellipsis; + + unsigned int layout_count; + Layout *layouts; + + unsigned int binding_count; + Binding *bindings; +} Settings; + +#define have_layout(i) (settings.layout_count > (i)) Client* window_build_client(Window); void client_free(Client*); @@ -161,6 +181,7 @@ short current_spot, current_mon; Window root, ewmh, current = None; Stack windows; static int (*xerror)(Display *, XErrorEvent *); +Settings settings; void catch_exit(int sig) { @@ -182,6 +203,7 @@ void exec_cmd(char *cmd) #include "spot.c" #include "event.c" #include "action.c" +#include "config.c" #include "setup.c" void (*handlers[LASTEvent])(XEvent*) = { @@ -236,6 +258,7 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } + configure(); setup(); // main event loop