16 Commits
v1.3 ... v1.4

Author SHA1 Message Date
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
16 changed files with 896 additions and 291 deletions

View File

@@ -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

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

@@ -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,8 +27,9 @@ 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
```
@@ -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) | ![-](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.

14
glava.c
View File

@@ -18,7 +18,7 @@
#include "render.h"
#include "xwin.h"
#define GLAVA_VERSION "1.3"
#define GLAVA_VERSION "1.4"
#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
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 */

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 */

316
render.c
View File

@@ -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;
@@ -676,7 +682,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 +697,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 +780,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 +793,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 +804,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 +854,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 +903,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 +1073,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 +1091,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 +1155,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 +1175,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 +1198,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 +1225,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 +1261,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 +1269,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 +1331,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) {
@@ -1321,6 +1365,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Current shader program */
struct gl_sfbo* current = &gl->stages[t];
if (current->nativeonly && !gl->premultiply_alpha)
continue;
/* Bind framebuffer if this is not the final pass */
if (current->valid)
@@ -1339,8 +1386,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 +1408,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);
}
@@ -1441,7 +1488,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 +1510,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);
@@ -1527,10 +1575,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 +1613,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 +1623,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

@@ -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
View File

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

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
}

264
xwin.c
View File

@@ -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
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);