Shared library rework & external async API support
This commit is contained in:
9
Makefile
9
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: all install clean
|
||||
.PHONY: all install clean ninja build
|
||||
|
||||
# In case these were specified explicitly as options instead of environment variables, export them to child processes
|
||||
export DESTDIR
|
||||
@@ -6,12 +6,12 @@ export CFLAGS
|
||||
|
||||
BUILD_DIR = build
|
||||
|
||||
MESON_CONF = $(BUILD_DIR)
|
||||
MESON_CONF = $(BUILD_DIR) -Ddisable_obs=true
|
||||
|
||||
# Support assigning standalone/debug builds as the old Makefile did, otherwise complain
|
||||
|
||||
ifneq ($(BUILD),debug)
|
||||
MESON_CONF += --buildtype=release
|
||||
MESON_CONF += --prefix /usr --buildtype=release
|
||||
ifdef BUILD
|
||||
@echo "WARNING: ignoring build option '$(BUILD)' in compatibility Makefile"
|
||||
endif
|
||||
@@ -34,6 +34,7 @@ $(shell if [ '$(STATE)' != "`cat build_state`" ]; then echo '$(STATE)' > build_s
|
||||
ifndef BUILD
|
||||
@echo ""
|
||||
@echo "PACKAGE MAINTAINER NOTICE: Configuring release build for compatibility with old makefile."
|
||||
@echo " Some new features may be missing."
|
||||
@echo " If you are a package maintainer consider using meson directly!"
|
||||
@echo ""
|
||||
endif
|
||||
@@ -50,7 +51,7 @@ ninja: build
|
||||
ninja -C $(BUILD_DIR)
|
||||
|
||||
install:
|
||||
cd $(BUILD_DIR) && meson install
|
||||
ninja -C build install
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
26
glava-cli/cli.c
Normal file
26
glava-cli/cli.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <glava.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static renderer_handle handle;
|
||||
|
||||
static void handle_term (int _) {
|
||||
printf("Interrupt recieved, closing...\n");
|
||||
glava_terminate(&handle);
|
||||
}
|
||||
static void handle_reload(int _) {
|
||||
printf("User signal recieved, reloading...\n");
|
||||
glava_reload(&handle);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const struct sigaction term_action = { .sa_handler = handle_term };
|
||||
const struct sigaction reload_action = { .sa_handler = handle_reload };
|
||||
sigaction(SIGTERM, &term_action, NULL);
|
||||
sigaction(SIGINT, &term_action, NULL);
|
||||
sigaction(SIGUSR1, &reload_action, NULL);
|
||||
|
||||
glava_entry(argc, argv, &handle);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
67
glava-obs/entry.c
Normal file
67
glava-obs/entry.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <stdlib.h>
|
||||
#include <obs-module.h>
|
||||
#include <obs.h>
|
||||
#include <util/threading.h>
|
||||
#include <util/platform.h>
|
||||
|
||||
OBS_DECLARE_MODULE();
|
||||
|
||||
struct mod_state {
|
||||
obs_source_t* source;
|
||||
os_event_t* stop_signal;
|
||||
pthread_t thread;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
static const char* get_name(void* _) {
|
||||
UNUSED_PARAMETER(_);
|
||||
return "GLava Source";
|
||||
}
|
||||
|
||||
static void destroy(void* data) {
|
||||
struct mod_state* s = (struct mod_state*) data;
|
||||
if (s) {
|
||||
if (s->initialized) {
|
||||
os_event_signal(s->stop_signal);
|
||||
pthread_join(s->thread, NULL);
|
||||
}
|
||||
os_event_destroy(s->stop_signal);
|
||||
bfree(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void* work_thread(void* data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* create(obs_data_t* settings, obs_source_t* source) {
|
||||
struct mod_state* s = bzalloc(sizeof(struct mod_state));
|
||||
s->source = source;
|
||||
|
||||
if (os_event_init(&s->stop_signal, OS_EVENT_TYPE_MANUAL) != 0) {
|
||||
destroy(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_create(&s->thread, NULL, work_thread, s) != 0) {
|
||||
destroy(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->initialized = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
struct obs_source_info glava_src = {
|
||||
.id = "glava",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_ASYNC_VIDEO,
|
||||
.get_name = get_name,
|
||||
.create = create,
|
||||
.destroy = destroy
|
||||
};
|
||||
|
||||
bool obs_module_load(void) {
|
||||
obs_register_source(&glava_src);
|
||||
return true;
|
||||
}
|
||||
107
glava/glava.c
107
glava/glava.c
@@ -5,7 +5,6 @@
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
@@ -73,7 +72,12 @@
|
||||
#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
|
||||
#endif
|
||||
|
||||
static bool reload = false;
|
||||
static volatile bool reload = false;
|
||||
|
||||
__attribute__((noreturn, visibility("default"))) void glava_return_builtin(void) { exit(EXIT_SUCCESS); }
|
||||
__attribute__((noreturn, visibility("default"))) void glava_abort_builtin (void) { exit(EXIT_FAILURE); }
|
||||
__attribute__((noreturn, visibility("default"))) void (*glava_return) (void) = glava_return_builtin;
|
||||
__attribute__((noreturn, visibility("default"))) void (*glava_abort) (void) = glava_abort_builtin;
|
||||
|
||||
/* Copy installed shaders/configuration from the installed location
|
||||
(usually /etc/xdg). Modules (folders) will be linked instead of
|
||||
@@ -86,13 +90,13 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
|
||||
DIR* dir = opendir(path);
|
||||
if (!dir) {
|
||||
fprintf(stderr, "'%s' does not exist\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
umask(~(S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH));
|
||||
if (mkdir(dest, ACCESSPERMS) && errno != EEXIST) {
|
||||
fprintf(stderr, "could not create directory '%s': %s\n", dest, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
struct dirent* d;
|
||||
@@ -232,28 +236,48 @@ static struct option p_opts[] = {
|
||||
|
||||
static renderer* rd = NULL;
|
||||
|
||||
static void handle_term(int signum) {
|
||||
if (rd->alive) {
|
||||
puts("\nInterrupt recieved, closing...");
|
||||
rd->alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_reload(int signum) {
|
||||
if (rd->alive) {
|
||||
puts("\nSIGUSR1 recieved, reloading...");
|
||||
rd->alive = false;
|
||||
}
|
||||
reload = true;
|
||||
}
|
||||
|
||||
#define append_buf(buf, sz_store, ...) \
|
||||
({ \
|
||||
buf = realloc(buf, ++(*sz_store) * sizeof(*buf)); \
|
||||
buf[*sz_store - 1] = __VA_ARGS__; \
|
||||
})
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
/* Wait for renderer target texture to be initialized and valid */
|
||||
__attribute__((visibility("default"))) void glava_wait(renderer_handle* ref) {
|
||||
while(__atomic_load_n(ref, __ATOMIC_SEQ_CST) == NULL) {
|
||||
/* Edge case: handle has not been assigned */
|
||||
struct timespec tv = {
|
||||
.tv_sec = 0, .tv_nsec = 10 * 1000000
|
||||
};
|
||||
nanosleep(&tv, NULL);
|
||||
}
|
||||
pthread_mutex_lock(&(*ref)->lock);
|
||||
pthread_cond_wait(&(*ref)->cond, &(*ref)->lock);
|
||||
pthread_mutex_unlock(&(*ref)->lock);
|
||||
}
|
||||
|
||||
/* Atomic size request */
|
||||
__attribute__((visibility("default"))) void glava_sizereq(renderer_handle r, int x, int y, int w, int h) {
|
||||
r->sizereq = (typeof(r->sizereq)) { .x = x, .y = y, .w = w, .h = h };
|
||||
__atomic_store_n(&r->sizereq_flag, GLAVA_REQ_RESIZE, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/* Atomic terminate request */
|
||||
__attribute__((visibility("default"))) void glava_terminate(renderer_handle* ref) {
|
||||
renderer_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
|
||||
__atomic_store_n(&store->alive, false, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/* Atomic reload request */
|
||||
__attribute__((visibility("default"))) void glava_reload(renderer_handle* ref) {
|
||||
renderer_handle store = __atomic_exchange_n(ref, NULL, __ATOMIC_SEQ_CST);
|
||||
__atomic_store_n(&reload, true, __ATOMIC_SEQ_CST);
|
||||
__atomic_store_n(&store->alive, false, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
|
||||
/* Main entry */
|
||||
__attribute__((visibility("default"))) void glava_entry(int argc, char** argv, renderer_handle* ret) {
|
||||
|
||||
/* Evaluate these macros only once, since they allocate */
|
||||
const char
|
||||
@@ -284,10 +308,10 @@ int main(int argc, char** argv) {
|
||||
case 'm': force = optarg; break;
|
||||
case 'b': backend = optarg; break;
|
||||
case 'a': audio_impl_name = optarg; break;
|
||||
case '?': exit(EXIT_FAILURE); break;
|
||||
case '?': glava_abort(); break;
|
||||
case 'V':
|
||||
puts(GLAVA_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
glava_return();
|
||||
break;
|
||||
default:
|
||||
case 'h': {
|
||||
@@ -297,7 +321,7 @@ int main(int argc, char** argv) {
|
||||
bsz += snprintf(buf + bsz, sizeof(buf) - bsz, "\t\"%s\"%s\n", audio_impls[t]->name,
|
||||
!strcmp(audio_impls[t]->name, audio_impl_name) ? " (default)" : "");
|
||||
printf(help_str, argc > 0 ? argv[0] : "glava", buf);
|
||||
exit(EXIT_SUCCESS);
|
||||
glava_return();
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
@@ -323,7 +347,7 @@ int main(int argc, char** argv) {
|
||||
if (*parsed_name == '\0') {
|
||||
fprintf(stderr, "Error: invalid pipe binding name: \"%s\"\n"
|
||||
"Zero length names are not permitted.\n", parsed_name);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
for (char* c = parsed_name; *c != '\0'; ++c) {
|
||||
switch (*c) {
|
||||
@@ -331,7 +355,7 @@ int main(int argc, char** argv) {
|
||||
if (c == parsed_name) {
|
||||
fprintf(stderr, "Error: invalid pipe binding name: \"%s\" ('%c')\n"
|
||||
"Valid names may not start with a number.\n", parsed_name, *c);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
@@ -340,13 +364,13 @@ int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Error: invalid pipe binding name: \"%s\" ('%c')\n"
|
||||
"Valid names may only contain [a..z], [A..Z], [0..9] "
|
||||
"and '_' characters.\n", parsed_name, *c);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
}
|
||||
for (size_t t = 0; t < binds_sz; ++t) {
|
||||
if (!strcmp(binds[t].name, parsed_name)) {
|
||||
fprintf(stderr, "Error: attempted to re-bind pipe argument: \"%s\"\n", parsed_name);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
}
|
||||
int type = -1;
|
||||
@@ -364,7 +388,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
if (type == -1) {
|
||||
fprintf(stderr, "Error: Unsupported `--pipe` GLSL type: \"%s\"\n", parsed_type);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
struct rd_bind bd = {
|
||||
.name = parsed_name,
|
||||
@@ -391,13 +415,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
if (stdin_type == -1) {
|
||||
fprintf(stderr, "Error: Unsupported `--stdin` GLSL type: \"%s\"\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
break;
|
||||
}
|
||||
conflict_error:
|
||||
fprintf(stderr, "Error: cannot use `--pipe` and `--stdin` together\n");
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
#ifdef GLAVA_DEBUG
|
||||
case 'T': {
|
||||
entry = "test_rc.glsl";
|
||||
@@ -409,7 +433,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (copy_mode) {
|
||||
copy_cfg(install_path, user_path, verbose);
|
||||
exit(EXIT_SUCCESS);
|
||||
glava_return();
|
||||
}
|
||||
|
||||
/* Handle `--force` argument as a request override */
|
||||
@@ -424,12 +448,6 @@ int main(int argc, char** argv) {
|
||||
append_buf(requests, &requests_sz, NULL);
|
||||
append_buf(binds, &binds_sz, (struct rd_bind) { .name = NULL });
|
||||
|
||||
const struct sigaction term_action = { .sa_handler = handle_term };
|
||||
const struct sigaction reload_action = { .sa_handler = handle_reload };
|
||||
sigaction(SIGTERM, &term_action, NULL);
|
||||
sigaction(SIGINT, &term_action, NULL);
|
||||
sigaction(SIGUSR1, &reload_action, NULL);
|
||||
|
||||
float* b0, * b1, * lb, * rb;
|
||||
size_t t;
|
||||
struct audio_data audio;
|
||||
@@ -446,13 +464,14 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (!impl) {
|
||||
fprintf(stderr, "The specified audio backend (\"%s\") is not available.\n", audio_impl_name);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
instantiate:
|
||||
reload = false;
|
||||
rd = rd_new(system_shader_paths, entry, (const char**) requests,
|
||||
instantiate: {}
|
||||
rd = rd_new(system_shader_paths, entry, (const char**) requests,
|
||||
backend, binds, stdin_type, desktop, verbose, test);
|
||||
if (ret)
|
||||
__atomic_store_n(ret, rd, __ATOMIC_SEQ_CST);
|
||||
|
||||
b0 = malloc(rd->bufsize_request * sizeof(float));
|
||||
b1 = malloc(rd->bufsize_request * sizeof(float));
|
||||
@@ -488,7 +507,7 @@ instantiate:
|
||||
if (verbose) printf("Using audio source: %s\n", audio.source);
|
||||
|
||||
pthread_create(&thread, NULL, impl->entry, (void*) &audio);
|
||||
while (rd->alive) {
|
||||
while (__atomic_load_n(&rd->alive, __ATOMIC_SEQ_CST)) {
|
||||
|
||||
rd_time(rd); /* update timer for this frame */
|
||||
|
||||
@@ -526,7 +545,7 @@ instantiate:
|
||||
if (rd_test_evaluate(rd)) {
|
||||
fprintf(stderr, "Test results did not match expected output\n");
|
||||
fflush(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -542,6 +561,6 @@ instantiate:
|
||||
free(lb);
|
||||
free(rb);
|
||||
rd_destroy(rd);
|
||||
if (reload)
|
||||
if (__atomic_exchange_n(&reload, false, __ATOMIC_SEQ_CST))
|
||||
goto instantiate;
|
||||
}
|
||||
|
||||
40
glava/glava.h
Normal file
40
glava/glava.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef _GLAVA_H
|
||||
#define _GLAVA_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define GLAVA_REQ_NONE 0
|
||||
#define GLAVA_REQ_RESIZE 1
|
||||
|
||||
struct gl_data;
|
||||
|
||||
typedef struct renderer {
|
||||
volatile bool alive;
|
||||
bool mirror_input;
|
||||
size_t bufsize_request, rate_request, samplesize_request;
|
||||
char* audio_source_request;
|
||||
int off_tex; /* final GL texture for offscreen rendering */
|
||||
pthread_mutex_t lock; /* lock for reading from offscreen texture */
|
||||
pthread_cond_t cond; /* cond for reading from offscreen texture */
|
||||
volatile struct {
|
||||
int x, y, w, h;
|
||||
} sizereq;
|
||||
volatile int sizereq_flag;
|
||||
struct gl_data* gl;
|
||||
} renderer;
|
||||
|
||||
/* External API */
|
||||
|
||||
typedef struct renderer* volatile renderer_handle;
|
||||
__attribute__((noreturn, visibility("default"))) void (*glava_abort) (void);
|
||||
__attribute__((noreturn, visibility("default"))) void (*glava_return) (void);
|
||||
__attribute__((visibility("default"))) void glava_assign_external_ctx (void* ctx);
|
||||
__attribute__((visibility("default"))) void glava_entry (int argc, char** argv, renderer_handle* ret);
|
||||
__attribute__((visibility("default"))) void glava_terminate (renderer_handle* ref);
|
||||
__attribute__((visibility("default"))) void glava_reload (renderer_handle* ref);
|
||||
__attribute__((visibility("default"))) void glava_sizereq (renderer_handle r, int x, int y, int w, int h);
|
||||
__attribute__((visibility("default"))) void glava_wait (renderer_handle* ref);
|
||||
|
||||
#endif /* _GLAVA_H */
|
||||
@@ -65,6 +65,8 @@ DECL_WINDOW_HINT_STUB(set_maximized);
|
||||
|
||||
extern struct gl_wcb wcb_glfw;
|
||||
|
||||
static bool offscreen(void) { return false; }
|
||||
|
||||
static void* create_and_bind(const char* name, const char* class,
|
||||
const char* type, const char** states,
|
||||
size_t states_sz,
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
typedef struct __GLXcontextRec *GLXContext;
|
||||
typedef struct __GLXcontextRec* GLXContext;
|
||||
typedef XID GLXPixmap;
|
||||
typedef XID GLXDrawable;
|
||||
|
||||
typedef void (*__GLXextFuncPtr)(void);
|
||||
|
||||
/* GLX 1.3 and later */
|
||||
typedef struct __GLXFBConfigRec *GLXFBConfig;
|
||||
typedef struct __GLXFBConfigRec* GLXFBConfig;
|
||||
typedef XID GLXFBConfigID;
|
||||
typedef XID GLXContextID;
|
||||
typedef XID GLXWindow;
|
||||
@@ -59,7 +59,6 @@ typedef XID GLXPbuffer;
|
||||
#define GLX_ACCUM_BLUE_SIZE 16
|
||||
#define GLX_ACCUM_ALPHA_SIZE 17
|
||||
|
||||
|
||||
/*
|
||||
* Error codes returned by glXGetConfig:
|
||||
*/
|
||||
@@ -184,6 +183,20 @@ struct glxwin {
|
||||
|
||||
static Atom ATOM__MOTIF_WM_HINTS, ATOM_WM_DELETE_WINDOW, ATOM_WM_PROTOCOLS, ATOM__NET_ACTIVE_WINDOW, ATOM__XROOTPMAP_ID;
|
||||
|
||||
static GLXContext sharelist_ctx;
|
||||
static bool sharelist_assigned = false;
|
||||
|
||||
static bool offscreen(void) {
|
||||
return sharelist_assigned;
|
||||
}
|
||||
|
||||
/* Public function that can be called before GLava instantiation for offscreen rendering hooks */
|
||||
/* This hook resides here since it relies on GLX functionality. */
|
||||
__attribute__((visibility("default"))) void glava_assign_external_ctx(void* ctx) {
|
||||
sharelist_ctx = (GLXContext) ctx;
|
||||
sharelist_assigned = true;
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
display = XOpenDisplay(NULL);
|
||||
if (!display) {
|
||||
@@ -201,7 +214,7 @@ static void init(void) {
|
||||
|
||||
if (!hgl && !hglx) {
|
||||
fprintf(stderr, "Failed to load GLX functions (libGL and libGLX do not exist!)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
/* Depending on the graphics driver, the GLX functions that we need may either be in libGL or
|
||||
@@ -212,7 +225,7 @@ static void init(void) {
|
||||
if (!s && hglx) s = dlsym(hglx, symbol);
|
||||
if (!s) {
|
||||
fprintf(stderr, "Failed to resolve GLX symbol: `%s`\n", symbol);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -338,7 +351,12 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
int d, int h,
|
||||
int x, int y,
|
||||
int version_major, int version_minor,
|
||||
bool clickthrough, bool offscreen) {
|
||||
bool clickthrough, bool off) {
|
||||
|
||||
/* Assume offscreen rendering if hook has been used */
|
||||
if (offscreen())
|
||||
off = true;
|
||||
|
||||
struct glxwin* w = malloc(sizeof(struct glxwin));
|
||||
*w = (struct glxwin) {
|
||||
.override_state = '\0',
|
||||
@@ -347,7 +365,7 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
.should_render = true,
|
||||
.bg_changed = false,
|
||||
.clickthrough = false,
|
||||
.offscreen = offscreen
|
||||
.offscreen = off
|
||||
};
|
||||
|
||||
XVisualInfo* vi;
|
||||
@@ -362,7 +380,7 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
"\nGLX extension version mismatch on the current display (1.4+ required, %d.%d available)\n"
|
||||
"This is usually due to an outdated X server or graphics drivers.\n\n",
|
||||
glx_minor, glx_major);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
static int gl_attrs[] = {
|
||||
@@ -393,7 +411,7 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
"glXChooseFBConfig(): failed with attrs "
|
||||
"(GLX_CONTEXT_MAJOR_VERSION_ARB, GLX_CONTEXT_MINOR_VERSION_ARB)\n\n",
|
||||
version_major, version_minor);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
for (int t = 0; t < fb_sz; ++t) {
|
||||
@@ -486,7 +504,7 @@ static void* create_and_bind(const char* name, const char* class,
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!(w->context = glXCreateContextAttribsARB(display, config, 0, True, context_attrs))) {
|
||||
if (!(w->context = glXCreateContextAttribsARB(display, config, sharelist_assigned ? sharelist_ctx : 0, True, context_attrs))) {
|
||||
fprintf(stderr, "glXCreateContextAttribsARB(): failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <dirent.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -147,24 +148,21 @@ struct gl_data {
|
||||
bool bg_setup;
|
||||
GLuint sm_utex, sm_usz, sm_uw;
|
||||
bool sm_setup;
|
||||
bool test_mode;
|
||||
struct gl_sfbo off_sfbo;
|
||||
#ifdef GLAVA_DEBUG
|
||||
struct {
|
||||
float r, g, b, a;
|
||||
} test_eval_color;
|
||||
bool debug_verbose;
|
||||
bool assigned_debug_cb;
|
||||
bool test_mode;
|
||||
struct gl_sfbo test_sfbo;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifdef GLAVA_DEBUG
|
||||
bool rd_get_test_mode(struct renderer* r) {
|
||||
struct gl_data* gl = r->gl;
|
||||
return gl->test_mode;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load shader file */
|
||||
static GLuint shaderload(const char* rpath,
|
||||
@@ -819,8 +817,14 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
.bufsize_request = 8192,
|
||||
.rate_request = 22000,
|
||||
.samplesize_request = 1024,
|
||||
.audio_source_request = NULL
|
||||
.audio_source_request = NULL,
|
||||
.off_tex = 0,
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
.cond = PTHREAD_COND_INITIALIZER,
|
||||
.sizereq_flag = 0
|
||||
};
|
||||
|
||||
pthread_mutex_lock(&r->lock);
|
||||
|
||||
struct gl_data* gl = r->gl;
|
||||
*gl = (struct gl_data) {
|
||||
@@ -863,19 +867,19 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
.binds = bindings,
|
||||
.bg_setup = false,
|
||||
.sm_setup = false,
|
||||
#ifdef GLAVA_DEBUG
|
||||
.test_eval_color = { 0.0F, 0.0F, 0.0F, 0.0F },
|
||||
.debug_verbose = verbose,
|
||||
.assigned_debug_cb = false,
|
||||
.test_mode = test_mode,
|
||||
.test_sfbo = {
|
||||
.off_sfbo = {
|
||||
.name = "test",
|
||||
.shader = 0,
|
||||
.indirect = false,
|
||||
.nativeonly = false,
|
||||
.binds = NULL,
|
||||
.binds_sz = 0
|
||||
}
|
||||
},
|
||||
#ifdef GLAVA_DEBUG
|
||||
.test_eval_color = { 0.0F, 0.0F, 0.0F, 0.0F },
|
||||
.debug_verbose = verbose,
|
||||
.assigned_debug_cb = false,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -905,7 +909,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
fprintf(stderr, "\t\"%s\"\n", wcbs[t]->name);
|
||||
}
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
if (verbose) printf("Using backend: '%s'\n", backend);
|
||||
@@ -966,7 +970,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
|
||||
if (!gl->copy_desktop && !native_opacity && strcmp("none", (char*) args[0])) {
|
||||
fprintf(stderr, "Invalid opacity option: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -992,7 +996,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
};
|
||||
if (!ext_parse_color((char*) args[0], 2, results)) {
|
||||
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -1008,7 +1012,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
};
|
||||
if (!ext_parse_color((char*) args[0], 2, results)) {
|
||||
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -1042,7 +1046,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
current->nativeonly = *(bool*) args[0];
|
||||
else {
|
||||
fprintf(stderr, "`nativeonly` request needs module context\n");
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -1157,7 +1161,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
fprintf(stderr, "Cannot add transformation to uniform '%s':"
|
||||
" uniform does not exist! (%d present in this unit)\n",
|
||||
(const char*) args[0], (int) current->binds_sz);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
struct gl_transform* tran = NULL;
|
||||
for (t = 0; t < sizeof(transform_functions) / sizeof(struct gl_transform); ++t) {
|
||||
@@ -1170,12 +1174,12 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
fprintf(stderr, "Cannot add transformation '%s' to uniform '%s':"
|
||||
" transform function does not exist!\n",
|
||||
(const char*) args[1], (const char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
if (tran->type != bind->type) {
|
||||
fprintf(stderr, "Cannot apply '%s' to uniform '%s': mismatching types\n",
|
||||
(const char*) args[1], (const char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
++bind->t_sz;
|
||||
bind->transformations =
|
||||
@@ -1190,13 +1194,13 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (!current) {
|
||||
fprintf(stderr, "Cannot bind uniform '%s' outside of a context"
|
||||
" (load a module first!)\n", (const char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
struct gl_bind_src* src = lookup_bind_src((const char*) args[0]);
|
||||
if (!src) {
|
||||
fprintf(stderr, "Cannot bind uniform '%s': bind type does not exist!\n",
|
||||
(const char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
++current->binds_sz;
|
||||
current->binds = realloc(current->binds, current->binds_sz * sizeof(struct gl_bind));
|
||||
@@ -1246,7 +1250,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
errno != ENOTDIR &&
|
||||
errno != ELOOP ) {
|
||||
fprintf(stderr, "Failed to load entry '%s': %s\n", se_buf, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
} else continue;
|
||||
}
|
||||
fstat(fd, &st);
|
||||
@@ -1275,7 +1279,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
errno != ELOOP) {
|
||||
fprintf(stderr, "Failed to load desktop environment specific presets "
|
||||
"at '%s': %s\n", se_buf, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
} else {
|
||||
printf("No presets for current desktop environment (\"%s\"), "
|
||||
"using default presets for embedding\n", env);
|
||||
@@ -1284,7 +1288,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Failed to load default presets at '%s': %s\n",
|
||||
se_buf, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1336,7 +1340,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
"No module was selected, edit '%s' to load "
|
||||
"a module with `#request mod [name]`\n",
|
||||
entry);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
|
||||
gl->w = gl->wcb->create_and_bind(wintitle, "GLava", xwintype, (const char**) xwinstates, xwinstates_sz,
|
||||
@@ -1442,7 +1446,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
if (skip && verbose) printf("disabled: '%s'\n", d->d_name);
|
||||
/* check for compilation failure */
|
||||
if (!id && !skip)
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
|
||||
s->shader = id;
|
||||
|
||||
@@ -1472,7 +1476,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
s->pipe_uniforms[u] = glGetUniformLocation(id, buf);
|
||||
} else {
|
||||
fprintf(stderr, "failed to format binding: \"%s\"\n", bd->name);
|
||||
exit(EXIT_FAILURE);
|
||||
glava_abort();
|
||||
}
|
||||
++u;
|
||||
}
|
||||
@@ -1492,14 +1496,15 @@ struct renderer* rd_new(const char** paths, const char* entry,
|
||||
|
||||
gl->stages = stages;
|
||||
gl->stages_sz = count;
|
||||
|
||||
#ifdef GLAVA_DEBUG
|
||||
{
|
||||
|
||||
if (gl->test_mode || gl->wcb->offscreen()) {
|
||||
int w, h;
|
||||
gl->wcb->get_fbsize(gl->w, &w, &h);
|
||||
setup_sfbo(&gl->test_sfbo, w, h);
|
||||
setup_sfbo(&gl->off_sfbo, w, h);
|
||||
r->off_tex = gl->off_sfbo.tex;
|
||||
pthread_cond_signal(&r->cond);
|
||||
pthread_mutex_unlock(&r->lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
struct gl_sfbo* final = NULL;
|
||||
@@ -1636,6 +1641,13 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
irb[t] = irbs + ((irbe - irbs) * mod);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle external resize requests */
|
||||
if (gl->wcb->offscreen()) {
|
||||
if (__atomic_exchange_n(&r->sizereq_flag, GLAVA_REQ_NONE, __ATOMIC_SEQ_CST) == GLAVA_REQ_RESIZE)
|
||||
gl->wcb->set_geometry(gl->w, r->sizereq.x, r->sizereq.y, r->sizereq.w, r->sizereq.h);
|
||||
}
|
||||
|
||||
int ww, wh, wx, wy;
|
||||
gl->wcb->get_fbsize(gl->w, &ww, &wh);
|
||||
gl->wcb->get_pos(gl->w, &wx, &wy);
|
||||
@@ -1647,9 +1659,8 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
setup_sfbo(&gl->stages[t], ww, wh);
|
||||
}
|
||||
}
|
||||
#ifdef GLAVA_DEBUG
|
||||
setup_sfbo(&gl->test_sfbo, ww, wh);
|
||||
#endif
|
||||
if (gl->test_mode || gl->wcb->offscreen())
|
||||
setup_sfbo(&gl->off_sfbo, ww, wh);
|
||||
}
|
||||
|
||||
/* Resize and grab new background data if needed */
|
||||
@@ -1843,11 +1854,9 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
if (current->indirect)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
|
||||
|
||||
#ifdef GLAVA_DEBUG
|
||||
if (!current->indirect && gl->test_mode) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl->test_sfbo.fbo);
|
||||
if (!current->indirect && (gl->test_mode || gl->wcb->offscreen())) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl->off_sfbo.fbo);
|
||||
}
|
||||
#endif
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include "glava.h"
|
||||
|
||||
extern const struct {
|
||||
const char* n;
|
||||
int i;
|
||||
@@ -18,15 +23,6 @@ extern bool glad_instantiated;
|
||||
|
||||
#define PIPE_DEFAULT "_"
|
||||
|
||||
struct gl_data;
|
||||
|
||||
typedef struct renderer {
|
||||
bool alive, mirror_input;
|
||||
size_t bufsize_request, rate_request, samplesize_request;
|
||||
char* audio_source_request;
|
||||
struct gl_data* gl;
|
||||
} renderer;
|
||||
|
||||
struct rd_bind {
|
||||
const char* name;
|
||||
const char* stype;
|
||||
@@ -53,6 +49,7 @@ struct gl_wcb* rd_get_wcb (struct renderer*);
|
||||
/* gl_wcb - OpenGL Window Creation Backend interface */
|
||||
struct gl_wcb {
|
||||
const char* name;
|
||||
bool (*offscreen) (void);
|
||||
void (*init) (void);
|
||||
void* (*create_and_bind)(const char* name, const char* class,
|
||||
const char* type, const char** states,
|
||||
@@ -96,6 +93,7 @@ struct gl_wcb {
|
||||
#define WCB_ATTACH(B, N) \
|
||||
struct gl_wcb N = { \
|
||||
.name = B, \
|
||||
WCB_FUNC(offscreen), \
|
||||
WCB_FUNC(init), \
|
||||
WCB_FUNC(create_and_bind), \
|
||||
WCB_FUNC(should_close), \
|
||||
|
||||
@@ -295,7 +295,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
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);
|
||||
glava_abort();
|
||||
}
|
||||
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
|
||||
shminfo.readOnly = false;
|
||||
@@ -314,7 +314,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
|
||||
if (image) {
|
||||
bool invalid = false, aligned = false;
|
||||
GLenum type;
|
||||
GLenum type = 0;
|
||||
switch (image->bits_per_pixel) {
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
|
||||
35
meson.build
35
meson.build
@@ -21,7 +21,6 @@ else
|
||||
add_project_arguments(
|
||||
'-O2',
|
||||
'-Wstringop-overflow=0',
|
||||
'-Wmaybe-uninitialized=0',
|
||||
language: 'c'
|
||||
)
|
||||
if get_option('glad')
|
||||
@@ -83,17 +82,27 @@ endif
|
||||
|
||||
add_project_arguments(
|
||||
'-DGLAVA_VERSION="'+glava_version+'"',
|
||||
'-DSHADER_INSTALL_PATH="'+shaderdir+'"',
|
||||
'-DSHADER_INSTALL_PATH="'+shaderdir+'/glava"',
|
||||
'-I/usr/include/obs',
|
||||
'-fvisibility=hidden',
|
||||
language: 'c'
|
||||
)
|
||||
|
||||
executable(
|
||||
libglava = shared_library(
|
||||
'glava',
|
||||
sources: run_command('find', 'glava', '-type', 'f', '-name', '*.c', '-print').stdout().strip().split('\n'),
|
||||
dependencies: glava_dependencies,
|
||||
install: true
|
||||
)
|
||||
|
||||
executable(
|
||||
'glava',
|
||||
sources: run_command('find', 'glava-cli', '-type', 'f', '-name', '*.c', '-print').stdout().strip().split('\n'),
|
||||
link_with: libglava,
|
||||
include_directories: 'glava',
|
||||
install: true
|
||||
)
|
||||
|
||||
executable(
|
||||
'glava-config',
|
||||
sources: run_command('find', 'glava-config', '-type', 'f', '-name', '*.c', '-print').stdout().strip().split('\n'),
|
||||
@@ -105,4 +114,22 @@ executable(
|
||||
install: true
|
||||
)
|
||||
|
||||
install_subdir('shaders/glava', install_dir: shaderdir+'/../')
|
||||
if not get_option('disable_obs')
|
||||
shared_library(
|
||||
'glava-obs',
|
||||
sources: run_command('find', 'glava-obs', '-type', 'f', '-name', '*.c', '-print').stdout().strip().split('\n'),
|
||||
link_with: libglava,
|
||||
dependencies: [
|
||||
dependency('threads'),
|
||||
cc.find_library('GL'),
|
||||
cc.find_library('X11'),
|
||||
cc.find_library('obs'),
|
||||
cc.find_library('dl')
|
||||
],
|
||||
install: true,
|
||||
install_dir : '/usr/lib/obs-plugins'
|
||||
)
|
||||
endif
|
||||
|
||||
install_subdir('shaders/glava', install_dir: shaderdir)
|
||||
install_headers('glava/glava.h')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
option('shaderdir', type: 'string', value: '/etc/xdg/glava/', description: 'Default shader directory')
|
||||
option('shaderdir', type: 'string', value: '/etc/xdg', description: 'System shader directory location')
|
||||
option('enable_glfw', type: 'boolean', value: false, description: 'Enable GLFW backend')
|
||||
option('disable_glx', type: 'boolean', value: false, description: 'Disable GLX')
|
||||
option('standalone', type: 'boolean', value: false, description: 'OS-independent build')
|
||||
option('glad', type: 'boolean', value: false, description: 'Regenerate GLAD')
|
||||
option('disable_obs', type: 'boolean', value: false, description: 'Dsiable OBS plugin support')
|
||||
|
||||
Reference in New Issue
Block a user