added transparency, cleanup

This commit is contained in:
Jarcode
2018-01-07 18:35:40 -08:00
parent 847f26c5fb
commit 2e0d0921c5
7 changed files with 304 additions and 81 deletions

View File

@@ -42,7 +42,7 @@ ifeq ($(INSTALL),osx)
SHADER_DIR = /Library/glava
endif
LDFLAGS = -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11
LDFLAGS = -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext
PYTHON = python

View File

@@ -15,13 +15,11 @@ $ glava
You can pass `BUILD=debug` to the makefile for debug builds of both glad and glava, and you can manually specify install targets with `INSTALL=...`, possible arguments are `unix` for FHS compliant Linux and BSD distros, `osx` for Mac OSX, and `standalone` which allows you to run GLava in the build directory.
**Note:** GLFW's most recent stable version is 3.2, whereas 3.3 is needed in order to support transparency (older versions still work, just without transparency). You can either find a more recent build or compile it yourself. Arch users can install `glfw-x11-git` from the AUR.
**Requirements:**
- X11
- PulseAudio
- GLFW (version 3.3+ needed for transparency support)
- GLFW
- Linux or BSD
**Additional compile time requirements:**
@@ -40,6 +38,8 @@ 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.
## To-Do
**What needs to be done:**
@@ -55,10 +55,7 @@ To embed GLava in your desktop (for EWMH compliant window managers), use `#reque
- Detecting if the window manager reports the currently focused window as fullscreen (and halting rendering for the duration)
- Fixed a memory corruption bug that crashed the `nvidia` driver on Linux
- Fix breaks in audio spectrum read from the PulseAudio server
**What will never be done:**
- Port to Windows (???)
- Transparency support using either the compositor or the root X window's pixmap
## Licensing
@@ -80,3 +77,7 @@ The below copyright applies for the modifications to the files listed above, and
`Copyright (c) 2017 Levi Webb`
**Why did you relicense this software?** Because my views align with the FSF's position on free software -- I see the MIT license as harmful to free software as it fails to preserve modifications of the source code as open source.
## Porting
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken all the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.

156
render.c
View File

@@ -355,14 +355,14 @@ static void drawoverlay(const struct overlay_data* d) {
struct gl_data {
struct gl_sfbo* stages;
struct overlay_data overlay;
GLuint audio_tex_r, audio_tex_l;
GLuint audio_tex_r, audio_tex_l, bg_tex;
size_t stages_sz, bufscale, avg_frames;
GLFWwindow* w;
int lww, lwh; /* last window height */
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;
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop;
void** t_data;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio;
float* interpolate_buf[6];
@@ -660,6 +660,7 @@ 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,
@@ -676,7 +677,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
.interpolate = true,
.force_geometry = false,
.smooth_distance = 0.01,
.smooth_ratio = 4
.smooth_ratio = 4,
.bg_tex = 0,
.copy_desktop = true,
.geometry = { 0, 0, 500, 400 }
};
#ifdef GLAD_DEBUG
@@ -696,39 +700,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
#elif GLFW_TRANSPARENT
glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE);
#else
printf("WARNING: the linked version of GLFW3 does not have transparency support"
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
#endif
if (!(gl->w = glfwCreateWindow(500, 400, "GLava", NULL, NULL))) {
glfwTerminate();
abort();
}
glfwMakeContextCurrent(gl->w);
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
glDisable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_CLAMP);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
int shader_version = 330;
const char* module = force_mod;
char* xwintype = NULL;
char* xwintype = NULL, * wintitle = "GLava";
bool loading_module = true;
struct gl_sfbo* current = NULL;
size_t t_count = 0;
@@ -739,9 +713,30 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct request_handler handlers[] = {
{
.name = "wavetype", .fmt = "s",
.name = "setopacity", .fmt = "s",
.handler = RHANDLER(name, args, {
printf("[STUB] wavetype = '%s'\n", (char*) args[0]);
bool native_opacity = !strcmp("native", (char*) args[0]);
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE);
#elif GLFW_TRANSPARENT
glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE);
#else
if (native_opacity)
printf("WARNING: the linked version of GLFW3 does not have transparency support"
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
#endif
if (!strcmp("xroot", (char*) args[0]))
gl->copy_desktop = true;
else
gl->copy_desktop = false;
if (!gl->copy_desktop && !native_opacity && strcmp("none", (char*) args[0])) {
fprintf(stderr, "Invalid opacity option: '%s'\n", (char*) args[0]);
exit(EXIT_FAILURE);
}
})
},
{
@@ -773,8 +768,6 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
gl->geometry[1] = *(int*) args[1];
gl->geometry[2] = *(int*) args[2];
gl->geometry[3] = *(int*) args[3];
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
})
},
{ .name = "setsource", .fmt = "s",
@@ -793,7 +786,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
{ .name = "setprintframes", .fmt = "b",
.handler = RHANDLER(name, args, { gl->print_fps = *(bool*) args[0]; }) },
{ .name = "settitle", .fmt = "s",
.handler = RHANDLER(name, args, { glfwSetWindowTitle(gl->w, (char*) args[0]); }) },
.handler = RHANDLER(name, args, { wintitle = strdup((char*) args[0]); }) },
{ .name = "setbufsize", .fmt = "i",
.handler = RHANDLER(name, args, { r->bufsize_request = *(int*) args[0]; }) },
{ .name = "setbufscale", .fmt = "i",
@@ -938,6 +931,29 @@ 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();
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);
glDisable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_CLAMP);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
size_t m_len = strlen(module);
size_t bsz = d_len + m_len + 2;
char shaders[bsz]; /* module pack path to use */
@@ -1146,19 +1162,28 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
irb[t] = irbs + ((irbe - irbs) * mod);
}
}
int ww, wh, wx, wy;
glfwGetFramebufferSize(gl->w, &ww, &wh);
glfwGetWindowPos(gl->w, &wx, &wy);
/* Resize screen textures if needed */
int ww, wh;
glfwGetFramebufferSize(gl->w, &ww, &wh);
if (ww != gl->lww || wh != gl->lwh) {
for (t = 0; t < gl->stages_sz; ++t) {
if (gl->stages[t].valid) {
setup_sfbo(&gl->stages[t], ww, wh);
}
}
}
/* Resize and grab new background data if needed */
if (gl->copy_desktop && (ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
gl->bg_tex = xwin_copyglbg(r, gl->bg_tex);
}
gl->lwx = wx;
gl->lwy = wy;
gl->lww = ww;
gl->lwh = wh;
}
glViewport(0, 0, ww, wh);
@@ -1170,9 +1195,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
bool needed[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
/* Select the program associated with this pass */
/* Current shader program */
struct gl_sfbo* current = &gl->stages[t];
glUseProgram(current->shader);
/* Bind framebuffer if this is not the final pass */
if (current->valid)
@@ -1180,6 +1204,48 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
glClear(GL_COLOR_BUFFER_BIT);
if (!current->valid && gl->copy_desktop) {
/* Self-contained code for drawing background image */
static GLuint bg_prog, bg_utex, bg_screen;
static bool setup = false;
/* Shader to flip texture and override alpha channel */
static const char* frag_shader =
"uniform sampler2D tex;" "\n"
"uniform ivec2 screen;" "\n"
"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.a = 1.0F;" "\n"
"}"; "\n";
if (!setup) {
bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC,
NULL, NULL, 330, true),
shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader,
NULL, NULL, 330, true));
bg_utex = glGetUniformLocation(bg_prog, "tex");
bg_screen = glGetUniformLocation(bg_prog, "screen");
glBindFragDataLocation(bg_prog, 1, "fragment");
setup = true;
}
glUseProgram(bg_prog);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gl->bg_tex);
glUniform2i(bg_screen, (GLint) ww, (GLint) wh);
glUniform1i(bg_utex, 0);
/* 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);
drawoverlay(&gl->overlay);
glEnable(GL_BLEND);
glUseProgram(0);
}
/* Select the program associated with this pass */
glUseProgram(current->shader);
bool prev_bound = false;
/* Iterate through each uniform binding, transforming and passing the

View File

@@ -21,17 +21,38 @@
#request setfocused true
#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. */
#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.
"xroot" - Maintain a copy of the root window's pixmap
(usually the desktop background) to provide a
pseudo-transparent effect. Useful when no compositor
is available or native transparency isn't nessecary.
Has very little performance impact.
"none" - Disable window opacity completely. */
#request setopacity "xroot"
/* 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 */
#request settitle "GLava"
/* GLFW window geometry (x, y, width, height) */
#request setgeometry 0 0 400 600
/* 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. */
#request setforcegeometry false
/* (X11 only) EWMH Window type. Possible values are:
"desktop", "dock", "toolbar", "menu",
@@ -139,8 +160,3 @@
the buffer. It is reccommended to use `setsamplerate` and
`setsamplesize` to improve performance or accuracy instead. */
#request setbufscale 1
/* OpenGL context and GLSL shader versions, do not change unless
you _absolutely_ know what you are doing. */
#request setversion 3 3
#request setshaderversion 330

139
xwin.c
View File

@@ -7,10 +7,15 @@
#include <string.h>
#include <limits.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#define GLFW_EXPOSE_NATIVE_X11
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
@@ -77,3 +82,137 @@ void xwin_settype(struct renderer* rd, const char* type) {
XChangeProperty(d, w, wtype, XA_ATOM, 32, PropModeReplace, (unsigned char*) &desk, 1);
XCloseDisplay(d);
}
static Pixmap get_pixmap(Display* d, Window w) {
Pixmap p;
Atom act_type;
int act_format;
unsigned long nitems, bytes_after;
unsigned char *data = NULL;
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);
}
}
return p;
}
unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
GLuint texture = (GLuint) tex;
if (!texture)
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
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);
int x, y, w, h;
glfwGetFramebufferSize(gwin, &w, &h);
glfwGetWindowPos(gwin, &x, &y);
XColor c;
Display* d = XOpenDisplay(0);
Pixmap p = get_pixmap(d, RootWindow(d, DefaultScreen(d)));
/* Obtain section of root pixmap using XShm */
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);
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777);
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;
}
}
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 = 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, GL_UNSIGNED_BYTE, buf);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
}
XFree(image);
XCloseDisplay(d);
return texture;
}

1
xwin.h
View File

@@ -1,3 +1,4 @@
bool xwin_should_render(void);
void xwin_settype(struct renderer* rd, const char* type);
unsigned int xwin_copyglbg(struct renderer* rd, unsigned int texture);