Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
426f70f579 | ||
|
|
1d2b014da7 | ||
|
|
6670c54827 | ||
|
|
f4ad41df32 | ||
|
|
93df114308 | ||
|
|
d21edcfb29 | ||
|
|
e0b4f7d6c7 | ||
|
|
386d32076c | ||
|
|
52af6ac173 | ||
|
|
3da1fb34dd | ||
|
|
2c99124556 | ||
|
|
9d1c3a177c | ||
|
|
db625cf00c | ||
|
|
1845fad7fd | ||
|
|
25c1701ce6 | ||
|
|
b627219109 | ||
|
|
3dfbdb5127 | ||
|
|
7bb7913bd3 | ||
|
|
1a1cbc9cc8 | ||
|
|
83a94e3eb4 | ||
|
|
acdbb8f3b5 | ||
|
|
829c1be370 | ||
|
|
261ba8ded5 | ||
|
|
fc9c74d256 | ||
|
|
a697af9a0a | ||
|
|
437aa146a4 | ||
|
|
1c2f362219 | ||
|
|
7dfb68fb1a | ||
|
|
353c3bd62f | ||
|
|
e4b16cdbb6 | ||
|
|
8102f99683 | ||
|
|
14747b1e75 | ||
|
|
217d5c9eea | ||
|
|
3be916f157 | ||
|
|
bc955a5b3d | ||
|
|
c7ad0a5024 | ||
|
|
83ad0d8f8a | ||
|
|
53f7352347 | ||
|
|
643b0cf3f5 | ||
|
|
11a6137370 | ||
|
|
d4b333e48c |
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.
|
||||
74
Makefile
74
Makefile
@@ -4,12 +4,13 @@ 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
|
||||
# ASAN = -lasan
|
||||
STRIP_CMD = $(info Skipping `strip` for debug builds)
|
||||
else
|
||||
CFLAGS_BUILD = -O2 -march=native -Wstringop-overflow=0
|
||||
CFLAGS_BUILD = -O2 -Wstringop-overflow=0
|
||||
GLAD_GEN = c
|
||||
STRIP_CMD = strip --strip-all glava
|
||||
endif
|
||||
|
||||
# Detect OS if not specified (OSX, Linux, BSD are supported)
|
||||
@@ -23,6 +24,10 @@ ifndef INSTALL
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef EXECDIR
|
||||
EXECDIR = /usr/bin/
|
||||
endif
|
||||
|
||||
# Install type parameter
|
||||
|
||||
ifeq ($(INSTALL),standalone)
|
||||
@@ -31,10 +36,12 @@ 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/
|
||||
else
|
||||
SHADERDIR = /etc/xdg/glava/
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -50,7 +57,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)
|
||||
@@ -62,36 +71,57 @@ 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),$(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
|
||||
$(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:
|
||||
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: ;
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(obj) glava glad.o
|
||||
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)
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -3,12 +3,12 @@
|
||||
|
||||
**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 [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:**
|
||||
**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/))**:**
|
||||
|
||||
```bash
|
||||
$ git clone --recursive https://github.com/wacossusca34/glava
|
||||
$ git clone https://github.com/wacossusca34/glava
|
||||
$ cd glava
|
||||
$ make
|
||||
$ CFLAGS="-march=native" make
|
||||
$ sudo make install
|
||||
$ glava
|
||||
```
|
||||
@@ -17,14 +17,12 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- X11
|
||||
- X11 (Xext, Xcomposite, & Xrender)
|
||||
- PulseAudio
|
||||
- Linux or BSD
|
||||
|
||||
**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:**
|
||||
@@ -53,7 +51,7 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
|
||||
| WM | ! | Details
|
||||
| :---: | --- | --- |
|
||||
| Mutter (GNOME, Budgie) |  | `"native"` (default) opacity should be used
|
||||
| KWin (KDE) |  | No issues
|
||||
| KWin (KDE) |  | "Show Desktop" [temporarily hides GLava](https://github.com/wacossusca34/glava/issues/4#issuecomment-419729184)
|
||||
| Openbox (LXDE or standalone) |  | No issues
|
||||
| Xfwm (XFCE) |  | No issues
|
||||
| Fluxbox |  | No issues
|
||||
@@ -61,8 +59,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)
|
||||
| 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
|
||||
@@ -72,7 +70,7 @@ Note that some WMs listed without issues have specific overrides when using the
|
||||
|
||||
## 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`
|
||||
@@ -83,7 +81,7 @@ The below copyright notice applies for the original versions of these files:
|
||||
|
||||
`Copyright (c) 2015 Karl Stavestrand <karl@stavestrand.no>`
|
||||
|
||||
The modified files are relicensed under the terms of the GPLv3. The MIT license is included for your convience and to satisfy the requirements of the original license, although it (no longer) applies to any code in this repository. You will find the original copyright notice and MIT license in the `LICENSE_ORIGINAL` file.
|
||||
The modified files are relicensed under the terms of the GPLv3. The MIT license is included for your convience and to satisfy the requirements of the original license, although it no longer applies to any code in this repository. You will find the original copyright notice and MIT license in the `LICENSE_ORIGINAL` file.
|
||||
|
||||
The below copyright applies for the modifications to the files listed above, and the remaining sources in the repository:
|
||||
|
||||
|
||||
58
fifo.c
58
fifo.c
@@ -10,10 +10,8 @@
|
||||
|
||||
#include "fifo.h"
|
||||
|
||||
//input: FIFO
|
||||
void* input_fifo(void* data)
|
||||
{
|
||||
struct audio_data *audio = (struct audio_data *)data;
|
||||
void* input_fifo(void* data) {
|
||||
struct audio_data* audio = (struct audio_data *) data;
|
||||
int fd;
|
||||
int n = 0;
|
||||
signed char buf[1024];
|
||||
@@ -28,12 +26,11 @@ void* input_fifo(void* data)
|
||||
fd = open(audio->source, O_RDONLY);
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
|
||||
while (1) {
|
||||
|
||||
bytes = read(fd, buf, sizeof(buf));
|
||||
|
||||
if (bytes == -1) { //if no bytes read sleep 10ms and zero shared buffer
|
||||
|
||||
if (bytes == -1) { /* if no bytes read, sleep 10ms and zero shared buffer */
|
||||
nanosleep (&req, NULL);
|
||||
t++;
|
||||
if (t > 10) {
|
||||
@@ -41,46 +38,43 @@ void* input_fifo(void* data)
|
||||
for (i = 0; i < 2048; i++)audio->audio_out_r[i] = 0;
|
||||
t = 0;
|
||||
}
|
||||
} else { //if bytes read go ahead
|
||||
} 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;
|
||||
|
||||
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;
|
||||
|
||||
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 == 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;
|
||||
}
|
||||
|
||||
157
glava.c
157
glava.c
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
@@ -54,11 +55,15 @@
|
||||
#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"
|
||||
@@ -108,8 +113,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;
|
||||
@@ -139,12 +143,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);
|
||||
@@ -162,9 +166,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"
|
||||
@@ -174,14 +179,23 @@ static const char* help_str =
|
||||
" system.\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"
|
||||
GLAVA_VERSION_STRING "\n";
|
||||
|
||||
static const char* opt_str = "dhvVe:Cm:b:";
|
||||
static const char* opt_str = "dhvVe:Cm:b:r:";
|
||||
static struct option p_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"desktop", no_argument, 0, 'd'},
|
||||
{"request", required_argument, 0, 'r'},
|
||||
{"entry", required_argument, 0, 'e'},
|
||||
{"force-mod", required_argument, 0, 'm'},
|
||||
{"copy-config", no_argument, 0, 'C'},
|
||||
@@ -190,36 +204,56 @@ static struct option p_opts[] = {
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static renderer* rd = NULL;
|
||||
|
||||
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;
|
||||
const char* system_shader_paths[] = { user_path, install_path, NULL };
|
||||
|
||||
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 '?': 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +262,27 @@ int main(int argc, char** argv) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
renderer* r = 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);
|
||||
}
|
||||
|
||||
float b0[r->bufsize_request], b1[r->bufsize_request];
|
||||
/* Null terminate array arguments */
|
||||
append_buf(requests, &requests_sz, NULL);
|
||||
|
||||
rd = rd_new(system_shader_paths, entry, (const char**) requests,
|
||||
backend, desktop, verbose);
|
||||
|
||||
struct sigaction action = { .sa_handler = handle_term };
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
|
||||
float b0[rd->bufsize_request], b1[rd->bufsize_request];
|
||||
size_t t;
|
||||
for (t = 0; t < r->bufsize_request; ++t) {
|
||||
for (t = 0; t < rd->bufsize_request; ++t) {
|
||||
b0[t] = 0.0F;
|
||||
b1[t] = 0.0F;
|
||||
}
|
||||
@@ -240,34 +290,34 @@ int main(int argc, char** argv) {
|
||||
struct audio_data audio = {
|
||||
.source = ({
|
||||
char* src = NULL;
|
||||
if (r->audio_source_request && strcmp(r->audio_source_request, "auto") != 0) {
|
||||
src = strdup(r->audio_source_request);
|
||||
if (rd->audio_source_request && strcmp(rd->audio_source_request, "auto") != 0) {
|
||||
src = strdup(rd->audio_source_request);
|
||||
}
|
||||
src;
|
||||
}),
|
||||
.rate = (unsigned int) r->rate_request,
|
||||
.rate = (unsigned int) rd->rate_request,
|
||||
.format = -1,
|
||||
.terminate = 0,
|
||||
.channels = r->mirror_input ? 1 : 2,
|
||||
.channels = rd->mirror_input ? 1 : 2,
|
||||
.audio_out_r = b0,
|
||||
.audio_out_l = b1,
|
||||
.mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
.audio_buf_sz = r->bufsize_request,
|
||||
.sample_sz = r->samplesize_request,
|
||||
.audio_buf_sz = rd->bufsize_request,
|
||||
.sample_sz = rd->samplesize_request,
|
||||
.modified = false
|
||||
};
|
||||
if (!audio.source) {
|
||||
get_pulse_default_sink(&audio);
|
||||
printf("Using default PulseAudio sink: %s\n", audio.source);
|
||||
if (verbose) printf("Using default PulseAudio sink: %s\n", audio.source);
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, input_pulse, (void*) &audio);
|
||||
|
||||
float lb[r->bufsize_request], rb[r->bufsize_request];
|
||||
while (r->alive) {
|
||||
float lb[rd->bufsize_request], rb[rd->bufsize_request];
|
||||
while (rd->alive) {
|
||||
|
||||
rd_time(r); /* update timer for this frame */
|
||||
rd_time(rd); /* update timer for this frame */
|
||||
|
||||
bool modified; /* if the audio buffer has been updated by the streaming thread */
|
||||
|
||||
@@ -277,13 +327,13 @@ int main(int argc, char** argv) {
|
||||
if (modified) {
|
||||
/* create our own copies of the audio buffers, so the streaming
|
||||
thread can continue to append to it */
|
||||
memcpy(lb, (void*) audio.audio_out_l, r->bufsize_request * sizeof(float));
|
||||
memcpy(rb, (void*) audio.audio_out_r, r->bufsize_request * sizeof(float));
|
||||
memcpy(lb, (void*) audio.audio_out_l, rd->bufsize_request * sizeof(float));
|
||||
memcpy(rb, (void*) audio.audio_out_r, rd->bufsize_request * sizeof(float));
|
||||
audio.modified = false; /* set this flag to false until the next time we read */
|
||||
}
|
||||
pthread_mutex_unlock(&audio.mutex);
|
||||
|
||||
if (!rd_update(r, lb, rb, r->bufsize_request, modified)) {
|
||||
if (!rd_update(rd, lb, rb, rd->bufsize_request, modified)) {
|
||||
/* Sleep for 50ms and then attempt to render again */
|
||||
struct timespec tv = {
|
||||
.tv_sec = 0, .tv_nsec = 50 * 1000000
|
||||
@@ -292,5 +342,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
rd_destroy(r);
|
||||
audio.terminate = 1;
|
||||
int return_status;
|
||||
if ((return_status = pthread_join(thread, NULL))) {
|
||||
fprintf(stderr, "Failed to join with audio thread: %s\n", strerror(return_status));
|
||||
}
|
||||
|
||||
free(audio.source);
|
||||
rd_destroy(rd);
|
||||
}
|
||||
|
||||
@@ -128,12 +128,16 @@ static void swap_buffers(GLFWwindow* w) {
|
||||
static Display* get_x11_display(void) { return glfwGetX11Display(); }
|
||||
static Window get_x11_window (GLFWwindow* w) { return glfwGetX11Window(w); }
|
||||
static bool should_close (GLFWwindow* w) { return glfwWindowShouldClose(w); }
|
||||
static bool should_render (GLFWwindow* w) { return true; }
|
||||
static bool bg_changed (GLFWwindow* w) { return false; }
|
||||
static void get_fbsize (GLFWwindow* w, int* d, int* h) { glfwGetFramebufferSize(w, d, h); }
|
||||
static void get_pos (GLFWwindow* w, int* x, int* y) { glfwGetWindowPos(w, x, y); }
|
||||
static double get_time (GLFWwindow* w) { return glfwGetTime(); }
|
||||
static void set_time (GLFWwindow* w, double time) { glfwSetTime(time); }
|
||||
static void set_swap (int i) { glfwSwapInterval(i); }
|
||||
static void raise (GLFWwindow* w) { glfwShowWindow(w); }
|
||||
static void destroy (GLFWwindow* w) { glfwDestroyWindow(w); }
|
||||
static void terminate (void) { glfwTerminate(); }
|
||||
|
||||
static const char* get_environment(void) { return xwin_detect_wm(&wcb_glfw); }
|
||||
|
||||
|
||||
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 */
|
||||
|
||||
212
glx_wcb.c
212
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"
|
||||
@@ -160,6 +160,8 @@ Bool (*glXMakeCurrent) (Display* dpy, GLXDrawable drawable,
|
||||
GLXDrawable (*glXGetCurrentDrawable) (void);
|
||||
__GLXextFuncPtr (*glXGetProcAddressARB) (const GLubyte *);
|
||||
void (*glXSwapBuffers) (Display* dpy, GLXDrawable drawable);
|
||||
void (*glXDestroyContext) (Display* dpy, GLXContext ctx);
|
||||
Bool (*glXQueryVersion) (Display* dpy, int* major, int* minor);
|
||||
|
||||
extern struct gl_wcb wcb_glx;
|
||||
|
||||
@@ -173,10 +175,11 @@ struct glxwin {
|
||||
Window w;
|
||||
GLXContext context;
|
||||
double time;
|
||||
bool should_close;
|
||||
bool should_close, should_render, bg_changed, clickthrough;
|
||||
char override_state;
|
||||
};
|
||||
|
||||
static Atom ATOM__MOTIF_WM_HINTS, ATOM_WM_DELETE_WINDOW, ATOM_WM_PROTOCOLS, ATOM__NET_ACTIVE_WINDOW;
|
||||
static Atom ATOM__MOTIF_WM_HINTS, ATOM_WM_DELETE_WINDOW, ATOM_WM_PROTOCOLS, ATOM__NET_ACTIVE_WINDOW, ATOM__XROOTPMAP_ID;
|
||||
|
||||
static void init(void) {
|
||||
display = XOpenDisplay(NULL);
|
||||
@@ -212,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);
|
||||
@@ -222,11 +225,14 @@ static void init(void) {
|
||||
resolve(glXGetCurrentDrawable);
|
||||
resolve(glXGetProcAddressARB);
|
||||
resolve(glXSwapBuffers);
|
||||
resolve(glXDestroyContext);
|
||||
resolve(glXQueryVersion);
|
||||
|
||||
intern(_MOTIF_WM_HINTS, false);
|
||||
intern(WM_DELETE_WINDOW, true);
|
||||
intern(WM_PROTOCOLS, true);
|
||||
intern(_NET_ACTIVE_WINDOW, false);
|
||||
intern(_XROOTPMAP_ID, false);
|
||||
|
||||
#undef intern
|
||||
#undef resolve
|
||||
@@ -248,6 +254,54 @@ static void apply_decorations(Window w) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void* create_and_bind(const char* name, const char* class,
|
||||
const char* type, const char** states,
|
||||
size_t states_sz,
|
||||
@@ -256,13 +310,29 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
int version_major, int version_minor,
|
||||
bool clickthrough) {
|
||||
struct glxwin* w = malloc(sizeof(struct glxwin));
|
||||
w->time = 0.0D;
|
||||
w->should_close = false;
|
||||
*w = (struct glxwin) {
|
||||
.override_state = '\0',
|
||||
.time = 0.0D,
|
||||
.should_close = false,
|
||||
.should_render = true,
|
||||
.bg_changed = false,
|
||||
.clickthrough = false
|
||||
};
|
||||
|
||||
XVisualInfo* vi;
|
||||
XSetWindowAttributes attr;
|
||||
XSetWindowAttributes attr = {};
|
||||
GLXFBConfig* fbc;
|
||||
int fb_sz, best = -1, samp = -1;
|
||||
|
||||
int glx_minor, glx_major;
|
||||
glXQueryVersion(display, &glx_minor, &glx_major);
|
||||
if (glx_major <= 1 && glx_minor < 4) {
|
||||
fprintf(stderr,
|
||||
"\nGLX extension version mismatch on the current display (1.4+ required, %d.%d available)\n"
|
||||
"This is usually due to an outdated X server or graphics drivers.\n\n",
|
||||
glx_minor, glx_major);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int gl_attrs[] = {
|
||||
GLX_X_RENDERABLE, True,
|
||||
@@ -286,8 +356,13 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
|
||||
fbc = glXChooseFBConfig(display, DefaultScreen(display), gl_attrs, &fb_sz);
|
||||
if (!fbc) {
|
||||
fprintf(stderr, "glXChooseFBConfig(): failed\n" );
|
||||
abort();
|
||||
fprintf(stderr,
|
||||
"\nFailed to obtain a GLX frame buffer that supports OpenGL %d.%d.\n"
|
||||
"This is usually due to running on very old hardware or not having appropriate drivers.\n\n"
|
||||
"glXChooseFBConfig(): failed with attrs "
|
||||
"(GLX_CONTEXT_MAJOR_VERSION_ARB, GLX_CONTEXT_MINOR_VERSION_ARB)\n\n",
|
||||
version_major, version_minor);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int t = 0; t < fb_sz; ++t) {
|
||||
@@ -320,15 +395,22 @@ 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 | PropertyChangeMask;
|
||||
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
|
||||
attr.event_mask |= PropertyChangeMask | VisibilityChangeMask;
|
||||
attr.background_pixmap = None;
|
||||
attr.border_pixel = 0;
|
||||
|
||||
unsigned long vmask = CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel;
|
||||
if (type[0] == '!') {
|
||||
vmask |= CWOverrideRedirect;
|
||||
attr.override_redirect = true;
|
||||
w->override_state = type[1];
|
||||
}
|
||||
|
||||
if (!(w->w = XCreateWindow(display, DefaultRootWindow(display)/**xwin_get_desktop_layer(&wcb_glx)*/,
|
||||
x, y, d, h, 0,
|
||||
vi->depth, InputOutput, vi->visual,
|
||||
CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel,
|
||||
&attr))) {
|
||||
vmask, &attr))) {
|
||||
fprintf(stderr, "XCreateWindow(): failed\n");
|
||||
abort();
|
||||
}
|
||||
@@ -358,34 +440,26 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
XSetWMProtocols(display, w->w, &ATOM_WM_DELETE_WINDOW, 1);
|
||||
|
||||
/* Eliminate the window's effective region */
|
||||
if (desktop || clickthrough) {
|
||||
int ignored;
|
||||
if (XShapeQueryExtension(display, &ignored, &ignored)) {
|
||||
Region region;
|
||||
if ((region = XCreateRegion())) {
|
||||
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
|
||||
XDestroyRegion(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w->clickthrough = desktop || clickthrough;
|
||||
apply_clickthrough(w);
|
||||
|
||||
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = NULL;
|
||||
glXSwapIntervalEXTProc glXSwapIntervalEXT = NULL;
|
||||
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
|
||||
glXGetProcAddressARB((const GLubyte*) "glXCreateContextAttribsARB");
|
||||
glXSwapIntervalEXT = (glXSwapIntervalEXTProc)
|
||||
glXGetProcAddressARB((const GLubyte*) "glXSwapIntervalEXT");
|
||||
|
||||
|
||||
if (!glXCreateContextAttribsARB) {
|
||||
fprintf(stderr, "glXGetProcAddressARB(\"glXCreateContextAttribsARB\"): failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
if (!(w->context = glXCreateContextAttribsARB(display, config, 0, True, context_attrs))) {
|
||||
fprintf(stderr, "glXCreateContextAttribsARB(): failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
XSync(display, False);
|
||||
|
||||
glXMakeCurrent(display, w->w, w->context);
|
||||
@@ -395,29 +469,32 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
|
||||
if (glXSwapIntervalEXT) glXSwapIntervalEXT(display, drawable, swap);
|
||||
|
||||
// XSelectInput(display, DefaultRootWindow(display), VisibilityChangeMask | PropertyChangeMask);
|
||||
if (!transparent)
|
||||
XSelectInput(display, DefaultRootWindow(display), PropertyChangeMask);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static void raise(struct glxwin* w) {
|
||||
XClientMessageEvent ev = {
|
||||
.type = ClientMessage,
|
||||
.serial = 0,
|
||||
.send_event = true,
|
||||
.display = display,
|
||||
.window = w->w,
|
||||
.message_type = ATOM__NET_ACTIVE_WINDOW,
|
||||
.format = 32,
|
||||
.data = { .l = {
|
||||
[0] = 1, /* source indication -- `1` when coming from an application */
|
||||
[1] = 0, /* timestamp -- `0` to (attempt to) ignore */
|
||||
[2] = w->w /* requestor's currently active window -- `0` for none */
|
||||
if (w->override_state == '\0') {
|
||||
XClientMessageEvent ev = {
|
||||
.type = ClientMessage,
|
||||
.serial = 0,
|
||||
.send_event = true,
|
||||
.display = display,
|
||||
.window = w->w,
|
||||
.message_type = ATOM__NET_ACTIVE_WINDOW,
|
||||
.format = 32,
|
||||
.data = { .l = {
|
||||
[0] = 1, /* source indication -- `1` when coming from an application */
|
||||
[1] = 0, /* timestamp -- `0` to (attempt to) ignore */
|
||||
[2] = w->w /* requestor's currently active window -- `0` for none */
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* Send the client message as defined by EWMH standards (usually works) */
|
||||
XSendEvent(display, DefaultRootWindow(display), false, StructureNotifyMask, (XEvent*) &ev);
|
||||
};
|
||||
/* Send the client message as defined by EWMH standards (usually works) */
|
||||
XSendEvent(display, DefaultRootWindow(display), false, StructureNotifyMask, (XEvent*) &ev);
|
||||
}
|
||||
/* Raise the client in the X11 stacking order (sometimes works, can be blocked by the WM) */
|
||||
XRaiseWindow(display, w->w);
|
||||
XFlush(display);
|
||||
@@ -435,30 +512,34 @@ 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);
|
||||
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;
|
||||
}
|
||||
XFlush(display);
|
||||
}
|
||||
else XUnmapWindow(display, w->w);
|
||||
}
|
||||
|
||||
static bool should_close(struct glxwin* w) {
|
||||
return w->should_close;
|
||||
static bool should_close (struct glxwin* w) { return w->should_close; }
|
||||
static bool bg_changed (struct glxwin* w) { return w->bg_changed; }
|
||||
static bool should_render(struct glxwin* w) {
|
||||
/* For nearly all window managers, windows are 'minimized' by unmapping parent windows.
|
||||
VisibilityNotify events are not sent in these instances, so we have to read window
|
||||
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;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
process_events(w);
|
||||
}
|
||||
|
||||
static void get_fbsize(struct glxwin* w, int* d, int* h) {
|
||||
@@ -481,6 +562,17 @@ static double get_timert(void) {
|
||||
return (double) tv.tv_sec + ((double) tv.tv_nsec / 1000000000.0D);
|
||||
}
|
||||
|
||||
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) {
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
static double get_time (struct glxwin* w) { return get_timert() - w->time; }
|
||||
static void set_time (struct glxwin* w, double time) { w->time = get_timert() - time; }
|
||||
static Display* get_x11_display(struct glxwin* w) { return display; }
|
||||
|
||||
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,7 +50,7 @@ static void pulseaudio_context_state_callback(pa_context* pulseaudio_context, vo
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +118,7 @@ void* input_pulse(void* data) {
|
||||
.channels = 2
|
||||
};
|
||||
const pa_buffer_attr pb = {
|
||||
.maxlength = ssz * 2,
|
||||
.maxlength = (uint32_t) -1,
|
||||
.fragsize = ssz
|
||||
};
|
||||
|
||||
|
||||
289
render.c
289
render.c
@@ -13,7 +13,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad.h"
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
@@ -32,7 +32,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 +69,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,7 +84,7 @@ 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 */
|
||||
@@ -112,6 +118,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 {
|
||||
@@ -128,6 +135,7 @@ 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,
|
||||
@@ -174,6 +182,7 @@ static GLuint shaderload(const char* rpath,
|
||||
.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
|
||||
@@ -198,10 +207,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 +218,14 @@ 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[ilen];
|
||||
glGetShaderInfoLog(s, ilen, NULL, ebuf);
|
||||
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
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Shader compilation failed for '%s', but no info was available\n", path);
|
||||
@@ -240,13 +253,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,11 +282,11 @@ 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, ...) \
|
||||
shaderbuild_f(gl, shader_path, c, d, r, v, (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,
|
||||
const char** arr) {
|
||||
@@ -289,15 +302,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_path, config, defaults, handlers,
|
||||
shader_version, false, 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 +323,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, gl);
|
||||
fflush(stdout);
|
||||
return shaderlink_f(shaders);
|
||||
}
|
||||
@@ -358,10 +371,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 +478,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 +541,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 +563,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; \
|
||||
} \
|
||||
@@ -680,9 +688,9 @@ 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,
|
||||
bool auto_desktop, bool verbose) {
|
||||
|
||||
xwin_wait_for_wm();
|
||||
|
||||
@@ -724,7 +732,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
.sm_prog = 0,
|
||||
.copy_desktop = true,
|
||||
.premultiply_alpha = true,
|
||||
.check_fullscreen = true,
|
||||
.check_fullscreen = false,
|
||||
.smooth_pass = true,
|
||||
.fft_scale = 10.2F,
|
||||
.fft_cutoff = 0.3F,
|
||||
@@ -762,7 +770,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 +785,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,8 +798,9 @@ 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;
|
||||
char* xwintype = NULL, * wintitle = "GLava";
|
||||
const char* module = NULL;
|
||||
const char* wintitle_default = "GLava";
|
||||
char* xwintype = NULL, * wintitle = (char*) wintitle_default;
|
||||
char** xwinstates = malloc(1);
|
||||
size_t xwinstates_sz = 0;
|
||||
bool loading_module = true, loading_smooth_pass = false, loading_presets = false;;
|
||||
@@ -859,7 +868,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 (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);
|
||||
@@ -910,6 +920,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
},
|
||||
{ .name = "setsource", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, {
|
||||
if (r->audio_source_request) free(r->audio_source_request);
|
||||
r->audio_source_request = strdup((char*) args[0]); }) },
|
||||
{ .name = "setclickthrough", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, { gl->clickthrough = *(bool*) args[0]; }) },
|
||||
@@ -918,7 +929,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
{ .name = "setforceraised", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, { gl->force_raised = *(bool*) args[0]; }) },
|
||||
{ .name = "setxwintype", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, { xwintype = strdup((char*) args[0]); }) },
|
||||
.handler = RHANDLER(name, args, {
|
||||
if (xwintype) free(xwintype);
|
||||
xwintype = strdup((char*) args[0]); }) },
|
||||
{ .name = "setshaderversion", .fmt = "i",
|
||||
.handler = RHANDLER(name, args, { shader_version = *(int*) args[0]; }) },
|
||||
{ .name = "setswap", .fmt = "i",
|
||||
@@ -928,7 +941,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
{ .name = "setprintframes", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, { gl->print_fps = *(bool*) args[0]; }) },
|
||||
{ .name = "settitle", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, { wintitle = strdup((char*) args[0]); }) },
|
||||
.handler = RHANDLER(name, args, {
|
||||
if (wintitle && wintitle != wintitle_default) free((char*) wintitle);
|
||||
wintitle = strdup((char*) args[0]); }) },
|
||||
{ .name = "setbufsize", .fmt = "i",
|
||||
.handler = RHANDLER(name, args, { r->bufsize_request = *(int*) args[0]; }) },
|
||||
{ .name = "setbufscale", .fmt = "i",
|
||||
@@ -1031,7 +1046,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 = {}
|
||||
};
|
||||
})
|
||||
},
|
||||
@@ -1045,9 +1060,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;
|
||||
@@ -1084,6 +1101,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);
|
||||
|
||||
@@ -1095,14 +1113,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);
|
||||
}
|
||||
}
|
||||
@@ -1110,21 +1131,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,
|
||||
@@ -1138,9 +1183,13 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
gl->geometry[2], gl->geometry[3], gl->geometry[0], gl->geometry[1],
|
||||
context_version_major, context_version_minor, gl->clickthrough);
|
||||
if (!gl->w) abort();
|
||||
|
||||
for (size_t t = 0; t < xwinstates_sz; ++t)
|
||||
free(xwinstates[t]);
|
||||
|
||||
if (xwintype) free(xwintype);
|
||||
if (xwinstates) free(xwinstates);
|
||||
if (wintitle && wintitle != wintitle_default) free(wintitle);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
@@ -1159,9 +1208,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
|
||||
@@ -1188,7 +1237,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;
|
||||
}
|
||||
@@ -1209,7 +1258,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("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) {
|
||||
@@ -1222,7 +1271,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
};
|
||||
|
||||
current = s;
|
||||
GLuint id = shaderbuild(gl, shaders, data, handlers, shader_version, d->d_name);
|
||||
GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, d->d_name);
|
||||
if (!id) {
|
||||
abort();
|
||||
}
|
||||
@@ -1283,7 +1332,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, "smooth_pass.frag")))
|
||||
abort();
|
||||
loading_smooth_pass = false;
|
||||
}
|
||||
@@ -1308,7 +1357,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;
|
||||
@@ -1318,7 +1368,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
overlay(&gl->overlay);
|
||||
|
||||
glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a);
|
||||
|
||||
|
||||
gl->wcb->set_visible(gl->w, true);
|
||||
|
||||
return r;
|
||||
@@ -1334,12 +1384,17 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
struct gl_data* gl = r->gl;
|
||||
size_t t, a, fbsz = bsz * sizeof(float);
|
||||
|
||||
r->alive = !gl->wcb->should_close(gl->w);
|
||||
if (!r->alive)
|
||||
if (gl->wcb->should_close(gl->w)) {
|
||||
r->alive = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Stop rendering if the backend has some reason not to render (minimized, obscured) */
|
||||
if (!gl->wcb->should_render(gl->w))
|
||||
return false;
|
||||
|
||||
/* Stop rendering when fullscreen windows are focused */
|
||||
if (gl->check_fullscreen && !xwin_should_render(r))
|
||||
if (gl->check_fullscreen && !xwin_should_render(gl->wcb, gl->w))
|
||||
return false;
|
||||
|
||||
/* Force disable interpolation if the update rate is close to or higher than the frame rate */
|
||||
@@ -1406,7 +1461,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
}
|
||||
|
||||
/* Resize and grab new background data if needed */
|
||||
if (gl->copy_desktop && (ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
|
||||
if (gl->copy_desktop && (gl->wcb->bg_changed(gl->w) || ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
|
||||
gl->bg_tex = xwin_copyglbg(r, gl->bg_tex);
|
||||
}
|
||||
|
||||
@@ -1454,9 +1509,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, gl),
|
||||
shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader,
|
||||
NULL, NULL, 330, true, gl));
|
||||
NULL, NULL, NULL, 330, true, gl));
|
||||
bg_utex = glGetUniformLocation(bg_prog, "tex");
|
||||
bg_screen = glGetUniformLocation(bg_prog, "screen");
|
||||
glBindFragDataLocation(bg_prog, 1, "fragment");
|
||||
@@ -1528,20 +1583,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);
|
||||
|
||||
@@ -1559,14 +1604,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);
|
||||
}
|
||||
@@ -1604,20 +1648,20 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1698,7 +1742,30 @@ void* rd_get_impl_window (struct renderer* r) { return r->gl->w; }
|
||||
struct gl_wcb* rd_get_wcb (struct renderer* r) { return r->gl->wcb; }
|
||||
|
||||
void rd_destroy(struct renderer* r) {
|
||||
/* TODO: delete everything else, not really needed though (as the application exits after here) */
|
||||
r->gl->wcb->destroy(r->gl->w);
|
||||
if (r->gl->interpolate) free(r->gl->interpolate_buf[0]);
|
||||
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) {
|
||||
struct gl_bind* bind = &stage->binds[b];
|
||||
free(bind->transformations);
|
||||
free((char*) bind->name); /* strdup */
|
||||
}
|
||||
free(stage->binds);
|
||||
free((char*) stage->name); /* strdup */
|
||||
}
|
||||
free(r->gl->stages);
|
||||
r->gl->wcb->terminate();
|
||||
free(r->gl);
|
||||
if (r->audio_source_request)
|
||||
free(r->audio_source_request);
|
||||
free(r);
|
||||
}
|
||||
|
||||
14
render.h
14
render.h
@@ -11,9 +11,9 @@ 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,
|
||||
bool auto_desktop, bool verbose);
|
||||
bool rd_update (struct renderer*, float* lb, float* rb,
|
||||
size_t bsz, bool modified);
|
||||
void rd_destroy (struct renderer*);
|
||||
@@ -33,8 +33,12 @@ struct gl_wcb {
|
||||
int version_major, int version_minor,
|
||||
bool clickthrough);
|
||||
bool (*should_close) (void* ptr);
|
||||
bool (*should_render) (void* ptr);
|
||||
bool (*bg_changed) (void* ptr);
|
||||
void (*swap_buffers) (void* ptr);
|
||||
void (*raise) (void* ptr);
|
||||
void (*destroy) (void* ptr);
|
||||
void (*terminate) (void);
|
||||
void (*get_pos) (void* ptr, int* x, int* y);
|
||||
void (*get_fbsize) (void* ptr, int* w, int* h);
|
||||
void (*set_geometry) (void* ptr, int x, int y, int w, int h);
|
||||
@@ -66,8 +70,12 @@ struct gl_wcb {
|
||||
WCB_FUNC(init), \
|
||||
WCB_FUNC(create_and_bind), \
|
||||
WCB_FUNC(should_close), \
|
||||
WCB_FUNC(should_render), \
|
||||
WCB_FUNC(bg_changed), \
|
||||
WCB_FUNC(swap_buffers), \
|
||||
WCB_FUNC(raise), \
|
||||
WCB_FUNC(destroy), \
|
||||
WCB_FUNC(terminate), \
|
||||
WCB_FUNC(set_swap), \
|
||||
WCB_FUNC(get_pos), \
|
||||
WCB_FUNC(get_fbsize), \
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
|
||||
/* center line thickness (pixels) */
|
||||
/* Center line thickness (pixels) */
|
||||
#define C_LINE 1
|
||||
|
||||
/* width (in pixels) of each bar */
|
||||
/* Width (in pixels) of each bar */
|
||||
#define BAR_WIDTH 4
|
||||
/* width (in pixels) of each bar gap */
|
||||
/* Width (in pixels) of each bar gap */
|
||||
#define BAR_GAP 2
|
||||
/* outline color */
|
||||
/* Outline color */
|
||||
#define BAR_OUTLINE #262626
|
||||
/* outline width (in pixels, set to 0 to disable outline drawing) */
|
||||
/* Outline width (in pixels, set to 0 to disable outline drawing) */
|
||||
#define BAR_OUTLINE_WIDTH 0
|
||||
/* Amplify magnitude of the results each bar displays */
|
||||
#define AMPLIFY 300
|
||||
/* Alpha channel for bars color */
|
||||
#define ALPHA 0.7
|
||||
/* 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 * ((d / 60) + 1))
|
||||
#define COLOR (#3366b2 * GRADIENT * ALPHA)
|
||||
/* Direction that the bars are facing, 0 for inward, 1 for outward */
|
||||
#define DIRECTION 0
|
||||
/* Whether to switch left/right audio buffers */
|
||||
#define INVERT 0
|
||||
/* Whether to flip the output vertically */
|
||||
#define FLIP 0
|
||||
/* Whether to mirror output along `Y = X`, causing output to render on the left side of the window */
|
||||
/* Use with `FLIP 1` to render on the right side */
|
||||
#define MIRROR_YX 0
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -29,17 +30,33 @@ out vec4 fragment;
|
||||
#define PI 3.14159265359
|
||||
|
||||
void main() {
|
||||
float /* (x, magnitude) of fragment */
|
||||
dx = (gl_FragCoord.x - (screen.x / 2)),
|
||||
d = gl_FragCoord.y;
|
||||
float nbars = floor((screen.x * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2;
|
||||
|
||||
#if MIRROR_YX == 0
|
||||
#define AREA_WIDTH screen.x
|
||||
#define AREA_HEIGHT screen.y
|
||||
#define AREA_X gl_FragCoord.x
|
||||
#define AREA_Y gl_FragCoord.y
|
||||
#else
|
||||
#define AREA_WIDTH screen.y
|
||||
#define AREA_HEIGHT screen.x
|
||||
#define AREA_X gl_FragCoord.y
|
||||
#define AREA_Y gl_FragCoord.x
|
||||
#endif
|
||||
|
||||
float dx = (AREA_X - (AREA_WIDTH / 2));
|
||||
#if FLIP == 0
|
||||
float d = AREA_Y;
|
||||
#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 */
|
||||
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)) */
|
||||
p += sign(p) * ((0.5F + center) / screen.x); /* index center of bar position */
|
||||
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)
|
||||
float v;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,21 @@
|
||||
/* 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
|
||||
/* outline color */
|
||||
#define OUTLINE #262626
|
||||
/* 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
|
||||
|
||||
@@ -1,44 +1,40 @@
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -61,7 +61,13 @@
|
||||
|
||||
This will set _NET_WM_WINDOW_TYPE to _NET_WM_WINDOW_TYPE_(TYPE),
|
||||
where (TYPE) is the one of the window types listed (after being
|
||||
converted to uppercase). */
|
||||
converted to uppercase).
|
||||
|
||||
Alternatively, you can set this value to "!", which will cause
|
||||
the window to be unmanaged. If this is set, then `addxwinstate`
|
||||
will do nothing, but you can use "!+" and "!-" to stack on top
|
||||
or below other windows.
|
||||
*/
|
||||
#request setxwintype "normal"
|
||||
|
||||
/* (X11 only) EWMH Window state atoms (multiple can be specified).
|
||||
@@ -119,14 +125,17 @@
|
||||
#request setinterpolate true
|
||||
|
||||
/* Frame limiter, set to the frames per second (FPS) desired or
|
||||
simple set to zero (or lower) to disable the frame limiter. */
|
||||
simply set to zero (or lower) to disable the frame limiter. */
|
||||
#request setframerate 0
|
||||
|
||||
/* Enable/disable fullscreen checks. This looks at the currently
|
||||
focused window and halts GLava's rendering if it is
|
||||
fullscreen. This prevents rendering from interfering with
|
||||
other graphically intensive tasks. */
|
||||
#request setfullscreencheck true
|
||||
/* Suspends rendering if a fullscreen window is focused while
|
||||
GLava is still visible (ie. on another monitor). This prevents
|
||||
rendering from interfering with other graphically intensive
|
||||
tasks.
|
||||
|
||||
If GLava is minimized or completely obscured, it will not
|
||||
render regardless of this option. */
|
||||
#request setfullscreencheck false
|
||||
|
||||
/* Enable/disable printing framerate every second. 'FPS' stands
|
||||
for 'Frames Per Second', and 'UPS' stands for 'Updates Per
|
||||
|
||||
@@ -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() {
|
||||
|
||||
86
xwin.c
86
xwin.c
@@ -19,14 +19,14 @@
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad.h"
|
||||
|
||||
#define GLAVA_RDX11
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
/* Note: currently unused */
|
||||
Window* xwin_get_desktop_layer(struct gl_wcb* wcb) {
|
||||
Window* __attribute__ ((unused)) xwin_get_desktop_layer(struct gl_wcb* wcb) {
|
||||
static Window desktop;
|
||||
static bool searched = false;
|
||||
if (!searched) {
|
||||
@@ -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);
|
||||
}
|
||||
@@ -135,20 +135,19 @@ const char* xwin_detect_wm(struct gl_wcb* wcb) {
|
||||
}
|
||||
|
||||
XFree(wm_check);
|
||||
printf("wm_name: \"%s\"\n", wm_name);
|
||||
|
||||
return (const char*) wm_name;
|
||||
|
||||
}
|
||||
|
||||
bool xwin_should_render(struct renderer* rd) {
|
||||
bool xwin_should_render(struct gl_wcb* wcb, void* impl) {
|
||||
bool ret = true, should_close = false;
|
||||
Display* d = rd_get_wcb(rd)->get_x11_display();
|
||||
Display* d = wcb->get_x11_display();
|
||||
if (!d) {
|
||||
d = XOpenDisplay(0);
|
||||
should_close = true;
|
||||
}
|
||||
|
||||
|
||||
Atom prop = XInternAtom(d, "_NET_ACTIVE_WINDOW", true);
|
||||
Atom fullscreen = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", true);
|
||||
|
||||
@@ -162,7 +161,7 @@ bool xwin_should_render(struct renderer* rd) {
|
||||
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 */
|
||||
}
|
||||
|
||||
@@ -187,7 +186,7 @@ bool xwin_should_render(struct renderer* rd) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
close:
|
||||
close:
|
||||
if (data)
|
||||
XFree(data);
|
||||
if (should_close)
|
||||
@@ -201,8 +200,8 @@ bool xwin_should_render(struct renderer* rd) {
|
||||
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)
|
||||
@@ -222,8 +221,10 @@ static void xwin_changeatom(struct gl_wcb* wcb, void* impl, const char* type,
|
||||
-> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */
|
||||
bool xwin_settype(struct gl_wcb* wcb, void* impl, const char* rtype) {
|
||||
S_UPPER(rtype, type);
|
||||
xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE",
|
||||
"_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
|
||||
if (type[0] != '!') {
|
||||
xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE",
|
||||
"_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
|
||||
}
|
||||
return !strcmp(type, "DESKTOP");
|
||||
}
|
||||
|
||||
@@ -279,7 +280,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
Display* d = rd_get_wcb(rd)->get_x11_display();
|
||||
Drawable src = get_drawable(d, DefaultRootWindow(d));
|
||||
bool use_shm = XShmQueryExtension(d);
|
||||
|
||||
|
||||
/* Obtain section of root pixmap */
|
||||
|
||||
XShmSegmentInfo shminfo;
|
||||
@@ -315,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;
|
||||
@@ -388,6 +389,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
}
|
||||
|
||||
if (image) XDestroyImage(image);
|
||||
XFree(info);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
2
xwin.h
2
xwin.h
@@ -6,7 +6,7 @@
|
||||
|
||||
typedef unsigned long int Window;
|
||||
|
||||
bool xwin_should_render(struct renderer* rd);
|
||||
bool xwin_should_render(struct gl_wcb* wcb, void* impl);
|
||||
void xwin_wait_for_wm(void);
|
||||
bool xwin_settype(struct gl_wcb* wcb, void* impl, const char* type);
|
||||
void xwin_setdesktop(struct gl_wcb* wcb, void* impl, unsigned long desktop);
|
||||
|
||||
Reference in New Issue
Block a user