diff --git a/glava.c b/glava.c index 4cf9f61..c67391d 100644 --- a/glava.c +++ b/glava.c @@ -18,7 +18,7 @@ #include "render.h" #include "xwin.h" -#define GLAVA_VERSION "1.2" +#define GLAVA_VERSION "1.3" #ifdef GLAD_DEBUG #define GLAVA_RELEASE_TYPE_PREFIX "debug, " #else @@ -151,6 +151,8 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) { closedir(dir); } +#define GLAVA_VERSION_STRING "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")" + static const char* help_str = "Usage: %s [OPTIONS]...\n" "Opens a window with an OpenGL context to draw an audio visualizer.\n" @@ -165,17 +167,19 @@ static const char* help_str = "-C, --copy-config creates copies and symbolic links in the user configuration\n" " directory for glava, copying any files in the root directory\n" " of the installed shader directory, and linking any modules.\n" + "-V, --version print application version and exit\n" "\n" - "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")\n" + GLAVA_VERSION_STRING "\n" " -- Copyright (C) 2017 Levi Webb\n"; -static const char* opt_str = "hve:Cm:"; +static const char* opt_str = "hvVe:Cm:"; static struct option p_opts[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"entry", required_argument, 0, 'e'}, {"force-mod", required_argument, 0, 'm'}, {"copy-config", no_argument, 0, 'C'}, + {"version", no_argument, 0, 'V'}, {0, 0, 0, 0 } }; @@ -198,10 +202,15 @@ int main(int argc, char** argv) { case 'e': entry = optarg; break; case 'm': force = optarg; break; case '?': exit(EXIT_FAILURE); break; + case 'V': + puts(GLAVA_VERSION_STRING); + exit(EXIT_SUCCESS); + break; default: case 'h': printf(help_str, argc > 0 ? argv[0] : "glava"); exit(EXIT_SUCCESS); + break; } } diff --git a/glsl_ext.c b/glsl_ext.c index e765b03..5710e40 100644 --- a/glsl_ext.c +++ b/glsl_ext.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) { int written; if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0) abort(); - sbuf->at += space; + sbuf->at += written; va_end(args); } @@ -68,6 +69,42 @@ struct schar { size_t sz; }; +bool ext_parse_color(const char* str, size_t elem_sz, float** results) { + size_t t, len = strlen(str), i = 0, s = 0; + uint8_t elem_bytes[elem_sz]; + /* Ignore '0x' prefix, if present */ + if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + len -= 2; + str += 2; + } + for (t = 0; t < len && t < 8; ++t) { + char c = str[t]; + uint8_t b; + /* obtain value from character */ + switch (c) { + case 'a' ... 'f': b = (c - 'a') + 10; break; + case 'A' ... 'F': b = (c - 'A') + 10; break; + case '0' ... '9': b = c - '0'; break; + default: return false; + } + elem_bytes[s] = b; + if (s >= elem_sz - 1) { /* advance to next element */ + uint32_t e = 0; /* component storage */ + /* mask storage with input data */ + for (size_t v = 0; v < elem_sz; ++v) { + e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4); + } + /* convert to [0, 1] as floating point value */ + *results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1); + s = 0; + ++i; + } else { /* advance character */ + ++s; + } + } + return true; +} + /* handle raw arguments for #include and #request directives */ /* NOTE: munmap needs to be called on the result */ static struct schar directive(struct glsl_ext* ext, char** args, @@ -91,7 +128,8 @@ static struct schar directive(struct glsl_ext* ext, char** args, int fd = open(path, O_RDONLY); if (fd == -1) { - parse_error(line, f, "failed to load GLSL shader source specified by #include directive '%s': %s\n", + parse_error(line, f, "failed to load GLSL shader source " + "specified by #include directive '%s': %s\n", path, strerror(errno)); } @@ -100,7 +138,8 @@ static struct schar directive(struct glsl_ext* ext, char** args, char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (!map) { - parse_error(line, f, "failed to map GLSL shader source specified by #include directive '%s': %s\n", + parse_error(line, f, "failed to map GLSL shader source " + "specified by #include directive '%s': %s\n", path, strerror(errno)); } @@ -128,7 +167,7 @@ static struct schar directive(struct glsl_ext* ext, char** args, struct request_handler* handler; bool found = false; - size_t t, i; + size_t t; for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) { if(!strcmp(handler->name, request)) { found = true; @@ -173,10 +212,12 @@ static struct schar directive(struct glsl_ext* ext, char** args, case '1': { v = true; break; } case '0': { v = false; break; } default: - parse_error_s(line, f, "tried to parse invalid raw string into a boolean"); + parse_error_s(line, f, + "tried to parse invalid raw string into a boolean"); } } else { - parse_error_s(line, f, "tried to parse invalid raw string into a boolean"); + parse_error_s(line, f, + "tried to parse invalid raw string into a boolean"); } processed_args[i] = malloc(sizeof(bool)); *(bool*) processed_args[i] = v; @@ -211,10 +252,11 @@ static struct schar directive(struct glsl_ext* ext, char** args, void ext_process(struct glsl_ext* ext, const char* f) { #define LINE_START 0 - #define IGNORING 1 + #define GLSL 1 #define MACRO 2 #define REQUEST 3 #define INCLUDE 4 + #define COLOR 5 struct sbuf sbuf = { .buf = malloc(256), @@ -226,12 +268,16 @@ void ext_process(struct glsl_ext* ext, const char* f) { size_t t; char at; int state = LINE_START; - size_t macro_start_idx, arg_start_idx; + size_t macro_start_idx, arg_start_idx, cbuf_idx; size_t line = 1; bool quoted = false, arg_start; + char cbuf[9]; char** args = NULL; size_t args_sz = 0; - + + bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false, + prev_escape = false, string = false; + for (t = 0; t <= source_len; ++t) { at = source_len == t ? '\0' : ext->source[t]; if (at == '\n') @@ -244,40 +290,128 @@ void ext_process(struct glsl_ext* ext, const char* f) { macro_start_idx = t; state = MACRO; continue; } - case ' ': - case '\t': case '\n': + if (comment && comment_line) { + comment = false; + comment_line = false; + } + case '\t': + case ' ': goto copy; - default: { - state = IGNORING; - goto copy; } + default: state = GLSL; + /* let execution continue into next state */ } } - case IGNORING: /* copying GLSL source or unrelated preprocessor syntax */ - if (at == '\n') { - state = LINE_START; + case GLSL: /* copying GLSL source or unrelated preprocessor syntax */ + { + switch (at) { + case '"': + if (!comment && !prev_escape) + string = !string; + goto normal_char; + case '\\': + if (!comment) { + prev_escape = !prev_escape; + prev_asterix = false; + prev_slash = false; + goto copy; + } else goto normal_char; + case '/': + if (!comment) { + if (prev_slash) { + comment = true; + comment_line = true; + prev_slash = false; + } else prev_slash = true; + } else if (!comment_line) { + if (prev_asterix) { + comment = false; + prev_asterix = false; + } + } + prev_escape = false; + goto copy; + case '*': + if (!comment) { + if (prev_slash) { + comment = true; + prev_slash = false; + } + } else prev_asterix = true; + prev_escape = false; + goto copy; + case '#': { + /* handle hex color syntax */ + if (!comment && !string) { + state = COLOR; + cbuf_idx = 0; + continue; + } else goto normal_char; + } + case '\n': + if (comment && comment_line) { + comment = false; + comment_line = false; + } + state = LINE_START; + normal_char: + default: + prev_asterix = false; + prev_slash = false; + prev_escape = false; + goto copy; + } + } + case COLOR: /* parse hex color syntax (#ffffffff -> vec4(1.0, 1.0, 1.0, 1.0)) */ + { + switch (at) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': { + cbuf[cbuf_idx] = at; + ++cbuf_idx; + if (cbuf_idx >= 8) + goto emit_color; + else continue; + } + emit_color: + default: + cbuf[cbuf_idx] = '\0'; /* null terminate */ + float r = 0.0F, g = 0.0F, b = 0.0F, a = 1.0F; + if (ext_parse_color(cbuf, 2, (float*[]) { &r, &g, &b, &a })) { + se_append(&sbuf, 64, " vec4(%.6f, %.6f, %.6f, %.6f) ", r, g, b, a); + } else { + parse_error(line, f, "Invalid color format '#%s' while " + "parsing GLSL color syntax extension", cbuf); + } + state = at == '\n' ? LINE_START : GLSL; + if (cbuf_idx >= 8) + continue; + else goto copy; /* copy character if it ended the sequence */ + } } - goto copy; case MACRO: /* processing start of macro */ { switch (at) { case '\n': case ' ': case '\t': - case '\0': + case '\0': { /* end parsing directive */ if (!strncmp("#request", &ext->source[macro_start_idx], t - macro_start_idx) || !strncmp("#REQUEST", &ext->source[macro_start_idx], t - macro_start_idx)) { state = REQUEST; goto prepare_arg_parse; - } else if (!strncmp("#include", &ext->source[macro_start_idx], t - macro_start_idx) - || !strncmp("#INCLUDE", &ext->source[macro_start_idx], t - macro_start_idx)) { + } else if (!strncmp("#include", &ext->source[macro_start_idx], + t - macro_start_idx) + || !strncmp("#INCLUDE", &ext->source[macro_start_idx], + t - macro_start_idx)) { state = INCLUDE; goto prepare_arg_parse; } else { n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]); - state = IGNORING; + state = at == '\n' ? LINE_START : GLSL; goto copy; } prepare_arg_parse: diff --git a/glsl_ext.h b/glsl_ext.h index fa9fc09..9b13338 100644 --- a/glsl_ext.h +++ b/glsl_ext.h @@ -35,3 +35,4 @@ struct glsl_ext { void ext_process(struct glsl_ext* ext, const char* f); void ext_free (struct glsl_ext* ext); +bool ext_parse_color(const char* hex, size_t elem_sz, float** results); diff --git a/render.c b/render.c index 21822e0..5ad31bd 100644 --- a/render.c +++ b/render.c @@ -753,10 +753,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force #ifdef GLFW_TRANSPARENT_FRAMEBUFFER glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE); - gl->use_alpha = false; + if (native_opacity) gl->use_alpha = false; #elif GLFW_TRANSPARENT glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE); - gl->use_alpha = false; + if (native_opacity) gl->use_alpha = false; #else if (native_opacity) printf("WARNING: the linked version of GLFW3 does not have transparency support" @@ -783,42 +783,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force &gl->clear_color.b, &gl->clear_color.a }; - const size_t elem_sz = 2; - char* str = (char*) args[0]; - size_t t, len = strlen(str), i = 0, s = 0; - uint8_t elem_bytes[elem_sz]; - /* Ignore '0x' prefix, if present */ - if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { - len -= 2; - str += 2; - } - for (t = 0; t < len && t < 8; ++t) { - char c = str[t]; - uint8_t b; - /* obtain value from character */ - switch (c) { - case 'a' ... 'f': b = (c - 'a') + 10; break; - case 'A' ... 'F': b = (c - 'A') + 10; break; - case '0' ... '9': b = c - '0'; break; - default: - fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]); - exit(EXIT_FAILURE); - } - elem_bytes[s] = b; - if (s >= elem_sz - 1) { /* advance to next element */ - uint32_t e = 0; /* component storage */ - /* mask storage with input data */ - for (size_t v = 0; v < elem_sz; ++v) { - e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4); - } - /* convert to [0, 1] as floating point value */ - *results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1); - printf("[DEBUG] component %d value: %d (float: %f)\n", i, e, *results[i]); - s = 0; - ++i; - } else { /* advance character */ - ++s; - } + if (!ext_parse_color((char*) args[0], 2, results)) { + fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]); + exit(EXIT_FAILURE); } }) }, @@ -1055,6 +1022,16 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force abort(); } + if (xwintype) { + xwin_settype(r, xwintype); + free(xwintype); + } + + for (size_t t = 0; t < xwinstates_sz; ++t) { + xwin_addstate(r, xwinstates[t]); + } + free(xwinstates); + glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]); glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]); @@ -1086,7 +1063,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force struct gl_sfbo* stages; size_t count = 0; - + { char buf[32]; DIR* dir = opendir(shaders); @@ -1219,20 +1196,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force } overlay(&gl->overlay); - - glfwShowWindow(gl->w); glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a); - - if (xwintype) { - xwin_settype(r, xwintype); - free(xwintype); - } - for (size_t t = 0; t < xwinstates_sz; ++t) { - xwin_addstate(r, xwinstates[t]); - } - free(xwinstates); + glfwShowWindow(gl->w); return r; } diff --git a/shaders/bars.glsl b/shaders/bars.glsl index 29b179c..c0ba8d8 100644 --- a/shaders/bars.glsl +++ b/shaders/bars.glsl @@ -7,13 +7,13 @@ /* width (in pixels) of each bar gap */ #define BAR_GAP 2 /* outline color */ -#define BAR_OUTLINE vec4(0.15, 0.15, 0.15, 1) +#define BAR_OUTLINE #262626 /* outline width (in pixels, set to 0 to disable outline drawing) */ #define BAR_OUTLINE_WIDTH 0 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 300 /* Bar color */ -#define COLOR (vec4(0.2, 0.4, 0.7, 1) * ((d / 60) + 1)) +#define COLOR (#3366b2 * ((d / 60) + 1)) /* Direction that the bars are facing, 0 for inward, 1 for outward */ #define DIRECTION 0 /* Whether to switch left/right audio buffers */ diff --git a/shaders/circle.glsl b/shaders/circle.glsl index ea4ffba..6be8473 100644 --- a/shaders/circle.glsl +++ b/shaders/circle.glsl @@ -3,7 +3,7 @@ /* center line thickness (pixels) */ #define C_LINE 2 /* outline color */ -#define OUTLINE vec4(0.20, 0.20, 0.20, 1) +#define OUTLINE #333333 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 150 /* Angle (in radians) for how much to rotate the visualizer */ diff --git a/shaders/graph.glsl b/shaders/graph.glsl index 113f48c..8faf0c3 100644 --- a/shaders/graph.glsl +++ b/shaders/graph.glsl @@ -17,7 +17,7 @@ /* actual color definition */ #define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1) /* outline color */ -#define OUTLINE vec4(0.15, 0.15, 0.15, 1) +#define OUTLINE #262626 /* 1 to invert (vertically), 0 otherwise */ #define INVERT 0 diff --git a/shaders/radial.glsl b/shaders/radial.glsl index 1f66a7c..3d3269b 100644 --- a/shaders/radial.glsl +++ b/shaders/radial.glsl @@ -4,7 +4,7 @@ /* center line thickness (pixels) */ #define C_LINE 2 /* outline color */ -#define OUTLINE vec4(0.20, 0.20, 0.20, 1) +#define OUTLINE #333333 /* number of bars (use even values for best results) */ #define NBARS 180 /* width (in pixels) of each bar*/ @@ -15,8 +15,8 @@ #define BAR_OUTLINE_WIDTH 0 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 300 -/* Bar color */ -#define COLOR (vec4(0.8, 0.2, 0.2, 1) * ((d / 40) + 1)) +/* Bar color */ +#define COLOR (#cc3333 * ((d / 40) + 1)) /* Angle (in radians) for how much to rotate the visualizer */ #define ROTATE (PI / 2) /* Whether to switch left/right audio buffers */ diff --git a/xwin.c b/xwin.c index 3f81118..97e7164 100644 --- a/xwin.c +++ b/xwin.c @@ -16,13 +16,16 @@ #include #define GLFW_EXPOSE_NATIVE_X11 -/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but - the old headers require one of them to be selected for exposure in glfw3native.h. */ -#if (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1) -#define GLFW_EXPOSE_NATIVE_GLX -#endif + #include #include + +/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but + the old headers require one of them to be selected for exposure in glfw3native.h. */ +#if GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1 +#define GLFW_EXPOSE_NATIVE_GLX +#error "GLX defined" +#endif #include #include "render.h"