Merge branch 'unstable'

This commit is contained in:
Jarcode
2018-02-12 16:41:18 -08:00
15 changed files with 887 additions and 288 deletions

View File

@@ -4,7 +4,7 @@ obj = $(src:.c=.o)
# Build type parameter # Build type parameter
ifeq ($(BUILD),debug) 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 GLAD_GEN = c-debug
ASAN = -lasan ASAN = -lasan
else else
@@ -38,12 +38,22 @@ ifeq ($(INSTALL),unix)
endif endif
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) ifeq ($(INSTALL),osx)
CFLAGS_INSTALL = -DGLAVA_OSX CFLAGS_INSTALL = -DGLAVA_OSX
SHADER_DIR = Library/glava SHADER_DIR = Library/glava
endif 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 PYTHON = python
@@ -51,7 +61,7 @@ GLAD_INSTALL_DIR = glad
GLAD_SRCFILE = ./glad/src/glad.c GLAD_SRCFILE = ./glad/src/glad.c
GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
CFLAGS_COMMON = -I glad/include 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 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 - X11
- PulseAudio - PulseAudio
- GLFW 3.1+ - GLFW 3.1+ (optional, disable with `DISABLE_GLFW=1`)
- Linux or BSD - Linux or BSD
**Additional compile time requirements:** **Additional compile time requirements:**
@@ -27,6 +27,7 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
- glad (included as a submodule) - glad (included as a submodule)
- python (required to generate bindings with glad) - python (required to generate bindings with glad)
- GCC (this program uses GNU C features) - GCC (this program uses GNU C features)
- GLX headers (optional, disable direct GLX support with `DISABLE_GLX=1`), usually the development packages for `libgl` include this on your distro
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava: **Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
```bash ```bash
@@ -43,22 +44,19 @@ 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. \* 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 ## 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: 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 | WM | ! | Details
| :---: | --- | --- | | :---: | --- | --- |
| 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/) | 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 | 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 | 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 | iceWM | ![-](https://placehold.it/15/118932/000000?text=+) | No notable issues
| 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 | 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
| GNOME (on X11) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [Some issues with `"xroot"` reported](https://github.com/wacossusca34/glava/issues/18)
| AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | Can still be focused, may require other changes to config depending on layout | AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | 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
| kwin (KDE) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing | 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) | 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 | EXWM | ![-](https://placehold.it/15/f03c15/000000?text=+) | EXWM does not have a desktop, and forces window decorations

14
glava.c
View File

@@ -18,7 +18,7 @@
#include "render.h" #include "render.h"
#include "xwin.h" #include "xwin.h"
#define GLAVA_VERSION "1.3" #define GLAVA_VERSION "1.4"
#ifdef GLAD_DEBUG #ifdef GLAD_DEBUG
#define GLAVA_RELEASE_TYPE_PREFIX "debug, " #define GLAVA_RELEASE_TYPE_PREFIX "debug, "
#else #else
@@ -167,18 +167,22 @@ static const char* help_str =
"-C, --copy-config creates copies and symbolic links in the user configuration\n" "-C, --copy-config creates copies and symbolic links in the user configuration\n"
" directory for glava, copying any files in the root directory\n" " directory for glava, copying any files in the root directory\n"
" of the installed shader directory, and linking any modules.\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" "-V, --version print application version and exit\n"
"\n" "\n"
GLAVA_VERSION_STRING "\n" GLAVA_VERSION_STRING "\n"
" -- Copyright (C) 2017 Levi Webb\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[] = { static struct option p_opts[] = {
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'}, {"verbose", no_argument, 0, 'v'},
{"entry", required_argument, 0, 'e'}, {"entry", required_argument, 0, 'e'},
{"force-mod", required_argument, 0, 'm'}, {"force-mod", required_argument, 0, 'm'},
{"copy-config", no_argument, 0, 'C'}, {"copy-config", no_argument, 0, 'C'},
{"backend", required_argument, 0, 'b'},
{"version", no_argument, 0, 'V'}, {"version", no_argument, 0, 'V'},
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
@@ -190,6 +194,7 @@ int main(int argc, char** argv) {
const char* user_path = SHADER_USER_PATH; const char* user_path = SHADER_USER_PATH;
const char* entry = "rc.glsl"; const char* entry = "rc.glsl";
const char* force = NULL; const char* force = NULL;
const char* backend = NULL;
const char* system_shader_paths[] = { user_path, install_path, NULL }; const char* system_shader_paths[] = { user_path, install_path, NULL };
bool verbose = false; bool verbose = false;
bool copy_mode = false; bool copy_mode = false;
@@ -201,6 +206,7 @@ int main(int argc, char** argv) {
case 'C': copy_mode = true; break; case 'C': copy_mode = true; break;
case 'e': entry = optarg; break; case 'e': entry = optarg; break;
case 'm': force = optarg; break; case 'm': force = optarg; break;
case 'b': backend = optarg; break;
case '?': exit(EXIT_FAILURE); break; case '?': exit(EXIT_FAILURE); break;
case 'V': case 'V':
puts(GLAVA_VERSION_STRING); puts(GLAVA_VERSION_STRING);
@@ -219,7 +225,7 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS); 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]; float b0[r->bufsize_request], b1[r->bufsize_request];
size_t t; size_t t;
@@ -274,7 +280,7 @@ int main(int argc, char** argv) {
pthread_mutex_unlock(&audio.mutex); pthread_mutex_unlock(&audio.mutex);
/* Only render if needed (ie. stop rendering when fullscreen windows are focused) */ /* 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); rd_update(r, lb, rb, r->bufsize_request, modified);
} else { } else {
/* Sleep for 50ms and then attempt to render again */ /* 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 */

314
render.c
View File

@@ -14,15 +14,6 @@
#include <unistd.h> #include <unistd.h>
#include <glad/glad.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 "render.h"
#include "xwin.h" #include "xwin.h"
@@ -44,6 +35,17 @@
#define VERTEX_SHADER_SRC \ #define VERTEX_SHADER_SRC \
"layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }" "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 */ /* GLSL bind source */
struct gl_bind_src { struct gl_bind_src {
@@ -83,7 +85,7 @@ struct gl_bind {
struct gl_sfbo { struct gl_sfbo {
GLuint fbo, tex, shader; GLuint fbo, tex, shader;
bool valid; bool valid, nativeonly;
const char* name; const char* name;
struct gl_bind* binds; struct gl_bind* binds;
size_t binds_sz; size_t binds_sz;
@@ -100,13 +102,14 @@ struct gl_data {
struct overlay_data overlay; struct overlay_data overlay;
GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog; GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog;
size_t stages_sz, bufscale, avg_frames; size_t stages_sz, bufscale, avg_frames;
GLFWwindow* w; void* w;
struct gl_wcb* wcb;
int lww, lwh, lwx, lwy; /* last window dimensions */ int lww, lwh, lwx, lwy; /* last window dimensions */
int rate; /* framerate */ int rate; /* framerate */
double tcounter; double tcounter;
int fcounter, ucounter, kcounter; int fcounter, ucounter, kcounter;
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop, bool print_fps, avg_window, interpolate, force_geometry, copy_desktop,
smooth_pass, use_alpha; smooth_pass, premultiply_alpha;
void** t_data; void** t_data;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio, float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
smooth_factor, fft_scale, fft_cutoff; smooth_factor, fft_scale, fft_cutoff;
@@ -117,6 +120,8 @@ struct gl_data {
int geometry[4]; int geometry[4];
}; };
/* load shader file */ /* load shader file */
static GLuint shaderload(const char* rpath, static GLuint shaderload(const char* rpath,
GLenum type, GLenum type,
@@ -160,7 +165,8 @@ static GLuint shaderload(const char* rpath,
"#define UNIFORM_LIMIT %d\n" "#define UNIFORM_LIMIT %d\n"
"#define PRE_SMOOTHED_AUDIO %d\n" "#define PRE_SMOOTHED_AUDIO %d\n"
"#define SMOOTH_FACTOR %.6f\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 = { struct glsl_ext ext = {
.source = raw ? NULL : map, .source = raw ? NULL : map,
@@ -175,11 +181,11 @@ static GLuint shaderload(const char* rpath,
/* If this is raw input, skip processing */ /* If this is raw input, skip processing */
if (!raw) ext_process(&ext, rpath); 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); GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms, int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms,
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor, gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor,
gl->use_alpha ? 1 : 0); 1, gl->premultiply_alpha ? 1 : 0);
if (written < 0) { if (written < 0) {
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path); fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
return 0; return 0;
@@ -676,7 +682,8 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
return NULL; 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)); renderer* r = malloc(sizeof(struct renderer));
*r = (struct renderer) { *r = (struct renderer) {
@@ -690,36 +697,80 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
struct gl_data* gl = r->gl; struct gl_data* gl = r->gl;
*gl = (struct gl_data) { *gl = (struct gl_data) {
.w = NULL, .w = NULL,
.stages = NULL, .wcb = NULL,
.rate = 0, .stages = NULL,
.tcounter = 0.0D, .rate = 0,
.fcounter = 0, .tcounter = 0.0D,
.ucounter = 0, .fcounter = 0,
.kcounter = 0, .ucounter = 0,
.fr = 1.0F, .kcounter = 0,
.ur = 1.0F, .fr = 1.0F,
.print_fps = true, .ur = 1.0F,
.bufscale = 1, .print_fps = true,
.avg_frames = 6, .bufscale = 1,
.avg_window = true, .avg_frames = 6,
.gravity_step = 4.2, .avg_window = true,
.interpolate = true, .gravity_step = 4.2,
.force_geometry = false, .interpolate = true,
.smooth_factor = 0.025, .force_geometry = false,
.smooth_distance = 0.01, .smooth_factor = 0.025,
.smooth_ratio = 4, .smooth_distance = 0.01,
.bg_tex = 0, .smooth_ratio = 4,
.sm_prog = 0, .bg_tex = 0,
.copy_desktop = true, .sm_prog = 0,
.use_alpha = true, .copy_desktop = true,
.smooth_pass = true, .premultiply_alpha = true,
.fft_scale = 10.2F, .smooth_pass = true,
.fft_cutoff = 0.3F, .fft_scale = 10.2F,
.geometry = { 0, 0, 500, 400 }, .fft_cutoff = 0.3F,
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F } .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 #ifdef GLAD_DEBUG
printf("Assigning debug callback\n"); printf("Assigning debug callback\n");
static bool assigned_debug_cb = false; static bool assigned_debug_cb = false;
@@ -729,15 +780,11 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
} }
#endif #endif
if (!glfwInit()) gl->wcb->init();
abort();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); int shader_version = 330,
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); context_version_major = 3,
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); context_version_minor = 3;
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
int shader_version = 330;
const char* module = force_mod; const char* module = force_mod;
char* xwintype = NULL, * wintitle = "GLava"; char* xwintype = NULL, * wintitle = "GLava";
char** xwinstates = malloc(1); 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; struct gl_sfbo* current = NULL;
size_t t_count = 0; size_t t_count = 0;
#define WINDOW_HINT(request, attr) \ #define WINDOW_HINT(request) \
{ .name = request, .fmt = "b", \ { .name = "set" #request, .fmt = "b", \
.handler = RHANDLER(name, args, { glfwWindowHint(attr, *(bool*) args[0]); }) } .handler = RHANDLER(name, args, { gl->wcb->set_##request(*(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); }) }
struct request_handler handlers[] = { 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]); bool native_opacity = !strcmp("native", (char*) args[0]);
gl->use_alpha = true; gl->premultiply_alpha = native_opacity;
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER gl->wcb->set_transparent(native_opacity);
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
if (!strcmp("xroot", (char*) args[0])) if (!strcmp("xroot", (char*) args[0]))
gl->copy_desktop = true; 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), .name = "nativeonly", .fmt = "b",
WINDOW_HINT("setfocused", GLFW_FOCUSED), .handler = RHANDLER(name, args, {
#ifdef GLFW_MAXIMIZED if (current)
WINDOW_HINT("setmaximized", GLFW_MAXIMIZED), current->nativeonly = *(bool*) args[0];
#else else {
STUB("setmaximized", "b"), fprintf(stderr, "`nativeonly` request needs module context\n");
#endif exit(EXIT_FAILURE);
}
})
},
WINDOW_HINT(floating),
WINDOW_HINT(decorated),
WINDOW_HINT(focused),
WINDOW_HINT(maximized),
{ {
.name = "setversion", .fmt = "ii", .name = "setversion", .fmt = "ii",
.handler = RHANDLER(name, args, { .handler = RHANDLER(name, args, {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, *(int*) args[0]); context_version_major = *(int*) args[0];
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, *(int*) args[1]); 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", { .name = "setshaderversion", .fmt = "i",
.handler = RHANDLER(name, args, { shader_version = *(int*) args[0]; }) }, .handler = RHANDLER(name, args, { shader_version = *(int*) args[0]; }) },
{ .name = "setswap", .fmt = "i", { .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", { .name = "setframerate", .fmt = "i",
.handler = RHANDLER(name, args, { gl->rate = *(int*) args[0]; }) }, .handler = RHANDLER(name, args, { gl->rate = *(int*) args[0]; }) },
{ .name = "setprintframes", .fmt = "b", { .name = "setprintframes", .fmt = "b",
@@ -1035,26 +1073,16 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!(gl->w = glfwCreateWindow(500, 400, wintitle, NULL, NULL))) { if (!(gl->w = gl->wcb->create_and_bind(wintitle, "GLava", xwintype,
glfwTerminate(); (const char**) xwinstates, xwinstates_sz,
gl->geometry[2], gl->geometry[3],
gl->geometry[0], gl->geometry[1],
context_version_major, context_version_minor))) {
abort(); abort();
} }
if (xwintype) { if (xwintype) free(xwintype);
xwin_settype(r, xwintype); if (xwinstates) free(xwinstates);
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);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_CLAMP); 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_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH); glDisable(GL_LINE_SMOOTH);
glEnable(GL_BLEND); if (!gl->premultiply_alpha) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
size_t m_len = strlen(module); size_t m_len = strlen(module);
size_t bsz = d_len + m_len + 2; 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]; struct gl_sfbo* s = &stages[idx - 1];
*s = (struct gl_sfbo) { *s = (struct gl_sfbo) {
.name = strdup(d->d_name), .name = strdup(d->d_name),
.shader = 0, .shader = 0,
.valid = false, .valid = false,
.binds = malloc(1), .nativeonly = false,
.binds_sz = 0 .binds = malloc(1),
.binds_sz = 0
}; };
current = s; current = s;
@@ -1144,7 +1175,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
as it can rendered directly */ as it can rendered directly */
if (idx != count) { if (idx != count) {
int w, h; int w, h;
glfwGetFramebufferSize(gl->w, &w, &h); gl->wcb->get_fbsize(gl->w, &w, &h);
setup_sfbo(&stages[idx - 1], w, h); setup_sfbo(&stages[idx - 1], w, h);
} }
@@ -1168,6 +1199,23 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
} }
} }
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 */ /* 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 */ char util[usz]; /* module pack path to use */
snprintf(util, usz, "%s/%s", data, util_folder); snprintf(util, usz, "%s/%s", data, util_folder);
loading_smooth_pass = true; 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(); abort();
}
loading_smooth_pass = false; loading_smooth_pass = false;
} }
gl->stages = stages;
gl->stages_sz = count;
/* target seconds per update */ /* target seconds per update */
gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request; 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); 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; 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) { void rd_time(struct renderer* r) {
struct gl_data* gl = r->gl; 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) { void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modified) {
struct gl_data* gl = r->gl; struct gl_data* gl = r->gl;
size_t t, a, fbsz = bsz * sizeof(float); size_t t, a, fbsz = bsz * sizeof(float);
r->alive = !glfwWindowShouldClose(gl->w); r->alive = !gl->wcb->should_close(gl->w);
if (!r->alive) if (!r->alive)
return; 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; int ww, wh, wx, wy;
glfwGetFramebufferSize(gl->w, &ww, &wh); gl->wcb->get_fbsize(gl->w, &ww, &wh);
glfwGetWindowPos(gl->w, &wx, &wy); gl->wcb->get_pos(gl->w, &wx, &wy);
/* Resize screen textures if needed */ /* Resize screen textures if needed */
if (ww != gl->lww || wh != gl->lwh) { if (ww != gl->lww || wh != gl->lwh) {
@@ -1322,6 +1366,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Current shader program */ /* Current shader program */
struct gl_sfbo* current = &gl->stages[t]; struct gl_sfbo* current = &gl->stages[t];
if (current->nativeonly && !gl->premultiply_alpha)
continue;
/* Bind framebuffer if this is not the final pass */ /* Bind framebuffer if this is not the final pass */
if (current->valid) if (current->valid)
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
@@ -1339,8 +1386,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
"out vec4 fragment;" "\n" "out vec4 fragment;" "\n"
"in vec4 gl_FragCoord;" "\n" "in vec4 gl_FragCoord;" "\n"
"void main() {" "\n" "void main() {" "\n"
" fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, " "\n" " fragment = texelFetch(tex, ivec2(gl_FragCoord.x, " "\n"
" (screen.y - gl_FragCoord.y) / screen.y));" "\n" " screen.y - gl_FragCoord.y), 0);" "\n"
" fragment.a = 1.0F;" "\n" " fragment.a = 1.0F;" "\n"
"}" "\n"; "}" "\n";
if (!setup) { 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 /* 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_`, to how we obtain the background texture (format is four byte `rgb_`,
where the last value is skipped) */ where the last value is skipped) */
glDisable(GL_BLEND); if (!gl->premultiply_alpha) glDisable(GL_BLEND);
drawoverlay(&gl->overlay); drawoverlay(&gl->overlay);
glEnable(GL_BLEND); if (!gl->premultiply_alpha) glEnable(GL_BLEND);
glUseProgram(0); 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 */ /* setup and bind framebuffer to texture */
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo); 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)) { switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
case GL_FRAMEBUFFER_COMPLETE: break; 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_uw, sz); /* target texture width */
glUniform1i(sm_usz, sz); /* source texture width */ glUniform1i(sm_usz, sz); /* source texture width */
glUniform1i(sm_utex, offset); glUniform1i(sm_utex, offset);
glDisable(GL_BLEND); if (!gl->premultiply_alpha) glDisable(GL_BLEND);
glViewport(0, 0, sz, 1); glViewport(0, 0, sz, 1);
drawoverlay(&gl->overlay); drawoverlay(&gl->overlay);
glViewport(0, 0, ww, wh); glViewport(0, 0, ww, wh);
glEnable(GL_BLEND); if (!gl->premultiply_alpha) glEnable(GL_BLEND);
/* Return state */ /* Return state */
glUseProgram(current->shader); 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) */ /* Swap buffers, handle events, etc. (vsync is potentially included here, too) */
glfwSwapBuffers(gl->w); gl->wcb->swap_buffers(gl->w);
glfwPollEvents();
double duration = glfwGetTime(); /* frame execution time */ double duration = gl->wcb->get_time(gl->w); /* frame execution time */
/* Handling sleeping (to meet target framerate) */ /* Handling sleeping (to meet target framerate) */
if (gl->rate > 0) { 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 */ /* Refresh window position and size if we are forcing it */
if (gl->force_geometry) { if (gl->force_geometry) {
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]); gl->wcb->set_geometry(gl->w,
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]); 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; 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) { void rd_destroy(struct renderer* r) {
/* TODO: delete everything else, not really needed though (as the application exits after here) */ /* TODO: delete everything else, not really needed though (as the application exits after here) */
glfwTerminate();
free(r->gl); free(r->gl);
free(r); free(r);
} }

View File

@@ -1,4 +1,7 @@
#ifndef RENDER_H
#define RENDER_H
struct gl_data; struct gl_data;
typedef struct renderer { typedef struct renderer {
@@ -8,8 +11,72 @@ typedef struct renderer {
struct gl_data* gl; struct gl_data* gl;
} renderer; } renderer;
struct renderer* rd_new(const char** paths, const char* entry, const char* force_mod); struct renderer* rd_new (const char** paths, const char* entry,
void rd_update(struct renderer*, float* lb, float* rb, size_t bsz, bool modified); const char* force_mod, const char* force_backend);
void rd_destroy(struct renderer*); void rd_update (struct renderer*, float* lb, float* rb,
void rd_time(struct renderer*); size_t bsz, bool modified);
void rd_destroy (struct renderer*);
void rd_time (struct renderer*);
void* rd_get_impl_window(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) */ /* center radius (pixels) */
#define C_RADIUS 128 #define C_RADIUS 128
/* center line thickness (pixels) */ /* center line thickness (pixels) */
#define C_LINE 2 #define C_LINE 1.5
/* outline color */ /* outline color */
#define OUTLINE #333333 #define OUTLINE #333333
/* Amplify magnitude of the results each bar displays */ /* 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

@@ -31,8 +31,8 @@ out vec4 fragment;
void main() { void main() {
#if USE_ALPHA > 0 #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)) #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.a = 0; fragment = #00000000;
#else #else
#define APPLY_FRAG(f, c) f = c #define APPLY_FRAG(f, c) f = c
#endif #endif
@@ -46,9 +46,9 @@ void main() {
float theta = atan(dy, dx); /* fragment angle with the center of the screen as the origin */ float theta = atan(dy, dx); /* fragment angle with the center of the screen as the origin */
float d = sqrt((dx * dx) + (dy * dy)); /* distance */ 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)) { 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 #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 #else
return; /* return immediately if there is no alpha blending available */ return; /* return immediately if there is no alpha blending available */
#endif #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. */ See documentation for more details. */
#request mod bars #request mod bars
/* GLFW window hints */ /* Window hints */
#request setfloating false #request setfloating false
#request setdecorated false #request setdecorated true
#request setfocused false #request setfocused false
#request setmaximized false #request setmaximized false
/* Force GLFW window geometry (locking the window in place), /* Force window geometry (locking the window in place), useful
useful for some pesky WMs that try to reposition the window for some pesky WMs that try to reposition the window when
when embedding in the desktop. */ embedding in the desktop. */
#request setforcegeometry false #request setforcegeometry false
/* Set window background opacity mode. Possible values are: /* Set window background opacity mode. Possible values are:
"native" - True transparency provided by the compositor. "native" - True transparency provided by the compositor. Can
Requires GLFW 3.3+ and an active compositor. Can reduce performance on some systems, depending on
reduce performance on some systems, and will not the compositor used.
blend with the visualizer's alpha layer.
"xroot" - Maintain a copy of the root window's pixmap "xroot" - Maintain a copy of the root window's pixmap
(usually the desktop background) to provide a (usually the desktop background) to provide a
@@ -40,22 +39,22 @@
Has very little performance impact. Has very little performance impact.
"none" - Disable window opacity completely. */ "none" - Disable window opacity completely. */
#request setopacity "xroot" #request setopacity "native"
/* OpenGL context and GLSL shader versions, do not change unless /* OpenGL context and GLSL shader versions, do not change unless
you *absolutely* know what you are doing. */ you *absolutely* know what you are doing. */
#request setversion 3 3 #request setversion 3 3
#request setshaderversion 330 #request setshaderversion 330
/* GLFW window title */ /* Window title */
#request settitle "GLava" #request settitle "GLava"
/* GLFW window geometry (x, y, width, height) */ /* Window geometry (x, y, width, height) */
#request setgeometry 0 0 400 600 #request setgeometry 0 0 800 600
/* Window background color (RGB format). /* Window background color (RGB format).
Only works with `setopacity "none"` */ Does not work with `setopacity "xroot"` */
#request setbg 3C3C3C #request setbg 00000000
/* (X11 only) EWMH Window type. Possible values are: /* (X11 only) EWMH Window type. Possible values are:
@@ -96,7 +95,7 @@
default output device. */ default output device. */
#request setsource "auto" #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 waiting for refresh, '1' (or more) to wait for the specified
amount of frames. */ amount of frames. */
#request setswap 1 #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 <sys/shm.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/XShm.h> #include <X11/extensions/XShm.h>
#define GLFW_EXPOSE_NATIVE_X11
#include <glad/glad.h> #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 "render.h"
#include "xwin.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; bool ret = true, should_close = false;
Display* d = glfwGetX11Display(); Display* d = rd_get_wcb(rd)->get_x11_display();
if (!d) { if (!d) {
d = XOpenDisplay(0); d = XOpenDisplay(0);
should_close = true; should_close = true;
@@ -50,11 +73,14 @@ bool xwin_should_render(void) {
XSetErrorHandler(handler); /* dummy error handler */ 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)) { &actual_type, &actual_format, &nitems, &bytes_after, &data)) {
goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */ goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */
} }
if (!nitems)
goto close;
Window active = ((Window*) data)[0]; Window active = ((Window*) data)[0];
prop = XInternAtom(d, "_NET_WM_STATE", true); 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: /* Set window types defined by the EWMH standard, possible values:
-> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */ -> "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) { static void xwin_changeatom(struct gl_wcb* wcb, void* impl, const char* type,
Window w = glfwGetX11Window((GLFWwindow*) rd_get_impl_window(rd)); const char* atom, const char* fmt, int mode) {
Display* d = glfwGetX11Display(); Window w = wcb->get_x11_window(impl);
Display* d = wcb->get_x11_display();
Atom wtype = XInternAtom(d, atom, false); Atom wtype = XInternAtom(d, atom, false);
size_t len = strlen(type), t; size_t len = strlen(type), t;
char formatted[len + 1]; 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); XChangeProperty(d, w, wtype, XA_ATOM, 32, mode, (unsigned char*) &desk, 1);
} }
void xwin_settype(struct renderer* rd, const char* type) { void xwin_settype(struct gl_wcb* wcb, void* impl, const char* type) {
xwin_changeatom(rd, type, "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_%s", PropModeReplace); xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
} }
void xwin_addstate(struct renderer* rd, const char* state) { void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* state) {
xwin_changeatom(rd, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend); xwin_changeatom(wcb, impl, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
} }
static Pixmap get_pixmap(Display* d, Window w) { static Drawable get_drawable(Display* d, Window w) {
Pixmap p; Drawable p;
Atom act_type; Atom act_type;
int act_format; int act_format;
unsigned long nitems, bytes_after; unsigned long nitems, bytes_after;
@@ -115,11 +142,11 @@ static Pixmap get_pixmap(Display* d, Window w) {
if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP, if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP,
&act_type, &act_format, &nitems, &bytes_after, &act_type, &act_format, &nitems, &bytes_after,
&data) == Success) { &data) == Success && data) {
if (data) { p = *((Pixmap *) data);
p = *((Pixmap *) data); XFree(data);
XFree(data); } else {
} p = w;
} }
return p; return p;
@@ -133,31 +160,39 @@ 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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
XColor c;
Display* d = glfwGetX11Display();
Pixmap p = get_pixmap(d, RootWindow(d, DefaultScreen(d)));
/* Obtain section of root pixmap using XShm */ int x, y, w, h;
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 = rd_get_wcb(rd)->get_x11_display();
Drawable src = get_drawable(d, find_desktop(rd));
/* Obtain section of root pixmap */
XShmSegmentInfo shminfo; XShmSegmentInfo shminfo;
Visual* visual = DefaultVisual(d, DefaultScreen(d)); Visual* visual = DefaultVisual(d, DefaultScreen(d));
XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) }; XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) };
int nret; int nret;
XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret); XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret);
XImage* image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL, XImage* image;
&shminfo, (unsigned int) w, (unsigned int) h); if (use_shm) {
if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777)) == -1) { image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
fprintf(stderr, "shmget() failed: %s\n", strerror(errno)); &shminfo, (unsigned int) w, (unsigned int) h);
exit(EXIT_FAILURE); 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\ /* Try to convert pixel bit depth to OpenGL storage format. The following formats\
will need intermediate conversion before OpenGL can accept the data: will need intermediate conversion before OpenGL can accept the data:
@@ -165,80 +200,83 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
- 8-bit pixel formats (retro displays, low-bandwidth virtual displays) - 8-bit pixel formats (retro displays, low-bandwidth virtual displays)
- 36-bit pixel formats (rare deep color displays) */ - 36-bit pixel formats (rare deep color displays) */
bool invalid = false, aligned = false; if (image) {
GLenum type; bool invalid = false, aligned = false;
switch (image->bits_per_pixel) { GLenum type;
case 16: switch (image->bits_per_pixel) {
switch (image->depth) { case 16:
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */ switch (image->depth) {
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */ case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
case 16: /* 16-bit, hi-color */ case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
type = GL_UNSIGNED_SHORT_5_6_5; case 16: /* 16-bit, hi-color */
aligned = true; type = GL_UNSIGNED_SHORT_5_6_5;
break; 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;
} }
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); uint8_t* buf;
free(buf); if (invalid) {
} else { abort();
/* Use image data directly. The alpha value is garbage/unassigned data, but /* Manual reformat (slow) */
we need to read it because X11 keeps pixel data aligned */ buf = malloc(4 * w * h);
buf = (uint8_t*) image->data; int xi, yi;
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth) Colormap map = DefaultColormap(d, DefaultScreen(d));
already ensures reads will be properly aligned across scanlines */ for (yi = 0; yi < h; ++yi) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (xi = 0; xi < w; ++xi) {
GLenum format = image->bitmap_bit_order == LSBFirst ? c.pixel = XGetPixel(image, xi, yi);
(!aligned ? GL_BGRA : GL_BGR) : XQueryColor(d, map, &c);
(!aligned ? GL_RGBA : GL_RGB); size_t base = (xi + (yi * w)) * 4;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf); buf[base + 0] = c.red / 256;
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */ 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); if (image) XDestroyImage(image);
shmdt(shminfo.shmaddr);
shmctl(shminfo.shmid, IPC_RMID, NULL);
XDestroyImage(image);
return texture; return texture;
} }

6
xwin.h
View File

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