Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c485d80013 | ||
|
|
b37ab5ba09 | ||
|
|
1a595c2f19 | ||
|
|
c9dac68bb4 | ||
|
|
c14c2a835a | ||
|
|
6f688e8b4d | ||
|
|
bfea8e167a | ||
|
|
d07b93d54e | ||
|
|
f7170afb75 | ||
|
|
8ccbf22732 | ||
|
|
5fdf0545a8 | ||
|
|
b77d1a7b3a | ||
|
|
f87c70af2b | ||
|
|
7c30c4a78a | ||
|
|
bc2db4a415 | ||
|
|
3ff1097941 | ||
|
|
041bfdfd55 | ||
|
|
8cbf7bc579 | ||
|
|
91530dfc49 | ||
|
|
a39a1324e1 | ||
|
|
140d98b404 | ||
|
|
f235362f44 | ||
|
|
32853b73d8 | ||
|
|
4705699bb6 | ||
|
|
3ef23f0db8 | ||
|
|
4d51ccbd54 | ||
|
|
6c02d15c80 | ||
|
|
40dac829cc | ||
|
|
bde3101c42 | ||
|
|
f2857e5f21 | ||
|
|
66a9b09b10 | ||
|
|
31a39b6ecd | ||
|
|
dd0f5bf0f9 | ||
|
|
1337253257 | ||
|
|
76325cb126 | ||
|
|
426f70f579 | ||
|
|
1d2b014da7 | ||
|
|
6670c54827 | ||
|
|
f4ad41df32 | ||
|
|
93df114308 | ||
|
|
d21edcfb29 | ||
|
|
e0b4f7d6c7 | ||
|
|
386d32076c | ||
|
|
52af6ac173 | ||
|
|
3da1fb34dd | ||
|
|
2c99124556 | ||
|
|
9d1c3a177c | ||
|
|
db625cf00c | ||
|
|
1845fad7fd | ||
|
|
25c1701ce6 | ||
|
|
b627219109 | ||
|
|
3dfbdb5127 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.o
|
||||
build_state
|
||||
glava
|
||||
44
CONTRIBUTING.md
Normal file
44
CONTRIBUTING.md
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
## Code Style
|
||||
|
||||
GLava uses a bastardized version of the [linux kernel style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html), with the following modifications:
|
||||
|
||||
* Opening braces are _always_ on the same line as the token it is associated with (`if`, `while`, labels, functions). The only time this is not honoured is when a set of braces has no associated token (ie. scope usage).
|
||||
|
||||
* Indentation is 4 spaces, and tabs are forbidden
|
||||
|
||||
* The content of a `switch` statement, including `case` labels, are indented.
|
||||
|
||||
* Preprocessor directives should inherit the same intentation level as the code it resides in.
|
||||
|
||||
* Align tokens in repetitious lines by padding spacing between tokens.
|
||||
|
||||
The following rules of the linux style are **ignored**:
|
||||
|
||||
* Function size and control flow recommendations
|
||||
* Comment formatting rules
|
||||
* Any other rules regarding preprocessor directives
|
||||
|
||||
Naming rules and the usage of `typedef` is strictly honoured from the Linux style. Anything not mentioned here is probably subjective and won't hurt your chances of getting a PR accepted.
|
||||
|
||||
If you use GNU Emacs, the above style can be configured via the following elisp:
|
||||
|
||||
```emacs
|
||||
(setq-default c-basic-offset 4)
|
||||
(setq c-default-style "linux")
|
||||
(setq tab-stop-list (number-sequence 4 200 4))
|
||||
(c-set-offset (quote cpp-macro) 0 nil)
|
||||
(c-set-offset 'case-label '+)
|
||||
```
|
||||
|
||||
## Shaders
|
||||
|
||||
If you author and maintain your own shader module for GLava, you are free to use your preferred code style. Otherwise, shaders follow the same style as GLava's C sources.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
You are free to make pull requests for any change, even if you are not sure if the proposed changes are appropriate. @wacossusca34 and/or @coderobe will be able to suggest changes or commentary on the PR if there is a reason it is not acceptable.
|
||||
|
||||
## Conduct
|
||||
|
||||
Engagement in the issue tracker and pull requests simply requires participants remain rational and on-topic.
|
||||
65
Makefile
65
Makefile
@@ -1,13 +1,14 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
src = $(wildcard *.c)
|
||||
obj = $(src:.c=.o)
|
||||
|
||||
# Build type parameter
|
||||
|
||||
ifeq ($(BUILD),debug)
|
||||
CFLAGS_BUILD = -O0 -ggdb -Wall #-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
CFLAGS_BUILD = -O0 -ggdb -Wall -DGLAVA_DEBUG
|
||||
GLAD_GEN = c-debug
|
||||
STRIP_CMD = $(info Skipping `strip` for debug builds)
|
||||
# ASAN = -lasan
|
||||
else
|
||||
CFLAGS_BUILD = -O2 -Wstringop-overflow=0
|
||||
GLAD_GEN = c
|
||||
@@ -25,6 +26,10 @@ ifndef INSTALL
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef EXECDIR
|
||||
EXECDIR = /usr/bin/
|
||||
endif
|
||||
|
||||
# Install type parameter
|
||||
|
||||
ifeq ($(INSTALL),standalone)
|
||||
@@ -33,10 +38,15 @@ endif
|
||||
|
||||
ifeq ($(INSTALL),unix)
|
||||
CFLAGS_INSTALL = -DGLAVA_UNIX
|
||||
ifdef XDG_CONFIG_DIRS
|
||||
SHADER_DIR = $(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava
|
||||
else
|
||||
SHADER_DIR = etc/xdg/glava
|
||||
ifndef SHADERDIR
|
||||
ifdef XDG_CONFIG_DIRS
|
||||
SHADERDIR = /$(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava/
|
||||
ifeq ($(wildcard $(SHADERDIR)/..),)
|
||||
SHADERDIR = /etc/xdg/glava/
|
||||
endif
|
||||
else
|
||||
SHADERDIR = /etc/xdg/glava/
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -52,7 +62,9 @@ endif
|
||||
|
||||
ifeq ($(INSTALL),osx)
|
||||
CFLAGS_INSTALL = -DGLAVA_OSX
|
||||
SHADER_DIR = Library/glava
|
||||
ifndef SHADERDIR
|
||||
SHADERDIR = /Library/glava
|
||||
endif
|
||||
endif
|
||||
|
||||
LDFLAGS += $(ASAN) -lpulse -lpulse-simple -pthread $(LDFLAGS_GLFW) -ldl -lm -lX11 -lXext $(LDFLAGS_GLX)
|
||||
@@ -64,30 +76,38 @@ ifeq ($(GLAVA_VERSION),\"\")
|
||||
GLAVA_VERSION = \"unknown\"
|
||||
endif
|
||||
|
||||
ifdef DESTDIR
|
||||
DESTDIR += /
|
||||
endif
|
||||
|
||||
GLAD_INSTALL_DIR = glad
|
||||
GLAD_SRCFILE = ./glad/src/glad.c
|
||||
GLAD_SRCFILE = glad.c
|
||||
GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
|
||||
CFLAGS_COMMON = -I glad/include -DGLAVA_VERSION="$(GLAVA_VERSION)"
|
||||
CFLAGS_COMMON = -DGLAVA_VERSION="$(GLAVA_VERSION)" -DSHADER_INSTALL_PATH="\"$(SHADERDIR)\""
|
||||
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS_GLX) $(CFLAGS_GLFW) $(CFLAGS_BUILD) $(CFLAGS_INSTALL) $(CFLAGS)
|
||||
|
||||
# Store relevant variables that may change depending on the environment or user input
|
||||
STATE = $(BUILD),$(INSTALL),$(ENABLE_GLFW),$(DISABLE_GLX),$(PYTHON),$(CC),$(CFLAGS_USE)
|
||||
STATE = $(BUILD),$(INSTALL),$(PREFIX),$(ENABLE_GLFW),$(DISABLE_GLX),$(PYTHON),$(CC),$(CFLAGS_USE)
|
||||
# Only update the file if the contents changed, `make` just looks at the timestamp
|
||||
$(shell if [[ ! -e build_state ]]; then touch build_state; fi)
|
||||
$(shell if [ '$(STATE)' != "`cat build_state`" ]; then echo '$(STATE)' > build_state; fi)
|
||||
|
||||
all: glava
|
||||
|
||||
%.o: %.c glad.o build_state
|
||||
$(CC) $(CFLAGS_USE) -o $@ -c $(firstword $<)
|
||||
%.o: %.c build_state
|
||||
@$(CC) $(CFLAGS_USE) -o $@ -c $(firstword $<)
|
||||
@echo "CC $@"
|
||||
|
||||
glava: $(obj)
|
||||
$(CC) -o glava $(obj) glad.o $(LDFLAGS)
|
||||
@$(CC) -o glava $(obj) $(LDFLAGS)
|
||||
@echo "CC glava"
|
||||
$(STRIP_CMD)
|
||||
|
||||
glad.o: build_state
|
||||
cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS) --out-path=.
|
||||
$(CC) $(CFLAGS_USE) -o glad.o $(GLAD_SRCFILE) -c
|
||||
.PHONY: glad
|
||||
glad: build_state
|
||||
@cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS) --local-files --out-path=.
|
||||
@cp glad/*.h .
|
||||
@cp glad/glad.c .
|
||||
|
||||
# Empty build state goal, used to force some of the above rules to re-run if `build_state` was updated
|
||||
build_state: ;
|
||||
@@ -96,14 +116,17 @@ build_state: ;
|
||||
clean:
|
||||
rm -f $(obj) glava glad.o build_state
|
||||
|
||||
EXECTARGET = $(shell readlink -m "$(DESTDIR)$(EXECDIR)/glava")
|
||||
SHADERTARGET = $(shell readlink -m "$(DESTDIR)$(SHADERDIR)")
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
install -Dm755 glava "$(DESTDIR)/usr/bin/glava"
|
||||
install -d "$(DESTDIR)/$(SHADER_DIR)"
|
||||
cp -Rv shaders/* "$(DESTDIR)/$(SHADER_DIR)"
|
||||
install -Dm755 glava $(EXECTARGET)
|
||||
install -d $(SHADERTARGET)
|
||||
cp -Rv shaders/* $(SHADERTARGET)
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
rm /usr/bin/glava
|
||||
rm -rf $(SHADER_DIR)/glava
|
||||
rm $(EXECTARGET)
|
||||
rm -rf $(SHADERTARGET)
|
||||
|
||||
|
||||
40
README.md
40
README.md
@@ -3,10 +3,10 @@
|
||||
|
||||
**GLava** is an OpenGL audio spectrum visualizer. Its primary use case is for desktop windows or backgrounds. Displayed to the left is the `radial` shader module, and [here is a demonstration video](https://streamable.com/dgpj8). Development is active, and reporting issues is encouranged.
|
||||
|
||||
**Compiling** (Or use the Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or the [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:**
|
||||
**Compiling:**
|
||||
|
||||
```bash
|
||||
$ git clone --recursive https://github.com/wacossusca34/glava
|
||||
$ git clone https://github.com/wacossusca34/glava
|
||||
$ cd glava
|
||||
$ CFLAGS="-march=native" make
|
||||
$ sudo make install
|
||||
@@ -23,8 +23,6 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
|
||||
|
||||
**Additional compile time requirements:**
|
||||
|
||||
- glad (included as a submodule)
|
||||
- python (required to generate bindings with glad)
|
||||
- GCC (this program uses GNU C features)
|
||||
|
||||
**Optional requirements:**
|
||||
@@ -33,9 +31,16 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
|
||||
|
||||
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
|
||||
```bash
|
||||
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libxext6 libxext-dev libxcomposite-dev python make gcc
|
||||
sudo apt-get install libpulse0 libpulse-dev libxext6 libxext-dev libxrender-dev libxcomposite-dev make gcc
|
||||
```
|
||||
|
||||
## Installation
|
||||
Some distributions have a package for `glava`. If your distribution is not listed please use the compilation instructions above.
|
||||
|
||||
- Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/)
|
||||
- NixOS [package](https://github.com/NixOS/nixpkgs/blob/release-18.09/pkgs/applications/misc/glava/default.nix)
|
||||
- openSUSE [package](https://build.opensuse.org/package/show/X11:Utilities/glava)
|
||||
|
||||
## [Configuration](https://github.com/wacossusca34/glava/wiki)
|
||||
|
||||
GLava will start by looking for an entry point in the user configuration folder (`~/.config/glava/rc.glsl`\*), and will fall back to loading from the shader installation folder (`/etc/xdg/glava`\*). The entry point will specify a module to load and should set global configuration variables. Configuration for specific modules can be done in their respective `.glsl` files, which the module itself will include.
|
||||
@@ -61,8 +66,8 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
|
||||
| Bspwm |  | No issues
|
||||
| Herbstluftwm |  | `hc rule windowtype~'_NET_WM_WINDOW_TYPE_DESKTOP' manage=off` can be used to unmanage desktop windows
|
||||
| Unity |  | No issues
|
||||
| AwesomeWM |  | Can still be focused, may require other changes to config depending on layout
|
||||
| i3 (and i3-gaps) |  | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6), try `#request setxwintype "!-"`
|
||||
| AwesomeWM |  | Defaults to unmanaged
|
||||
| i3 (and i3-gaps) |  | Defaults to unmanaged
|
||||
| EXWM |  | EXWM does not have a desktop, and forces window decorations
|
||||
| Enlightenment |  | Needs testing
|
||||
| Xmonad |  | Needs testing
|
||||
@@ -70,9 +75,24 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
|
||||
|
||||
Note that some WMs listed without issues have specific overrides when using the `--desktop` flag. See `shaders/env_*.glsl` files for details.
|
||||
|
||||
## Reading from MPD's FIFO output
|
||||
|
||||
Add the following to your `~/.config/mpd.conf`:
|
||||
|
||||
```
|
||||
audio_output {
|
||||
type "fifo"
|
||||
name "glava_fifo"
|
||||
path "/tmp/mpd.fifo"
|
||||
format "22050:16:2"
|
||||
}
|
||||
```
|
||||
|
||||
Note the `22050` sample rate -- this is the reccommended setting for GLava. Restart MPD (if nessecary) and start GLava with `glava --audio=fifo`.
|
||||
|
||||
## Licensing
|
||||
|
||||
GLava is licensed under the terms of the GPLv3. GLava includes some (heavily modified) source code that originated from [cava](https://github.com/karlstav/cava), which was initially provided under the MIT license. The source files that originated from cava are the following:
|
||||
GLava is licensed under the terms of the GPLv3, with the exemption of `khrplatform.h`, which is licensed under the terms in its header. GLava includes some (heavily modified) source code that originated from [cava](https://github.com/karlstav/cava), which was initially provided under the MIT license. The source files that originated from cava are the following:
|
||||
|
||||
- `[cava]/input/fifo.c -> [glava]/fifo.c`
|
||||
- `[cava]/input/fifo.h -> [glava]/fifo.h`
|
||||
@@ -88,7 +108,3 @@ The modified files are relicensed under the terms of the GPLv3. The MIT license
|
||||
The below copyright applies for the modifications to the files listed above, and the remaining sources in the repository:
|
||||
|
||||
`Copyright (c) 2017 Levi Webb`
|
||||
|
||||
## Porting
|
||||
|
||||
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken most of the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.
|
||||
|
||||
183
fifo.c
183
fifo.c
@@ -7,80 +7,123 @@
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include "fifo.h"
|
||||
|
||||
//input: FIFO
|
||||
void* input_fifo(void* data)
|
||||
{
|
||||
struct audio_data *audio = (struct audio_data *)data;
|
||||
int fd;
|
||||
int n = 0;
|
||||
signed char buf[1024];
|
||||
int tempr, templ, lo;
|
||||
int q, i;
|
||||
int t = 0;
|
||||
int size = 1024;
|
||||
int bytes = 0;
|
||||
int flags;
|
||||
struct timespec req = { .tv_sec = 0, .tv_nsec = 10000000 };
|
||||
|
||||
fd = open(audio->source, O_RDONLY);
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
/* Implementation struct storage */
|
||||
|
||||
while (1) {
|
||||
typeof(*audio_impls) audio_impls[sizeof(audio_impls) / sizeof(struct audio_impl*)] = {};
|
||||
size_t audio_impls_idx = 0;
|
||||
|
||||
bytes = read(fd, buf, sizeof(buf));
|
||||
/* FIFO backend */
|
||||
|
||||
if (bytes == -1) { //if no bytes read sleep 10ms and zero shared buffer
|
||||
nanosleep (&req, NULL);
|
||||
t++;
|
||||
if (t > 10) {
|
||||
for (i = 0; i < 2048; i++)audio->audio_out_l[i] = 0;
|
||||
for (i = 0; i < 2048; i++)audio->audio_out_r[i] = 0;
|
||||
t = 0;
|
||||
}
|
||||
} else { //if bytes read go ahead
|
||||
t = 0;
|
||||
for (q = 0; q < (size / 4); q++) {
|
||||
|
||||
tempr = ( buf[ 4 * q + 3] << 2);
|
||||
|
||||
lo = ( buf[4 * q + 2] >> 6);
|
||||
if (lo < 0)lo = abs(lo) + 1;
|
||||
if (tempr >= 0)tempr = tempr + lo;
|
||||
else tempr = tempr - lo;
|
||||
|
||||
templ = ( buf[ 4 * q + 1] << 2);
|
||||
|
||||
lo = ( buf[ 4 * q] >> 6);
|
||||
if (lo < 0)lo = abs(lo) + 1;
|
||||
if (templ >= 0)templ = templ + lo;
|
||||
else templ = templ - lo;
|
||||
|
||||
if (audio->channels == 1) audio->audio_out_l[n] = (tempr +
|
||||
templ) /
|
||||
2;
|
||||
|
||||
|
||||
//stereo storing channels in buffer
|
||||
if (audio->channels == 2) {
|
||||
audio->audio_out_l[n] = templ;
|
||||
audio->audio_out_r[n] = tempr;
|
||||
}
|
||||
|
||||
n++;
|
||||
if (n == 2048 - 1)n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->terminate == 1) {
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void init(struct audio_data* audio) {
|
||||
if (!audio->source) {
|
||||
audio->source = strdup("/tmp/mpd.fifo");
|
||||
}
|
||||
}
|
||||
|
||||
static void* entry(void* data) {
|
||||
struct audio_data* audio = (struct audio_data *) data;
|
||||
|
||||
float* bl = (float*) audio->audio_out_l;
|
||||
float* br = (float*) audio->audio_out_r;
|
||||
size_t fsz = audio->audio_buf_sz;
|
||||
size_t ssz = audio->sample_sz;
|
||||
|
||||
int fd;
|
||||
int16_t buf[ssz / 2];
|
||||
size_t q;
|
||||
int timeout = 50;
|
||||
|
||||
struct timespec tv_last = {}, tv;
|
||||
bool measured = false;
|
||||
|
||||
if ((fd = open(audio->source, O_RDONLY)) == -1) {
|
||||
fprintf(stderr, "failed to open FIFO audio source \"%s\": %s\n", audio->source, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct pollfd pfd = {
|
||||
.fd = fd,
|
||||
.events = POLLIN
|
||||
};
|
||||
|
||||
size_t buffer_offset = (fsz - (ssz / 4));
|
||||
|
||||
while (true) {
|
||||
|
||||
/* The poll timeout is set to accommodate an approximate UPS, but has little purpose except
|
||||
for effectively setting the rate of empty samples in the event of the FIFO descriptor
|
||||
blocking for long periods of time. */
|
||||
|
||||
switch (poll(&pfd, 1, timeout)) {
|
||||
case -1:
|
||||
fprintf(stderr, "FIFO backend: poll() failed (%s)\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
case 0:
|
||||
pthread_mutex_lock(&audio->mutex);
|
||||
|
||||
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
|
||||
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
|
||||
|
||||
for (q = 0; q < (ssz / 4); ++q) bl[buffer_offset + q] = 0;
|
||||
for (q = 0; q < (ssz / 4); ++q) br[buffer_offset + q] = 0;
|
||||
|
||||
audio->modified = true;
|
||||
|
||||
pthread_mutex_unlock(&audio->mutex);
|
||||
break;
|
||||
default: {
|
||||
read(fd, buf, sizeof(buf));
|
||||
clock_gettime(CLOCK_REALTIME, measured ? &tv : &tv_last);
|
||||
if (measured) {
|
||||
/* Set the timeout slightly higher than the delay between samples to prevent empty writes */
|
||||
timeout = (((tv.tv_sec - tv_last.tv_sec) * 1000) + ((tv.tv_nsec - tv_last.tv_nsec) / 1000000)) + 1;
|
||||
tv_last = tv;
|
||||
} else measured = true;
|
||||
|
||||
pthread_mutex_lock(&audio->mutex);
|
||||
|
||||
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
|
||||
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
|
||||
|
||||
for (size_t n = 0, q = 0; q < (ssz / 2); q += 2) {
|
||||
|
||||
size_t idx = (fsz - (ssz / 4)) + n;
|
||||
|
||||
if (audio->channels == 1) {
|
||||
float sample = ((buf[q] + buf[q + 1]) / 2) / (float) 65535;
|
||||
bl[idx] = sample;
|
||||
br[idx] = sample;
|
||||
}
|
||||
|
||||
if (audio->channels == 2) {
|
||||
bl[idx] = buf[q] / (float) 65535;
|
||||
br[idx] = buf[q + 1] / (float) 65535;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
audio->modified = true;
|
||||
|
||||
pthread_mutex_unlock(&audio->mutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->terminate == 1) {
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AUDIO_ATTACH(fifo);
|
||||
|
||||
24
fifo.h
24
fifo.h
@@ -14,4 +14,26 @@ struct audio_data {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
void* input_fifo(void* data);
|
||||
struct audio_impl {
|
||||
const char* name;
|
||||
void (*init)(struct audio_data* data);
|
||||
void* (*entry)(void* data);
|
||||
};
|
||||
|
||||
#define AUDIO_FUNC(F) \
|
||||
.F = (typeof(((struct audio_impl*) NULL)->F)) &F
|
||||
|
||||
extern struct audio_impl* audio_impls[4];
|
||||
extern size_t audio_impls_idx;
|
||||
|
||||
static inline void register_audio_impl(struct audio_impl* impl) { audio_impls[audio_impls_idx++] = impl; }
|
||||
|
||||
#define AUDIO_ATTACH(N) \
|
||||
static struct audio_impl N##_var = { \
|
||||
.name = #N, \
|
||||
AUDIO_FUNC(init), \
|
||||
AUDIO_FUNC(entry), \
|
||||
}; \
|
||||
void __attribute__((constructor)) _##N##_construct(void) { \
|
||||
register_audio_impl(&N##_var); \
|
||||
}
|
||||
|
||||
169
glava.c
169
glava.c
@@ -55,16 +55,24 @@
|
||||
#define SHADER_USER_PATH "userconf"
|
||||
/* FHS compliant systems */
|
||||
#elif defined(__unix__) || defined(GLAVA_UNIX)
|
||||
#ifndef SHADER_INSTALL_PATH
|
||||
#define SHADER_INSTALL_PATH "/etc/xdg/glava"
|
||||
#endif
|
||||
#define SHADER_USER_PATH FORMAT("%s/glava", ENV("XDG_CONFIG_HOME", "%s/.config", ENV("HOME", "/home")))
|
||||
/* OSX */
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) || defined(GLAVA_OSX)
|
||||
#ifndef SHADER_INSTALL_PATH
|
||||
#define SHADER_INSTALL_PATH "/Library/glava"
|
||||
#endif
|
||||
#define SHADER_USER_PATH FORMAT("%s/Library/Preferences/glava", ENV("HOME", "/"))
|
||||
#else
|
||||
#error "Unsupported target system"
|
||||
#endif
|
||||
|
||||
#ifndef ACCESSPERMS
|
||||
#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
|
||||
#endif
|
||||
|
||||
/* Copy installed shaders/configuration from the installed location
|
||||
(usually /etc/xdg). Modules (folders) will be linked instead of
|
||||
copied. */
|
||||
@@ -109,8 +117,7 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
{
|
||||
case 1: {
|
||||
int source = -1, dest = -1;
|
||||
uint8_t buf[pgsz];
|
||||
ssize_t r, t, w, a;
|
||||
@@ -140,12 +147,12 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
|
||||
if (dest > 0) close(dest);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (symlink(p, f) && errno != EEXIST)
|
||||
fprintf(stderr, "failed to symlink '%s' -> '%s': %s\n", p, f, strerror(errno));
|
||||
else if (verbose)
|
||||
printf("symlink '%s' -> '%s'\n", p, f);
|
||||
break;
|
||||
case 2:
|
||||
if (symlink(p, f) && errno != EEXIST)
|
||||
fprintf(stderr, "failed to symlink '%s' -> '%s': %s\n", p, f, strerror(errno));
|
||||
else if (verbose)
|
||||
printf("symlink '%s' -> '%s'\n", p, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
@@ -163,9 +170,10 @@ static const char* help_str =
|
||||
"-d, --desktop enables running glava as a desktop window by detecting the\n"
|
||||
" desktop environment and setting the appropriate properties\n"
|
||||
" automatically. Can override properties in \"rc.glsl\".\n"
|
||||
"-r, --request=REQUEST evaluates the specified request after loading \"rc.glsl\".\n"
|
||||
"-m, --force-mod=NAME forces the specified module to load instead, ignoring any\n"
|
||||
" `#request mod` instances in the entry point.\n"
|
||||
"-e, --entry=NAME specifies the name of the file to look for when loading shaders,\n"
|
||||
"-e, --entry=FILE specifies the name of the file to look for when loading shaders,\n"
|
||||
" by default this is \"rc.glsl\".\n"
|
||||
"-C, --copy-config creates copies and symbolic links in the user configuration\n"
|
||||
" directory for glava, copying any files in the root directory\n"
|
||||
@@ -173,63 +181,120 @@ static const char* help_str =
|
||||
"-b, --backend specifies a window creation backend to use. By default, the most\n"
|
||||
" appropriate backend will be used for the underlying windowing\n"
|
||||
" system.\n"
|
||||
"-a, --audio=BACKEND specifies an audio input backend to use.\n"
|
||||
"-i, --stdin[=FORMAT] specifies a format for input to be read from stdin. The input\n"
|
||||
" may be read from the STDIN macro from within shader sources.\n"
|
||||
" A stream of inputs (each overriding the previous) must be\n"
|
||||
" separated by newline (\'\\n\') characters.\n"
|
||||
"-V, --version print application version and exit\n"
|
||||
"\n"
|
||||
GLAVA_VERSION_STRING "\n"
|
||||
" -- Copyright (C) 2017 Levi Webb\n";
|
||||
"The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n"
|
||||
"in GLSL files.\n"
|
||||
"\n"
|
||||
"The DEFINE argument is appended to the associated file before it is processed. It is\n"
|
||||
"evaluated identically to the \'#define' preprocessor directive.\n"
|
||||
"\n"
|
||||
"The FILE argument may be any file path. All specified file paths are relative to the\n"
|
||||
"active configuration root (usually ~/.config/glava if present).\n"
|
||||
"\n"
|
||||
"The BACKEND argument may be any of the following strings (for this particular build):\n"
|
||||
"%s"
|
||||
"\n"
|
||||
"The FORMAT argument must be a valid GLSL type. If `--stdin` is used without an argument,\n"
|
||||
"the default type is `vec4` (type used for RGBA colors)\n"
|
||||
"\n"
|
||||
GLAVA_VERSION_STRING "\n";
|
||||
|
||||
static const char* opt_str = "dhvVe:Cm:b:";
|
||||
static const char* opt_str = "dhvVe:Cm:b:r:a:i::";
|
||||
static struct option p_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"desktop", no_argument, 0, 'd'},
|
||||
{"audio", required_argument, 0, 'a'},
|
||||
{"request", required_argument, 0, 'r'},
|
||||
{"entry", required_argument, 0, 'e'},
|
||||
{"force-mod", required_argument, 0, 'm'},
|
||||
{"copy-config", no_argument, 0, 'C'},
|
||||
{"backend", required_argument, 0, 'b'},
|
||||
{"stdin", optional_argument, 0, 'i'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static renderer* rd = NULL;
|
||||
|
||||
void handle_term(int signum) {
|
||||
static void handle_term(int signum) {
|
||||
if (rd->alive) {
|
||||
puts("\nInterrupt recieved, closing...");
|
||||
rd->alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void append_buf(char** buf, size_t* sz_store, char* str) {
|
||||
buf = realloc(buf, ++(*sz_store) * sizeof(char*));
|
||||
buf[*sz_store - 1] = str;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
/* Evaluate these macros only once, since they allocate */
|
||||
const char* install_path = SHADER_INSTALL_PATH;
|
||||
const char* user_path = SHADER_USER_PATH;
|
||||
const char* entry = "rc.glsl";
|
||||
const char* force = NULL;
|
||||
const char* backend = NULL;
|
||||
const char
|
||||
* install_path = SHADER_INSTALL_PATH,
|
||||
* user_path = SHADER_USER_PATH,
|
||||
* entry = "rc.glsl",
|
||||
* force = NULL,
|
||||
* backend = NULL,
|
||||
* audio_impl_name = "pulseaudio";
|
||||
const char* system_shader_paths[] = { user_path, install_path, NULL };
|
||||
int stdin_type = STDIN_TYPE_NONE;
|
||||
|
||||
char** requests = malloc(1);
|
||||
size_t requests_sz = 0;
|
||||
|
||||
bool verbose = false, copy_mode = false, desktop = false;
|
||||
|
||||
int c, idx;
|
||||
while ((c = getopt_long(argc, argv, opt_str, p_opts, &idx)) != -1) {
|
||||
switch (c) {
|
||||
case 'v': verbose = true; break;
|
||||
case 'C': copy_mode = true; break;
|
||||
case 'd': desktop = true; break;
|
||||
case 'e': entry = optarg; break;
|
||||
case 'm': force = optarg; break;
|
||||
case 'b': backend = 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;
|
||||
case 'v': verbose = true; break;
|
||||
case 'C': copy_mode = true; break;
|
||||
case 'd': desktop = true; break;
|
||||
case 'r': append_buf(requests, &requests_sz, optarg); break;
|
||||
case 'e': entry = optarg; break;
|
||||
case 'm': force = optarg; break;
|
||||
case 'b': backend = optarg; break;
|
||||
case 'a': audio_impl_name = optarg; break;
|
||||
case '?': exit(EXIT_FAILURE); break;
|
||||
case 'V':
|
||||
puts(GLAVA_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
case 'h': {
|
||||
char buf[2048];
|
||||
size_t bsz = 0;
|
||||
for (size_t t = 0; t < audio_impls_idx; ++t)
|
||||
bsz += snprintf(buf + bsz, sizeof(buf) - bsz, "\t\"%s\"%s\n", audio_impls[t]->name,
|
||||
!strcmp(audio_impls[t]->name, audio_impl_name) ? " (default)" : "");
|
||||
printf(help_str, argc > 0 ? argv[0] : "glava", buf);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
stdin_type = -1;
|
||||
for (size_t t = 0 ; stdin_types[t].n != NULL; ++t) {
|
||||
if (optarg == NULL) {
|
||||
stdin_type = STDIN_TYPE_VEC4;
|
||||
} else if (!strcmp(stdin_types[t].n, optarg)) {
|
||||
stdin_type = stdin_types[t].i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stdin_type == -1) {
|
||||
fprintf(stderr, "Unsupported `--stdin` GLSL type: \"%s\"\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +303,19 @@ int main(int argc, char** argv) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
rd = rd_new(system_shader_paths, entry, force, backend, desktop);
|
||||
/* Handle `--force` argument as a request override */
|
||||
if (force) {
|
||||
const size_t bsz = 5 + strlen(force);
|
||||
char* force_req_buf = malloc(bsz);
|
||||
snprintf(force_req_buf, bsz, "mod %s", force);
|
||||
append_buf(requests, &requests_sz, force_req_buf);
|
||||
}
|
||||
|
||||
/* Null terminate array arguments */
|
||||
append_buf(requests, &requests_sz, NULL);
|
||||
|
||||
rd = rd_new(system_shader_paths, entry, (const char**) requests,
|
||||
backend, stdin_type, desktop, verbose);
|
||||
|
||||
struct sigaction action = { .sa_handler = handle_term };
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
@@ -270,13 +347,27 @@ int main(int argc, char** argv) {
|
||||
.sample_sz = rd->samplesize_request,
|
||||
.modified = false
|
||||
};
|
||||
if (!audio.source) {
|
||||
get_pulse_default_sink(&audio);
|
||||
printf("Using default PulseAudio sink: %s\n", audio.source);
|
||||
|
||||
struct audio_impl* impl = NULL;
|
||||
|
||||
for (size_t t = 0; t < audio_impls_idx; ++t) {
|
||||
if (!strcmp(audio_impls[t]->name, audio_impl_name)) {
|
||||
impl = audio_impls[t];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!impl) {
|
||||
fprintf(stderr, "The specified audio backend (\"%s\") is not available.\n", audio_impl_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
impl->init(&audio);
|
||||
|
||||
if (verbose) printf("Using audio source: %s\n", audio.source);
|
||||
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, input_pulse, (void*) &audio);
|
||||
pthread_create(&thread, NULL, impl->entry, (void*) &audio);
|
||||
|
||||
float lb[rd->bufsize_request], rb[rd->bufsize_request];
|
||||
while (rd->alive) {
|
||||
|
||||
681
glsl_ext.c
681
glsl_ext.c
@@ -15,6 +15,14 @@
|
||||
|
||||
#include "glsl_ext.h"
|
||||
|
||||
#define LINE_START 0
|
||||
#define GLSL 1
|
||||
#define MACRO 2
|
||||
#define REQUEST 3
|
||||
#define INCLUDE 4
|
||||
#define COLOR 5
|
||||
#define DEFINE 6
|
||||
|
||||
struct sbuf {
|
||||
char* buf;
|
||||
size_t at; /* index of final null character */
|
||||
@@ -56,12 +64,12 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#define parse_error(line, f, fmt, ...) \
|
||||
#define parse_error(line, f, fmt, ...) \
|
||||
fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \
|
||||
abort()
|
||||
|
||||
#define parse_error_s(line, f, s) \
|
||||
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
|
||||
#define parse_error_s(line, f, s) \
|
||||
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
|
||||
abort()
|
||||
|
||||
struct schar {
|
||||
@@ -82,10 +90,10 @@ bool ext_parse_color(const char* str, size_t elem_sz, float** results) {
|
||||
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;
|
||||
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 */
|
||||
@@ -105,158 +113,188 @@ bool ext_parse_color(const char* str, size_t elem_sz, float** results) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_after(struct glsl_ext* ext, void* ptr) {
|
||||
++ext->destruct_sz;
|
||||
ext->destruct = realloc(ext->destruct, sizeof(void*) * ext->destruct_sz);
|
||||
ext->destruct[ext->destruct_sz - 1] = ptr;
|
||||
}
|
||||
|
||||
static void inherit(struct glsl_ext* parent, struct glsl_ext* child) {
|
||||
free_after(parent, child->processed);
|
||||
parent->destruct = realloc(parent->destruct, sizeof(void*) * (parent->destruct_sz + child->destruct_sz));
|
||||
memcpy(parent->destruct + parent->destruct_sz, child->destruct, sizeof(void*) * child->destruct_sz);
|
||||
parent->destruct_sz += child->destruct_sz;
|
||||
free(child->destruct);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
size_t args_sz, bool include,
|
||||
size_t line, const char* f) {
|
||||
if (include) {
|
||||
if (args_sz == 0) {
|
||||
parse_error_s(line, f, "No arguments provided to #include directive!");
|
||||
size_t args_sz, int state,
|
||||
size_t line, const char* f) {
|
||||
switch (state) {
|
||||
case DEFINE: {
|
||||
/* Workaround for re-defining macros in GLSL. By default this is generally an error in most
|
||||
compilers/drivers, but we would prefer to override (non-function) definitions instead.
|
||||
|
||||
Due to how this directive is parsed, the macro itself is still emitted afterwards. */
|
||||
if (args_sz == 0) {
|
||||
parse_error_s(line, f, "No arguments provided to #define directive!");
|
||||
}
|
||||
size_t bsz = (strlen(args[0]) * 3) + 64;
|
||||
struct schar ret = { .buf = malloc(bsz) };
|
||||
int r = snprintf(ret.buf, bsz, "#ifdef %1$s\n#undef %1$s\n#endif\n", args[0]);
|
||||
if (r < 0) abort();
|
||||
ret.sz = r;
|
||||
free_after(ext, ret.buf);
|
||||
return ret;
|
||||
}
|
||||
char* target = args[0];
|
||||
case INCLUDE: {
|
||||
if (args_sz == 0) {
|
||||
parse_error_s(line, f, "No arguments provided to #include directive!");
|
||||
}
|
||||
char* target = args[0];
|
||||
|
||||
/* Handle `:` config specifier */
|
||||
size_t tsz = strlen(target);
|
||||
if (tsz && target[0] == ':' && ext->cfd) {
|
||||
target = &target[1];
|
||||
ext->cd = ext->cfd;
|
||||
}
|
||||
|
||||
char path[strlen(ext->cd) + tsz + 2];
|
||||
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
|
||||
|
||||
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",
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
|
||||
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",
|
||||
path, strerror(errno));
|
||||
/* Handle `:` config specifier */
|
||||
size_t tsz = strlen(target);
|
||||
if (tsz && target[0] == ':' && ext->cfd) {
|
||||
target = &target[1];
|
||||
ext->cd = ext->cfd;
|
||||
}
|
||||
/* Handle `@` default specifier */
|
||||
if (tsz && target[0] == '@') {
|
||||
if (!ext->dd) {
|
||||
parse_error_s(line, f, "encountered '@' path specifier while no default "
|
||||
"directory is available in the current context");
|
||||
}
|
||||
target = &target[1];
|
||||
ext->cd = ext->dd;
|
||||
}
|
||||
|
||||
char path[strlen(ext->cd) + tsz + 2];
|
||||
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
|
||||
|
||||
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",
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
|
||||
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",
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
struct glsl_ext next = {
|
||||
.source = map,
|
||||
.source_len = st.st_size,
|
||||
.cd = ext->cd,
|
||||
.cfd = ext->cfd,
|
||||
.dd = ext->dd,
|
||||
.handlers = ext->handlers
|
||||
};
|
||||
|
||||
/* recursively process */
|
||||
ext_process(&next, target);
|
||||
inherit(ext, &next);
|
||||
munmap(map, st.st_size);
|
||||
|
||||
struct schar ret = {
|
||||
.buf = next.processed,
|
||||
.sz = next.p_len
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
case REQUEST: {
|
||||
if (args_sz > 0) {
|
||||
char* request = args[0];
|
||||
|
||||
struct glsl_ext next = {
|
||||
.source = map,
|
||||
.source_len = st.st_size,
|
||||
.cd = ext->cd,
|
||||
.cfd = ext->cfd,
|
||||
.handlers = ext->handlers
|
||||
};
|
||||
|
||||
/* recursively process */
|
||||
ext_process(&next, target);
|
||||
|
||||
struct schar ret = {
|
||||
.buf = next.processed,
|
||||
.sz = next.p_len
|
||||
};
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
|
||||
if (args_sz > 0) {
|
||||
char* request = args[0];
|
||||
|
||||
struct request_handler* handler;
|
||||
bool found = false;
|
||||
size_t t;
|
||||
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
|
||||
if(!strcmp(handler->name, request)) {
|
||||
found = true;
|
||||
void** processed_args = malloc(strlen(handler->fmt) * sizeof(void*));
|
||||
struct request_handler* handler;
|
||||
bool found = false;
|
||||
size_t t;
|
||||
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
|
||||
if(!strcmp(handler->name, request)) {
|
||||
found = true;
|
||||
void** processed_args = malloc(strlen(handler->fmt) * sizeof(void*));
|
||||
|
||||
char c;
|
||||
size_t i;
|
||||
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
|
||||
if (args_sz <= 1 + i) {
|
||||
parse_error(line, f,
|
||||
"failed to execute request '%s': expected format '%s'\n",
|
||||
request, handler->fmt);
|
||||
}
|
||||
char* raw = args[1 + i];
|
||||
switch (c) {
|
||||
case 'i':
|
||||
{
|
||||
int v = (int) strtol(raw, NULL, 0);
|
||||
processed_args[i] = malloc(sizeof(int));
|
||||
*(int*) processed_args[i] = v;
|
||||
break;
|
||||
char c;
|
||||
size_t i;
|
||||
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
|
||||
if (args_sz <= 1 + i) {
|
||||
parse_error(line, f,
|
||||
"failed to execute request '%s': expected format '%s'\n",
|
||||
request, handler->fmt);
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
float f = strtof(raw, NULL);
|
||||
processed_args[i] = malloc(sizeof(float));
|
||||
*(float*) processed_args[i] = f;
|
||||
break;
|
||||
}
|
||||
case 's': { *(char**) &processed_args[i] = raw; break; }
|
||||
case 'b':
|
||||
{
|
||||
bool v;
|
||||
if (!strcmp(raw, "true")) {
|
||||
v = true;
|
||||
} else if (!strcmp(raw, "false")) {
|
||||
v = false;
|
||||
} else if (strlen(raw) == 1) {
|
||||
switch (raw[0]) {
|
||||
case 't': { v = true; break; }
|
||||
case 'f': { v = false; break; }
|
||||
case '1': { v = true; break; }
|
||||
case '0': { v = false; break; }
|
||||
default:
|
||||
char* raw = args[1 + i];
|
||||
switch (c) {
|
||||
case 'i': {
|
||||
int v = (int) strtol(raw, NULL, 0);
|
||||
processed_args[i] = malloc(sizeof(int));
|
||||
*(int*) processed_args[i] = v;
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
float f = strtof(raw, NULL);
|
||||
processed_args[i] = malloc(sizeof(float));
|
||||
*(float*) processed_args[i] = f;
|
||||
break;
|
||||
}
|
||||
case 's': { *(char**) &processed_args[i] = raw; break; }
|
||||
case 'b': {
|
||||
bool v;
|
||||
if (!strcmp(raw, "true")) {
|
||||
v = true;
|
||||
} else if (!strcmp(raw, "false")) {
|
||||
v = false;
|
||||
} else if (strlen(raw) == 1) {
|
||||
switch (raw[0]) {
|
||||
case 't': { v = true; break; }
|
||||
case 'f': { v = false; break; }
|
||||
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");
|
||||
}
|
||||
} else {
|
||||
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");
|
||||
processed_args[i] = malloc(sizeof(bool));
|
||||
*(bool*) processed_args[i] = v;
|
||||
break;
|
||||
}
|
||||
processed_args[i] = malloc(sizeof(bool));
|
||||
*(bool*) processed_args[i] = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handler->handler(request, processed_args);
|
||||
handler->handler(request, processed_args);
|
||||
|
||||
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i)
|
||||
if (c != 's')
|
||||
free(processed_args[i]);
|
||||
free(processed_args);
|
||||
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i)
|
||||
if (c != 's')
|
||||
free(processed_args[i]);
|
||||
free(processed_args);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
parse_error(line, f, "unknown request type '%s'", request);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
parse_error(line, f, "unknown request type '%s'", request);
|
||||
}
|
||||
}
|
||||
|
||||
struct schar ret = {
|
||||
.buf = NULL,
|
||||
.sz = 0
|
||||
};
|
||||
|
||||
return ret;
|
||||
default: return (struct schar) { .buf = NULL, .sz = 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/* state machine parser */
|
||||
void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
|
||||
#define LINE_START 0
|
||||
#define GLSL 1
|
||||
#define MACRO 2
|
||||
#define REQUEST 3
|
||||
#define INCLUDE 4
|
||||
#define COLOR 5
|
||||
ext->destruct = malloc(1);
|
||||
ext->destruct_sz = 0;
|
||||
|
||||
struct sbuf sbuf = {
|
||||
.buf = malloc(256),
|
||||
@@ -272,7 +310,7 @@ void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
size_t line = 1;
|
||||
bool quoted = false, arg_start;
|
||||
char cbuf[9];
|
||||
char** args = NULL;
|
||||
char** args = malloc(sizeof(char*));
|
||||
size_t args_sz = 0;
|
||||
|
||||
bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false,
|
||||
@@ -283,213 +321,220 @@ void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
if (at == '\n')
|
||||
++line;
|
||||
switch (state) {
|
||||
case LINE_START: /* processing start of line */
|
||||
{
|
||||
case LINE_START: { /* processing start of line */
|
||||
switch (at) {
|
||||
case '#': {
|
||||
macro_start_idx = t;
|
||||
state = MACRO;
|
||||
continue; }
|
||||
case '\n':
|
||||
if (comment && comment_line) {
|
||||
comment = false;
|
||||
comment_line = false;
|
||||
case '#': {
|
||||
macro_start_idx = t;
|
||||
state = MACRO;
|
||||
continue;
|
||||
}
|
||||
case '\t':
|
||||
case ' ':
|
||||
goto copy;
|
||||
default: state = GLSL;
|
||||
/* let execution continue into next state */
|
||||
case '\n':
|
||||
if (comment && comment_line) {
|
||||
comment = false;
|
||||
comment_line = false;
|
||||
}
|
||||
case '\t':
|
||||
case ' ':
|
||||
goto copy;
|
||||
default: state = GLSL;
|
||||
/* let execution continue into next state */
|
||||
}
|
||||
}
|
||||
case GLSL: /* copying GLSL source or unrelated preprocessor syntax */
|
||||
{
|
||||
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;
|
||||
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;
|
||||
} 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)) */
|
||||
{
|
||||
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);
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
case '0' ... '9': {
|
||||
cbuf[cbuf_idx] = at;
|
||||
++cbuf_idx;
|
||||
if (cbuf_idx >= 8)
|
||||
goto emit_color;
|
||||
else continue;
|
||||
}
|
||||
state = at == '\n' ? LINE_START : GLSL;
|
||||
if (cbuf_idx >= 8)
|
||||
continue;
|
||||
else goto copy; /* copy character if it ended the sequence */
|
||||
}
|
||||
}
|
||||
case MACRO: /* processing start of macro */
|
||||
{
|
||||
switch (at) {
|
||||
case '\n':
|
||||
case ' ':
|
||||
case '\t':
|
||||
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)) {
|
||||
state = INCLUDE;
|
||||
goto prepare_arg_parse;
|
||||
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 {
|
||||
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]);
|
||||
state = at == '\n' ? LINE_START : GLSL;
|
||||
goto copy;
|
||||
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 */
|
||||
}
|
||||
}
|
||||
/* emit contents from start of macro to current index and resume regular parsing*/
|
||||
#define skip_macro() \
|
||||
do { \
|
||||
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]); \
|
||||
state = at == '\n' ? LINE_START : GLSL; \
|
||||
goto copy; \
|
||||
} while (0)
|
||||
case MACRO: { /* processing start of macro */
|
||||
switch (at) {
|
||||
case '\n':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\0': { /* end parsing directive */
|
||||
|
||||
#define DIRECTIVE_CMP(lower, upper) \
|
||||
(!strncmp("#" lower, &ext->source[macro_start_idx], t - macro_start_idx) \
|
||||
|| !strncmp("#" upper, &ext->source[macro_start_idx], t - macro_start_idx))
|
||||
#define DIRECTIVE_CASE(lower, upper) \
|
||||
do { if (state == MACRO && DIRECTIVE_CMP(#lower, #upper)) { state = upper; goto prepare_arg_parse; } } while (0)
|
||||
|
||||
DIRECTIVE_CASE(request, REQUEST);
|
||||
DIRECTIVE_CASE(include, INCLUDE);
|
||||
DIRECTIVE_CASE(define, DEFINE);
|
||||
|
||||
/* no match */
|
||||
if (state == MACRO) skip_macro();
|
||||
#undef DIRECTIVE_CMP
|
||||
#undef DIRECTIVE_CASE
|
||||
prepare_arg_parse:
|
||||
{
|
||||
arg_start_idx = t + 1;
|
||||
arg_start = true;
|
||||
args = malloc(sizeof(char*));
|
||||
args_sz = 0;
|
||||
*args = NULL;
|
||||
}
|
||||
}
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
continue;
|
||||
default:
|
||||
/* invalid char, malformed! */
|
||||
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
continue;
|
||||
default:
|
||||
/* invalid char, malformed! */
|
||||
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
|
||||
}
|
||||
}
|
||||
/* scope-violating macro to copy the result of the currently parsed argument */
|
||||
#define copy_arg(end) \
|
||||
do { if (end - arg_start_idx > 0) { \
|
||||
++args_sz; \
|
||||
args = realloc(args, sizeof(char*) * args_sz); \
|
||||
args[args_sz - 1] = malloc((end - arg_start_idx) + 1); \
|
||||
memcpy(args[args_sz - 1], &ext->source[arg_start_idx], end - arg_start_idx); \
|
||||
args[args_sz - 1][end - arg_start_idx] = '\0'; \
|
||||
} } while (0)
|
||||
case REQUEST:
|
||||
case INCLUDE:
|
||||
{
|
||||
/* scope-violating macro to copy the result of the currently parsed argument */
|
||||
#define copy_arg(end) \
|
||||
do { if (end - arg_start_idx > 0) { \
|
||||
++args_sz; \
|
||||
args = realloc(args, sizeof(char*) * args_sz); \
|
||||
args[args_sz - 1] = malloc((end - arg_start_idx) + 1); \
|
||||
memcpy(args[args_sz - 1], &ext->source[arg_start_idx], end - arg_start_idx); \
|
||||
args[args_sz - 1][end - arg_start_idx] = '\0'; \
|
||||
} } while (0)
|
||||
case REQUEST:
|
||||
case INCLUDE:
|
||||
case DEFINE: {
|
||||
switch (at) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\0':
|
||||
if (!quoted) {
|
||||
/* end arg */
|
||||
copy_arg(t);
|
||||
arg_start = true;
|
||||
arg_start_idx = t + 1;
|
||||
} else arg_start = false;
|
||||
|
||||
if (at == '\n') {
|
||||
/* end directive */
|
||||
size_t a;
|
||||
struct schar r = directive(ext, args, args_sz, state == INCLUDE, line, f);
|
||||
for (a = 0; a < args_sz; ++a) {
|
||||
free(args[a]);
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\0':
|
||||
if (!quoted) {
|
||||
/* end arg */
|
||||
copy_arg(t);
|
||||
arg_start = true;
|
||||
arg_start_idx = t + 1;
|
||||
} else arg_start = false;
|
||||
|
||||
if (at == '\n' || at == '\0' || state == DEFINE) {
|
||||
/* end directive */
|
||||
size_t a;
|
||||
struct schar r = directive(ext, args, args_sz, state, line, f);
|
||||
for (a = 0; a < args_sz; ++a) {
|
||||
free(args[a]);
|
||||
}
|
||||
args_sz = 0;
|
||||
/* if something was returned (ie. included file), paste the results */
|
||||
if (r.buf) {
|
||||
n_append(&sbuf, r.sz, r.buf);
|
||||
append(&sbuf, "\n");
|
||||
}
|
||||
if (state == DEFINE) skip_macro();
|
||||
else state = LINE_START;
|
||||
}
|
||||
args_sz = 0;
|
||||
/* if something was returned (ie. included file), paste the results */
|
||||
if (r.buf) {
|
||||
n_append(&sbuf, r.sz, r.buf);
|
||||
append(&sbuf, "\n");
|
||||
}
|
||||
munmap(r.buf, r.sz);
|
||||
state = LINE_START;
|
||||
break;
|
||||
case '(':
|
||||
if (state != DEFINE || args_sz != 0) goto arg; /* only handle first arg of #define */
|
||||
skip_macro(); /* ignore macro functions */
|
||||
case '"':
|
||||
if (state == DEFINE) goto arg; /* do not handle quoting for #define */
|
||||
if (quoted) {
|
||||
/* end arg */
|
||||
copy_arg(t);
|
||||
quoted = false;
|
||||
arg_start = true;
|
||||
arg_start_idx = t + 1;
|
||||
} else if (arg_start) {
|
||||
++arg_start_idx;
|
||||
quoted = true;
|
||||
} else arg_start = false;
|
||||
break;
|
||||
default: {
|
||||
arg: arg_start = false;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
if (quoted) {
|
||||
/* end arg */
|
||||
copy_arg(t);
|
||||
quoted = false;
|
||||
arg_start = true;
|
||||
arg_start_idx = t + 1;
|
||||
} else if (arg_start) {
|
||||
++arg_start_idx;
|
||||
quoted = true;
|
||||
} else arg_start = false;
|
||||
break;
|
||||
default:
|
||||
arg_start = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
#undef copy_arg
|
||||
}
|
||||
copy:
|
||||
if (at != '\0')
|
||||
@@ -497,10 +542,20 @@ void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
}
|
||||
ext->processed = sbuf.buf;
|
||||
ext->p_len = sbuf.at;
|
||||
|
||||
if (args)
|
||||
|
||||
if (args) {
|
||||
for (t = 0; t < args_sz; ++t) {
|
||||
free(args[t]);
|
||||
}
|
||||
free(args);
|
||||
}
|
||||
}
|
||||
|
||||
void ext_free(struct glsl_ext* ext) {
|
||||
free(ext->processed);
|
||||
size_t t;
|
||||
for (t = 0; t < ext->destruct_sz; ++t) {
|
||||
free(ext->destruct[t]);
|
||||
}
|
||||
free(ext->destruct);
|
||||
}
|
||||
|
||||
15
glsl_ext.h
15
glsl_ext.h
@@ -21,12 +21,15 @@ struct request_handler {
|
||||
};
|
||||
|
||||
struct glsl_ext {
|
||||
char* processed; /* OUT: null terminated processed source */
|
||||
size_t p_len; /* OUT: length of processed buffer, excluding null char */
|
||||
const char* source; /* IN: raw data passed via ext_process */
|
||||
size_t source_len; /* IN: raw source len */
|
||||
const char* cd; /* IN: current directory */
|
||||
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
|
||||
char* processed; /* OUT: null terminated processed source */
|
||||
size_t p_len; /* OUT: length of processed buffer, excluding null char */
|
||||
const char* source; /* IN: raw data passed via ext_process */
|
||||
size_t source_len; /* IN: raw source len */
|
||||
const char* cd; /* IN: current directory */
|
||||
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
|
||||
const char* dd; /* IN: default directory */
|
||||
void** destruct; /* internal */
|
||||
size_t destruct_sz; /* internal */
|
||||
|
||||
/* IN: NULL (where the last element's 'name' member is NULL) terminated
|
||||
array of request handlers */
|
||||
|
||||
122
glx_wcb.c
122
glx_wcb.c
@@ -20,7 +20,7 @@
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad.h"
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
@@ -193,8 +193,8 @@ static void init(void) {
|
||||
maximized = false;
|
||||
transparent = false;
|
||||
|
||||
void* hgl = dlopen("libGL.so", RTLD_LAZY);
|
||||
void* hglx = dlopen("libGLX.so", RTLD_LAZY);
|
||||
void* hgl = dlopen("libGL.so.1", RTLD_LAZY);
|
||||
void* hglx = dlopen("libGLX.so.0", RTLD_LAZY);
|
||||
|
||||
if (!hgl && !hglx) {
|
||||
fprintf(stderr, "Failed to load GLX functions (libGL and libGLX do not exist!)\n");
|
||||
@@ -215,7 +215,7 @@ static void init(void) {
|
||||
}
|
||||
|
||||
#define resolve(name) do { name = (typeof(name)) resolve_f(#name); } while (0)
|
||||
#define intern(name, only_if_exists) \
|
||||
#define intern(name, only_if_exists) \
|
||||
do { ATOM_##name = XInternAtom(display, #name, only_if_exists); } while (0)
|
||||
|
||||
resolve(glXChooseFBConfig);
|
||||
@@ -254,15 +254,76 @@ static void apply_decorations(Window w) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool find_parent(Window w, Window* parent) {
|
||||
Window root, *children = NULL;
|
||||
unsigned int num_children;
|
||||
|
||||
if(!XQueryTree(display, w, &root, parent, &children, &num_children))
|
||||
return false;
|
||||
|
||||
if (children)
|
||||
XFree(children);
|
||||
|
||||
return *parent != None;
|
||||
}
|
||||
|
||||
static void apply_clickthrough(struct glxwin* w) {
|
||||
if (w->clickthrough) {
|
||||
int ignored;
|
||||
if (XShapeQueryExtension(display, &ignored, &ignored)) {
|
||||
Region region;
|
||||
if ((region = XCreateRegion())) {
|
||||
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
|
||||
XDestroyRegion(region);
|
||||
Window root = DefaultRootWindow(display);
|
||||
Window win = w->w;
|
||||
while (win != None) {
|
||||
Region region;
|
||||
if ((region = XCreateRegion())) {
|
||||
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
|
||||
XDestroyRegion(region);
|
||||
}
|
||||
Window parent;
|
||||
find_parent(win, &parent);
|
||||
win = (parent == root ? None : parent);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Warning: XShape extension not available\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_events(struct glxwin* w) {
|
||||
while (XPending(display) > 0) {
|
||||
XEvent ev;
|
||||
XNextEvent(display, &ev);
|
||||
switch (ev.type) {
|
||||
case ClientMessage:
|
||||
if (ev.xclient.message_type == ATOM_WM_PROTOCOLS
|
||||
&& ev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
|
||||
w->should_close = true;
|
||||
}
|
||||
break;
|
||||
case MapNotify:
|
||||
apply_clickthrough(w);
|
||||
XFlush(display);
|
||||
break;
|
||||
case VisibilityNotify:
|
||||
switch (ev.xvisibility.state) {
|
||||
case VisibilityFullyObscured:
|
||||
w->should_render = false;
|
||||
break;
|
||||
case VisibilityUnobscured:
|
||||
case VisibilityPartiallyObscured:
|
||||
w->should_render = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid VisibilityNotify event state (%d)\n", ev.xvisibility.state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PropertyNotify:
|
||||
if (ev.xproperty.atom == ATOM__XROOTPMAP_ID) {
|
||||
w->bg_changed = true;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,7 +421,7 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
vi = glXGetVisualFromFBConfig(display, config);
|
||||
|
||||
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vi->visual, AllocNone);
|
||||
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
|
||||
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
|
||||
attr.event_mask |= PropertyChangeMask | VisibilityChangeMask;
|
||||
attr.background_pixmap = None;
|
||||
attr.border_pixel = 0;
|
||||
@@ -479,11 +540,10 @@ static void set_geometry(struct glxwin* w, int x, int y, int d, int h) {
|
||||
static void set_visible(struct glxwin* w, bool visible) {
|
||||
if (visible) {
|
||||
XMapWindow(display, w->w);
|
||||
apply_clickthrough(w);
|
||||
switch (w->override_state) {
|
||||
case '+': XRaiseWindow(display, w->w); break;
|
||||
case '-': XLowerWindow(display, w->w); break;
|
||||
default: break;
|
||||
case '+': XRaiseWindow(display, w->w); break;
|
||||
case '-': XLowerWindow(display, w->w); break;
|
||||
default: break;
|
||||
}
|
||||
XFlush(display);
|
||||
}
|
||||
@@ -498,44 +558,13 @@ static bool should_render(struct glxwin* w) {
|
||||
attributes to see if our window isn't viewable. */
|
||||
XWindowAttributes attrs;
|
||||
XGetWindowAttributes(display, w->w, &attrs);
|
||||
process_events(w);
|
||||
return w->should_render && attrs.map_state == IsViewable;
|
||||
}
|
||||
|
||||
static void swap_buffers(struct glxwin* w) {
|
||||
glXSwapBuffers(display, w->w);
|
||||
|
||||
while (XPending(display) > 0) {
|
||||
XEvent ev;
|
||||
XNextEvent(display, &ev);
|
||||
switch (ev.type) {
|
||||
case ClientMessage:
|
||||
if (ev.xclient.message_type == ATOM_WM_PROTOCOLS
|
||||
&& ev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
|
||||
w->should_close = true;
|
||||
}
|
||||
break;
|
||||
case VisibilityNotify:
|
||||
switch (ev.xvisibility.state) {
|
||||
case VisibilityFullyObscured:
|
||||
w->should_render = false;
|
||||
break;
|
||||
case VisibilityUnobscured:
|
||||
case VisibilityPartiallyObscured:
|
||||
w->should_render = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid VisibilityNotify event state (%d)\n", ev.xvisibility.state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PropertyNotify:
|
||||
if (ev.xproperty.atom == ATOM__XROOTPMAP_ID) {
|
||||
w->bg_changed = true;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
process_events(w);
|
||||
}
|
||||
|
||||
static void get_fbsize(struct glxwin* w, int* d, int* h) {
|
||||
@@ -562,6 +591,7 @@ static void destroy(struct glxwin* w) {
|
||||
glXMakeCurrent(display, None, NULL); /* release context */
|
||||
glXDestroyContext(display, w->context);
|
||||
XDestroyWindow(display, w->w);
|
||||
free(w);
|
||||
}
|
||||
|
||||
static void terminate(void) {
|
||||
|
||||
282
khrplatform.h
Normal file
282
khrplatform.h
Normal file
@@ -0,0 +1,282 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are 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 Materials.
|
||||
**
|
||||
** THE MATERIALS ARE 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
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
@@ -35,16 +35,11 @@ static void cb(__attribute__((unused)) pa_context* pulseaudio_context,
|
||||
static void pulseaudio_context_state_callback(pa_context* pulseaudio_context, void* userdata) {
|
||||
|
||||
/* Ensure loop is ready */
|
||||
switch (pa_context_get_state(pulseaudio_context))
|
||||
{
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
break;
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
break;
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
break;
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
break;
|
||||
switch (pa_context_get_state(pulseaudio_context)) {
|
||||
case PA_CONTEXT_UNCONNECTED: break;
|
||||
case PA_CONTEXT_CONNECTING: break;
|
||||
case PA_CONTEXT_AUTHORIZING: break;
|
||||
case PA_CONTEXT_SETTING_NAME: break;
|
||||
case PA_CONTEXT_READY: /* extract default sink name */
|
||||
pa_operation_unref(pa_context_get_server_info(pulseaudio_context, cb, userdata));
|
||||
break;
|
||||
@@ -55,11 +50,13 @@ static void pulseaudio_context_state_callback(pa_context* pulseaudio_context, vo
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void get_pulse_default_sink(struct audio_data* audio) {
|
||||
static void init(struct audio_data* audio) {
|
||||
|
||||
if (audio->source) return;
|
||||
|
||||
pa_mainloop_api* mainloop_api;
|
||||
pa_context* pulseaudio_context;
|
||||
@@ -111,7 +108,7 @@ void get_pulse_default_sink(struct audio_data* audio) {
|
||||
#error "Unsupported float format (requires 32 bit IEEE (little or big endian) floating point support)"
|
||||
#endif
|
||||
|
||||
void* input_pulse(void* data) {
|
||||
static void* entry(void* data) {
|
||||
struct audio_data* audio = (struct audio_data*) data;
|
||||
int i, n;
|
||||
size_t ssz = audio->sample_sz;
|
||||
@@ -193,3 +190,5 @@ void* input_pulse(void* data) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AUDIO_ATTACH(pulseaudio);
|
||||
|
||||
544
render.c
544
render.c
@@ -7,18 +7,30 @@
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad.h"
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
#include "glsl_ext.h"
|
||||
|
||||
typeof(stdin_types) stdin_types = {
|
||||
[STDIN_TYPE_NONE] = { .n = "NONE", .i = STDIN_TYPE_NONE },
|
||||
[STDIN_TYPE_INT] = { .n = "int", .i = STDIN_TYPE_INT },
|
||||
[STDIN_TYPE_FLOAT] = { .n = "float", .i = STDIN_TYPE_FLOAT },
|
||||
[STDIN_TYPE_BOOL] = { .n = "bool", .i = STDIN_TYPE_BOOL },
|
||||
[STDIN_TYPE_VEC2] = { .n = "vec2", .i = STDIN_TYPE_VEC2 },
|
||||
[STDIN_TYPE_VEC3] = { .n = "vec3", .i = STDIN_TYPE_VEC3 },
|
||||
[STDIN_TYPE_VEC4] = { .n = "vec4", .i = STDIN_TYPE_VEC4 },
|
||||
{}
|
||||
};
|
||||
|
||||
#define TWOPI 6.28318530718
|
||||
#define PI 3.14159265359
|
||||
#define swap(a, b) do { __auto_type tmp = a; a = b; b = tmp; } while (0)
|
||||
@@ -32,7 +44,7 @@
|
||||
|
||||
/* Only a single vertex shader is needed for GLava, since all rendering is done in the fragment shader
|
||||
over a fullscreen quad */
|
||||
#define VERTEX_SHADER_SRC \
|
||||
#define VERTEX_SHADER_SRC \
|
||||
"layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }"
|
||||
|
||||
struct gl_wcb* wcbs[2] = {};
|
||||
@@ -69,6 +81,12 @@ struct gl_sampler_data {
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
/* per-bind data containing the framebuffer and 1D texture to render for smoothing. */
|
||||
|
||||
struct sm_fb {
|
||||
GLuint fbo, tex;
|
||||
};
|
||||
|
||||
/* GLSL uniform bind */
|
||||
|
||||
struct gl_bind {
|
||||
@@ -78,14 +96,14 @@ struct gl_bind {
|
||||
int src_type;
|
||||
void (**transformations)(struct gl_data*, void**, void* data);
|
||||
size_t t_sz;
|
||||
void* udata;
|
||||
struct sm_fb sm;
|
||||
};
|
||||
|
||||
/* GL screen framebuffer object */
|
||||
|
||||
struct gl_sfbo {
|
||||
GLuint fbo, tex, shader;
|
||||
bool valid, nativeonly;
|
||||
GLuint fbo, tex, shader, stdin_uniform;
|
||||
bool indirect, nativeonly;
|
||||
const char* name;
|
||||
struct gl_bind* binds;
|
||||
size_t binds_sz;
|
||||
@@ -112,6 +130,7 @@ struct gl_data {
|
||||
copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen,
|
||||
clickthrough;
|
||||
void** t_data;
|
||||
size_t t_count;
|
||||
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
|
||||
smooth_factor, fft_scale, fft_cutoff;
|
||||
struct {
|
||||
@@ -119,6 +138,7 @@ struct gl_data {
|
||||
} clear_color;
|
||||
float* interpolate_buf[6];
|
||||
int geometry[4];
|
||||
int stdin_type;
|
||||
};
|
||||
|
||||
|
||||
@@ -128,9 +148,11 @@ static GLuint shaderload(const char* rpath,
|
||||
GLenum type,
|
||||
const char* shader,
|
||||
const char* config,
|
||||
const char* defaults,
|
||||
struct request_handler* handlers,
|
||||
int shader_version,
|
||||
bool raw,
|
||||
bool* skipped,
|
||||
struct gl_data* gl) {
|
||||
|
||||
size_t s_len = strlen(shader);
|
||||
@@ -167,13 +189,18 @@ static GLuint shaderload(const char* rpath,
|
||||
"#define PRE_SMOOTHED_AUDIO %d\n"
|
||||
"#define SMOOTH_FACTOR %.6f\n"
|
||||
"#define USE_ALPHA %d\n"
|
||||
"#define PREMULTIPLY_ALPHA %d\n";
|
||||
"#define PREMULTIPLY_ALPHA %d\n"
|
||||
"#define USE_STDIN %d\n"
|
||||
"#if USE_STDIN == 1\n"
|
||||
"uniform %s STDIN;\n"
|
||||
"#endif\n";
|
||||
|
||||
struct glsl_ext ext = {
|
||||
.source = raw ? NULL : map,
|
||||
.source_len = raw ? 0 : st.st_size,
|
||||
.cd = shader,
|
||||
.cfd = config,
|
||||
.dd = defaults,
|
||||
.handlers = handlers,
|
||||
.processed = (char*) (raw ? shader : NULL),
|
||||
.p_len = raw ? s_len : 0
|
||||
@@ -186,7 +213,8 @@ static GLuint shaderload(const char* rpath,
|
||||
GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
|
||||
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms,
|
||||
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor,
|
||||
1, gl->premultiply_alpha ? 1 : 0);
|
||||
1, gl->premultiply_alpha ? 1 : 0,
|
||||
gl->stdin_type != STDIN_TYPE_NONE, stdin_types[gl->stdin_type].n);
|
||||
if (written < 0) {
|
||||
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
|
||||
return 0;
|
||||
@@ -198,10 +226,10 @@ static GLuint shaderload(const char* rpath,
|
||||
GLint sl = (GLint) (ext.p_len + written);
|
||||
glShaderSource(s, 1, (const GLchar* const*) &buf, &sl);
|
||||
switch (glGetError()) {
|
||||
case GL_INVALID_VALUE:
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, "invalid operation while loading shader source\n");
|
||||
return 0;
|
||||
case GL_INVALID_VALUE:
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, "invalid operation while loading shader source\n");
|
||||
return 0;
|
||||
}
|
||||
glCompileShader(s);
|
||||
GLint ret, ilen;
|
||||
@@ -209,10 +237,33 @@ static GLuint shaderload(const char* rpath,
|
||||
if (ret == GL_FALSE) {
|
||||
glGetShaderiv(s, GL_INFO_LOG_LENGTH, &ilen);
|
||||
if (ilen) {
|
||||
GLchar buf[ilen];
|
||||
glGetShaderInfoLog(s, ilen, NULL, buf);
|
||||
GLchar* ebuf = malloc(sizeof(GLchar) * ilen);
|
||||
glGetShaderInfoLog(s, ilen, NULL, ebuf);
|
||||
|
||||
/* check for `#error __disablestage` and flag `*skipped` accordingly */
|
||||
if (skipped != NULL) {
|
||||
bool last = false;
|
||||
static const char* skip_keyword = "__disablestage";
|
||||
size_t sksz = sizeof(skip_keyword);
|
||||
for(size_t t = 0; t < (size_t) ilen; ++t) {
|
||||
if (ebuf[t] == '_') {
|
||||
if (last && !strncmp(ebuf + t - 1, skip_keyword, sksz)) {
|
||||
*skipped = true;
|
||||
goto free_ebuf;
|
||||
} else last = true;
|
||||
} else last = false;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Shader compilation failed for '%s':\n", path);
|
||||
fwrite(buf, sizeof(GLchar), ilen - 1, stderr);
|
||||
fwrite(ebuf, sizeof(GLchar), ilen - 1, stderr);
|
||||
#ifdef GLAVA_DEBUG
|
||||
fprintf(stderr, "Processed shader source for '%s':\n", path);
|
||||
fwrite(buf, sizeof(GLchar), sl, stderr);
|
||||
#endif
|
||||
|
||||
free_ebuf:
|
||||
free(ebuf);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Shader compilation failed for '%s', but no info was available\n", path);
|
||||
@@ -240,13 +291,13 @@ static GLuint shaderlink_f(GLuint* arr) {
|
||||
while ((f = arr[i++]) != 0) {
|
||||
glAttachShader(p, f);
|
||||
switch (glGetError()) {
|
||||
case GL_INVALID_VALUE:
|
||||
fprintf(stderr, "tried to pass invalid value to glAttachShader\n");
|
||||
return 0;
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, "shader is already attached, or argument types "
|
||||
"were invalid when calling glAttachShader\n");
|
||||
return 0;
|
||||
case GL_INVALID_VALUE:
|
||||
fprintf(stderr, "tried to pass invalid value to glAttachShader\n");
|
||||
return 0;
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, "shader is already attached, or argument types "
|
||||
"were invalid when calling glAttachShader\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
glLinkProgram(p);
|
||||
@@ -269,14 +320,16 @@ static GLuint shaderlink_f(GLuint* arr) {
|
||||
}
|
||||
|
||||
/* load shaders */
|
||||
#define shaderbuild(gl, shader_path, c, r, v, ...) \
|
||||
shaderbuild_f(gl, shader_path, c, r, v, (const char*[]) {__VA_ARGS__, 0})
|
||||
#define shaderbuild(gl, shader_path, c, d, r, v, s, ...) \
|
||||
shaderbuild_f(gl, shader_path, c, d, r, v, s, (const char*[]) {__VA_ARGS__, 0})
|
||||
static GLuint shaderbuild_f(struct gl_data* gl,
|
||||
const char* shader_path,
|
||||
const char* config,
|
||||
const char* config, const char* defaults,
|
||||
struct request_handler* handlers,
|
||||
int shader_version,
|
||||
bool* skipped,
|
||||
const char** arr) {
|
||||
if (skipped) *skipped = false;
|
||||
const char* str;
|
||||
int i = 0, sz = 0, t;
|
||||
while ((str = arr[i++]) != NULL) ++sz;
|
||||
@@ -289,15 +342,15 @@ static GLuint shaderbuild_f(struct gl_data* gl,
|
||||
if (path[t] == '.') {
|
||||
if (!strcmp(path + t + 1, "frag") || !strcmp(path + t + 1, "glsl")) {
|
||||
if (!(shaders[i] = shaderload(path, GL_FRAGMENT_SHADER,
|
||||
shader_path, config, handlers,
|
||||
shader_version, false, gl))) {
|
||||
shader_path, config, defaults, handlers,
|
||||
shader_version, false, skipped, gl))) {
|
||||
return 0;
|
||||
}
|
||||
} else if (!strcmp(path + t + 1, "vert")) {
|
||||
/*
|
||||
if (!(shaders[i] = shaderload(path, GL_VERTEX_SHADER, shader_path))) {
|
||||
return 0;
|
||||
}
|
||||
if (!(shaders[i] = shaderload(path, GL_VERTEX_SHADER, shader_path))) {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
fprintf(stderr, "shaderbuild(): vertex shaders not allowed: %s\n", path);
|
||||
abort();
|
||||
@@ -310,7 +363,7 @@ static GLuint shaderbuild_f(struct gl_data* gl,
|
||||
}
|
||||
}
|
||||
/* load builtin vertex shader */
|
||||
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, handlers, shader_version, true, gl);
|
||||
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, NULL, gl);
|
||||
fflush(stdout);
|
||||
return shaderlink_f(shaders);
|
||||
}
|
||||
@@ -345,9 +398,9 @@ static void update_1d_tex(GLuint tex, size_t w, float* data) {
|
||||
/* setup screen framebuffer object and its texture */
|
||||
|
||||
static void setup_sfbo(struct gl_sfbo* s, int w, int h) {
|
||||
GLuint tex = s->valid ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; });
|
||||
GLuint fbo = s->valid ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; });
|
||||
s->valid = true;
|
||||
GLuint tex = s->indirect ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; });
|
||||
GLuint fbo = s->indirect ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; });
|
||||
s->indirect = true;
|
||||
/* bind texture and setup space */
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
@@ -358,10 +411,10 @@ static void setup_sfbo(struct gl_sfbo* s, int w, int h) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
|
||||
case GL_FRAMEBUFFER_COMPLETE: break;
|
||||
default:
|
||||
fprintf(stderr, "error in frambuffer state\n");
|
||||
abort();
|
||||
case GL_FRAMEBUFFER_COMPLETE: break;
|
||||
default:
|
||||
fprintf(stderr, "error in frambuffer state\n");
|
||||
abort();
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
@@ -465,10 +518,9 @@ static struct gl_bind_src bind_sources[] = {
|
||||
};
|
||||
|
||||
#define window(t, sz) (0.53836 - (0.46164 * cos(TWOPI * (double) t / (double)(sz - 1))))
|
||||
#define ALLOC_ONCE(u, udata, ...) \
|
||||
#define ALLOC_ONCE(u, udata, sz) \
|
||||
if (*udata == NULL) { \
|
||||
u = malloc(sizeof(*u)); \
|
||||
*u = (typeof(*u)) __VA_ARGS__; \
|
||||
u = calloc(sz, sizeof(typeof(*u))); \
|
||||
*udata = u; \
|
||||
} else u = (typeof(u)) *udata;
|
||||
|
||||
@@ -529,18 +581,16 @@ void transform_gravity(struct gl_data* d, void** udata, void* data) {
|
||||
float* b = s->buf;
|
||||
size_t sz = s->sz, t;
|
||||
|
||||
struct {
|
||||
float* applied;
|
||||
}* u;
|
||||
ALLOC_ONCE(u, udata, { .applied = calloc(sz, sizeof(float)) });
|
||||
float* applied;
|
||||
ALLOC_ONCE(applied, udata, sz);
|
||||
|
||||
float g = d->gravity_step * (1.0F / d->ur);
|
||||
|
||||
for (t = 0; t < sz; ++t) {
|
||||
if (b[t] >= u->applied[t]) {
|
||||
u->applied[t] = b[t] - g;
|
||||
} else u->applied[t] -= g;
|
||||
b[t] = u->applied[t];
|
||||
if (b[t] >= applied[t]) {
|
||||
applied[t] = b[t] - g;
|
||||
} else applied[t] -= g;
|
||||
b[t] = applied[t];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,20 +603,18 @@ void transform_average(struct gl_data* d, void** udata, void* data) {
|
||||
float v;
|
||||
bool use_window = d->avg_window;
|
||||
|
||||
struct {
|
||||
float* bufs;
|
||||
}* u;
|
||||
ALLOC_ONCE(u, udata, { .bufs = calloc(tsz, sizeof(float)) });
|
||||
float* bufs;
|
||||
ALLOC_ONCE(bufs, udata, tsz);
|
||||
|
||||
memmove(u->bufs, &u->bufs[sz], (tsz - sz) * sizeof(float));
|
||||
memcpy(&u->bufs[tsz - sz], b, sz * sizeof(float));
|
||||
memmove(bufs, &bufs[sz], (tsz - sz) * sizeof(float));
|
||||
memcpy(&bufs[tsz - sz], b, sz * sizeof(float));
|
||||
|
||||
#define DO_AVG(w) \
|
||||
do { \
|
||||
for (t = 0; t < sz; ++t) { \
|
||||
v = 0.0F; \
|
||||
for (f = 0; f < d->avg_frames; ++f) { \
|
||||
v += w * u->bufs[(f * sz) + t]; \
|
||||
v += w * bufs[(f * sz) + t]; \
|
||||
} \
|
||||
b[t] = v / d->avg_frames; \
|
||||
} \
|
||||
@@ -609,48 +657,48 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
|
||||
float wtemp, wr, wpr, wpi, wi, theta;
|
||||
float tempr, tempi;
|
||||
|
||||
// reverse-binary reindexing
|
||||
n = nn<<1;
|
||||
j=1;
|
||||
for (i=1; i<n; i+=2) {
|
||||
if (j>i) {
|
||||
/* reverse-binary reindexing */
|
||||
n = nn << 1;
|
||||
j = 1;
|
||||
for (i = 1; i < n; i += 2) {
|
||||
if (j > i) {
|
||||
swap(data[j-1], data[i-1]);
|
||||
swap(data[j], data[i]);
|
||||
}
|
||||
m = nn;
|
||||
while (m>=2 && j>m) {
|
||||
while (m >= 2 && j > m) {
|
||||
j -= m;
|
||||
m >>= 1;
|
||||
}
|
||||
j += m;
|
||||
};
|
||||
|
||||
// here begins the Danielson-Lanczos section
|
||||
mmax=2;
|
||||
while (n>mmax) {
|
||||
istep = mmax<<1;
|
||||
theta = -(2*M_PI/mmax);
|
||||
wtemp = sin(0.5*theta);
|
||||
wpr = -2.0*wtemp*wtemp;
|
||||
/* here begins the Danielson-Lanczos section */
|
||||
mmax = 2;
|
||||
while (n > mmax) {
|
||||
istep = mmax << 1;
|
||||
theta = -(2 * M_PI / mmax);
|
||||
wtemp = sin(0.5 * theta);
|
||||
wpr = -2.0 * wtemp * wtemp;
|
||||
wpi = sin(theta);
|
||||
wr = 1.0;
|
||||
wi = 0.0;
|
||||
for (m=1; m < mmax; m += 2) {
|
||||
for (i=m; i <= n; i += istep) {
|
||||
j=i+mmax;
|
||||
tempr = wr*data[j-1] - wi*data[j];
|
||||
tempi = wr * data[j] + wi*data[j-1];
|
||||
for (m = 1; m < mmax; m += 2) {
|
||||
for (i = m; i <= n; i += istep) {
|
||||
j= i + mmax;
|
||||
tempr = wr * data[j-1] - wi * data[j];
|
||||
tempi = wr * data[j] + wi * data[j-1];
|
||||
|
||||
data[j-1] = data[i-1] - tempr;
|
||||
data[j] = data[i] - tempi;
|
||||
data[j] = data[i] - tempi;
|
||||
data[i-1] += tempr;
|
||||
data[i] += tempi;
|
||||
data[i] += tempi;
|
||||
}
|
||||
wtemp=wr;
|
||||
wr += wr*wpr - wi*wpi;
|
||||
wi += wi*wpr + wtemp*wpi;
|
||||
wtemp = wr;
|
||||
wr += wr * wpr - wi * wpi;
|
||||
wi += wi * wpr + wtemp * wpi;
|
||||
}
|
||||
mmax=istep;
|
||||
mmax = istep;
|
||||
}
|
||||
|
||||
/* abs and log scale */
|
||||
@@ -680,9 +728,10 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct renderer* rd_new(const char** paths, const char* entry,
|
||||
const char* force_mod, const char* force_backend,
|
||||
bool auto_desktop) {
|
||||
struct renderer* rd_new(const char** paths, const char* entry,
|
||||
const char** requests, const char* force_backend,
|
||||
int stdin_type, bool auto_desktop,
|
||||
bool verbose) {
|
||||
|
||||
xwin_wait_for_wm();
|
||||
|
||||
@@ -730,7 +779,8 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
.fft_cutoff = 0.3F,
|
||||
.geometry = { 0, 0, 500, 400 },
|
||||
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F },
|
||||
.clickthrough = false
|
||||
.clickthrough = false,
|
||||
.stdin_type = stdin_type
|
||||
};
|
||||
|
||||
bool forced = force_backend != NULL;
|
||||
@@ -762,7 +812,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Using backend: '%s'\n", backend);
|
||||
if (verbose) printf("Using backend: '%s'\n", backend);
|
||||
|
||||
for (size_t t = 0; t < wcbs_idx; ++t) {
|
||||
if (wcbs[t]->name && !strcmp(wcbs[t]->name, backend)) {
|
||||
@@ -777,7 +827,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
}
|
||||
|
||||
#ifdef GLAD_DEBUG
|
||||
printf("Assigning debug callback\n");
|
||||
if (verbose) printf("Assigning debug callback\n");
|
||||
static bool assigned_debug_cb = false;
|
||||
if (!assigned_debug_cb) {
|
||||
glad_set_post_callback(glad_debugcb);
|
||||
@@ -790,7 +840,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
int shader_version = 330,
|
||||
context_version_major = 3,
|
||||
context_version_minor = 3;
|
||||
const char* module = force_mod;
|
||||
const char* module = NULL;
|
||||
const char* wintitle_default = "GLava";
|
||||
char* xwintype = NULL, * wintitle = (char*) wintitle_default;
|
||||
char** xwinstates = malloc(1);
|
||||
@@ -860,8 +910,8 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
{
|
||||
.name = "mod", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, {
|
||||
if (loading_module && !force_mod) {
|
||||
if (module != NULL && module != force_mod) free((char*) module);
|
||||
if (loading_module) {
|
||||
if (module != NULL) free((char*) module);
|
||||
size_t len = strlen((char*) args[0]);
|
||||
char* str = malloc(sizeof(char) * (len + 1));
|
||||
strncpy(str, (char*) args[0], len + 1);
|
||||
@@ -872,6 +922,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
{
|
||||
.name = "nativeonly", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, {
|
||||
fprintf(stderr, "WARNING: `nativeonly` is deprecated, use `#if PREMULTIPLY_ALPHA == 1`!\n");
|
||||
if (current)
|
||||
current->nativeonly = *(bool*) args[0];
|
||||
else {
|
||||
@@ -1038,7 +1089,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
.src_type = src->src_type,
|
||||
.transformations = malloc(1),
|
||||
.t_sz = 0,
|
||||
.udata = NULL
|
||||
.sm = {}
|
||||
};
|
||||
})
|
||||
},
|
||||
@@ -1052,9 +1103,11 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
directories will be populated with symlinks to the installed modules. */
|
||||
|
||||
const char* data;
|
||||
const char* dd; /* defaults dir (system) */
|
||||
const char* env = gl->wcb->get_environment();
|
||||
size_t d_len, e_len;
|
||||
|
||||
|
||||
for (const char** i = paths; (data = *i) != NULL; ++i) dd = data;
|
||||
for (const char** i = paths; (data = *i) != NULL; ++i) {
|
||||
d_len = strlen(data);
|
||||
e_len = env ? strlen(env) : 0;
|
||||
@@ -1091,6 +1144,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
};
|
||||
|
||||
ext_process(&ext, se_buf);
|
||||
ext_free(&ext);
|
||||
|
||||
munmap((void*) map, st.st_size);
|
||||
|
||||
@@ -1102,14 +1156,17 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (errno != ENOENT &&
|
||||
errno != ENOTDIR &&
|
||||
errno != ELOOP) {
|
||||
fprintf(stderr, "Failed to load desktop environment specific presets at '%s': %s\n", se_buf, strerror(errno));
|
||||
fprintf(stderr, "Failed to load desktop environment specific presets "
|
||||
"at '%s': %s\n", se_buf, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
printf("No presets for current desktop environment (\"%s\"), using default presets for embedding\n", env);
|
||||
printf("No presets for current desktop environment (\"%s\"), "
|
||||
"using default presets for embedding\n", env);
|
||||
snprintf(se_buf, bsz, "%s/env_default.glsl", data);
|
||||
fd = open(se_buf, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Failed to load default presets at '%s': %s\n", se_buf, strerror(errno));
|
||||
fprintf(stderr, "Failed to load default presets at '%s': %s\n",
|
||||
se_buf, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@@ -1117,21 +1174,45 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
fstat(fd, &st);
|
||||
map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
|
||||
ext.source = map;
|
||||
ext.source = map;
|
||||
ext.source_len = st.st_size;
|
||||
|
||||
loading_presets = true;
|
||||
ext_process(&ext, se_buf);
|
||||
ext_free(&ext);
|
||||
loading_presets = false;
|
||||
|
||||
munmap((void*) map, st.st_size);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to detect the desktop environment! Is the window manager EWMH compliant?");
|
||||
fprintf(stderr, "Failed to detect the desktop environment! "
|
||||
"Is the window manager EWMH compliant?");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
struct glsl_ext ext = {
|
||||
.cd = data,
|
||||
.handlers = handlers
|
||||
};
|
||||
|
||||
const char* req;
|
||||
char fbuf[64];
|
||||
int idx = 1;
|
||||
for (const char** i = requests; (req = *i) != NULL; ++i) {
|
||||
size_t rlen = strlen(req) + 16;
|
||||
char* rbuf = malloc(rlen);
|
||||
rlen = snprintf(rbuf, rlen, "#request %s", req);
|
||||
snprintf(fbuf, sizeof(fbuf), "[request arg %d]", idx);
|
||||
ext.source = rbuf;
|
||||
ext.source_len = rlen;
|
||||
ext_process(&ext, fbuf);
|
||||
ext_free(&ext);
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (!module) {
|
||||
fprintf(stderr,
|
||||
@@ -1170,9 +1251,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
char shaders[bsz]; /* module pack path to use */
|
||||
snprintf(shaders, bsz, "%s/%s", data, module);
|
||||
|
||||
printf("Loading module: '%s'\n", module);
|
||||
if (verbose) printf("Loading module: '%s'\n", module);
|
||||
|
||||
if (!force_mod) free((void*) module);
|
||||
free((void*) module);
|
||||
loading_module = false;
|
||||
|
||||
/* Iterate through shader passes in the shader directory and build textures, framebuffers, and
|
||||
@@ -1199,7 +1280,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (d->d_type == DT_REG || d->d_type == DT_UNKNOWN) {
|
||||
snprintf(buf, sizeof(buf), "%d." SHADER_EXT_FRAG, (int) idx);
|
||||
if (!strcmp(buf, d->d_name)) {
|
||||
printf("found GLSL stage: '%s'\n", d->d_name);
|
||||
if (verbose) printf("found GLSL stage: '%s'\n", d->d_name);
|
||||
++count;
|
||||
found = true;
|
||||
}
|
||||
@@ -1220,43 +1301,49 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (d->d_type == DT_REG || d->d_type == DT_UNKNOWN) {
|
||||
snprintf(buf, sizeof(buf), "%d." SHADER_EXT_FRAG, (int) idx);
|
||||
if (!strcmp(buf, d->d_name)) {
|
||||
printf("compiling: '%s'\n", d->d_name);
|
||||
if (verbose) printf("compiling: '%s'\n", d->d_name);
|
||||
|
||||
struct gl_sfbo* s = &stages[idx - 1];
|
||||
*s = (struct gl_sfbo) {
|
||||
.name = strdup(d->d_name),
|
||||
.shader = 0,
|
||||
.valid = false,
|
||||
.indirect = false,
|
||||
.nativeonly = false,
|
||||
.binds = malloc(1),
|
||||
.binds_sz = 0
|
||||
};
|
||||
|
||||
current = s;
|
||||
GLuint id = shaderbuild(gl, shaders, data, handlers, shader_version, d->d_name);
|
||||
if (!id) {
|
||||
bool skip;
|
||||
GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, &skip, d->d_name);
|
||||
if (skip && verbose) printf("disabled: '%s'\n", d->d_name);
|
||||
if (!id && !skip)
|
||||
abort();
|
||||
}
|
||||
|
||||
s->shader = id;
|
||||
|
||||
/* Only setup a framebuffer and texture if this isn't the final step,
|
||||
as it can rendered directly */
|
||||
if (idx != count) {
|
||||
int w, h;
|
||||
gl->wcb->get_fbsize(gl->w, &w, &h);
|
||||
setup_sfbo(&stages[idx - 1], w, h);
|
||||
if (id) {
|
||||
/* Only setup a framebuffer and texture if this isn't the final step,
|
||||
as it can rendered directly */
|
||||
if (idx != count) {
|
||||
int w, h;
|
||||
gl->wcb->get_fbsize(gl->w, &w, &h);
|
||||
setup_sfbo(&stages[idx - 1], w, h);
|
||||
}
|
||||
|
||||
glUseProgram(id);
|
||||
|
||||
/* Setup uniform bindings */
|
||||
size_t b;
|
||||
for (b = 0; b < s->binds_sz; ++b) {
|
||||
s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name);
|
||||
}
|
||||
if (gl->stdin_type != STDIN_TYPE_NONE) {
|
||||
s->stdin_uniform = glGetUniformLocation(id, "STDIN");
|
||||
}
|
||||
glBindFragDataLocation(id, 1, "fragment");
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
glUseProgram(id);
|
||||
|
||||
/* Setup uniform bindings */
|
||||
size_t b;
|
||||
for (b = 0; b < s->binds_sz; ++b) {
|
||||
s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name);
|
||||
}
|
||||
glBindFragDataLocation(id, 1, "fragment");
|
||||
glUseProgram(0);
|
||||
|
||||
found = true;
|
||||
}
|
||||
@@ -1273,16 +1360,13 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
|
||||
{
|
||||
struct gl_sfbo* final = NULL;
|
||||
if (!gl->premultiply_alpha) {
|
||||
for (size_t t = 0; t < gl->stages_sz; ++t) {
|
||||
if (!gl->stages[t].nativeonly) {
|
||||
final = &gl->stages[t];
|
||||
}
|
||||
for (size_t t = 0; t < gl->stages_sz; ++t) {
|
||||
if (gl->stages[t].shader && (gl->premultiply_alpha || !gl->stages[t].nativeonly)) {
|
||||
final = &gl->stages[t];
|
||||
}
|
||||
}
|
||||
/* Invalidate framebuffer and use direct rendering if it was instantiated
|
||||
due to a following `nativeonly` shader pass. */
|
||||
if (final) final->valid = false;
|
||||
/* Use dirct rendering on final pass */
|
||||
if (final) final->indirect = false;
|
||||
}
|
||||
|
||||
/* Compile smooth pass shader */
|
||||
@@ -1294,7 +1378,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
char util[usz]; /* module pack path to use */
|
||||
snprintf(util, usz, "%s/%s", data, util_folder);
|
||||
loading_smooth_pass = true;
|
||||
if (!(gl->sm_prog = shaderbuild(gl, util, data, handlers, shader_version, "smooth_pass.frag")))
|
||||
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, NULL, "smooth_pass.frag")))
|
||||
abort();
|
||||
loading_smooth_pass = false;
|
||||
}
|
||||
@@ -1319,7 +1403,8 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
}
|
||||
|
||||
{
|
||||
gl->t_data = malloc(sizeof(void*) * t_count);
|
||||
gl->t_data = malloc(sizeof(void*) * t_count);
|
||||
gl->t_count = t_count;
|
||||
size_t t;
|
||||
for (t = 0; t < t_count; ++t) {
|
||||
gl->t_data[t] = NULL;
|
||||
@@ -1415,7 +1500,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
/* Resize screen textures if needed */
|
||||
if (ww != gl->lww || wh != gl->lwh) {
|
||||
for (t = 0; t < gl->stages_sz; ++t) {
|
||||
if (gl->stages[t].valid) {
|
||||
if (gl->stages[t].indirect) {
|
||||
setup_sfbo(&gl->stages[t], ww, wh);
|
||||
}
|
||||
}
|
||||
@@ -1432,6 +1517,101 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
gl->lwh = wh;
|
||||
|
||||
glViewport(0, 0, ww, wh);
|
||||
|
||||
static bool stdin_uniform_ready = false;
|
||||
static union {
|
||||
bool b;
|
||||
int i;
|
||||
float f[4];
|
||||
} stdin_parsed;
|
||||
|
||||
/* Parse stdin data, if nessecary */
|
||||
if (gl->stdin_type != STDIN_TYPE_NONE) {
|
||||
static char stdin_buf[64] = {};
|
||||
static size_t stdin_idx = 0;
|
||||
int c, n, p;
|
||||
setvbuf(stdin, NULL, _IOLBF, 64);
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
struct timeval timeout = { 0, 0 };
|
||||
n = select(1, &fds, NULL, NULL, &timeout);
|
||||
|
||||
for (p = 0; n > 0; ++p) {
|
||||
c = getchar();
|
||||
if (stdin_idx >= (sizeof(stdin_buf) / sizeof(*stdin_buf)) - 1)
|
||||
break;
|
||||
if (c != EOF && c != '\n')
|
||||
stdin_buf[stdin_idx++] = c;
|
||||
else {
|
||||
stdin_buf[stdin_idx] = '\0';
|
||||
switch (gl->stdin_type) {
|
||||
case STDIN_TYPE_BOOL:
|
||||
if (!strcmp("true", stdin_buf) ||
|
||||
!strcmp("TRUE", stdin_buf) ||
|
||||
!strcmp("True", stdin_buf) ||
|
||||
!strcmp("1", stdin_buf)) {
|
||||
stdin_parsed.b = true;
|
||||
stdin_uniform_ready = true;
|
||||
} else if (!strcmp("false", stdin_buf) ||
|
||||
!strcmp("FALSE", stdin_buf) ||
|
||||
!strcmp("False", stdin_buf) ||
|
||||
!strcmp("0", stdin_buf)) {
|
||||
stdin_parsed.b = false;
|
||||
stdin_uniform_ready = true;
|
||||
}
|
||||
break;
|
||||
case STDIN_TYPE_INT:
|
||||
errno = 0;
|
||||
stdin_parsed.i = (int) strtol(stdin_buf, NULL, 10);
|
||||
if (errno != ERANGE) stdin_uniform_ready = true;
|
||||
break;
|
||||
case STDIN_TYPE_FLOAT:
|
||||
errno = 0;
|
||||
stdin_parsed.f[0] = strtof(stdin_buf, NULL);
|
||||
if (errno != ERANGE) stdin_uniform_ready = true;
|
||||
break;
|
||||
case STDIN_TYPE_VEC2:
|
||||
if (EOF != sscanf(stdin_buf, "%f,%f",
|
||||
&stdin_parsed.f[0], &stdin_parsed.f[1]))
|
||||
stdin_uniform_ready = true;
|
||||
break;
|
||||
case STDIN_TYPE_VEC3:
|
||||
if (EOF != sscanf(stdin_buf, "%f,%f,%f",
|
||||
&stdin_parsed.f[0], &stdin_parsed.f[1],
|
||||
&stdin_parsed.f[2]))
|
||||
stdin_uniform_ready = true;
|
||||
break;
|
||||
case STDIN_TYPE_VEC4:
|
||||
if (stdin_buf[0] == '#') {
|
||||
stdin_parsed.f[0] = 0.0F;
|
||||
stdin_parsed.f[1] = 0.0F;
|
||||
stdin_parsed.f[2] = 0.0F;
|
||||
stdin_parsed.f[3] = 1.0F;
|
||||
float* ptrs[] = {
|
||||
&stdin_parsed.f[0], &stdin_parsed.f[1],
|
||||
&stdin_parsed.f[2], &stdin_parsed.f[3]
|
||||
};
|
||||
ext_parse_color(stdin_buf + 1, 2, ptrs);
|
||||
stdin_uniform_ready = true;
|
||||
} else if (EOF != sscanf(stdin_buf, "%f,%f,%f,%f",
|
||||
&stdin_parsed.f[0], &stdin_parsed.f[1],
|
||||
&stdin_parsed.f[2], &stdin_parsed.f[3]))
|
||||
stdin_uniform_ready = true;
|
||||
break;
|
||||
}
|
||||
stdin_buf[0] = '\0';
|
||||
stdin_idx = 0;
|
||||
break;
|
||||
|
||||
if (c == EOF) {
|
||||
gl->stdin_type = STDIN_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
struct gl_sfbo* prev;
|
||||
|
||||
@@ -1444,16 +1624,16 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
/* Current shader program */
|
||||
struct gl_sfbo* current = &gl->stages[t];
|
||||
|
||||
if (current->nativeonly && !gl->premultiply_alpha)
|
||||
if (!current->shader || (current->nativeonly && !gl->premultiply_alpha))
|
||||
continue;
|
||||
|
||||
/* Bind framebuffer if this is not the final pass */
|
||||
if (current->valid)
|
||||
if (current->indirect)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!current->valid && gl->copy_desktop) {
|
||||
if (!current->indirect && gl->copy_desktop) {
|
||||
/* Self-contained code for drawing background image */
|
||||
static GLuint bg_prog, bg_utex, bg_screen;
|
||||
static bool setup = false;
|
||||
@@ -1470,9 +1650,9 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
"}" "\n";
|
||||
if (!setup) {
|
||||
bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC,
|
||||
NULL, NULL, 330, true, gl),
|
||||
NULL, NULL, NULL, 330, true, NULL, gl),
|
||||
shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader,
|
||||
NULL, NULL, 330, true, gl));
|
||||
NULL, NULL, NULL, 330, true, NULL, gl));
|
||||
bg_utex = glGetUniformLocation(bg_prog, "tex");
|
||||
bg_screen = glGetUniformLocation(bg_prog, "screen");
|
||||
glBindFragDataLocation(bg_prog, 1, "fragment");
|
||||
@@ -1495,6 +1675,37 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
/* Select the program associated with this pass */
|
||||
glUseProgram(current->shader);
|
||||
|
||||
/* Pass STDIN unfirom if one has been parsed */
|
||||
if (stdin_uniform_ready) {
|
||||
switch (gl->stdin_type) {
|
||||
case STDIN_TYPE_BOOL:
|
||||
glUniform1i(current->stdin_uniform, (int) stdin_parsed.b);
|
||||
break;
|
||||
case STDIN_TYPE_INT:
|
||||
glUniform1i(current->stdin_uniform, stdin_parsed.i);
|
||||
break;
|
||||
case STDIN_TYPE_FLOAT:
|
||||
glUniform1f(current->stdin_uniform, stdin_parsed.f[0]);
|
||||
break;
|
||||
case STDIN_TYPE_VEC2:
|
||||
glUniform2f(current->stdin_uniform,
|
||||
stdin_parsed.f[0], stdin_parsed.f[1]);
|
||||
break;
|
||||
case STDIN_TYPE_VEC3:
|
||||
glUniform3f(current->stdin_uniform,
|
||||
stdin_parsed.f[0], stdin_parsed.f[1],
|
||||
stdin_parsed.f[2]);
|
||||
break;
|
||||
case STDIN_TYPE_VEC4:
|
||||
glUniform4f(current->stdin_uniform,
|
||||
stdin_parsed.f[0], stdin_parsed.f[1],
|
||||
stdin_parsed.f[2], stdin_parsed.f[3]);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
stdin_uniform_ready = false;
|
||||
}
|
||||
|
||||
bool prev_bound = false;
|
||||
|
||||
/* Iterate through each uniform binding, transforming and passing the
|
||||
@@ -1544,20 +1755,10 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
setup = true;
|
||||
}
|
||||
|
||||
/* Per-bind data containing the framebuffer and 1D texture to render to for
|
||||
this smoothing step. */
|
||||
|
||||
struct sm_fb {
|
||||
GLuint fbo, tex;
|
||||
};
|
||||
|
||||
/* Allocate and setup our per-bind data, if needed */
|
||||
|
||||
struct sm_fb* sm;
|
||||
if (!bind->udata) {
|
||||
sm = malloc(sizeof(struct sm_fb));
|
||||
bind->udata = sm;
|
||||
|
||||
struct sm_fb* sm = &bind->sm;
|
||||
if (sm->tex == 0) {
|
||||
glGenTextures(1, &sm->tex);
|
||||
glGenFramebuffers(1, &sm->fbo);
|
||||
|
||||
@@ -1575,14 +1776,13 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
GL_TEXTURE_1D, sm->tex, 0);
|
||||
|
||||
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
|
||||
case GL_FRAMEBUFFER_COMPLETE: break;
|
||||
default:
|
||||
fprintf(stderr, "error in frambuffer state\n");
|
||||
abort();
|
||||
case GL_FRAMEBUFFER_COMPLETE: break;
|
||||
default:
|
||||
fprintf(stderr, "error in frambuffer state\n");
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* Just bind our data if it was already allocated and setup */
|
||||
sm = (struct sm_fb*) bind->udata;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
|
||||
glBindTexture(GL_TEXTURE_1D, sm->tex);
|
||||
}
|
||||
@@ -1601,7 +1801,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
/* Return state */
|
||||
glUseProgram(current->shader);
|
||||
if (current->valid)
|
||||
if (current->indirect)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
|
||||
else
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
@@ -1620,27 +1820,27 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
(currently) exists. */
|
||||
|
||||
switch (bind->src_type) {
|
||||
case SRC_PREV:
|
||||
/* bind texture and pass it to the shader uniform if we need to pass
|
||||
the sampler from the previous pass */
|
||||
if (!prev_bound && prev != NULL) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, prev->tex);
|
||||
prev_bound = true;
|
||||
}
|
||||
glUniform1i(bind->uniform, 0);
|
||||
break;
|
||||
case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1, true); break;
|
||||
case SRC_AUDIO_R: handle_1d_tex(gl->audio_tex_r, rb, irb, bsz, 2, true); break;
|
||||
case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break;
|
||||
case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break;
|
||||
case SRC_PREV:
|
||||
/* bind texture and pass it to the shader uniform if we need to pass
|
||||
the sampler from the previous pass */
|
||||
if (!prev_bound && prev != NULL) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, prev->tex);
|
||||
prev_bound = true;
|
||||
}
|
||||
glUniform1i(bind->uniform, 0);
|
||||
break;
|
||||
case SRC_AUDIO_L: handle_1d_tex(gl->audio_tex_l, lb, ilb, bsz, 1, true); break;
|
||||
case SRC_AUDIO_R: handle_1d_tex(gl->audio_tex_r, rb, irb, bsz, 2, true); break;
|
||||
case SRC_AUDIO_SZ: glUniform1i(bind->uniform, bsz); break;
|
||||
case SRC_SCREEN: glUniform2i(bind->uniform, (GLint) ww, (GLint) wh); break;
|
||||
}
|
||||
}
|
||||
|
||||
drawoverlay(&gl->overlay); /* Fullscreen quad (actually just two triangles) */
|
||||
|
||||
/* Reset some state */
|
||||
if (current->valid)
|
||||
if (current->indirect)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glUseProgram(0);
|
||||
|
||||
@@ -1716,8 +1916,14 @@ struct gl_wcb* rd_get_wcb (struct renderer* r) { return r->gl->wcb; }
|
||||
void rd_destroy(struct renderer* r) {
|
||||
r->gl->wcb->destroy(r->gl->w);
|
||||
if (r->gl->interpolate) free(r->gl->interpolate_buf[0]);
|
||||
if (r->gl->t_data) free(r->gl->t_data);
|
||||
size_t t, b;
|
||||
if (r->gl->t_data) {
|
||||
for (t = 0; t < r->gl->t_count; ++t) {
|
||||
if (r->gl->t_data[t])
|
||||
free(r->gl->t_data[t]);
|
||||
}
|
||||
free(r->gl->t_data);
|
||||
}
|
||||
for (t = 0; t < r->gl->stages_sz; ++t) {
|
||||
struct gl_sfbo* stage = &r->gl->stages[t];
|
||||
for (b = 0; b < stage->binds_sz; ++b) {
|
||||
|
||||
20
render.h
20
render.h
@@ -2,6 +2,19 @@
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
extern const struct {
|
||||
const char* n;
|
||||
int i;
|
||||
} stdin_types[];
|
||||
|
||||
#define STDIN_TYPE_NONE 0
|
||||
#define STDIN_TYPE_INT 1
|
||||
#define STDIN_TYPE_FLOAT 2
|
||||
#define STDIN_TYPE_BOOL 3
|
||||
#define STDIN_TYPE_VEC2 4
|
||||
#define STDIN_TYPE_VEC3 5
|
||||
#define STDIN_TYPE_VEC4 6
|
||||
|
||||
struct gl_data;
|
||||
|
||||
typedef struct renderer {
|
||||
@@ -11,9 +24,10 @@ typedef struct renderer {
|
||||
struct gl_data* gl;
|
||||
} renderer;
|
||||
|
||||
struct renderer* rd_new (const char** paths, const char* entry,
|
||||
const char* force_mod, const char* force_backend,
|
||||
bool auto_desktop);
|
||||
struct renderer* rd_new (const char** paths, const char* entry,
|
||||
const char** requests, const char* force_backend,
|
||||
int stdin_type, bool auto_desktop,
|
||||
bool verbose);
|
||||
bool rd_update (struct renderer*, float* lb, float* rb,
|
||||
size_t bsz, bool modified);
|
||||
void rd_destroy (struct renderer*);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
/* Center line thickness (pixels) */
|
||||
#define C_LINE 1
|
||||
|
||||
/* Width (in pixels) of each bar */
|
||||
#define BAR_WIDTH 4
|
||||
/* Width (in pixels) of each bar gap */
|
||||
@@ -12,14 +11,16 @@
|
||||
#define BAR_OUTLINE_WIDTH 0
|
||||
/* Amplify magnitude of the results each bar displays */
|
||||
#define AMPLIFY 300
|
||||
/* Alpha channel for bars color */
|
||||
#define ALPHA 0.7
|
||||
/* Whether the current settings use the alpha channel;
|
||||
enabling this is required for alpha to function
|
||||
correctly on X11 with `"native"` transparency. */
|
||||
#define USE_ALPHA 0
|
||||
/* How strong the gradient changes */
|
||||
#define GRADIENT_POWER 60
|
||||
/* Bar color changes with height */
|
||||
#define GRADIENT (d / GRADIENT_POWER + 1)
|
||||
/* Bar color */
|
||||
#define COLOR (#3366b2 * GRADIENT * ALPHA)
|
||||
#define COLOR (#3366b2 * GRADIENT)
|
||||
/* Direction that the bars are facing, 0 for inward, 1 for outward */
|
||||
#define DIRECTION 0
|
||||
/* Whether to switch left/right audio buffers */
|
||||
|
||||
@@ -6,6 +6,7 @@ uniform ivec2 screen;
|
||||
#request uniform "audio_sz" audio_sz
|
||||
uniform int audio_sz;
|
||||
|
||||
#include "@bars.glsl"
|
||||
#include ":bars.glsl"
|
||||
|
||||
#request uniform "audio_l" audio_l
|
||||
@@ -48,13 +49,15 @@ void main() {
|
||||
#else
|
||||
float d = AREA_HEIGHT - AREA_Y;
|
||||
#endif
|
||||
float nbars = floor((AREA_WIDTH * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2;
|
||||
float section = BAR_WIDTH + BAR_GAP; /* size of section for each bar (including gap) */
|
||||
float center = section / 2.0F; /* half section, distance to center */
|
||||
float m = abs(mod(dx, section)); /* position in section */
|
||||
float md = m - center; /* position in section from center line */
|
||||
float nbars = floor((AREA_WIDTH * 0.5F) / section) * 2;
|
||||
float p, s;
|
||||
if (md < ceil(float(BAR_WIDTH) / 2) && md >= -floor(float(BAR_WIDTH) / 2)) { /* if not in gap */
|
||||
float p = int(dx / section) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
|
||||
s = dx / section;
|
||||
p = (sign(s) == 1.0 ? ceil(s) : floor(s)) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
|
||||
p += sign(p) * ((0.5F + center) / AREA_WIDTH); /* index center of bar position */
|
||||
/* Apply smooth function and index texture */
|
||||
#define smooth_f(tex, p) smooth_audio(tex, audio_sz, p)
|
||||
@@ -65,7 +68,7 @@ void main() {
|
||||
return;
|
||||
}
|
||||
/* handle user options and store result of indexing in 'v' */
|
||||
if (p >= 0.0F) {
|
||||
if (p > 0.0F) {
|
||||
#if DIRECTION == 1
|
||||
p = 1.0F - p;
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@ uniform ivec2 screen;
|
||||
uniform int audio_sz;
|
||||
|
||||
#include ":util/smooth.glsl"
|
||||
#include "@circle.glsl"
|
||||
#include ":circle.glsl"
|
||||
|
||||
#request uniform "audio_l" audio_l
|
||||
@@ -31,7 +32,6 @@ out vec4 fragment;
|
||||
/* This shader is based on radial.glsl, refer to it for more commentary */
|
||||
|
||||
float apply_smooth(float theta) {
|
||||
|
||||
float idx = theta + ROTATE;
|
||||
float dir = mod(abs(idx), TWOPI);
|
||||
if (dir > PI)
|
||||
@@ -50,6 +50,7 @@ float apply_smooth(float theta) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
fragment = vec4(0, 0, 0, 0);
|
||||
float
|
||||
dx = gl_FragCoord.x - (screen.x / 2),
|
||||
dy = gl_FragCoord.y - (screen.y / 2);
|
||||
|
||||
@@ -6,6 +6,7 @@ uniform sampler2D tex; /* screen texture */
|
||||
|
||||
out vec4 fragment; /* output */
|
||||
|
||||
#include "@circle.glsl"
|
||||
#include ":circle.glsl"
|
||||
|
||||
void main() {
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#request setxwintype "desktop"
|
||||
#request addxwinstate "pinned"
|
||||
#request setxwintype "!-"
|
||||
|
||||
1
shaders/env_i3.glsl
Normal file
1
shaders/env_i3.glsl
Normal file
@@ -0,0 +1 @@
|
||||
#request setxwintype "!-"
|
||||
@@ -4,24 +4,27 @@
|
||||
/* Rendering direction, either -1 (outwards) or 1 (inwards). */
|
||||
#define DIRECTION 1
|
||||
|
||||
/* The `RCOL_OFF`, `LCOL_OFF` AND `LSTEP` definitions are used to calculate
|
||||
the `COLOR` macro definition for output. You can remove all these values
|
||||
any simply define the `COLOR` macro yourself. */
|
||||
|
||||
/* right color offset */
|
||||
#define RCOL_OFF (gl_FragCoord.x / 3000)
|
||||
/* left color offset */
|
||||
#define LCOL_OFF ((screen.x - gl_FragCoord.x) / 3000)
|
||||
/* vertical color step */
|
||||
#define LSTEP (pos / 170)
|
||||
/* actual color definition */
|
||||
#define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1)
|
||||
/* Color gradient scale, (optionally) used in `COLOR` macro */
|
||||
#define GRADIENT_SCALE 75
|
||||
/* Color definition. By default this is a gradient formed by mixing two colors.
|
||||
`pos` represents the pixel position relative to the visualizer baseline. */
|
||||
#define COLOR mix(#802A2A, #4F4F92, clamp(pos / GRADIENT_SCALE, 0, 1))
|
||||
/* 1 to draw outline, 0 to disable */
|
||||
#define DRAW_OUTLINE 0
|
||||
/* 1 to draw edge highlight, 0 to disable */
|
||||
#define DRAW_HIGHLIGHT 1
|
||||
/* Whether to anti-alias the border of the graph, creating a smoother curve.
|
||||
This may have a small impact on performance.
|
||||
Note: requires `xroot` or `none` opacity to be set */
|
||||
#define ANTI_ALIAS 0
|
||||
/* outline color */
|
||||
#define OUTLINE #262626
|
||||
/* 1 to join the two channels together in the middle, 0 to clamp both down to zero */
|
||||
#define JOIN_CHANNELS 0
|
||||
/* 1 to invert (vertically), 0 otherwise */
|
||||
#define INVERT 0
|
||||
|
||||
/* Gravity step, overrude frin `smooth_parameters.glsl` */
|
||||
/* Gravity step, overrude from `smooth_parameters.glsl` */
|
||||
#request setgravitystep 2.4
|
||||
|
||||
/* Smoothing factor, override from `smooth_parameters.glsl` */
|
||||
|
||||
@@ -40,6 +40,7 @@ uniform int audio_sz;
|
||||
*/
|
||||
|
||||
#include ":util/smooth.glsl"
|
||||
#include "@graph.glsl"
|
||||
#include ":graph.glsl"
|
||||
|
||||
#request uniform "audio_l" audio_l
|
||||
@@ -80,16 +81,31 @@ out vec4 fragment;
|
||||
#define TWOPI 6.28318530718
|
||||
|
||||
float half_w;
|
||||
float middle;
|
||||
highp float pixel = 1.0F / float(screen.x);
|
||||
|
||||
void render_side(in sampler1D tex, float idx) {
|
||||
highp float pixel = 1.0F / float(screen.x);
|
||||
float get_line_height(in sampler1D tex, float idx) {
|
||||
float s = smooth_audio_adj(tex, audio_sz, idx / half_w, pixel);
|
||||
/* scale the data upwards so we can see it */
|
||||
s *= VSCALE;
|
||||
/* clamp far ends of the screen down to make the ends of the graph smoother */
|
||||
s *= clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
|
||||
|
||||
float fact = clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
|
||||
#if JOIN_CHANNELS > 0
|
||||
fact = -2 * pow(fact, 3) + 3 * pow(fact, 2); /* To avoid spikes */
|
||||
s = fact * s + (1 - fact) * middle;
|
||||
#else
|
||||
s *= fact;
|
||||
#endif
|
||||
|
||||
s *= clamp((min(gl_FragCoord.x, screen.x - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void render_side(in sampler1D tex, float idx) {
|
||||
float s = get_line_height(tex, idx);
|
||||
|
||||
/* and finally set fragment color if we are in range */
|
||||
#if INVERT > 0
|
||||
float pos = float(screen.y) - gl_FragCoord.y;
|
||||
@@ -105,6 +121,9 @@ void render_side(in sampler1D tex, float idx) {
|
||||
|
||||
void main() {
|
||||
half_w = (screen.x / 2);
|
||||
|
||||
middle = VSCALE * (smooth_audio_adj(audio_l, audio_sz, 1, pixel) + smooth_audio_adj(audio_r, audio_sz, 0, pixel)) / 2;
|
||||
|
||||
if (gl_FragCoord.x < half_w) {
|
||||
render_side(audio_l, LEFT_IDX);
|
||||
} else {
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
|
||||
layout(pixel_center_integer) in vec4 gl_FragCoord;
|
||||
in vec4 gl_FragCoord;
|
||||
|
||||
#request uniform "prev" tex
|
||||
uniform sampler2D tex; /* screen texture */
|
||||
#request uniform "screen" screen
|
||||
uniform ivec2 screen; /* screen dimensions */
|
||||
|
||||
out vec4 fragment; /* output */
|
||||
|
||||
#include "@graph.glsl"
|
||||
#include ":graph.glsl"
|
||||
|
||||
#if DRAW_OUTLINE == 0 && DRAW_HIGHLIGHT == 0
|
||||
#error __disablestage
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, gl_FragCoord.y / screen.y));
|
||||
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
|
||||
|
||||
vec4
|
||||
a0 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)),
|
||||
a1 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 1) / screen.y)),
|
||||
a2 = texture(tex, vec2((gl_FragCoord.x + 0) / screen.x, (gl_FragCoord.y + 1) / screen.y)),
|
||||
a3 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)),
|
||||
a0 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 0)), 0),
|
||||
a1 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 1)), 0),
|
||||
a2 = texelFetch(tex, ivec2((gl_FragCoord.x + 0), (gl_FragCoord.y + 1)), 0),
|
||||
a3 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 0)), 0),
|
||||
|
||||
a4 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)),
|
||||
a5 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 1) / screen.y)),
|
||||
a6 = texture(tex, vec2((gl_FragCoord.x - 0) / screen.x, (gl_FragCoord.y - 1) / screen.y)),
|
||||
a7 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y));
|
||||
a4 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 0)), 0),
|
||||
a5 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 1)), 0),
|
||||
a6 = texelFetch(tex, ivec2((gl_FragCoord.x - 0), (gl_FragCoord.y - 1)), 0),
|
||||
a7 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 0)), 0);
|
||||
|
||||
vec4 avg = (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) / 8.0;
|
||||
if (avg.a > 0){
|
||||
#if INVERT > 0
|
||||
#define EDGE_CHECK (gl_FragCoord.y != 0)
|
||||
#define TEDGE_CHECK (gl_FragCoord.y != screen.y - 1)
|
||||
#else
|
||||
#define EDGE_CHECK (gl_FragCoord.y != screen.y - 1)
|
||||
#define TEDGE_CHECK (gl_FragCoord.y != 0)
|
||||
#endif
|
||||
if (fragment.a <= 0 && EDGE_CHECK) {
|
||||
if (fragment.a <= 0) {
|
||||
/* outline */
|
||||
#if DRAW_OUTLINE > 0
|
||||
fragment = OUTLINE;
|
||||
} else if (avg.a < 1 && TEDGE_CHECK) {
|
||||
/* creates a nice 'glint' along the edge of the spectrum */
|
||||
#endif
|
||||
} else if (avg.a < 1) {
|
||||
/* creates a highlight along the edge of the spectrum */
|
||||
#if DRAW_HIGHLIGHT > 0
|
||||
fragment.rgb *= avg.a * 2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
shaders/graph/3.frag
Normal file
104
shaders/graph/3.frag
Normal file
@@ -0,0 +1,104 @@
|
||||
|
||||
in vec4 gl_FragCoord;
|
||||
|
||||
#request uniform "screen" screen
|
||||
uniform ivec2 screen; /* screen dimensions */
|
||||
|
||||
#request uniform "prev" tex
|
||||
uniform sampler2D tex; /* screen texture */
|
||||
|
||||
out vec4 fragment; /* output */
|
||||
|
||||
#include "@graph.glsl"
|
||||
#include ":graph.glsl"
|
||||
|
||||
#if ANTI_ALIAS == 0
|
||||
#error __disablestage
|
||||
#endif
|
||||
|
||||
/* Moves toward the border of the graph, gives the
|
||||
y coordinate of the last colored pixel */
|
||||
float get_col_height_up(float x, float oy) {
|
||||
float y = oy;
|
||||
#if INVERT > 0
|
||||
while (y >= 0) {
|
||||
#else
|
||||
while (y < screen.y) {
|
||||
#endif
|
||||
vec4 f = texelFetch(tex, ivec2(x, y), 0);
|
||||
if (f.a <= 0) {
|
||||
#if INVERT > 0
|
||||
y += 1;
|
||||
#else
|
||||
y -= 1;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#if INVERT > 0
|
||||
y -= 1;
|
||||
#else
|
||||
y += 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Moves toward the base of the graph, gives the
|
||||
y coordinate of the first colored pixel */
|
||||
float get_col_height_down(float x, float oy) {
|
||||
float y = oy;
|
||||
#if INVERT > 0
|
||||
while (y < screen.y) {
|
||||
#else
|
||||
while (y >= 0) {
|
||||
#endif
|
||||
vec4 f = texelFetch(tex, ivec2(x, y), 0);
|
||||
if (f.a > 0) {
|
||||
break;
|
||||
}
|
||||
#if INVERT > 0
|
||||
y += 1;
|
||||
#else
|
||||
y -= 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
void main() {
|
||||
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
|
||||
|
||||
#if ANTI_ALIAS > 0
|
||||
|
||||
if (fragment.a <= 0) {
|
||||
bool left_done = false;
|
||||
float h2;
|
||||
float a_fact = 0;
|
||||
|
||||
if (texelFetch(tex, ivec2(gl_FragCoord.x - 1, gl_FragCoord.y), 0).a > 0) {
|
||||
float h1 = get_col_height_up(gl_FragCoord.x - 1, gl_FragCoord.y);
|
||||
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
|
||||
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
|
||||
|
||||
a_fact = clamp(abs((h1 - gl_FragCoord.y) / (h2 - h1)), 0.0, 1.0);
|
||||
|
||||
left_done = true;
|
||||
}
|
||||
if (texelFetch(tex, ivec2(gl_FragCoord.x + 1, gl_FragCoord.y), 0).a > 0) {
|
||||
if (!left_done) {
|
||||
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
|
||||
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
|
||||
}
|
||||
float h3 = get_col_height_up(gl_FragCoord.x + 1, gl_FragCoord.y);
|
||||
|
||||
a_fact = max(a_fact, clamp(abs((h3 - gl_FragCoord.y) / (h2 - h3)), 0.0, 1.0));
|
||||
}
|
||||
|
||||
fragment.a *= a_fact;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -27,8 +27,12 @@
|
||||
the background. */
|
||||
#define BAR_ALIAS_FACTOR 1.2
|
||||
#define C_ALIAS_FACTOR 1.8
|
||||
/* Offset (Y) of the visualization */
|
||||
#define CENTER_OFFSET_Y 0
|
||||
/* Offset (X) of the visualization */
|
||||
#define CENTER_OFFSET_X 0
|
||||
|
||||
/* Gravity step, overrude frin `smooth_parameters.glsl` */
|
||||
/* Gravity step, override from `smooth_parameters.glsl` */
|
||||
#request setgravitystep 5.0
|
||||
|
||||
/* Smoothing factor, override from `smooth_parameters.glsl` */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
layout(pixel_center_integer) in vec4 gl_FragCoord;
|
||||
in vec4 gl_FragCoord;
|
||||
|
||||
#request uniform "screen" screen
|
||||
uniform ivec2 screen;
|
||||
@@ -7,6 +7,7 @@ uniform ivec2 screen;
|
||||
uniform int audio_sz;
|
||||
|
||||
#include ":util/smooth.glsl"
|
||||
#include "@radial.glsl"
|
||||
#include ":radial.glsl"
|
||||
|
||||
#request uniform "audio_l" audio_l
|
||||
@@ -41,8 +42,8 @@ void main() {
|
||||
Alpha layer blending is only applied when `xroot` transparency is enabled. */
|
||||
|
||||
float /* translate (x, y) to use (0, 0) as the center of the screen */
|
||||
dx = gl_FragCoord.x - (screen.x / 2),
|
||||
dy = gl_FragCoord.y - (screen.y / 2);
|
||||
dx = gl_FragCoord.x - (screen.x / 2) + CENTER_OFFSET_X,
|
||||
dy = gl_FragCoord.y - (screen.y / 2) + CENTER_OFFSET_Y;
|
||||
float theta = atan(dy, dx); /* fragment angle with the center of the screen as the origin */
|
||||
float d = sqrt((dx * dx) + (dy * dy)); /* distance */
|
||||
if (d > C_RADIUS - (float(C_LINE) / 2.0F) && d < C_RADIUS + (float(C_LINE) / 2.0F)) {
|
||||
|
||||
@@ -98,9 +98,14 @@
|
||||
default when GLava itself is a desktop window. */
|
||||
#request setclickthrough false
|
||||
|
||||
/* PulseAudio source. Can be a number or a name of an audio
|
||||
sink or device to record from. Set to "auto" to use the
|
||||
default output device. */
|
||||
/* Audio source
|
||||
|
||||
When the "pulseaudio" backend is set, this can be a number or
|
||||
a name of an audio sink or device to record from. Set to "auto"
|
||||
to use the default output device.
|
||||
|
||||
When the "fifo" backend is set, "auto" is interpreted as
|
||||
"/tmp/mpd.fifo". Otherwise, a valid path should be provided. */
|
||||
#request setsource "auto"
|
||||
|
||||
/* Buffer swap interval (vsync), set to '0' to prevent
|
||||
@@ -189,7 +194,11 @@
|
||||
|
||||
Lower sample rates also can make output more choppy, when
|
||||
not using interpolation. It's generally OK to leave this
|
||||
value unless you have a strange PulseAudio configuration. */
|
||||
value unless you have a strange PulseAudio configuration.
|
||||
|
||||
This option does nothing when using the "fifo" audio
|
||||
backend. Instead, an ideal rate should be be configured
|
||||
in the application generating the output. */
|
||||
#request setsamplerate 22050
|
||||
|
||||
/* ** DEPRECATED **
|
||||
|
||||
@@ -13,10 +13,25 @@
|
||||
- circular heavily rounded points
|
||||
- sinusoidal rounded at both low and high weighted values
|
||||
like a sine wave
|
||||
- linear not rounded at all, just use linear distance
|
||||
- linear not rounded at all; linear distance
|
||||
*/
|
||||
#define ROUND_FORMULA sinusoidal
|
||||
|
||||
/* The sampling mode for processing raw FFT input:
|
||||
|
||||
- average averages all the inputs in the sample range for
|
||||
a given point. Produces smooth output, but peaks
|
||||
are not well represented
|
||||
- maximum obtains the best value from the closest peak in
|
||||
the sample range. Very accurate peaks, but
|
||||
output is jagged and sporadic.
|
||||
- hybrid uses the results from both `average` and `maximum`
|
||||
with the weight provided in `SAMPLE_HYBRID_WEIGHT` */
|
||||
#define SAMPLE_MODE average
|
||||
/* Weight should be provided in the range (0, 1). Higher values favour
|
||||
averaged results. `hybrid` mode only. */
|
||||
#define SAMPLE_HYBRID_WEIGHT 0.65
|
||||
|
||||
/* Factor used to scale frequencies. Lower values allows lower
|
||||
frequencies to occupy more space. */
|
||||
#define SAMPLE_SCALE 8
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
#request nativeonly true
|
||||
#if PREMULTIPLY_ALPHA == 0
|
||||
#error __distablestage
|
||||
#endif
|
||||
|
||||
#request uniform "prev" tex
|
||||
uniform sampler2D tex;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define PI 3.14159265359
|
||||
#endif
|
||||
|
||||
#include "@smooth_parameters.glsl"
|
||||
#include ":smooth_parameters.glsl"
|
||||
|
||||
/* window value t that resides in range [0, sz)*/
|
||||
@@ -21,6 +22,10 @@
|
||||
/* take value x that scales linearly between [0, 1) and return its circlar curve */
|
||||
#define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1)))
|
||||
|
||||
#define average 0
|
||||
#define maximum 1
|
||||
#define hybrid 2
|
||||
|
||||
float scale_audio(float idx) {
|
||||
return -log((-(SAMPLE_RANGE) * idx) + 1) / (SAMPLE_SCALE);
|
||||
}
|
||||
@@ -32,20 +37,43 @@ float iscale_audio(float idx) {
|
||||
/* Note: the SMOOTH_FACTOR macro is defined by GLava itself, from `#request setsmoothfactor`*/
|
||||
|
||||
float smooth_audio(in sampler1D tex, int tex_sz, highp float idx) {
|
||||
|
||||
#if PRE_SMOOTHED_AUDIO < 1
|
||||
float
|
||||
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
|
||||
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz,
|
||||
avg = 0, s, weight = 0;
|
||||
float m = ((smax - smin) / 2.0F);
|
||||
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
|
||||
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz;
|
||||
float m = ((smax - smin) / 2.0F), s, w;
|
||||
float rm = smin + m; /* middle */
|
||||
|
||||
#if SAMPLE_MODE == average
|
||||
float avg = 0, weight = 0;
|
||||
for (s = smin; s <= smax; s += 1.0F) {
|
||||
float w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
|
||||
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
|
||||
weight += w;
|
||||
avg += texelFetch(tex, int(round(s)), 0).r * w;
|
||||
}
|
||||
avg /= weight;
|
||||
return avg;
|
||||
#elif SAMPLE_MODE == hybrid
|
||||
float vmax = 0, avg = 0, weight = 0, v;
|
||||
for (s = smin; s < smax; s += 1.0F) {
|
||||
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
|
||||
weight += w;
|
||||
v = texelFetch(tex, int(round(s)), 0).r * w;
|
||||
avg += v;
|
||||
if (vmax < v)
|
||||
vmax = v;
|
||||
}
|
||||
return (vmax * (1 - SAMPLE_HYBRID_WEIGHT)) + ((avg / weight) * SAMPLE_HYBRID_WEIGHT);
|
||||
#elif SAMPLE_MODE == maximum
|
||||
float vmax = 0, v;
|
||||
for (s = smin; s < smax; s += 1.0F) {
|
||||
w = texelFetch(tex, int(round(s)), 0).r * ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
|
||||
if (vmax < w)
|
||||
vmax = w;
|
||||
}
|
||||
return vmax;
|
||||
#endif
|
||||
#else
|
||||
return texelFetch(tex, int(round(idx * tex_sz)), 0).r;
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@ uniform sampler1D audio_l;
|
||||
|
||||
out vec4 fragment;
|
||||
|
||||
#include "@wave.glsl"
|
||||
#include ":wave.glsl"
|
||||
|
||||
#define index(offset) ((texture(audio_l, (gl_FragCoord.x + offset) / screen.x).r - 0.5) * AMPLIFY) + 0.5F
|
||||
|
||||
@@ -8,6 +8,7 @@ uniform ivec2 screen; /* screen dimensions */
|
||||
|
||||
out vec4 fragment; /* output */
|
||||
|
||||
#include "@wave.glsl"
|
||||
#include ":wave.glsl"
|
||||
|
||||
void main() {
|
||||
|
||||
69
xwin.c
69
xwin.c
@@ -19,7 +19,7 @@
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad.h"
|
||||
|
||||
#define GLAVA_RDX11
|
||||
#include "render.h"
|
||||
@@ -82,7 +82,7 @@ void xwin_wait_for_wm(void) {
|
||||
bool exists = false;
|
||||
struct timespec tv = { .tv_sec = 0, .tv_nsec = 50 * 1000000 };
|
||||
|
||||
do {
|
||||
do {
|
||||
if (check == None) {
|
||||
check = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", true);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ bool xwin_should_render(struct gl_wcb* wcb, void* impl) {
|
||||
XSetErrorHandler(handler); /* dummy error handler */
|
||||
|
||||
if (Success != XGetWindowProperty(d, DefaultRootWindow(d), prop, 0, 1, false, AnyPropertyType,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
|
||||
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
|
||||
goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ bool xwin_should_render(struct gl_wcb* wcb, void* impl) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
close:
|
||||
close:
|
||||
if (data)
|
||||
XFree(data);
|
||||
if (should_close)
|
||||
@@ -200,8 +200,8 @@ bool xwin_should_render(struct gl_wcb* wcb, void* impl) {
|
||||
for (size_t t = 0; t < sizeof(out) / sizeof(char); ++t) { \
|
||||
char c = in[t]; \
|
||||
switch (c) { \
|
||||
case 'a' ... 'z': c -= 'a' - 'A'; \
|
||||
default: out[t] = c; \
|
||||
case 'a' ... 'z': c -= 'a' - 'A'; \
|
||||
default: out[t] = c; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -316,35 +316,35 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
bool invalid = false, aligned = false;
|
||||
GLenum type;
|
||||
switch (image->bits_per_pixel) {
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
|
||||
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
|
||||
case 16: /* 16-bit, hi-color */
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
aligned = true;
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
|
||||
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
|
||||
case 16: /* 16-bit, hi-color */
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
aligned = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (image->depth) {
|
||||
case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */
|
||||
case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (image->depth == 48) /* 48-bit deep color */
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else goto invalid;
|
||||
break;
|
||||
/* >64-bit formats */
|
||||
case 128:
|
||||
if (image->depth == 96)
|
||||
type = GL_UNSIGNED_INT;
|
||||
else goto invalid;
|
||||
break;
|
||||
default:
|
||||
invalid: invalid = true;
|
||||
case 32:
|
||||
switch (image->depth) {
|
||||
case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */
|
||||
case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (image->depth == 48) /* 48-bit deep color */
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else goto invalid;
|
||||
break;
|
||||
/* >64-bit formats */
|
||||
case 128:
|
||||
if (image->depth == 96)
|
||||
type = GL_UNSIGNED_INT;
|
||||
else goto invalid;
|
||||
break;
|
||||
default:
|
||||
invalid: invalid = true;
|
||||
}
|
||||
|
||||
uint8_t* buf;
|
||||
@@ -389,6 +389,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
}
|
||||
|
||||
if (image) XDestroyImage(image);
|
||||
XFree(info);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user