Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
273cca946e | ||
|
|
95ceadf0e1 | ||
|
|
f32f0ded0f | ||
|
|
3a1dcac8c9 | ||
|
|
8adc8fe459 | ||
|
|
e2f32a14cf | ||
|
|
318b4b3395 | ||
|
|
b89fcd2e44 | ||
|
|
baed3086d0 | ||
|
|
b4fe4b5c97 | ||
|
|
0849cb485e | ||
|
|
c086222fb2 | ||
|
|
639c6d7fe9 | ||
|
|
22bc7c446c | ||
|
|
b86870e0fd | ||
|
|
f021457abd | ||
|
|
4fd4ce4c3f | ||
|
|
e5f5671acc | ||
|
|
79d06b070e | ||
|
|
3df89da8bb | ||
|
|
79b99b09be | ||
|
|
0f4eed5e20 | ||
|
|
ae09561d11 | ||
|
|
33aa5274ae | ||
|
|
caee6a739c |
@@ -1,4 +1,10 @@
|
||||
## 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
|
||||
|
||||
|
||||
16
Makefile
16
Makefile
@@ -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
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -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,10 +27,11 @@ 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 and run GLava:
|
||||
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
|
||||
```bash
|
||||
sudo apt-get install libpulse libpulse-dev libglfw3 libglfw3-dev libxext6 libxext6-dev python make gcc
|
||||
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libglx0 libxext6 libxext-dev python make gcc
|
||||
```
|
||||
|
||||
## [Configuration](https://github.com/wacossusca34/glava/wiki)
|
||||
@@ -43,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) |  | No notable issues
|
||||
| Mutter (GNOME, Budgie) |  | `"native"` (default) opacity should be used
|
||||
| Openbox (LXDE or standalone) |  | [Some tweaks may be required](https://www.reddit.com/r/unixporn/comments/7vcgi4/oc_after_receiving_positive_feedback_here_i/dtrkvja/)
|
||||
| Xfwm (XFCE) |  | Untested, but should work without issues
|
||||
| Fluxbox |  | Untested, but should work without issues
|
||||
| iceWM |  | No notable issues
|
||||
| AwesomeWM |  | 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 |  | `"xroot"` transparency breaks with Budgie's wallpaper window
|
||||
| Herbstluftwm |  | `hc rule windowtype~'_NET_WM_WINDOW_TYPE_DESKTOP' manage=off` can be used to unmanage desktop windows
|
||||
| AwesomeWM |  | Can still be focused, may require other changes to config depending on layout
|
||||
| kwin (KDE) |  | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing
|
||||
| i3 (and i3-gaps) |  | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6)
|
||||
| EXWM |  | EXWM does not have a desktop, and forces window decorations
|
||||
| Unity |  | Needs testing
|
||||
| Enlightenment |  | Needs testing
|
||||
| Bspwm |  | Needs testing
|
||||
| Herbstluftwm |  | Needs testing
|
||||
| xmonad |  | Needs testing
|
||||
| Any non EWMH-compliant WM |  | Window types and hints will not work if the window manager does not support the EWMH standards.
|
||||
|
||||
|
||||
14
glava.c
14
glava.c
@@ -18,7 +18,7 @@
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
#define GLAVA_VERSION "1.3"
|
||||
#define GLAVA_VERSION "1.4.1"
|
||||
#ifdef GLAD_DEBUG
|
||||
#define GLAVA_RELEASE_TYPE_PREFIX "debug, "
|
||||
#else
|
||||
@@ -167,18 +167,22 @@ 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_VERSION_STRING "\n"
|
||||
" -- Copyright (C) 2017 Levi Webb\n";
|
||||
|
||||
static const char* opt_str = "hvVe: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 }
|
||||
};
|
||||
@@ -190,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;
|
||||
@@ -201,6 +206,7 @@ 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);
|
||||
@@ -219,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;
|
||||
@@ -274,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
138
glfw_wcb.c
Normal 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 */
|
||||
278
glx_wcb.c
Normal file
278
glx_wcb.c
Normal 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 */
|
||||
335
render.c
335
render.c
@@ -14,15 +14,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
/* Fixes for old GLFW versions */
|
||||
#ifndef GLFW_TRUE
|
||||
#define GLFW_TRUE GL_TRUE
|
||||
#endif
|
||||
#ifndef GLFW_FALSE
|
||||
#define GLFW_FALSE GL_FALSE
|
||||
#endif
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
@@ -44,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 {
|
||||
@@ -83,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;
|
||||
@@ -100,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;
|
||||
@@ -117,6 +120,8 @@ struct gl_data {
|
||||
int geometry[4];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* load shader file */
|
||||
static GLuint shaderload(const char* rpath,
|
||||
GLenum type,
|
||||
@@ -160,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,
|
||||
@@ -175,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;
|
||||
@@ -320,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
|
||||
@@ -676,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) {
|
||||
@@ -690,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");
|
||||
@@ -729,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);
|
||||
@@ -746,15 +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 STUB(request, f) \
|
||||
{ .name = request, .fmt = f, \
|
||||
.handler = RHANDLER(name, args, { \
|
||||
fprintf(stderr, "warning: '%s' request is not implemented for this build\n", \
|
||||
request); }) }
|
||||
#define WINDOW_HINT(request) \
|
||||
{ .name = "set" #request, .fmt = "b", \
|
||||
.handler = RHANDLER(name, args, { gl->wcb->set_##request(*(bool*) args[0]); }) }
|
||||
|
||||
struct request_handler handlers[] = {
|
||||
{
|
||||
@@ -763,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);
|
||||
if (native_opacity) gl->use_alpha = false;
|
||||
#elif GLFW_TRANSPARENT
|
||||
glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE);
|
||||
if (native_opacity) 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;
|
||||
@@ -823,19 +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),
|
||||
#ifdef GLFW_MAXIMIZED
|
||||
WINDOW_HINT("setmaximized", GLFW_MAXIMIZED),
|
||||
#else
|
||||
STUB("setmaximized", "b"),
|
||||
#endif
|
||||
{
|
||||
.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];
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -865,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",
|
||||
@@ -1035,26 +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();
|
||||
}
|
||||
|
||||
if (xwintype) {
|
||||
xwin_settype(r, xwintype);
|
||||
free(xwintype);
|
||||
}
|
||||
|
||||
for (size_t t = 0; t < xwinstates_sz; ++t) {
|
||||
xwin_addstate(r, xwinstates[t]);
|
||||
}
|
||||
free(xwinstates);
|
||||
|
||||
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
|
||||
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
|
||||
|
||||
glfwMakeContextCurrent(gl->w);
|
||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
if (xwintype) free(xwintype);
|
||||
if (xwinstates) free(xwinstates);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
@@ -1063,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;
|
||||
@@ -1125,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;
|
||||
@@ -1144,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);
|
||||
}
|
||||
|
||||
@@ -1167,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 */
|
||||
|
||||
@@ -1177,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;
|
||||
|
||||
@@ -1217,7 +1259,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a);
|
||||
|
||||
glfwShowWindow(gl->w);
|
||||
gl->wcb->set_visible(gl->w, true);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1225,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;
|
||||
|
||||
@@ -1287,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) {
|
||||
@@ -1317,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)
|
||||
@@ -1339,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) {
|
||||
@@ -1361,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);
|
||||
}
|
||||
|
||||
@@ -1381,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 */
|
||||
@@ -1396,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);
|
||||
@@ -1441,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;
|
||||
@@ -1462,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);
|
||||
@@ -1478,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);
|
||||
}
|
||||
|
||||
@@ -1527,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) {
|
||||
@@ -1566,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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1575,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);
|
||||
}
|
||||
|
||||
75
render.h
75
render.h
@@ -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 */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* center radius (pixels) */
|
||||
#define C_RADIUS 128
|
||||
/* center line thickness (pixels) */
|
||||
#define C_LINE 2
|
||||
#define C_LINE 1.5
|
||||
/* outline color */
|
||||
#define OUTLINE #333333
|
||||
/* Amplify magnitude of the results each bar displays */
|
||||
|
||||
1
shaders/circle/3.frag
Normal file
1
shaders/circle/3.frag
Normal file
@@ -0,0 +1 @@
|
||||
#include ":util/premultiply.frag"
|
||||
@@ -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
1
shaders/radial/2.frag
Normal file
@@ -0,0 +1 @@
|
||||
#include ":util/premultiply.frag"
|
||||
@@ -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
|
||||
|
||||
15
shaders/util/premultiply.frag
Normal file
15
shaders/util/premultiply.frag
Normal 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
|
||||
}
|
||||
264
xwin.c
264
xwin.c
@@ -12,27 +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
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
/* 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>
|
||||
|
||||
#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;
|
||||
@@ -50,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);
|
||||
@@ -76,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];
|
||||
@@ -95,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;
|
||||
@@ -112,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;
|
||||
@@ -133,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
6
xwin.h
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user