43 Commits
v1.2 ... v1.4.1

Author SHA1 Message Date
Jarcode
273cca946e Merge branch 'master' of https://github.com/wacossusca34/glava 2018-03-04 12:49:51 -08:00
Jarcode
95ceadf0e1 Merge branch 'unstable' 2018-03-04 12:49:23 -08:00
Jarcode
f32f0ded0f Version increase 2018-03-04 12:41:51 -08:00
Jarcode
3a1dcac8c9 Fixed texture load order, closes #28 2018-03-04 12:40:17 -08:00
Levi Webb
8adc8fe459 Merge pull request #26 from coderobe/master
Fix LDFLAGS
2018-03-02 17:41:49 -08:00
Robin Broda
e2f32a14cf Fix LDFLAGS 2018-03-02 18:40:35 +01:00
Levi Webb
318b4b3395 Update README.md 2018-02-20 19:05:00 -08:00
Levi Webb
b89fcd2e44 Merge pull request #23 from Aaahh/patch-3
Update Package names
2018-02-17 17:18:15 -08:00
Aaahh Ahh
baed3086d0 Update Package names
libpulse and libxext6-dev don't exist, also add libglx0 as it's not installed by default
2018-02-17 15:25:36 -05:00
Jarcode
b4fe4b5c97 stopped using the word currently frequently 2018-02-12 17:00:07 -08:00
Jarcode
0849cb485e Merge branch 'unstable' 2018-02-12 16:50:29 -08:00
Jarcode
c086222fb2 updated changelog 2018-02-12 16:50:08 -08:00
Jarcode
639c6d7fe9 Merge branch 'unstable' 2018-02-12 16:41:18 -08:00
Jarcode
22bc7c446c Fixed window class not being set by glx backend, + other fixes 2018-02-12 16:36:27 -08:00
Jarcode
b86870e0fd Added passes for premultiplied alpha, fixed alpha blending for native transparency 2018-02-12 16:25:32 -08:00
Jarcode
f021457abd Added GLX window creation backend, addresses #18 2018-02-12 12:47:59 -08:00
Jarcode
4fd4ce4c3f added 'glfw_wcb.c' 2018-02-12 07:17:33 -08:00
Jarcode
e5f5671acc Added gl_wcb interface to abstract out glfw and glx usage 2018-02-11 20:43:52 -08:00
Jarcode
79d06b070e Fixed potetial segmentation fault for xwin_should_render, addresses #19 2018-02-11 12:02:26 -08:00
Jarcode
3df89da8bb fixed alpha blending for 'radial' inner circle 2018-02-11 11:42:26 -08:00
Jarcode
79b99b09be refactored X11 code in 'render.c' and 'xwin.c' 2018-02-11 09:38:23 -08:00
Jarcode
0f4eed5e20 Updated readme with list of compatibility issues 2018-02-08 17:25:57 -08:00
Jarcode
ae09561d11 added test case for mutter gaurd window, addresses #11 and #18 2018-02-08 17:13:38 -08:00
Jarcode
33aa5274ae Merge branch 'master' into unstable 2018-02-08 16:52:31 -08:00
Jarcode
caee6a739c added case for mutter gaurd window 2018-02-08 16:50:52 -08:00
Jarcode
c8cce220d8 fixed GLFW_MAXIMIZED macro for GLFW 3.1 2018-02-08 06:36:40 -08:00
Jarcode
be63f40dc2 Merge branch 'unstable' 2018-02-07 20:57:00 -08:00
Jarcode
f5a9f801d1 Backported for GLFW 3.1, fixes #13, *again* 2018-02-07 20:56:41 -08:00
Jarcode
53bd657226 Added debian(-based) compile instructions 2018-02-07 20:42:05 -08:00
Jarcode
e6f2507b53 Merge branch 'unstable' 2018-02-07 20:23:15 -08:00
Jarcode
2805528d43 fixed directive issue 2018-02-07 20:22:54 -08:00
Jarcode
1de3a5215b updated changelog 2018-02-07 20:18:05 -08:00
Jarcode
1db4ea3ded updated changelog 2018-02-07 20:17:43 -08:00
Jarcode
901cdb7d67 Merge branch 'unstable' 2018-02-07 20:06:09 -08:00
Jarcode
d82762a471 changed version, added -V (--version) flag 2018-02-07 20:02:24 -08:00
Jarcode
ec6d4d0c37 exempted glsl color syntax from string contents 2018-02-07 19:53:23 -08:00
Jarcode
aea30abf67 added RGBA hex color parsing for GLSL, fixed alpha blending bug 2018-02-07 19:29:56 -08:00
Jarcode
79d391938d moved hex color parsing to ext_parse_color, removed debug message 2018-02-07 17:46:34 -08:00
Jarcode
8c3a404f37 fixed include order in xwin.c, fixes #13 2018-02-07 17:17:58 -08:00
Jarcode
f539f18135 Always define GLFW_EXPOSE_NATIVE_GLX 2018-02-07 17:12:45 -08:00
Jarcode
ab55156826 Set window types and state ealier during initialization, fixed #17 2018-02-07 16:58:39 -08:00
Jarcode
5e813e25a9 Changed position of 'glfwShowWindow' call, related to #17 2018-02-07 16:41:12 -08:00
Jarcode
83f5583eeb Updated version 2018-02-06 19:04:25 -08:00
21 changed files with 1101 additions and 349 deletions

View File

@@ -1,7 +1,23 @@
## 1.4
* Added window creation backends. GLava automatically uses the appropriate backend, currently `"glx"` and `"glfw"` are available. The GLX window creation code nicely supports transparent windows so that users do not need to use GLFW 3.3 for native transparency, which remains unstable and unreleased.
* Added a `--backend` (or `-b`) flag that can be used to manually select window creation backends.
* Fixed alpha blending with `"native"` transparency. GLava now correctly uses premultiplied alpha to provide alpha blending with other X windows, where a compositor is available.
* Fixed a segmentation fault that occurs with uncommon window managers, see #19
* Fixed various other issues with alpha blending in shader code.
## 1.3
* Added RGBA color parsing for GLSL code and directives (does not work within `#request` directives). The behaviour converts `#3ef708ff` -> `vec4(0.243, 0.967, 0.031, 1.0)`. If the alpha component is omitted, it is assumed to be `1.0`. The parsing occurs during compile time, so it does not incurr any performance penalty.
* Fixed stray `[DEBUG]` messages that were errornously being printed
* _Actually_ added support for GLFW 3.1, see issue #13
* Fixed issue with some WMs not responding to GLava's window types and hints, see #17
* Fixed alpha blending not working, `radial` and `circle` image quality should be drastically improved on `xroot` and `none` opacity settings.
* Added `--version` command line flag
## 1.2
* Added support for GLFW 3.1, see issue #13
* Attempted to add support for GLFW 3.1, see issue #13
* Added configurable solid background color, see #16
* Fixed issue with excessive file reads from `~/.Xauthority` see #15

View File

@@ -4,7 +4,7 @@ obj = $(src:.c=.o)
# Build type parameter
ifeq ($(BUILD),debug)
CFLAGS_BUILD = -O1 -ggdb -Wall -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
CFLAGS_BUILD = -O0 -ggdb -Wall -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
GLAD_GEN = c-debug
ASAN = -lasan
else
@@ -38,12 +38,22 @@ ifeq ($(INSTALL),unix)
endif
endif
ifndef DISABLE_GLFW
CFLAGS_GLFW = -DGLAVA_GLFW
LDFLAGS_GLFW = -lglfw
endif
ifndef DISABLE_GLX
CFLAGS_GLX = -DGLAVA_GLX
LDFLAGS_GLX = -lGLX -lXrender
endif
ifeq ($(INSTALL),osx)
CFLAGS_INSTALL = -DGLAVA_OSX
SHADER_DIR = Library/glava
endif
LDFLAGS = $(ASAN) -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext
LDFLAGS += $(ASAN) -lpulse -lpulse-simple -pthread $(LDFLAGS_GLFW) -ldl -lm -lX11 -lXext $(LDFLAGS_GLX)
PYTHON = python
@@ -51,7 +61,7 @@ GLAD_INSTALL_DIR = glad
GLAD_SRCFILE = ./glad/src/glad.c
GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
CFLAGS_COMMON = -I glad/include
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS_BUILD) $(CFLAGS_INSTALL) $(CFLAGS)
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS_GLX) $(CFLAGS_GLFW) $(CFLAGS_BUILD) $(CFLAGS_INSTALL) $(CFLAGS)
all: glava

View File

@@ -1,7 +1,7 @@
## GLava
<img align="left" width="200" height="200" src="https://thumbs.gfycat.com/DefiantInformalIndianspinyloach-size_restricted.gif" />
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.
**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/))**:**
@@ -19,7 +19,7 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
- X11
- PulseAudio
- GLFW 3.1+
- GLFW 3.1+ (optional, disable with `DISABLE_GLFW=1`)
- Linux or BSD
**Additional compile time requirements:**
@@ -27,6 +27,12 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
- glad (included as a submodule)
- python (required to generate bindings with glad)
- GCC (this program uses GNU C features)
- GLX headers (optional, disable direct GLX support with `DISABLE_GLX=1`), usually the development packages for `libgl` include this on your distro
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
```bash
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libglx0 libxext6 libxext-dev python make gcc
```
## [Configuration](https://github.com/wacossusca34/glava/wiki)
@@ -38,28 +44,25 @@ To embed GLava in your desktop (for EWMH compliant window managers), use `#reque
\* On an XDG compliant Linux or BSD system. OSX will use `/Library/glava` and `~/Library/Preferences/glava` instead.
**Note for `#request setopacity`:** While most users will prefer the faster `xroot` transparency, GLFW 3.3 (unreleased) is needed in order to support the `native` transparency option (older versions still work). Arch users can install `glfw-x11-git` from the AUR and recompile GLava for this feature.
## Desktop window compatibility
GLava aims to be compatible with _most_ EWMH compliant window managers. Below is a list of common window managers and issues specific to them for trying to get GLava to behave as a desktop window or widget:
| WM | ! | Details
| :---: | --- | --- |
| GNOME (on X11) | ![-](https://placehold.it/15/118932/000000?text=+) | No notable issues
| Mutter (GNOME, Budgie) | ![-](https://placehold.it/15/118932/000000?text=+) | `"native"` (default) opacity should be used
| Openbox (LXDE or standalone) | ![-](https://placehold.it/15/118932/000000?text=+) | [Some tweaks may be required](https://www.reddit.com/r/unixporn/comments/7vcgi4/oc_after_receiving_positive_feedback_here_i/dtrkvja/)
| Xfwm (XFCE) | ![-](https://placehold.it/15/118932/000000?text=+) | Untested, but should work without issues
| Fluxbox | ![-](https://placehold.it/15/118932/000000?text=+) | Untested, but should work without issues
| iceWM | ![-](https://placehold.it/15/118932/000000?text=+) | No notable issues
| AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | Requires the WM to be restarted (`Super + Ctl + R`) in order for new desktop windows to behave correctly, can still be focused, may require other changes to config depending on layout
| Budgie Desktop | ![-](https://placehold.it/15/f09c00/000000?text=+) | `"xroot"` transparency breaks with Budgie's wallpaper window
| Herbstluftwm | ![-](https://placehold.it/15/118932/000000?text=+) | `hc rule windowtype~'_NET_WM_WINDOW_TYPE_DESKTOP' manage=off` can be used to unmanage desktop windows
| AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | Can still be focused, may require other changes to config depending on layout
| kwin (KDE) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing
| i3 (and i3-gaps) | ![-](https://placehold.it/15/f03c15/000000?text=+) | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6)
| EXWM | ![-](https://placehold.it/15/f03c15/000000?text=+) | EXWM does not have a desktop, and forces window decorations
| Unity | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Enlightenment | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Bspwm | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Herbstluftwm | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| xmonad | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Any non EWMH-compliant WM | ![-](https://placehold.it/15/f03c15/000000?text=+) | Window types and hints will not work if the window manager does not support the EWMH standards.

25
glava.c
View File

@@ -18,7 +18,7 @@
#include "render.h"
#include "xwin.h"
#define GLAVA_VERSION "1.2"
#define GLAVA_VERSION "1.4.1"
#ifdef GLAD_DEBUG
#define GLAVA_RELEASE_TYPE_PREFIX "debug, "
#else
@@ -151,6 +151,8 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
closedir(dir);
}
#define GLAVA_VERSION_STRING "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")"
static const char* help_str =
"Usage: %s [OPTIONS]...\n"
"Opens a window with an OpenGL context to draw an audio visualizer.\n"
@@ -165,17 +167,23 @@ static const char* help_str =
"-C, --copy-config creates copies and symbolic links in the user configuration\n"
" directory for glava, copying any files in the root directory\n"
" of the installed shader directory, and linking any modules.\n"
"-b, --backend specifies a window creation backend to use. By default, the most\n"
" appropriate backend will be used for the underlying windowing\n"
" system.\n"
"-V, --version print application version and exit\n"
"\n"
"GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")\n"
GLAVA_VERSION_STRING "\n"
" -- Copyright (C) 2017 Levi Webb\n";
static const char* opt_str = "hve:Cm:";
static const char* opt_str = "hvVe:Cm:b:";
static struct option p_opts[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"entry", required_argument, 0, 'e'},
{"force-mod", required_argument, 0, 'm'},
{"copy-config", no_argument, 0, 'C'},
{"backend", required_argument, 0, 'b'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0 }
};
@@ -186,6 +194,7 @@ int main(int argc, char** argv) {
const char* user_path = SHADER_USER_PATH;
const char* entry = "rc.glsl";
const char* force = NULL;
const char* backend = NULL;
const char* system_shader_paths[] = { user_path, install_path, NULL };
bool verbose = false;
bool copy_mode = false;
@@ -197,11 +206,17 @@ int main(int argc, char** argv) {
case 'C': copy_mode = 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;
}
}
@@ -210,7 +225,7 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
renderer* r = rd_new(system_shader_paths, entry, force);
renderer* r = rd_new(system_shader_paths, entry, force, backend);
float b0[r->bufsize_request], b1[r->bufsize_request];
size_t t;
@@ -265,7 +280,7 @@ int main(int argc, char** argv) {
pthread_mutex_unlock(&audio.mutex);
/* Only render if needed (ie. stop rendering when fullscreen windows are focused) */
if (xwin_should_render()) {
if (xwin_should_render(r)) {
rd_update(r, lb, rb, r->bufsize_request, modified);
} else {
/* Sleep for 50ms and then attempt to render again */

138
glfw_wcb.c Normal file
View File

@@ -0,0 +1,138 @@
/* GLFW window and OpenGL context creation. */
#ifdef GLAVA_GLFW
#define GLAVA_RDX11
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "render.h"
#include "xwin.h"
#define GLFW_EXPOSE_NATIVE_X11
/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but
the old headers require one of them to be selected for exposure in glfw3native.h. */
#if GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include <GLFW/glfw3native.h>
/* Fixes for old GLFW versions */
#ifndef GLFW_TRUE
#define GLFW_TRUE GL_TRUE
#endif
#ifndef GLFW_FALSE
#define GLFW_FALSE GL_FALSE
#endif
#define DECL_WINDOW_HINT(F, H) \
static void F(bool var) { glfwWindowHint(H, var); }
#define DECL_WINDOW_HINT_STUB(F) \
static void F(bool _) { fprintf(stderr, "Warning: " #F " not implemented for GLFW backend\n"); }
static void init(void) {
if (!glfwInit()) {
fprintf(stderr, "glfwInit(): failed\n");
abort();
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
}
DECL_WINDOW_HINT(set_floating, GLFW_FLOATING);
DECL_WINDOW_HINT(set_decorated, GLFW_DECORATED);
DECL_WINDOW_HINT(set_focused, GLFW_FOCUSED);
#ifdef GLFW_MAXIMIZED
DECL_WINDOW_HINT(set_maximized, GLFW_MAXIMIZED);
#else
DECL_WINDOW_HINT_STUB(set_maximized);
#endif
extern struct gl_wcb wcb_glfw;
static void* create_and_bind(const char* name, const char* class,
const char* type, const char** states,
size_t states_sz,
int d, int h,
int x, int y,
int version_major, int version_minor) {
GLFWwindow* w;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, version_major);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, version_minor);
if (!(w = glfwCreateWindow(d, h, class, NULL, NULL))) {
fprintf(stderr, "glfwCreateWindow(): failed\n");
glfwTerminate();
return NULL;
}
if (type)
xwin_settype(&wcb_glfw, w, type);
for (size_t t = 0; t < states_sz; ++t)
xwin_addstate(&wcb_glfw, w, states[t]);
glfwSetWindowPos(w, x, y);
glfwMakeContextCurrent(w);
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
return w;
}
static void set_transparent(bool transparent) {
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, transparent ? GLFW_TRUE : GLFW_FALSE);
#elif GLFW_TRANSPARENT
glfwWindowHint(GLFW_TRANSPARENT, transparent ? GLFW_TRUE : GLFW_FALSE);
#else
if (transparent)
fprintf(stderr, "Warning: the linked version of GLFW3 does not have transparency support"
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
#endif
}
static void set_geometry(GLFWwindow* w, int x, int y, int d, int h) {
glfwSetWindowPos(w, x, y);
glfwSetWindowSize(w, d, h);
}
static void set_visible(GLFWwindow* w, bool visible) {
if (visible) glfwShowWindow(w);
else glfwHideWindow(w);
}
static bool swap_buffers(GLFWwindow* w) {
glfwSwapBuffers(w);
glfwPollEvents();
}
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 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); }
WCB_ATTACH("glfw", wcb_glfw);
#endif /* GLAVA_GLFW */

View File

@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
@@ -51,7 +52,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
int written;
if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0)
abort();
sbuf->at += space;
sbuf->at += written;
va_end(args);
}
@@ -68,6 +69,42 @@ struct schar {
size_t sz;
};
bool ext_parse_color(const char* str, size_t elem_sz, float** results) {
size_t t, len = strlen(str), i = 0, s = 0;
uint8_t elem_bytes[elem_sz];
/* Ignore '0x' prefix, if present */
if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
len -= 2;
str += 2;
}
for (t = 0; t < len && t < 8; ++t) {
char c = str[t];
uint8_t b;
/* obtain value from character */
switch (c) {
case 'a' ... 'f': b = (c - 'a') + 10; break;
case 'A' ... 'F': b = (c - 'A') + 10; break;
case '0' ... '9': b = c - '0'; break;
default: return false;
}
elem_bytes[s] = b;
if (s >= elem_sz - 1) { /* advance to next element */
uint32_t e = 0; /* component storage */
/* mask storage with input data */
for (size_t v = 0; v < elem_sz; ++v) {
e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4);
}
/* convert to [0, 1] as floating point value */
*results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1);
s = 0;
++i;
} else { /* advance character */
++s;
}
}
return true;
}
/* handle raw arguments for #include and #request directives */
/* NOTE: munmap needs to be called on the result */
static struct schar directive(struct glsl_ext* ext, char** args,
@@ -91,7 +128,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
int fd = open(path, O_RDONLY);
if (fd == -1) {
parse_error(line, f, "failed to load GLSL shader source specified by #include directive '%s': %s\n",
parse_error(line, f, "failed to load GLSL shader source "
"specified by #include directive '%s': %s\n",
path, strerror(errno));
}
@@ -100,7 +138,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (!map) {
parse_error(line, f, "failed to map GLSL shader source specified by #include directive '%s': %s\n",
parse_error(line, f, "failed to map GLSL shader source "
"specified by #include directive '%s': %s\n",
path, strerror(errno));
}
@@ -128,7 +167,7 @@ static struct schar directive(struct glsl_ext* ext, char** args,
struct request_handler* handler;
bool found = false;
size_t t, i;
size_t t;
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
if(!strcmp(handler->name, request)) {
found = true;
@@ -173,10 +212,12 @@ static struct schar directive(struct glsl_ext* ext, char** args,
case '1': { v = true; break; }
case '0': { v = false; break; }
default:
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
parse_error_s(line, f,
"tried to parse invalid raw string into a boolean");
}
} else {
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
parse_error_s(line, f,
"tried to parse invalid raw string into a boolean");
}
processed_args[i] = malloc(sizeof(bool));
*(bool*) processed_args[i] = v;
@@ -211,10 +252,11 @@ static struct schar directive(struct glsl_ext* ext, char** args,
void ext_process(struct glsl_ext* ext, const char* f) {
#define LINE_START 0
#define IGNORING 1
#define GLSL 1
#define MACRO 2
#define REQUEST 3
#define INCLUDE 4
#define COLOR 5
struct sbuf sbuf = {
.buf = malloc(256),
@@ -226,12 +268,16 @@ void ext_process(struct glsl_ext* ext, const char* f) {
size_t t;
char at;
int state = LINE_START;
size_t macro_start_idx, arg_start_idx;
size_t macro_start_idx, arg_start_idx, cbuf_idx;
size_t line = 1;
bool quoted = false, arg_start;
char cbuf[9];
char** args = NULL;
size_t args_sz = 0;
bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false,
prev_escape = false, string = false;
for (t = 0; t <= source_len; ++t) {
at = source_len == t ? '\0' : ext->source[t];
if (at == '\n')
@@ -244,40 +290,128 @@ void ext_process(struct glsl_ext* ext, const char* f) {
macro_start_idx = t;
state = MACRO;
continue; }
case ' ':
case '\t':
case '\n':
if (comment && comment_line) {
comment = false;
comment_line = false;
}
case '\t':
case ' ':
goto copy;
default: {
state = IGNORING;
goto copy; }
default: state = GLSL;
/* let execution continue into next state */
}
}
case IGNORING: /* copying GLSL source or unrelated preprocessor syntax */
if (at == '\n') {
state = LINE_START;
case GLSL: /* copying GLSL source or unrelated preprocessor syntax */
{
switch (at) {
case '"':
if (!comment && !prev_escape)
string = !string;
goto normal_char;
case '\\':
if (!comment) {
prev_escape = !prev_escape;
prev_asterix = false;
prev_slash = false;
goto copy;
} else goto normal_char;
case '/':
if (!comment) {
if (prev_slash) {
comment = true;
comment_line = true;
prev_slash = false;
} else prev_slash = true;
} else if (!comment_line) {
if (prev_asterix) {
comment = false;
prev_asterix = false;
}
}
prev_escape = false;
goto copy;
case '*':
if (!comment) {
if (prev_slash) {
comment = true;
prev_slash = false;
}
} else prev_asterix = true;
prev_escape = false;
goto copy;
case '#': {
/* handle hex color syntax */
if (!comment && !string) {
state = COLOR;
cbuf_idx = 0;
continue;
} else goto normal_char;
}
case '\n':
if (comment && comment_line) {
comment = false;
comment_line = false;
}
state = LINE_START;
normal_char:
default:
prev_asterix = false;
prev_slash = false;
prev_escape = false;
goto copy;
}
}
case COLOR: /* parse hex color syntax (#ffffffff -> vec4(1.0, 1.0, 1.0, 1.0)) */
{
switch (at) {
case 'a' ... 'z':
case 'A' ... 'Z':
case '0' ... '9': {
cbuf[cbuf_idx] = at;
++cbuf_idx;
if (cbuf_idx >= 8)
goto emit_color;
else continue;
}
emit_color:
default:
cbuf[cbuf_idx] = '\0'; /* null terminate */
float r = 0.0F, g = 0.0F, b = 0.0F, a = 1.0F;
if (ext_parse_color(cbuf, 2, (float*[]) { &r, &g, &b, &a })) {
se_append(&sbuf, 64, " vec4(%.6f, %.6f, %.6f, %.6f) ", r, g, b, a);
} else {
parse_error(line, f, "Invalid color format '#%s' while "
"parsing GLSL color syntax extension", cbuf);
}
state = at == '\n' ? LINE_START : GLSL;
if (cbuf_idx >= 8)
continue;
else goto copy; /* copy character if it ended the sequence */
}
}
goto copy;
case MACRO: /* processing start of macro */
{
switch (at) {
case '\n':
case ' ':
case '\t':
case '\0':
case '\0':
{
/* end parsing directive */
if (!strncmp("#request", &ext->source[macro_start_idx], t - macro_start_idx)
|| !strncmp("#REQUEST", &ext->source[macro_start_idx], t - macro_start_idx)) {
state = REQUEST;
goto prepare_arg_parse;
} else if (!strncmp("#include", &ext->source[macro_start_idx], t - macro_start_idx)
|| !strncmp("#INCLUDE", &ext->source[macro_start_idx], t - macro_start_idx)) {
} else if (!strncmp("#include", &ext->source[macro_start_idx],
t - macro_start_idx)
|| !strncmp("#INCLUDE", &ext->source[macro_start_idx],
t - macro_start_idx)) {
state = INCLUDE;
goto prepare_arg_parse;
} else {
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]);
state = IGNORING;
state = at == '\n' ? LINE_START : GLSL;
goto copy;
}
prepare_arg_parse:

View File

@@ -35,3 +35,4 @@ struct glsl_ext {
void ext_process(struct glsl_ext* ext, const char* f);
void ext_free (struct glsl_ext* ext);
bool ext_parse_color(const char* hex, size_t elem_sz, float** results);

278
glx_wcb.c Normal file
View File

@@ -0,0 +1,278 @@
/* Xlib window creation and GLX context creation backend */
#ifdef GLAVA_GLX
#define GLAVA_RDX11
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <X11/Xatom.h>
#include <glad/glad.h>
#include <GL/glx.h>
#include "render.h"
#include "xwin.h"
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
typedef void (*glXSwapIntervalEXTProc) (Display*, GLXDrawable, int);
extern struct gl_wcb wcb_glx;
static Display* display;
static int swap;
static bool floating, decorated, focused, maximized, transparent;
struct glxwin {
Window w;
GLXContext context;
double time;
bool should_close;
};
static void init(void) {
display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "XOpenDisplay(): could not establish connection to X11 server\n");
abort();
}
floating = false;
decorated = true;
focused = false;
maximized = false;
transparent = false;
}
static void apply_decorations(Window w) {
if (!decorated) {
struct {
unsigned long flags, functions, decorations;
long input_mode;
unsigned long status;
} hints;
hints.flags = 2;
hints.decorations = 0;
Atom motif = XInternAtom(display, "_MOTIF_WM_HINTS", false);
XChangeProperty(display, w, motif, motif, 32, PropModeReplace,
(unsigned char*) &hints, sizeof(hints) / sizeof(long));
}
}
static void* create_and_bind(const char* name, const char* class,
const char* type, const char** states,
size_t states_sz,
int d, int h,
int x, int y,
int version_major, int version_minor) {
struct glxwin* w = malloc(sizeof(struct glxwin));
w->time = 0.0D;
w->should_close = false;
XVisualInfo* vi;
XSetWindowAttributes attr;
GLXFBConfig* fbc;
int fb_sz, best = -1, samp = -1;
static int gl_attrs[] = {
GLX_X_RENDERABLE, True,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
None
};
int context_attrs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, version_major,
GLX_CONTEXT_MINOR_VERSION_ARB, version_minor,
// GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
None
};
fbc = glXChooseFBConfig(display, DefaultScreen(display), gl_attrs, &fb_sz);
if (!fbc) {
fprintf(stderr, "glXChooseFBConfig(): failed\n" );
abort();
}
for (int t = 0; t < fb_sz; ++t) {
XVisualInfo* xvi = glXGetVisualFromFBConfig(display, fbc[t]);
if (xvi) {
int samp_buf, samples;
glXGetFBConfigAttrib(display, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf);
glXGetFBConfigAttrib(display, fbc[t], GLX_SAMPLES, &samples );
XRenderPictFormat* fmt = XRenderFindVisualFormat(display, xvi->visual);
if (!fmt || (transparent ? fmt->direct.alphaMask == 0 : fmt->direct.alphaMask != 0))
continue;
if (best < 0 || samp_buf && samples > samp) {
best = t;
samp = samples;
}
XFree(xvi);
}
}
if (best == -1) {
fprintf(stderr, "Could not find suitable format for FBConfig\n");
abort();
}
GLXFBConfig config = fbc[best];
XFree(fbc);
vi = glXGetVisualFromFBConfig(display, config);
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vi->visual, AllocNone);
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
attr.background_pixmap = None;
attr.border_pixel = 0;
if (!(w->w = XCreateWindow(display, DefaultRootWindow(display),
x, y, d, h, 0,
vi->depth, InputOutput, vi->visual,
CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel,
&attr))) {
fprintf(stderr, "XCreateWindow(): failed\n");
abort();
}
if (type)
xwin_settype(&wcb_glx, w, type);
for (size_t t = 0; t < states_sz; ++t)
xwin_addstate(&wcb_glx, w, states[t]);
if (floating) xwin_addstate(&wcb_glx, w, "above");
if (maximized) {
xwin_addstate(&wcb_glx, w, "maximized_horz");
xwin_addstate(&wcb_glx, w, "maximized_vert");
}
XSetClassHint(display, w->w, &((XClassHint) { .res_name = (char*) class, .res_class = (char*) class }));
apply_decorations(w->w);
XFree(vi);
XStoreName(display, w->w, name);
Atom dwin = XInternAtom(display, "WM_DELETE_WINDOW", false);
XSetWMProtocols(display, w->w, &dwin, 1);
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);
gladLoadGL();
GLXDrawable drawable = glXGetCurrentDrawable();
if (glXSwapIntervalEXT) glXSwapIntervalEXT(display, drawable, swap);
return w;
}
static void set_swap (int _swap) { swap = _swap; }
static void set_floating (bool _floating) { floating = _floating; }
static void set_decorated (bool _decorated) { decorated = _decorated; }
static void set_focused (bool _focused) { focused = _focused; }
static void set_maximized (bool _maximized) { maximized = _maximized; }
static void set_transparent(bool _transparent) { transparent = _transparent; }
static void set_geometry(struct glxwin* w, int x, int y, int d, int h) {
XMoveResizeWindow(display, w->w, x, y, (unsigned int) d, (unsigned int) h);
}
static void set_visible(struct glxwin* w, bool visible) {
if (visible) XMapWindow(display, w->w);
else XUnmapWindow(display, w->w);
}
static bool should_close(struct glxwin* w) {
return w->should_close;
}
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 == XInternAtom(display, "WM_PROTOCOLS", 1)
&& ev.xclient.data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", 1)) {
w->should_close = true;
}
default: break;
}
}
}
static void get_fbsize(struct glxwin* w, int* d, int* h) {
XWindowAttributes a;
XGetWindowAttributes(display, w->w, &a);
*d = a.width;
*h = a.height;
}
static void get_pos(struct glxwin* w, int* x, int* y) {
XWindowAttributes a;
Window _ignored;
XTranslateCoordinates(display, w->w, DefaultRootWindow(display), 0, 0, x, y, &_ignored);
}
static double get_timert(void) {
struct timespec tv;
if (clock_gettime(CLOCK_REALTIME, &tv)) {
fprintf(stderr, "clock_gettime(CLOCK_REALTIME, ...): %s\n", strerror(errno));
}
return (double) tv.tv_sec + ((double) tv.tv_nsec / 1000000000.0D);
}
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; }
static Window get_x11_window (struct glxwin* w) { return w->w; }
WCB_ATTACH("glx", wcb_glx);
#endif /* GLAVA_GLX */

358
render.c
View File

@@ -14,7 +14,6 @@
#include <unistd.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "render.h"
#include "xwin.h"
@@ -36,6 +35,17 @@
#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] = {};
static size_t wcbs_idx = 0;
static inline void register_wcb(struct gl_wcb* wcb) { wcbs[wcbs_idx++] = wcb; }
#define DECL_WCB(N) \
do { \
extern struct gl_wcb N; \
register_wcb(&N); \
} while (0)
/* GLSL bind source */
struct gl_bind_src {
@@ -75,7 +85,7 @@ struct gl_bind {
struct gl_sfbo {
GLuint fbo, tex, shader;
bool valid;
bool valid, nativeonly;
const char* name;
struct gl_bind* binds;
size_t binds_sz;
@@ -92,13 +102,14 @@ struct gl_data {
struct overlay_data overlay;
GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog;
size_t stages_sz, bufscale, avg_frames;
GLFWwindow* w;
void* w;
struct gl_wcb* wcb;
int lww, lwh, lwx, lwy; /* last window dimensions */
int rate; /* framerate */
double tcounter;
int fcounter, ucounter, kcounter;
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop,
smooth_pass, use_alpha;
smooth_pass, premultiply_alpha;
void** t_data;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
smooth_factor, fft_scale, fft_cutoff;
@@ -109,6 +120,8 @@ struct gl_data {
int geometry[4];
};
/* load shader file */
static GLuint shaderload(const char* rpath,
GLenum type,
@@ -152,7 +165,8 @@ static GLuint shaderload(const char* rpath,
"#define UNIFORM_LIMIT %d\n"
"#define PRE_SMOOTHED_AUDIO %d\n"
"#define SMOOTH_FACTOR %.6f\n"
"#define USE_ALPHA %d\n";
"#define USE_ALPHA %d\n"
"#define PREMULTIPLY_ALPHA %d\n";
struct glsl_ext ext = {
.source = raw ? NULL : map,
@@ -167,11 +181,11 @@ static GLuint shaderload(const char* rpath,
/* If this is raw input, skip processing */
if (!raw) ext_process(&ext, rpath);
size_t blen = strlen(header_fmt) + 42;
size_t blen = strlen(header_fmt) + 64;
GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms,
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor,
gl->use_alpha ? 1 : 0);
1, gl->premultiply_alpha ? 1 : 0);
if (written < 0) {
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
return 0;
@@ -312,10 +326,8 @@ static GLuint create_1d_tex() {
}
static void update_1d_tex(GLuint tex, size_t w, float* data) {
float buf[w];
memcpy(buf, data, w * sizeof(float));
glBindTexture(GL_TEXTURE_1D, tex);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, w, 0, GL_RED, GL_FLOAT, buf);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, w, 0, GL_RED, GL_FLOAT, data);
}
#define BIND_VEC2 0
@@ -668,7 +680,8 @@ 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) {
struct renderer* rd_new(const char** paths, const char* entry,
const char* force_mod, const char* force_backend) {
renderer* r = malloc(sizeof(struct renderer));
*r = (struct renderer) {
@@ -682,35 +695,79 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct gl_data* gl = r->gl;
*gl = (struct gl_data) {
.w = NULL,
.stages = NULL,
.rate = 0,
.tcounter = 0.0D,
.fcounter = 0,
.ucounter = 0,
.kcounter = 0,
.fr = 1.0F,
.ur = 1.0F,
.print_fps = true,
.bufscale = 1,
.avg_frames = 6,
.avg_window = true,
.gravity_step = 4.2,
.interpolate = true,
.force_geometry = false,
.smooth_factor = 0.025,
.smooth_distance = 0.01,
.smooth_ratio = 4,
.bg_tex = 0,
.sm_prog = 0,
.copy_desktop = true,
.use_alpha = true,
.smooth_pass = true,
.fft_scale = 10.2F,
.fft_cutoff = 0.3F,
.geometry = { 0, 0, 500, 400 },
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F }
.w = NULL,
.wcb = NULL,
.stages = NULL,
.rate = 0,
.tcounter = 0.0D,
.fcounter = 0,
.ucounter = 0,
.kcounter = 0,
.fr = 1.0F,
.ur = 1.0F,
.print_fps = true,
.bufscale = 1,
.avg_frames = 6,
.avg_window = true,
.gravity_step = 4.2,
.interpolate = true,
.force_geometry = false,
.smooth_factor = 0.025,
.smooth_distance = 0.01,
.smooth_ratio = 4,
.bg_tex = 0,
.sm_prog = 0,
.copy_desktop = true,
.premultiply_alpha = true,
.smooth_pass = true,
.fft_scale = 10.2F,
.fft_cutoff = 0.3F,
.geometry = { 0, 0, 500, 400 },
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F }
};
bool forced = force_backend != NULL;
const char* backend = force_backend;
/* Window creation backend interfaces */
#ifdef GLAVA_GLFW
DECL_WCB(wcb_glfw);
if (!forced) backend = "glfw";
#endif
#ifdef GLAVA_GLX
DECL_WCB(wcb_glx);
if (!forced && !getenv("WAYLAND_DISPLAY") && getenv("DISPLAY")) {
backend = "glx";
}
#endif
if (!backend) {
fprintf(stderr, "No backend available for the active windowing system\n");
if (wcbs_idx == 0) {
fprintf(stderr, "None have been compiled into this build.\n");
} else {
fprintf(stderr, "Available backends:\n");
for (size_t t = 0; t < wcbs_idx; ++t) {
fprintf(stderr, "\t\"%s\"\n", wcbs[t]->name);
}
}
exit(EXIT_FAILURE);
}
printf("Using backend: '%s'\n", backend);
for (size_t t = 0; t < wcbs_idx; ++t) {
if (wcbs[t]->name && !strcmp(wcbs[t]->name, backend)) {
gl->wcb = wcbs[t];
break;
}
};
if (!gl->wcb) {
fprintf(stderr, "Invalid window creation backend selected: '%s'\n", gl->wcb);
abort();
}
#ifdef GLAD_DEBUG
printf("Assigning debug callback\n");
@@ -721,15 +778,11 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
}
#endif
if (!glfwInit())
abort();
gl->wcb->init();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
int shader_version = 330;
int shader_version = 330,
context_version_major = 3,
context_version_minor = 3;
const char* module = force_mod;
char* xwintype = NULL, * wintitle = "GLava";
char** xwinstates = malloc(1);
@@ -738,9 +791,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct gl_sfbo* current = NULL;
size_t t_count = 0;
#define WINDOW_HINT(request, attr) \
{ .name = request, .fmt = "b", \
.handler = RHANDLER(name, args, { glfwWindowHint(attr, *(bool*) args[0]); }) }
#define WINDOW_HINT(request) \
{ .name = "set" #request, .fmt = "b", \
.handler = RHANDLER(name, args, { gl->wcb->set_##request(*(bool*) args[0]); }) }
struct request_handler handlers[] = {
{
@@ -749,19 +802,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
bool native_opacity = !strcmp("native", (char*) args[0]);
gl->use_alpha = true;
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE);
gl->use_alpha = false;
#elif GLFW_TRANSPARENT
glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE);
gl->use_alpha = false;
#else
if (native_opacity)
printf("WARNING: the linked version of GLFW3 does not have transparency support"
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
#endif
gl->premultiply_alpha = native_opacity;
gl->wcb->set_transparent(native_opacity);
if (!strcmp("xroot", (char*) args[0]))
gl->copy_desktop = true;
@@ -783,42 +826,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
&gl->clear_color.b,
&gl->clear_color.a
};
const size_t elem_sz = 2;
char* str = (char*) args[0];
size_t t, len = strlen(str), i = 0, s = 0;
uint8_t elem_bytes[elem_sz];
/* Ignore '0x' prefix, if present */
if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
len -= 2;
str += 2;
}
for (t = 0; t < len && t < 8; ++t) {
char c = str[t];
uint8_t b;
/* obtain value from character */
switch (c) {
case 'a' ... 'f': b = (c - 'a') + 10; break;
case 'A' ... 'F': b = (c - 'A') + 10; break;
case '0' ... '9': b = c - '0'; break;
default:
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
exit(EXIT_FAILURE);
}
elem_bytes[s] = b;
if (s >= elem_sz - 1) { /* advance to next element */
uint32_t e = 0; /* component storage */
/* mask storage with input data */
for (size_t v = 0; v < elem_sz; ++v) {
e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4);
}
/* convert to [0, 1] as floating point value */
*results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1);
printf("[DEBUG] component %d value: %d (float: %f)\n", i, e, *results[i]);
s = 0;
++i;
} else { /* advance character */
++s;
}
if (!ext_parse_color((char*) args[0], 2, results)) {
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
exit(EXIT_FAILURE);
}
})
},
@@ -842,15 +852,26 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
}
})
},
WINDOW_HINT("setfloating", GLFW_FLOATING),
WINDOW_HINT("setdecorated", GLFW_DECORATED),
WINDOW_HINT("setfocused", GLFW_FOCUSED),
WINDOW_HINT("setmaximized", GLFW_MAXIMIZED),
{
.name = "nativeonly", .fmt = "b",
.handler = RHANDLER(name, args, {
if (current)
current->nativeonly = *(bool*) args[0];
else {
fprintf(stderr, "`nativeonly` request needs module context\n");
exit(EXIT_FAILURE);
}
})
},
WINDOW_HINT(floating),
WINDOW_HINT(decorated),
WINDOW_HINT(focused),
WINDOW_HINT(maximized),
{
.name = "setversion", .fmt = "ii",
.handler = RHANDLER(name, args, {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, *(int*) args[0]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, *(int*) args[1]);
context_version_major = *(int*) args[0];
context_version_minor = *(int*) args[1];
})
},
{
@@ -880,7 +901,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
{ .name = "setshaderversion", .fmt = "i",
.handler = RHANDLER(name, args, { shader_version = *(int*) args[0]; }) },
{ .name = "setswap", .fmt = "i",
.handler = RHANDLER(name, args, { glfwSwapInterval(*(int*) args[0]); }) },
.handler = RHANDLER(name, args, { gl->wcb->set_swap(*(int*) args[0]); }) },
{ .name = "setframerate", .fmt = "i",
.handler = RHANDLER(name, args, { gl->rate = *(int*) args[0]; }) },
{ .name = "setprintframes", .fmt = "b",
@@ -1050,16 +1071,16 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
exit(EXIT_FAILURE);
}
if (!(gl->w = glfwCreateWindow(500, 400, wintitle, NULL, NULL))) {
glfwTerminate();
if (!(gl->w = gl->wcb->create_and_bind(wintitle, "GLava", xwintype,
(const char**) xwinstates, xwinstates_sz,
gl->geometry[2], gl->geometry[3],
gl->geometry[0], gl->geometry[1],
context_version_major, context_version_minor))) {
abort();
}
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
glfwMakeContextCurrent(gl->w);
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
if (xwintype) free(xwintype);
if (xwinstates) free(xwinstates);
glDisable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_CLAMP);
@@ -1068,8 +1089,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
glDisable(GL_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (!gl->premultiply_alpha) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
size_t m_len = strlen(module);
size_t bsz = d_len + m_len + 2;
@@ -1086,7 +1109,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct gl_sfbo* stages;
size_t count = 0;
{
char buf[32];
DIR* dir = opendir(shaders);
@@ -1130,11 +1153,12 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct gl_sfbo* s = &stages[idx - 1];
*s = (struct gl_sfbo) {
.name = strdup(d->d_name),
.shader = 0,
.valid = false,
.binds = malloc(1),
.binds_sz = 0
.name = strdup(d->d_name),
.shader = 0,
.valid = false,
.nativeonly = false,
.binds = malloc(1),
.binds_sz = 0
};
current = s;
@@ -1149,7 +1173,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
as it can rendered directly */
if (idx != count) {
int w, h;
glfwGetFramebufferSize(gl->w, &w, &h);
gl->wcb->get_fbsize(gl->w, &w, &h);
setup_sfbo(&stages[idx - 1], w, h);
}
@@ -1172,6 +1196,23 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
} while (found);
}
}
gl->stages = stages;
gl->stages_sz = count;
{
struct gl_sfbo* final = NULL;
if (!gl->premultiply_alpha) {
for (size_t t = 0; t < gl->stages_sz; ++t) {
if (!gl->stages[t].nativeonly) {
final = &gl->stages[t];
}
}
}
/* Invalidate framebuffer and use direct rendering if it was instantiated
due to a following `nativeonly` shader pass. */
if (final) final->valid = false;
}
/* Compile smooth pass shader */
@@ -1182,15 +1223,11 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
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, handlers, shader_version, "smooth_pass.frag")))
abort();
}
loading_smooth_pass = false;
}
gl->stages = stages;
gl->stages_sz = count;
/* target seconds per update */
gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request;
@@ -1219,20 +1256,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
}
overlay(&gl->overlay);
glfwShowWindow(gl->w);
glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a);
if (xwintype) {
xwin_settype(r, xwintype);
free(xwintype);
}
for (size_t t = 0; t < xwinstates_sz; ++t) {
xwin_addstate(r, xwinstates[t]);
}
free(xwinstates);
gl->wcb->set_visible(gl->w, true);
return r;
}
@@ -1240,14 +1267,14 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
void rd_time(struct renderer* r) {
struct gl_data* gl = r->gl;
glfwSetTime(0.0D); /* reset time for measuring this frame */
gl->wcb->set_time(gl->w, 0.0D); /* reset time for measuring this frame */
}
void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modified) {
struct gl_data* gl = r->gl;
size_t t, a, fbsz = bsz * sizeof(float);
r->alive = !glfwWindowShouldClose(gl->w);
r->alive = !gl->wcb->should_close(gl->w);
if (!r->alive)
return;
@@ -1302,8 +1329,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
}
}
int ww, wh, wx, wy;
glfwGetFramebufferSize(gl->w, &ww, &wh);
glfwGetWindowPos(gl->w, &wx, &wy);
gl->wcb->get_fbsize(gl->w, &ww, &wh);
gl->wcb->get_pos(gl->w, &wx, &wy);
/* Resize screen textures if needed */
if (ww != gl->lww || wh != gl->lwh) {
@@ -1332,10 +1359,13 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
for (t = 0; t < gl->stages_sz; ++t) {
bool needed[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
bool load_flags[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
/* Current shader program */
struct gl_sfbo* current = &gl->stages[t];
if (current->nativeonly && !gl->premultiply_alpha)
continue;
/* Bind framebuffer if this is not the final pass */
if (current->valid)
@@ -1354,8 +1384,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
"out vec4 fragment;" "\n"
"in vec4 gl_FragCoord;" "\n"
"void main() {" "\n"
" fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, " "\n"
" (screen.y - gl_FragCoord.y) / screen.y));" "\n"
" fragment = texelFetch(tex, ivec2(gl_FragCoord.x, " "\n"
" screen.y - gl_FragCoord.y), 0);" "\n"
" fragment.a = 1.0F;" "\n"
"}" "\n";
if (!setup) {
@@ -1376,9 +1406,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* We need to disable blending, we might read in bogus alpha values due
to how we obtain the background texture (format is four byte `rgb_`,
where the last value is skipped) */
glDisable(GL_BLEND);
if (!gl->premultiply_alpha) glDisable(GL_BLEND);
drawoverlay(&gl->overlay);
glEnable(GL_BLEND);
if (!gl->premultiply_alpha) glEnable(GL_BLEND);
glUseProgram(0);
}
@@ -1396,6 +1426,10 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Handle transformations and bindings for 1D samplers */
void handle_1d_tex(GLuint tex, float* buf, float* ubuf, size_t sz, int offset, bool audio) {
if (load_flags[offset])
goto bind_uniform;
load_flags[offset] = true;
/* Only apply transformations if the buffers we
were given are newly copied from PA */
@@ -1411,6 +1445,7 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
transform types are added) */
}
}
glActiveTexture(GL_TEXTURE0 + offset);
/* Update texture with our data */
update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf);
@@ -1456,7 +1491,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* setup and bind framebuffer to texture */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_1D, sm->tex, 0);
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,\
GL_TEXTURE_1D, sm->tex, 0);
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
case GL_FRAMEBUFFER_COMPLETE: break;
@@ -1477,11 +1513,11 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
glUniform1i(sm_uw, sz); /* target texture width */
glUniform1i(sm_usz, sz); /* source texture width */
glUniform1i(sm_utex, offset);
glDisable(GL_BLEND);
if (!gl->premultiply_alpha) glDisable(GL_BLEND);
glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh);
glEnable(GL_BLEND);
if (!gl->premultiply_alpha) glEnable(GL_BLEND);
/* Return state */
glUseProgram(current->shader);
@@ -1493,11 +1529,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
tex = sm->tex; /* replace input texture with our processed one */
}
if (!needed[offset]) {
glActiveTexture(GL_TEXTURE0 + offset);
glBindTexture(GL_TEXTURE_1D, tex);
needed[offset] = true;
}
glActiveTexture(GL_TEXTURE0 + offset);
glBindTexture(GL_TEXTURE_1D, tex);
bind_uniform:
glUniform1i(bind->uniform, offset);
}
@@ -1542,10 +1576,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
}
/* Swap buffers, handle events, etc. (vsync is potentially included here, too) */
glfwSwapBuffers(gl->w);
glfwPollEvents();
gl->wcb->swap_buffers(gl->w);
double duration = glfwGetTime(); /* frame execution time */
double duration = gl->wcb->get_time(gl->w); /* frame execution time */
/* Handling sleeping (to meet target framerate) */
if (gl->rate > 0) {
@@ -1581,8 +1614,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Refresh window position and size if we are forcing it */
if (gl->force_geometry) {
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
gl->wcb->set_geometry(gl->w,
gl->geometry[0], gl->geometry[1],
gl->geometry[2], gl->geometry[3]);
}
}
@@ -1590,11 +1624,11 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
gl->interpolate = old_interpolate;
}
void* rd_get_impl_window(struct renderer* r) { return r->gl->w; }
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) */
glfwTerminate();
free(r->gl);
free(r);
}

View File

@@ -1,4 +1,7 @@
#ifndef RENDER_H
#define RENDER_H
struct gl_data;
typedef struct renderer {
@@ -8,8 +11,72 @@ typedef struct renderer {
struct gl_data* gl;
} renderer;
struct renderer* rd_new(const char** paths, const char* entry, const char* force_mod);
void rd_update(struct renderer*, float* lb, float* rb, size_t bsz, bool modified);
void rd_destroy(struct renderer*);
void rd_time(struct renderer*);
struct renderer* rd_new (const char** paths, const char* entry,
const char* force_mod, const char* force_backend);
void rd_update (struct renderer*, float* lb, float* rb,
size_t bsz, bool modified);
void rd_destroy (struct renderer*);
void rd_time (struct renderer*);
void* rd_get_impl_window(struct renderer*);
struct gl_wcb* rd_get_wcb (struct renderer*);
/* gl_wcb - OpenGL Window Creation Backend interface */
struct gl_wcb {
const char* name;
void (*init) (void);
void* (*create_and_bind)(const char* name, const char* class,
const char* type, const char** states,
size_t states_sz,
int w, int h,
int x, int y,
int version_major, int version_minor);
bool (*should_close) (void* ptr);
bool (*swap_buffers) (void* ptr);
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);
void (*set_swap) (int interval);
void (*set_floating) (bool floating);
void (*set_decorated) (bool decorated);
void (*set_focused) (bool focused);
void (*set_maximized) (bool maximized);
void (*set_transparent)(bool transparent);
double (*get_time) (void* ptr);
void (*set_time) (void* ptr, double time);
void (*set_visible) (void* ptr, bool visible);
#ifdef GLAVA_RDX11
Display* (*get_x11_display)(void);
Window (*get_x11_window) (void* ptr);
#else /* define placeholders to ensure equal struct size */
void* _X11_DISPLAY_PLACEHOLDER;
void* _X11_WINDOW_PLACEHOLDER;
#endif
};
#define WCB_FUNC(F) \
.F = (typeof(((struct gl_wcb*) NULL)->F)) &F
#define WCB_ATTACH(B, N) \
struct gl_wcb N = { \
.name = B, \
WCB_FUNC(init), \
WCB_FUNC(create_and_bind), \
WCB_FUNC(should_close), \
WCB_FUNC(swap_buffers), \
WCB_FUNC(set_swap), \
WCB_FUNC(get_pos), \
WCB_FUNC(get_fbsize), \
WCB_FUNC(set_geometry), \
WCB_FUNC(set_floating), \
WCB_FUNC(set_decorated), \
WCB_FUNC(set_focused), \
WCB_FUNC(set_maximized), \
WCB_FUNC(set_transparent), \
WCB_FUNC(set_time), \
WCB_FUNC(get_time), \
WCB_FUNC(set_visible), \
WCB_FUNC(get_x11_display), \
WCB_FUNC(get_x11_window) \
}
#endif /* RENDER_H */

View File

@@ -7,13 +7,13 @@
/* width (in pixels) of each bar gap */
#define BAR_GAP 2
/* outline color */
#define BAR_OUTLINE vec4(0.15, 0.15, 0.15, 1)
#define BAR_OUTLINE #262626
/* outline width (in pixels, set to 0 to disable outline drawing) */
#define BAR_OUTLINE_WIDTH 0
/* Amplify magnitude of the results each bar displays */
#define AMPLIFY 300
/* Bar color */
#define COLOR (vec4(0.2, 0.4, 0.7, 1) * ((d / 60) + 1))
#define COLOR (#3366b2 * ((d / 60) + 1))
/* Direction that the bars are facing, 0 for inward, 1 for outward */
#define DIRECTION 0
/* Whether to switch left/right audio buffers */

View File

@@ -1,9 +1,9 @@
/* center radius (pixels) */
#define C_RADIUS 128
/* center line thickness (pixels) */
#define C_LINE 2
#define C_LINE 1.5
/* outline color */
#define OUTLINE vec4(0.20, 0.20, 0.20, 1)
#define OUTLINE #333333
/* Amplify magnitude of the results each bar displays */
#define AMPLIFY 150
/* Angle (in radians) for how much to rotate the visualizer */

1
shaders/circle/3.frag Normal file
View File

@@ -0,0 +1 @@
#include ":util/premultiply.frag"

View File

@@ -17,7 +17,7 @@
/* actual color definition */
#define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1)
/* outline color */
#define OUTLINE vec4(0.15, 0.15, 0.15, 1)
#define OUTLINE #262626
/* 1 to invert (vertically), 0 otherwise */
#define INVERT 0

View File

@@ -4,7 +4,7 @@
/* center line thickness (pixels) */
#define C_LINE 2
/* outline color */
#define OUTLINE vec4(0.20, 0.20, 0.20, 1)
#define OUTLINE #333333
/* number of bars (use even values for best results) */
#define NBARS 180
/* width (in pixels) of each bar*/
@@ -15,8 +15,8 @@
#define BAR_OUTLINE_WIDTH 0
/* Amplify magnitude of the results each bar displays */
#define AMPLIFY 300
/* Bar color */
#define COLOR (vec4(0.8, 0.2, 0.2, 1) * ((d / 40) + 1))
/* Bar color */
#define COLOR (#cc3333 * ((d / 40) + 1))
/* Angle (in radians) for how much to rotate the visualizer */
#define ROTATE (PI / 2)
/* Whether to switch left/right audio buffers */

View File

@@ -29,10 +29,10 @@ out vec4 fragment;
#define PI 3.14159265359
void main() {
#if USE_ALPHA > 0
#define APPLY_FRAG(f, c) f = vec4(f.rgb * f.a + c.rgb * (1 - f.a), max(c.a, f.a))
fragment.a = 0;
#define APPLY_FRAG(f, c) f = vec4(f.rgb * f.a + c.rgb * (1 - clamp(f.a, 0, 1)), max(c.a, f.a))
fragment = #00000000;
#else
#define APPLY_FRAG(f, c) f = c
#endif
@@ -46,9 +46,9 @@ void main() {
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)) {
fragment = OUTLINE;
APPLY_FRAG(fragment, OUTLINE);
#if USE_ALPHA > 0
fragment.a *= ((float(C_LINE) / 2.0F) - abs(d - C_RADIUS)) * C_ALIAS_FACTOR;
fragment.a *= clamp(((C_LINE / 2) - abs(C_RADIUS - d)) * C_ALIAS_FACTOR, 0, 1);
#else
return; /* return immediately if there is no alpha blending available */
#endif

1
shaders/radial/2.frag Normal file
View File

@@ -0,0 +1 @@
#include ":util/premultiply.frag"

View File

@@ -15,23 +15,22 @@
See documentation for more details. */
#request mod bars
/* GLFW window hints */
/* Window hints */
#request setfloating false
#request setdecorated false
#request setdecorated true
#request setfocused false
#request setmaximized false
/* Force GLFW window geometry (locking the window in place),
useful for some pesky WMs that try to reposition the window
when embedding in the desktop. */
/* Force window geometry (locking the window in place), useful
for some pesky WMs that try to reposition the window when
embedding in the desktop. */
#request setforcegeometry false
/* Set window background opacity mode. Possible values are:
"native" - True transparency provided by the compositor.
Requires GLFW 3.3+ and an active compositor. Can
reduce performance on some systems, and will not
blend with the visualizer's alpha layer.
"native" - True transparency provided by the compositor. Can
reduce performance on some systems, depending on
the compositor used.
"xroot" - Maintain a copy of the root window's pixmap
(usually the desktop background) to provide a
@@ -40,22 +39,22 @@
Has very little performance impact.
"none" - Disable window opacity completely. */
#request setopacity "xroot"
#request setopacity "native"
/* OpenGL context and GLSL shader versions, do not change unless
you *absolutely* know what you are doing. */
#request setversion 3 3
#request setshaderversion 330
/* GLFW window title */
/* Window title */
#request settitle "GLava"
/* GLFW window geometry (x, y, width, height) */
#request setgeometry 0 0 400 600
/* Window geometry (x, y, width, height) */
#request setgeometry 0 0 800 600
/* Window background color (RGB format).
Only works with `setopacity "none"` */
#request setbg 3C3C3C
Does not work with `setopacity "xroot"` */
#request setbg 00000000
/* (X11 only) EWMH Window type. Possible values are:
@@ -96,7 +95,7 @@
default output device. */
#request setsource "auto"
/* GLFW buffer swap interval (vsync), set to '0' to prevent
/* Buffer swap interval (vsync), set to '0' to prevent
waiting for refresh, '1' (or more) to wait for the specified
amount of frames. */
#request setswap 1

View File

@@ -0,0 +1,15 @@
#request nativeonly true
#request uniform "prev" tex
uniform sampler2D tex;
out vec4 fragment;
in vec4 gl_FragCoord;
void main() {
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
#if PREMULTIPLY_ALPHA > 0
fragment.rgb *= fragment.a;
#endif
}

262
xwin.c
View File

@@ -12,25 +12,50 @@
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/XShm.h>
#define GLFW_EXPOSE_NATIVE_X11
/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but
the old headers require one of them to be selected for exposure in glfw3native.h. */
#if (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1)
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#define GLAVA_RDX11
#include "render.h"
#include "xwin.h"
bool xwin_should_render(void) {
static Window find_desktop(struct renderer* r) {
static Window desktop;
static bool searched = false;
if (!searched) {
Display* d = rd_get_wcb(r)->get_x11_display();
desktop = DefaultRootWindow(d);
Window _ignored, * children;
unsigned int nret;
XQueryTree(d, desktop, &_ignored, &_ignored, &children, &nret);
if (children) {
for (unsigned int t = 0; t < nret; ++t) {
char* name;
XFetchName(d, children[t], &name);
if (name) {
/* Mutter-based window managers */
if (!strcmp(name, "mutter guard window")) {
printf("Using mutter guard window instead of root window\n");
// desktop = children[t];
t = nret; /* break after */
}
XFree(name);
}
}
XFree(children);
}
searched = true;
}
return desktop;
}
bool xwin_should_render(struct renderer* rd) {
bool ret = true, should_close = false;
Display* d = glfwGetX11Display();
Display* d = rd_get_wcb(rd)->get_x11_display();
if (!d) {
d = XOpenDisplay(0);
should_close = true;
@@ -48,11 +73,14 @@ bool xwin_should_render(void) {
XSetErrorHandler(handler); /* dummy error handler */
if (Success != XGetWindowProperty(d, RootWindow(d, 0), prop, 0, 1, false, AnyPropertyType,
if (Success != XGetWindowProperty(d, DefaultRootWindow(d), prop, 0, 1, false, AnyPropertyType,
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */
}
if (!nitems)
goto close;
Window active = ((Window*) data)[0];
prop = XInternAtom(d, "_NET_WM_STATE", true);
@@ -74,9 +102,10 @@ bool xwin_should_render(void) {
/* Set window types defined by the EWMH standard, possible values:
-> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */
static void xwin_changeatom(struct renderer* rd, const char* type, const char* atom, const char* fmt, int mode) {
Window w = glfwGetX11Window((GLFWwindow*) rd_get_impl_window(rd));
Display* d = glfwGetX11Display();
static void xwin_changeatom(struct gl_wcb* wcb, void* impl, const char* type,
const char* atom, const char* fmt, int mode) {
Window w = wcb->get_x11_window(impl);
Display* d = wcb->get_x11_display();
Atom wtype = XInternAtom(d, atom, false);
size_t len = strlen(type), t;
char formatted[len + 1];
@@ -93,16 +122,16 @@ static void xwin_changeatom(struct renderer* rd, const char* type, const char* a
XChangeProperty(d, w, wtype, XA_ATOM, 32, mode, (unsigned char*) &desk, 1);
}
void xwin_settype(struct renderer* rd, const char* type) {
xwin_changeatom(rd, type, "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
void xwin_settype(struct gl_wcb* wcb, void* impl, const char* type) {
xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
}
void xwin_addstate(struct renderer* rd, const char* state) {
xwin_changeatom(rd, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* state) {
xwin_changeatom(wcb, impl, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
}
static Pixmap get_pixmap(Display* d, Window w) {
Pixmap p;
static Drawable get_drawable(Display* d, Window w) {
Drawable p;
Atom act_type;
int act_format;
unsigned long nitems, bytes_after;
@@ -110,14 +139,14 @@ static Pixmap get_pixmap(Display* d, Window w) {
Atom id;
id = XInternAtom(d, "_XROOTPMAP_ID", False);
if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP,
&act_type, &act_format, &nitems, &bytes_after,
&data) == Success) {
if (data) {
p = *((Pixmap *) data);
XFree(data);
}
&data) == Success && data) {
p = *((Pixmap *) data);
XFree(data);
} else {
p = w;
}
return p;
@@ -131,112 +160,123 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFWwindow* gwin = (GLFWwindow*) rd_get_impl_window(rd);
bool use_shm = true;
int x, y, w, h;
glfwGetFramebufferSize(gwin, &w, &h);
glfwGetWindowPos(gwin, &x, &y);
rd_get_wcb(rd)->get_fbsize(rd_get_impl_window(rd), &w, &h);
rd_get_wcb(rd)->get_pos(rd_get_impl_window(rd), &x, &y);
XColor c;
Display* d = glfwGetX11Display();
Pixmap p = get_pixmap(d, RootWindow(d, DefaultScreen(d)));
Display* d = rd_get_wcb(rd)->get_x11_display();
Drawable src = get_drawable(d, find_desktop(rd));
/* Obtain section of root pixmap using XShm */
/* Obtain section of root pixmap */
XShmSegmentInfo shminfo;
Visual* visual = DefaultVisual(d, DefaultScreen(d));
XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) };
int nret;
XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret);
XImage* image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
&shminfo, (unsigned int) w, (unsigned int) h);
if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777)) == -1) {
fprintf(stderr, "shmget() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
XImage* image;
if (use_shm) {
image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
&shminfo, (unsigned int) w, (unsigned int) h);
if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height,
IPC_CREAT | 0777)) == -1) {
fprintf(stderr, "shmget() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = false;
XShmAttach(d, &shminfo);
XShmGetImage(d, src, image, x, y, AllPlanes);
} else {
image = XGetImage(d, src, x, y, (unsigned int) w, (unsigned int) h,
ZPixmap, AllPlanes);
}
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = false;
XShmAttach(d, &shminfo);
XShmGetImage(d, p, image, x, y, AllPlanes);
/* Try to convert pixel bit depth to OpenGL storage format. The following formats\
will need intermediate conversion before OpenGL can accept the data:
- 8-bit pixel formats (retro displays, low-bandwidth virtual displays)
- 36-bit pixel formats (rare deep color displays) */
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;
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;
}
uint8_t* buf;
if (invalid) {
abort();
/* Manual reformat (slow) */
buf = malloc(4 * w * h);
int xi, yi;
Colormap map = DefaultColormap(d, DefaultScreen(d));
for (yi = 0; yi < h; ++yi) {
for (xi = 0; xi < w; ++xi) {
c.pixel = XGetPixel(image, xi, yi);
XQueryColor(d, map, &c);
size_t base = (xi + (yi * w)) * 4;
buf[base + 0] = c.red / 256;
buf[base + 1] = c.green / 256;
buf[base + 2] = c.blue / 256;
buf[base + 3] = 255;
if (image) {
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;
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;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
free(buf);
} else {
/* Use image data directly. The alpha value is garbage/unassigned data, but
we need to read it because X11 keeps pixel data aligned */
buf = (uint8_t*) image->data;
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth)
already ensures reads will be properly aligned across scanlines */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLenum format = image->bitmap_bit_order == LSBFirst ?
(!aligned ? GL_BGRA : GL_BGR) :
(!aligned ? GL_RGBA : GL_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
uint8_t* buf;
if (invalid) {
abort();
/* Manual reformat (slow) */
buf = malloc(4 * w * h);
int xi, yi;
Colormap map = DefaultColormap(d, DefaultScreen(d));
for (yi = 0; yi < h; ++yi) {
for (xi = 0; xi < w; ++xi) {
c.pixel = XGetPixel(image, xi, yi);
XQueryColor(d, map, &c);
size_t base = (xi + (yi * w)) * 4;
buf[base + 0] = c.red / 256;
buf[base + 1] = c.green / 256;
buf[base + 2] = c.blue / 256;
buf[base + 3] = 255;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
free(buf);
} else {
/* Use image data directly. The alpha value is garbage/unassigned data, but
we need to read it because X11 keeps pixel data aligned */
buf = (uint8_t*) image->data;
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth)
already ensures reads will be properly aligned across scanlines */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLenum format = image->bitmap_bit_order == LSBFirst ?
(!aligned ? GL_BGRA : GL_BGR) :
(!aligned ? GL_RGBA : GL_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
}
}
if (use_shm) {
XShmDetach(d, &shminfo);
shmdt(shminfo.shmaddr);
shmctl(shminfo.shmid, IPC_RMID, NULL);
}
XShmDetach(d, &shminfo);
shmdt(shminfo.shmaddr);
shmctl(shminfo.shmid, IPC_RMID, NULL);
XDestroyImage(image);
if (image) XDestroyImage(image);
return texture;
}

6
xwin.h
View File

@@ -1,5 +1,5 @@
bool xwin_should_render(void);
void xwin_settype(struct renderer* rd, const char* type);
void xwin_addstate(struct renderer* rd, const char* state);
bool xwin_should_render(struct renderer* rd);
void xwin_settype(struct gl_wcb* wcb, void* impl, const char* type);
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* state);
unsigned int xwin_copyglbg(struct renderer* rd, unsigned int texture);