42 Commits

Author SHA1 Message Date
Jarcode
acdbb8f3b5 updated README.md 2018-10-07 12:04:00 -07:00
Jarcode
829c1be370 Merge branch 'master' of https://github.com/wacossusca34/glava 2018-10-07 08:20:33 -07:00
Jarcode
261ba8ded5 Added more meaningful error messages to the GLX backend 2018-10-07 08:20:19 -07:00
Levi Webb
fc9c74d256 Merge pull request #66 from coderobe/patch-readme
README: Add required X extensions
2018-10-06 15:21:11 -07:00
Levi Webb
a697af9a0a Merge pull request #67 from coderobe/patch-readme-archlinux
README: Mention Arch Linux binary release (community)
2018-10-06 15:20:40 -07:00
Robin B
437aa146a4 README: Mention Arch Linux binary release (community)
GLava is now part of the official Arch Linux [community] repository, maintained by me
2018-10-06 20:41:11 +02:00
Robin B
1c2f362219 README: Add required X extensions 2018-10-06 19:28:32 +02:00
Levi Webb
7dfb68fb1a Update README.md 2018-10-05 21:38:40 -07:00
Jarcode
353c3bd62f Added support for unmanaged windows, see #65 and #4 2018-10-04 20:14:37 -07:00
Jarcode
e4b16cdbb6 Added option to render bars on left/right sides, see #64 2018-10-03 17:45:48 -07:00
Jarcode
8102f99683 Added option to vertically flip bars output, see #64 2018-10-03 17:21:53 -07:00
Jarcode
14747b1e75 Use default PulseAudio buffer sizes when reading input from server, see #63 2018-09-30 14:50:10 -07:00
Levi Webb
217d5c9eea Fix spelling error 2018-09-26 19:04:44 -07:00
Jarcode
3be916f157 Fixed clickthrough issue on Openbox and XFCE, closes #61 2018-09-17 19:23:06 -07:00
Jarcode
bc955a5b3d Merge branch 'master' of https://github.com/wacossusca34/glava 2018-09-12 21:01:46 -07:00
Jarcode
c7ad0a5024 Added signal handler for SIGINT and SIGTERM 2018-09-12 21:01:12 -07:00
Levi Webb
83ad0d8f8a Merge pull request #60 from Smarthard/master
bars COLOR refactored & alpha channel support added
2018-09-11 21:31:25 -07:00
Smarthard
53f7352347 bars COLOR refactored & alpha channel support added 2018-09-11 19:01:24 +03:00
Jarcode
643b0cf3f5 Added detection for variable changes in Makefile, see #59 2018-09-09 19:26:32 -07:00
Jarcode
11a6137370 Added graceful renderer and audio thread destruction, fixes #47 2018-09-09 07:54:51 -07:00
Jarcode
d4b333e48c removed pointless message 2018-09-08 21:25:11 -07:00
Jarcode
d49be40fd3 Added --desktop flag with presets, added setclickthrough option, removed kde previous kde changes, updated readme 2018-09-08 21:06:18 -07:00
Jarcode
fb1d85dbf9 Added potential fixes for KDE 2018-09-08 14:05:40 -07:00
Levi Webb
31ebab0549 Merge pull request #56 from Aaahh/patch-5
Stray letter
2018-08-15 22:13:24 -07:00
Aaahh Ahh
3f621420a3 Stray letter 2018-08-15 23:14:54 -04:00
Levi Webb
9a9c6eaa37 Update README.md 2018-08-13 19:28:06 -07:00
Jarcode
3e06fa683a Updated readme with XFCE4 instructions 2018-08-06 09:12:26 -07:00
Jarcode
b971c1c8f9 Updated readme with XFCE4 instructions 2018-08-05 22:30:26 -07:00
Jarcode
45b614a692 Added configuration option for fullscreen check 2018-08-05 21:24:18 -07:00
Jarcode
fc80db664b fix startup resource leak 2018-08-05 19:47:12 -07:00
Jarcode
75a3affbc0 changed defaults to disable mirroring 2018-08-05 19:27:50 -07:00
Jarcode
198596eaee Wait for EWMH WM to add _NET_SUPPORTING_WM_CHECK property, addresses #54 2018-08-05 18:22:07 -07:00
Jarcode
e694261f4d Merge branch 'master' of https://github.com/wacossusca34/glava 2018-08-05 17:39:58 -07:00
Jarcode
8c3b9a5f21 Added audio buffer mirroring, closes #53 2018-08-05 17:38:59 -07:00
Levi Webb
ebade264ea Fix hyperlink text in license
GitHub displays license information at the head of the repostiory contents for certain licenses, however this breaks when license text is slightly altered. In this case, github only works with the GPLv3 when the license text contains `http:` instead of `https:`!
2018-06-02 09:42:07 -07:00
Jarcode
469b8ec7ad Merge branch 'master' of https://github.com/wacossusca34/glava 2018-06-02 09:29:51 -07:00
Jarcode
4cc8a5e3ba Added "pinned" option for setxwinstate, addresses #4 2018-06-02 09:28:13 -07:00
Levi Webb
309b1beeec Merge pull request #44 from coderobe/patch-1
Fix fatal make issue when using XDG_CONFIG_DIRS
2018-05-07 14:04:14 -07:00
Robin B
7b72a28f19 Fix fatal make issue when using XDG_CONFIG_DIRS 2018-05-07 11:48:17 +02:00
Levi Webb
b83c7cc59e Merge pull request #43 from Aaahh/patch-3
Update README.md
2018-05-02 06:33:29 -07:00
Aaahh Ahh
6318c57ff7 Update README.md 2018-04-27 17:30:07 -04:00
Jarcode
279437dcd1 Update README with accurate instructions for Ubuntu/Debian 2018-04-06 17:05:58 -07:00
20 changed files with 598 additions and 163 deletions

View File

@@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -4,12 +4,14 @@ obj = $(src:.c=.o)
# Build type parameter
ifeq ($(BUILD),debug)
CFLAGS_BUILD = -O0 -ggdb -Wall -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
CFLAGS_BUILD = -O0 -ggdb -Wall #-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
GLAD_GEN = c-debug
ASAN = -lasan
STRIP_CMD = $(info Skipping `strip` for debug builds)
# ASAN = -lasan
else
CFLAGS_BUILD = -O2 -march=native
CFLAGS_BUILD = -O2 -march=native -Wstringop-overflow=0
GLAD_GEN = c
STRIP_CMD = strip --strip-all glava
endif
# Detect OS if not specified (OSX, Linux, BSD are supported)
@@ -32,13 +34,13 @@ endif
ifeq ($(INSTALL),unix)
CFLAGS_INSTALL = -DGLAVA_UNIX
ifdef XDG_CONFIG_DIRS
SHADER_DIR = $(firstword $(subst :, ,$XDG_CONFIG_DIR))/glava
SHADER_DIR = $(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava
else
SHADER_DIR = etc/xdg/glava
endif
endif
ifndef DISABLE_GLFW
ifdef ENABLE_GLFW
CFLAGS_GLFW = -DGLAVA_GLFW
LDFLAGS_GLFW = -lglfw
endif
@@ -68,21 +70,31 @@ GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,
CFLAGS_COMMON = -I glad/include -DGLAVA_VERSION="$(GLAVA_VERSION)"
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS_GLX) $(CFLAGS_GLFW) $(CFLAGS_BUILD) $(CFLAGS_INSTALL) $(CFLAGS)
# Store relevant variables that may change depending on the environment or user input
STATE = $(BUILD),$(INSTALL),$(ENABLE_GLFW),$(DISABLE_GLX),$(PYTHON),$(CC),$(CFLAGS_USE)
# Only update the file if the contents changed, `make` just looks at the timestamp
$(shell if [[ ! -e build_state ]]; then touch build_state; fi)
$(shell if [ '$(STATE)' != "`cat build_state`" ]; then echo '$(STATE)' > build_state; fi)
all: glava
%.o: %.c glad.o
%.o: %.c glad.o build_state
$(CC) $(CFLAGS_USE) -o $@ -c $(firstword $<)
glava: $(obj)
$(CC) -o glava $(obj) glad.o $(LDFLAGS)
$(STRIP_CMD)
glad.o:
glad.o: build_state
cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS) --out-path=.
$(CC) $(CFLAGS_USE) -o glad.o $(GLAD_SRCFILE) -c
# Empty build state goal, used to force some of the above rules to re-run if `build_state` was updated
build_state: ;
.PHONY: clean
clean:
rm -f $(obj) glava glad.o
rm -f $(obj) glava glad.o build_state
.PHONY: install
install:

View File

@@ -3,7 +3,7 @@
**GLava** is an OpenGL audio spectrum visualizer. Its primary use case is for desktop windows or backgrounds. Displayed to the left is the `radial` shader module, and [here is a demonstration video](https://streamable.com/dgpj8). Development is active, and reporting issues is encouranged.
**Compiling** (Or use the [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:**
**Compiling** (Or use the Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or the [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:**
```bash
$ git clone --recursive https://github.com/wacossusca34/glava
@@ -17,9 +17,8 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
**Requirements:**
- X11
- X11 (Xext, Xcomposite, & Xrender)
- PulseAudio
- GLFW 3.1+ (optional, disable with `DISABLE_GLFW=1`)
- Linux or BSD
**Additional compile time requirements:**
@@ -28,9 +27,13 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
- python (required to generate bindings with glad)
- GCC (this program uses GNU C features)
**Optional requirements:**
- GLFW 3.1+ (optional, enable with `ENABLE_GLFW=1`)
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
```bash
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libglx0 libxext6 libxext-dev python make gcc
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libxext6 libxext-dev libxcomposite-dev python make gcc
```
## [Configuration](https://github.com/wacossusca34/glava/wiki)
@@ -39,9 +42,9 @@ GLava will start by looking for an entry point in the user configuration folder
You should start by running `glava --copy-config`. This will copy over default configuration files and create symlinks to modules in your user config folder. GLava will either load system configuration files or the user provided ones, so it's not advised to copy these files selectively.
To embed GLava in your desktop (for EWMH compliant window managers), use `#request setxwintype "desktop"` and then position it accordingly with `#request setgeometry x y width height`. You may want to also use `#request setforcegeometry true` for some window managers.
To embed GLava in your desktop (for EWMH compliant window managers), run it with the `--desktop` flag and then position it accordingly with `#request setgeometry x y width height` in your `rc.glsl`.
\* 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.
## Desktop window compatibility
@@ -50,21 +53,23 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
| WM | ! | Details
| :---: | --- | --- |
| Mutter (GNOME, Budgie) | ![-](https://placehold.it/15/118932/000000?text=+) | `"native"` (default) opacity should be used
| KWin (KDE) | ![-](https://placehold.it/15/118932/000000?text=+) | "Show Desktop" [temporarily hides GLava](https://github.com/wacossusca34/glava/issues/4#issuecomment-419729184)
| Openbox (LXDE or standalone) | ![-](https://placehold.it/15/118932/000000?text=+) | No 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
| Xfwm (XFCE) | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Fluxbox | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| IceWM | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Bspwm | ![-](https://placehold.it/15/118932/000000?text=+) | No 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
| Unity | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | Can still be focused, may require other changes to config depending on layout
| kwin (KDE) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing
| i3 (and i3-gaps) | ![-](https://placehold.it/15/f03c15/000000?text=+) | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6)
| i3 (and i3-gaps) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6), try `#request setxwintype "!-"`
| EXWM | ![-](https://placehold.it/15/f03c15/000000?text=+) | EXWM does not have a desktop, and forces window decorations
| Unity | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Enlightenment | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Xmonad | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Any non EWMH-compliant WM | ![-](https://placehold.it/15/f03c15/000000?text=+) | Window types and hints will not work if the window manager does not support the EWMH standards.
Note that some WMs listed without issues have specific overrides when using the `--desktop` flag. See `shaders/env_*.glsl` files for details.
## Licensing
GLava is licensed under the terms of the GPLv3. GLava includes some (heavily modified) source code that originated from [cava](https://github.com/karlstav/cava), which was initially provided under the MIT license. The source files that originated from cava are the following:
@@ -78,7 +83,7 @@ The below copyright notice applies for the original versions of these files:
`Copyright (c) 2015 Karl Stavestrand <karl@stavestrand.no>`
The modified files are relicensed under the terms of the GPLv3. The MIT license is included for your convience and to satisfy the requirements of the original license, although it (no longer) applies to any code in this repository. You will find the original copyright notice and MIT license in the `LICENSE_ORIGINAL` file.
The modified files are relicensed under the terms of the GPLv3. The MIT license is included for your convience and to satisfy the requirements of the original license, although it no longer applies to any code in this repository. You will find the original copyright notice and MIT license in the `LICENSE_ORIGINAL` file.
The below copyright applies for the modifications to the files listed above, and the remaining sources in the repository:
@@ -86,4 +91,4 @@ The below copyright applies for the modifications to the files listed above, and
## Porting
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken all the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken most of the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.

2
glad

Submodule glad updated: ec01ac515d...c33992f23c

66
glava.c
View File

@@ -5,6 +5,7 @@
#include <string.h>
#include <pthread.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
@@ -159,6 +160,9 @@ static const char* help_str =
"Available arguments:\n"
"-h, --help show this help and exit\n"
"-v, --verbose enables printing of detailed information about execution\n"
"-d, --desktop enables running glava as a desktop window by detecting the\n"
" desktop environment and setting the appropriate properties\n"
" automatically. Can override properties in \"rc.glsl\".\n"
"-m, --force-mod=NAME forces the specified module to load instead, ignoring any\n"
" `#request mod` instances in the entry point.\n"
"-e, --entry=NAME specifies the name of the file to look for when loading shaders,\n"
@@ -174,10 +178,11 @@ static const char* help_str =
GLAVA_VERSION_STRING "\n"
" -- Copyright (C) 2017 Levi Webb\n";
static const char* opt_str = "hvVe:Cm:b:";
static const char* opt_str = "dhvVe:Cm:b:";
static struct option p_opts[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"desktop", no_argument, 0, 'd'},
{"entry", required_argument, 0, 'e'},
{"force-mod", required_argument, 0, 'm'},
{"copy-config", no_argument, 0, 'C'},
@@ -186,6 +191,15 @@ static struct option p_opts[] = {
{0, 0, 0, 0 }
};
static renderer* rd = NULL;
void handle_term(int signum) {
if (rd->alive) {
puts("\nInterrupt recieved, closing...");
rd->alive = false;
}
}
int main(int argc, char** argv) {
/* Evaluate these macros only once, since they allocate */
@@ -195,14 +209,14 @@ int main(int argc, char** argv) {
const char* force = NULL;
const char* backend = NULL;
const char* system_shader_paths[] = { user_path, install_path, NULL };
bool verbose = false;
bool copy_mode = false;
bool verbose = false, copy_mode = false, desktop = false;
int c, idx;
while ((c = getopt_long(argc, argv, opt_str, p_opts, &idx)) != -1) {
switch (c) {
case 'v': verbose = true; break;
case 'C': copy_mode = true; break;
case 'd': desktop = true; break;
case 'e': entry = optarg; break;
case 'm': force = optarg; break;
case 'b': backend = optarg; break;
@@ -224,11 +238,15 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
renderer* r = rd_new(system_shader_paths, entry, force, backend);
rd = rd_new(system_shader_paths, entry, force, backend, desktop);
float b0[r->bufsize_request], b1[r->bufsize_request];
struct sigaction action = { .sa_handler = handle_term };
sigaction(SIGTERM, &action, NULL);
sigaction(SIGINT, &action, NULL);
float b0[rd->bufsize_request], b1[rd->bufsize_request];
size_t t;
for (t = 0; t < r->bufsize_request; ++t) {
for (t = 0; t < rd->bufsize_request; ++t) {
b0[t] = 0.0F;
b1[t] = 0.0F;
}
@@ -236,20 +254,20 @@ int main(int argc, char** argv) {
struct audio_data audio = {
.source = ({
char* src = NULL;
if (r->audio_source_request && strcmp(r->audio_source_request, "auto") != 0) {
src = strdup(r->audio_source_request);
if (rd->audio_source_request && strcmp(rd->audio_source_request, "auto") != 0) {
src = strdup(rd->audio_source_request);
}
src;
}),
.rate = (unsigned int) r->rate_request,
.rate = (unsigned int) rd->rate_request,
.format = -1,
.terminate = 0,
.channels = 2,
.channels = rd->mirror_input ? 1 : 2,
.audio_out_r = b0,
.audio_out_l = b1,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.audio_buf_sz = r->bufsize_request,
.sample_sz = r->samplesize_request,
.audio_buf_sz = rd->bufsize_request,
.sample_sz = rd->samplesize_request,
.modified = false
};
if (!audio.source) {
@@ -260,10 +278,10 @@ int main(int argc, char** argv) {
pthread_t thread;
pthread_create(&thread, NULL, input_pulse, (void*) &audio);
float lb[r->bufsize_request], rb[r->bufsize_request];
while (r->alive) {
float lb[rd->bufsize_request], rb[rd->bufsize_request];
while (rd->alive) {
rd_time(r); /* update timer for this frame */
rd_time(rd); /* update timer for this frame */
bool modified; /* if the audio buffer has been updated by the streaming thread */
@@ -273,16 +291,13 @@ int main(int argc, char** argv) {
if (modified) {
/* create our own copies of the audio buffers, so the streaming
thread can continue to append to it */
memcpy(lb, (void*) audio.audio_out_l, r->bufsize_request * sizeof(float));
memcpy(rb, (void*) audio.audio_out_r, r->bufsize_request * sizeof(float));
memcpy(lb, (void*) audio.audio_out_l, rd->bufsize_request * sizeof(float));
memcpy(rb, (void*) audio.audio_out_r, rd->bufsize_request * sizeof(float));
audio.modified = false; /* set this flag to false until the next time we read */
}
pthread_mutex_unlock(&audio.mutex);
/* Only render if needed (ie. stop rendering when fullscreen windows are focused) */
if (xwin_should_render(r)) {
rd_update(r, lb, rb, r->bufsize_request, modified);
} else {
if (!rd_update(rd, lb, rb, rd->bufsize_request, modified)) {
/* Sleep for 50ms and then attempt to render again */
struct timespec tv = {
.tv_sec = 0, .tv_nsec = 50 * 1000000
@@ -291,5 +306,12 @@ int main(int argc, char** argv) {
}
}
rd_destroy(r);
audio.terminate = 1;
int return_status;
if ((return_status = pthread_join(thread, NULL))) {
fprintf(stderr, "Failed to join with audio thread: %s\n", strerror(return_status));
}
free(audio.source);
rd_destroy(rd);
}

View File

@@ -70,7 +70,8 @@ static void* create_and_bind(const char* name, const char* class,
size_t states_sz,
int d, int h,
int x, int y,
int version_major, int version_minor) {
int version_major, int version_minor,
bool clickthrough) {
GLFWwindow* w;
@@ -132,6 +133,11 @@ static void get_pos (GLFWwindow* w, int* x, int* y) { glfwGetWindowPo
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); }
static void raise (GLFWwindow* w) { glfwShowWindow(w); }
static void destroy (GLFWwindow* w) { glfwDestroyWindow(w); }
static void terminate (void) { glfwTerminate(); }
static const char* get_environment(void) { return xwin_detect_wm(&wcb_glfw); }
WCB_ATTACH("glfw", wcb_glfw);

146
glx_wcb.c
View File

@@ -160,6 +160,8 @@ Bool (*glXMakeCurrent) (Display* dpy, GLXDrawable drawable,
GLXDrawable (*glXGetCurrentDrawable) (void);
__GLXextFuncPtr (*glXGetProcAddressARB) (const GLubyte *);
void (*glXSwapBuffers) (Display* dpy, GLXDrawable drawable);
void (*glXDestroyContext) (Display* dpy, GLXContext ctx);
Bool (*glXQueryVersion) (Display* dpy, int* major, int* minor);
extern struct gl_wcb wcb_glx;
@@ -173,9 +175,12 @@ struct glxwin {
Window w;
GLXContext context;
double time;
bool should_close;
bool should_close, clickthrough;
char override_state;
};
static Atom ATOM__MOTIF_WM_HINTS, ATOM_WM_DELETE_WINDOW, ATOM_WM_PROTOCOLS, ATOM__NET_ACTIVE_WINDOW;
static void init(void) {
display = XOpenDisplay(NULL);
if (!display) {
@@ -210,6 +215,8 @@ static void init(void) {
}
#define resolve(name) do { name = (typeof(name)) resolve_f(#name); } while (0)
#define intern(name, only_if_exists) \
do { ATOM_##name = XInternAtom(display, #name, only_if_exists); } while (0)
resolve(glXChooseFBConfig);
resolve(glXGetVisualFromFBConfig);
@@ -218,7 +225,15 @@ static void init(void) {
resolve(glXGetCurrentDrawable);
resolve(glXGetProcAddressARB);
resolve(glXSwapBuffers);
resolve(glXDestroyContext);
resolve(glXQueryVersion);
intern(_MOTIF_WM_HINTS, false);
intern(WM_DELETE_WINDOW, true);
intern(WM_PROTOCOLS, true);
intern(_NET_ACTIVE_WINDOW, false);
#undef intern
#undef resolve
}
@@ -233,28 +248,52 @@ static void apply_decorations(Window w) {
hints.flags = 2;
hints.decorations = 0;
Atom motif = XInternAtom(display, "_MOTIF_WM_HINTS", false);
XChangeProperty(display, w, motif, motif, 32, PropModeReplace,
XChangeProperty(display, w, ATOM__MOTIF_WM_HINTS, ATOM__MOTIF_WM_HINTS, 32, PropModeReplace,
(unsigned char*) &hints, sizeof(hints) / sizeof(long));
}
}
static void apply_clickthrough(struct glxwin* w) {
if (w->clickthrough) {
int ignored;
if (XShapeQueryExtension(display, &ignored, &ignored)) {
Region region;
if ((region = XCreateRegion())) {
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
}
}
}
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) {
int version_major, int version_minor,
bool clickthrough) {
struct glxwin* w = malloc(sizeof(struct glxwin));
w->override_state = '\0';
w->time = 0.0D;
w->should_close = false;
w->clickthrough = false;
XVisualInfo* vi;
XSetWindowAttributes attr;
XSetWindowAttributes attr = {};
GLXFBConfig* fbc;
int fb_sz, best = -1, samp = -1;
int glx_minor, glx_major;
glXQueryVersion(display, &glx_minor, &glx_major);
if (glx_major <= 1 && glx_minor < 4) {
fprintf(stderr,
"\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);
}
static int gl_attrs[] = {
GLX_X_RENDERABLE, True,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
@@ -277,8 +316,13 @@ static void* create_and_bind(const char* name, const char* class,
fbc = glXChooseFBConfig(display, DefaultScreen(display), gl_attrs, &fb_sz);
if (!fbc) {
fprintf(stderr, "glXChooseFBConfig(): failed\n" );
abort();
fprintf(stderr,
"\nFailed to obtain a GLX frame buffer that supports OpenGL %d.%d.\n"
"This is usually due to running on very old hardware or not having appropriate drivers.\n\n"
"glXChooseFBConfig(): failed with attrs "
"(GLX_CONTEXT_MAJOR_VERSION_ARB, GLX_CONTEXT_MINOR_VERSION_ARB)\n\n",
version_major, version_minor);
exit(EXIT_FAILURE);
}
for (int t = 0; t < fb_sz; ++t) {
@@ -311,15 +355,21 @@ static void* create_and_bind(const char* name, const char* class,
vi = glXGetVisualFromFBConfig(display, config);
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vi->visual, AllocNone);
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
attr.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | PropertyChangeMask;
attr.background_pixmap = None;
attr.border_pixel = 0;
if (!(w->w = XCreateWindow(display, DefaultRootWindow(display),
unsigned long vmask = CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel;
if (type[0] == '!') {
vmask |= CWOverrideRedirect;
attr.override_redirect = true;
w->override_state = type[1];
}
if (!(w->w = XCreateWindow(display, DefaultRootWindow(display)/**xwin_get_desktop_layer(&wcb_glx)*/,
x, y, d, h, 0,
vi->depth, InputOutput, vi->visual,
CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel,
&attr))) {
vmask, &attr))) {
fprintf(stderr, "XCreateWindow(): failed\n");
abort();
}
@@ -346,22 +396,11 @@ static void* create_and_bind(const char* name, const char* class,
XStoreName(display, w->w, name);
Atom dwin = XInternAtom(display, "WM_DELETE_WINDOW", false);
XSetWMProtocols(display, w->w, &dwin, 1);
// XReparentWindow(display, w->w, DefaultRootWindow(display), 0, 0);
XSetWMProtocols(display, w->w, &ATOM_WM_DELETE_WINDOW, 1);
/* Eliminate the window's effective region */
if (desktop){
int ignored;
if (XShapeQueryExtension(display, &ignored, &ignored)) {
Region region;
if ((region = XCreateRegion())) {
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
}
}
w->clickthrough = desktop || clickthrough;
apply_clickthrough(w);
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = NULL;
glXSwapIntervalEXTProc glXSwapIntervalEXT = NULL;
@@ -389,9 +428,36 @@ static void* create_and_bind(const char* name, const char* class,
if (glXSwapIntervalEXT) glXSwapIntervalEXT(display, drawable, swap);
// XSelectInput(display, DefaultRootWindow(display), VisibilityChangeMask | PropertyChangeMask);
return w;
}
static void raise(struct glxwin* w) {
if (w->override_state == '\0') {
XClientMessageEvent ev = {
.type = ClientMessage,
.serial = 0,
.send_event = true,
.display = display,
.window = w->w,
.message_type = ATOM__NET_ACTIVE_WINDOW,
.format = 32,
.data = { .l = {
[0] = 1, /* source indication -- `1` when coming from an application */
[1] = 0, /* timestamp -- `0` to (attempt to) ignore */
[2] = w->w /* requestor's currently active window -- `0` for none */
}
}
};
/* Send the client message as defined by EWMH standards (usually works) */
XSendEvent(display, DefaultRootWindow(display), false, StructureNotifyMask, (XEvent*) &ev);
}
/* Raise the client in the X11 stacking order (sometimes works, can be blocked by the WM) */
XRaiseWindow(display, w->w);
XFlush(display);
}
static void set_swap (int _swap) { swap = _swap; }
static void set_floating (bool _floating) { floating = _floating; }
static void set_decorated (bool _decorated) { decorated = _decorated; }
@@ -404,7 +470,16 @@ static void set_geometry(struct glxwin* w, int x, int y, int d, int h) {
}
static void set_visible(struct glxwin* w, bool visible) {
if (visible) XMapWindow(display, w->w);
if (visible) {
XMapWindow(display, w->w);
apply_clickthrough(w);
switch (w->override_state) {
case '+': XRaiseWindow(display, w->w); break;
case '-': XLowerWindow(display, w->w); break;
default: break;
}
XFlush(display);
}
else XUnmapWindow(display, w->w);
}
@@ -420,10 +495,11 @@ static void swap_buffers(struct glxwin* w) {
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)) {
if (ev.xclient.message_type == ATOM_WM_PROTOCOLS
&& ev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
w->should_close = true;
}
break;
default: break;
}
}
@@ -449,11 +525,23 @@ static double get_timert(void) {
return (double) tv.tv_sec + ((double) tv.tv_nsec / 1000000000.0D);
}
static void destroy(struct glxwin* w) {
glXMakeCurrent(display, None, NULL); /* release context */
glXDestroyContext(display, w->context);
XDestroyWindow(display, w->w);
}
static void terminate(void) {
XCloseDisplay(display);
}
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; }
static const char* get_environment(void) { return xwin_detect_wm(&wcb_glx); }
WCB_ATTACH("glx", wcb_glx);
#endif /* GLAVA_GLX */

View File

@@ -123,7 +123,7 @@ void* input_pulse(void* data) {
.channels = 2
};
const pa_buffer_attr pb = {
.maxlength = ssz * 2,
.maxlength = (uint32_t) -1,
.fragsize = ssz
};
@@ -168,7 +168,11 @@ void* input_pulse(void* data) {
int idx = (fsz - (ssz / 4)) + n;
if (audio->channels == 1) bl[idx] = (buf[i] + buf[i + 1]) / 2;
if (audio->channels == 1) {
float sample = (buf[i] + buf[i + 1]) / 2;
bl[idx] = sample;
br[idx] = sample;
}
/* stereo storing channels in buffer */
if (audio->channels == 2) {

154
render.c
View File

@@ -108,8 +108,9 @@ struct gl_data {
int rate; /* framerate */
double tcounter;
int fcounter, ucounter, kcounter;
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop,
smooth_pass, premultiply_alpha;
bool print_fps, avg_window, interpolate, force_geometry, force_raised,
copy_desktop, smooth_pass, premultiply_alpha, check_fullscreen,
clickthrough;
void** t_data;
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio,
smooth_factor, fft_scale, fft_cutoff;
@@ -680,11 +681,15 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
}
struct renderer* rd_new(const char** paths, const char* entry,
const char* force_mod, const char* force_backend) {
const char* force_mod, const char* force_backend,
bool auto_desktop) {
xwin_wait_for_wm();
renderer* r = malloc(sizeof(struct renderer));
*r = (struct renderer) {
.alive = true,
.mirror_input = false,
.gl = malloc(sizeof(struct gl_data)),
.bufsize_request = 8192,
.rate_request = 22000,
@@ -711,6 +716,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
.gravity_step = 4.2,
.interpolate = true,
.force_geometry = false,
.force_raised = false,
.smooth_factor = 0.025,
.smooth_distance = 0.01,
.smooth_ratio = 4,
@@ -718,11 +724,13 @@ struct renderer* rd_new(const char** paths, const char* entry,
.sm_prog = 0,
.copy_desktop = true,
.premultiply_alpha = true,
.check_fullscreen = true,
.smooth_pass = true,
.fft_scale = 10.2F,
.fft_cutoff = 0.3F,
.geometry = { 0, 0, 500, 400 },
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F }
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F },
.clickthrough = false
};
bool forced = force_backend != NULL;
@@ -783,10 +791,11 @@ struct renderer* rd_new(const char** paths, const char* entry,
context_version_major = 3,
context_version_minor = 3;
const char* module = force_mod;
char* xwintype = NULL, * wintitle = "GLava";
const char* wintitle_default = "GLava";
char* xwintype = NULL, * wintitle = (char*) wintitle_default;
char** xwinstates = malloc(1);
size_t xwinstates_sz = 0;
bool loading_module = true, loading_smooth_pass = false;
bool loading_module = true, loading_smooth_pass = false, loading_presets = false;;
struct gl_sfbo* current = NULL;
size_t t_count = 0;
@@ -816,6 +825,14 @@ struct renderer* rd_new(const char** paths, const char* entry,
}
})
},
{
.name = "setmirror", .fmt = "b",
.handler = RHANDLER(name, args, { r->mirror_input = *(bool*) args[0]; })
},
{
.name = "setfullscreencheck", .fmt = "b",
.handler = RHANDLER(name, args, { gl->check_fullscreen = *(bool*) args[0]; })
},
{
.name = "setbg", .fmt = "s",
.handler = RHANDLER(name, args, {
@@ -844,8 +861,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
.name = "mod", .fmt = "s",
.handler = RHANDLER(name, args, {
if (loading_module && !force_mod) {
if (module != NULL && module != force_mod) free((char*) module);
size_t len = strlen((char*) args[0]);
char* str = malloc(sizeof(char) * (strlen((char*) args[0]) + 1));
char* str = malloc(sizeof(char) * (len + 1));
strncpy(str, (char*) args[0], len + 1);
module = str;
}
@@ -885,18 +903,27 @@ struct renderer* rd_new(const char** paths, const char* entry,
{
.name = "addxwinstate", .fmt = "s",
.handler = RHANDLER(name, args, {
++xwinstates_sz;
xwinstates = realloc(xwinstates, sizeof(*xwinstates) * xwinstates_sz);
xwinstates[xwinstates_sz - 1] = strdup((char*) args[0]);
if (!auto_desktop || loading_presets) {
++xwinstates_sz;
xwinstates = realloc(xwinstates, sizeof(*xwinstates) * xwinstates_sz);
xwinstates[xwinstates_sz - 1] = strdup((char*) args[0]);
}
})
},
{ .name = "setsource", .fmt = "s",
.handler = RHANDLER(name, args, {
if (r->audio_source_request) free(r->audio_source_request);
r->audio_source_request = strdup((char*) args[0]); }) },
{ .name = "setclickthrough", .fmt = "b",
.handler = RHANDLER(name, args, { gl->clickthrough = *(bool*) args[0]; }) },
{ .name = "setforcegeometry", .fmt = "b",
.handler = RHANDLER(name, args, { gl->force_geometry = *(bool*) args[0]; }) },
{ .name = "setforceraised", .fmt = "b",
.handler = RHANDLER(name, args, { gl->force_raised = *(bool*) args[0]; }) },
{ .name = "setxwintype", .fmt = "s",
.handler = RHANDLER(name, args, { xwintype = strdup((char*) args[0]); }) },
.handler = RHANDLER(name, args, {
if (xwintype) free(xwintype);
xwintype = strdup((char*) args[0]); }) },
{ .name = "setshaderversion", .fmt = "i",
.handler = RHANDLER(name, args, { shader_version = *(int*) args[0]; }) },
{ .name = "setswap", .fmt = "i",
@@ -906,7 +933,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
{ .name = "setprintframes", .fmt = "b",
.handler = RHANDLER(name, args, { gl->print_fps = *(bool*) args[0]; }) },
{ .name = "settitle", .fmt = "s",
.handler = RHANDLER(name, args, { wintitle = strdup((char*) args[0]); }) },
.handler = RHANDLER(name, args, {
if (wintitle && wintitle != wintitle_default) free((char*) wintitle);
wintitle = strdup((char*) args[0]); }) },
{ .name = "setbufsize", .fmt = "i",
.handler = RHANDLER(name, args, { r->bufsize_request = *(int*) args[0]; }) },
{ .name = "setbufscale", .fmt = "i",
@@ -1023,12 +1052,18 @@ struct renderer* rd_new(const char** paths, const char* entry,
directories will be populated with symlinks to the installed modules. */
const char* data;
size_t d_len;
const char* env = gl->wcb->get_environment();
size_t d_len, e_len;
for (const char** i = paths; (data = *i) != NULL; ++i) {
d_len = strlen(data);
e_len = env ? strlen(env) : 0;
size_t se_len = strlen(entry);
size_t bsz = se_len + d_len + 2;
/* '/' + \0 + "env_" + ".glsl" = 11 char padding, min 7 for "default" */
size_t bsz = se_len + 11;
if (d_len > e_len && d_len >= 7) bsz += d_len;
else if (e_len >= 7) bsz += e_len;
else bsz += 7;
char se_buf[bsz];
snprintf(se_buf, bsz, "%s/%s", data, entry);
@@ -1040,7 +1075,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
if (errno != ENOENT &&
errno != ENOTDIR &&
errno != ELOOP ) {
fprintf(stderr, "failed to load entry '%s': %s\n", se_buf, strerror(errno));
fprintf(stderr, "Failed to load entry '%s': %s\n", se_buf, strerror(errno));
exit(EXIT_FAILURE);
} else continue;
}
@@ -1059,6 +1094,42 @@ struct renderer* rd_new(const char** paths, const char* entry,
munmap((void*) map, st.st_size);
if (auto_desktop) {
if (env) {
snprintf(se_buf, bsz, "%s/env_%s.glsl", data, env);
fd = open(se_buf, O_RDONLY);
if (fd == -1) {
if (errno != ENOENT &&
errno != ENOTDIR &&
errno != ELOOP) {
fprintf(stderr, "Failed to load desktop environment specific presets at '%s': %s\n", se_buf, strerror(errno));
exit(EXIT_FAILURE);
} else {
printf("No presets for current desktop environment (\"%s\"), using default presets for embedding\n", env);
snprintf(se_buf, bsz, "%s/env_default.glsl", data);
fd = open(se_buf, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Failed to load default presets at '%s': %s\n", se_buf, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
fstat(fd, &st);
map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
ext.source = map;
ext.source_len = st.st_size;
loading_presets = true;
ext_process(&ext, se_buf);
loading_presets = false;
munmap((void*) map, st.st_size);
} else {
fprintf(stderr, "Failed to detect the desktop environment! Is the window manager EWMH compliant?");
}
}
break;
}
@@ -1070,16 +1141,17 @@ struct renderer* rd_new(const char** paths, const char* entry,
exit(EXIT_FAILURE);
}
if (!(gl->w = gl->wcb->create_and_bind(wintitle, "GLava", xwintype,
(const char**) xwinstates, xwinstates_sz,
gl->geometry[2], gl->geometry[3],
gl->geometry[0], gl->geometry[1],
context_version_major, context_version_minor))) {
abort();
}
gl->w = gl->wcb->create_and_bind(wintitle, "GLava", xwintype, (const char**) xwinstates, xwinstates_sz,
gl->geometry[2], gl->geometry[3], gl->geometry[0], gl->geometry[1],
context_version_major, context_version_minor, gl->clickthrough);
if (!gl->w) abort();
for (size_t t = 0; t < xwinstates_sz; ++t)
free(xwinstates[t]);
if (xwintype) free(xwintype);
if (xwinstates) free(xwinstates);
if (wintitle && wintitle != wintitle_default) free(wintitle);
glDisable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_CLAMP);
@@ -1269,13 +1341,18 @@ void rd_time(struct renderer* r) {
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) {
bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modified) {
struct gl_data* gl = r->gl;
size_t t, a, fbsz = bsz * sizeof(float);
r->alive = !gl->wcb->should_close(gl->w);
if (!r->alive)
return;
if (gl->wcb->should_close(gl->w)) {
r->alive = false;
return true;
}
/* Stop rendering when fullscreen windows are focused */
if (gl->check_fullscreen && !xwin_should_render(r))
return false;
/* Force disable interpolation if the update rate is close to or higher than the frame rate */
float uratio = (gl->ur / gl->fr); /* update : framerate ratio */
@@ -1617,17 +1694,40 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
gl->geometry[0], gl->geometry[1],
gl->geometry[2], gl->geometry[3]);
}
if (gl->force_raised) {
gl->wcb->raise(gl->w);
}
}
/* Restore interpolation settings */
gl->interpolate = old_interpolate;
return true;
}
void* rd_get_impl_window (struct renderer* r) { return r->gl->w; }
struct gl_wcb* rd_get_wcb (struct renderer* r) { return r->gl->wcb; }
void rd_destroy(struct renderer* r) {
/* TODO: delete everything else, not really needed though (as the application exits after here) */
r->gl->wcb->destroy(r->gl->w);
if (r->gl->interpolate) free(r->gl->interpolate_buf[0]);
if (r->gl->t_data) free(r->gl->t_data);
size_t t, b;
for (t = 0; t < r->gl->stages_sz; ++t) {
struct gl_sfbo* stage = &r->gl->stages[t];
for (b = 0; b < stage->binds_sz; ++b) {
struct gl_bind* bind = &stage->binds[b];
free(bind->transformations);
free((char*) bind->name); /* strdup */
}
free(stage->binds);
free((char*) stage->name); /* strdup */
}
free(r->gl->stages);
r->gl->wcb->terminate();
free(r->gl);
if (r->audio_source_request)
free(r->audio_source_request);
free(r);
}

View File

@@ -5,15 +5,16 @@
struct gl_data;
typedef struct renderer {
bool alive;
bool alive, mirror_input;
size_t bufsize_request, rate_request, samplesize_request;
char* audio_source_request;
struct gl_data* gl;
} renderer;
struct renderer* rd_new (const char** paths, const char* entry,
const char* force_mod, const char* force_backend);
void rd_update (struct renderer*, float* lb, float* rb,
const char* force_mod, const char* force_backend,
bool auto_desktop);
bool rd_update (struct renderer*, float* lb, float* rb,
size_t bsz, bool modified);
void rd_destroy (struct renderer*);
void rd_time (struct renderer*);
@@ -29,9 +30,13 @@ struct gl_wcb {
size_t states_sz,
int w, int h,
int x, int y,
int version_major, int version_minor);
int version_major, int version_minor,
bool clickthrough);
bool (*should_close) (void* ptr);
void (*swap_buffers) (void* ptr);
void (*raise) (void* ptr);
void (*destroy) (void* ptr);
void (*terminate) (void);
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);
@@ -44,6 +49,7 @@ struct gl_wcb {
double (*get_time) (void* ptr);
void (*set_time) (void* ptr, double time);
void (*set_visible) (void* ptr, bool visible);
const char* (*get_environment) (void);
#ifdef GLAVA_RDX11
Display* (*get_x11_display)(void);
Window (*get_x11_window) (void* ptr);
@@ -63,6 +69,9 @@ struct gl_wcb {
WCB_FUNC(create_and_bind), \
WCB_FUNC(should_close), \
WCB_FUNC(swap_buffers), \
WCB_FUNC(raise), \
WCB_FUNC(destroy), \
WCB_FUNC(terminate), \
WCB_FUNC(set_swap), \
WCB_FUNC(get_pos), \
WCB_FUNC(get_fbsize), \
@@ -75,6 +84,7 @@ struct gl_wcb {
WCB_FUNC(set_time), \
WCB_FUNC(get_time), \
WCB_FUNC(set_visible), \
WCB_FUNC(get_environment), \
WCB_FUNC(get_x11_display), \
WCB_FUNC(get_x11_window) \
}

View File

@@ -1,21 +1,32 @@
/* center line thickness (pixels) */
/* Center line thickness (pixels) */
#define C_LINE 1
/* width (in pixels) of each bar */
/* Width (in pixels) of each bar */
#define BAR_WIDTH 4
/* width (in pixels) of each bar gap */
/* Width (in pixels) of each bar gap */
#define BAR_GAP 2
/* outline color */
/* Outline color */
#define BAR_OUTLINE #262626
/* outline width (in pixels, set to 0 to disable outline drawing) */
/* Outline width (in pixels, set to 0 to disable outline drawing) */
#define BAR_OUTLINE_WIDTH 0
/* Amplify magnitude of the results each bar displays */
#define AMPLIFY 300
/* Alpha channel for bars color */
#define ALPHA 0.7
/* How strong the gradient changes */
#define GRADIENT_POWER 60
/* Bar color changes with height */
#define GRADIENT (d / GRADIENT_POWER + 1)
/* Bar color */
#define COLOR (#3366b2 * ((d / 60) + 1))
#define COLOR (#3366b2 * GRADIENT * ALPHA)
/* Direction that the bars are facing, 0 for inward, 1 for outward */
#define DIRECTION 0
/* Whether to switch left/right audio buffers */
#define INVERT 0
/* Whether to flip the output vertically */
#define FLIP 0
/* Whether to mirror output along `Y = X`, causing output to render on the left side of the window */
/* Use with `FLIP 1` to render on the right side */
#define MIRROR_YX 0

View File

@@ -29,17 +29,33 @@ out vec4 fragment;
#define PI 3.14159265359
void main() {
float /* (x, magnitude) of fragment */
dx = (gl_FragCoord.x - (screen.x / 2)),
d = gl_FragCoord.y;
float nbars = floor((screen.x * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2;
#if MIRROR_YX == 0
#define AREA_WIDTH screen.x
#define AREA_HEIGHT screen.y
#define AREA_X gl_FragCoord.x
#define AREA_Y gl_FragCoord.y
#else
#define AREA_WIDTH screen.y
#define AREA_HEIGHT screen.x
#define AREA_X gl_FragCoord.y
#define AREA_Y gl_FragCoord.x
#endif
float dx = (AREA_X - (AREA_WIDTH / 2));
#if FLIP == 0
float d = AREA_Y;
#else
float d = AREA_HEIGHT - AREA_Y;
#endif
float nbars = floor((AREA_WIDTH * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2;
float section = BAR_WIDTH + BAR_GAP; /* size of section for each bar (including gap) */
float center = section / 2.0F; /* half section, distance to center */
float m = abs(mod(dx, section)); /* position in section */
float md = m - center; /* position in section from center line */
if (md < ceil(float(BAR_WIDTH) / 2) && md >= -floor(float(BAR_WIDTH) / 2)) { /* if not in gap */
float p = int(dx / section) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
p += sign(p) * ((0.5F + center) / screen.x); /* index center of bar position */
p += sign(p) * ((0.5F + center) / AREA_WIDTH); /* index center of bar position */
/* Apply smooth function and index texture */
#define smooth_f(tex, p) smooth_audio(tex, audio_sz, p)
float v;

8
shaders/env_KWin.glsl Normal file
View File

@@ -0,0 +1,8 @@
#request setdecorated false
#request setxwintype "normal"
#request addxwinstate "below"
#request addxwinstate "skip_taskbar"
#request addxwinstate "skip_pager"
#request addxwinstate "pinned"
#request setclickthrough true

2
shaders/env_Openbox.glsl Normal file
View File

@@ -0,0 +1,2 @@
#request setxwintype "desktop"
#request addxwinstate "pinned"

3
shaders/env_Xfwm4.glsl Normal file
View File

@@ -0,0 +1,3 @@
#request setxwintype "desktop"
#request addxwinstate "pinned"
#request addxwinstate "below"

2
shaders/env_awesome.glsl Normal file
View File

@@ -0,0 +1,2 @@
#request setxwintype "desktop"
#request addxwinstate "pinned"

1
shaders/env_default.glsl Normal file
View File

@@ -0,0 +1 @@
#request setxwintype "desktop"

View File

@@ -21,11 +21,6 @@
#request setfocused false
#request setmaximized false
/* Force window geometry (locking the window in place), useful
for some pesky WMs that try to reposition the window when
embedding in the desktop. */
#request setforcegeometry false
/* Set window background opacity mode. Possible values are:
"native" - True transparency provided by the compositor. Can
@@ -41,6 +36,9 @@
"none" - Disable window opacity completely. */
#request setopacity "native"
/* Whether to mirror left and right audio input channels from PulseAudio.*/
#request setmirror false
/* OpenGL context and GLSL shader versions, do not change unless
you *absolutely* know what you are doing. */
#request setversion 3 3
@@ -63,9 +61,12 @@
This will set _NET_WM_WINDOW_TYPE to _NET_WM_WINDOW_TYPE_(TYPE),
where (TYPE) is the one of the window types listed (after being
converted to uppercase). More information can be found at:
converted to uppercase).
https://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#idm140130317606816
Alternatively, you can set this value to "!", which will cause
the window to be unmanaged. If this is set, then `addxwinstate`
will do nothing, but you can use "!+" and "!-" to stack on top
or below other windows.
*/
#request setxwintype "normal"
@@ -74,7 +75,7 @@
"modal", "sticky", "maximized_vert", "maximized_horz",
"shaded", "skip_taskbar", "skip_pager", "hidden", "fullscreen",
"above", "below", "demands_attention", "focused"
"above", "below", "demands_attention", "focused", "pinned"
This will add _NET_WM_STATE_(TYPE) atoms to _NET_WM_STATE,
where (TYPE) is one of the window states listed (after being
@@ -89,6 +90,13 @@
// #request addxwinstate "skip_taskbar"
// #request addxwinstate "skip_pager"
// #request addxwinstate "above"
// #request addxwinstate "pinned"
/* (X11 only) Use the XShape extension to support clicking through
the GLava window. Useful when you want to interact with other
desktop windows (icons, menus, desktop shells). Enabled by
default when GLava itself is a desktop window. */
#request setclickthrough false
/* PulseAudio source. Can be a number or a name of an audio
sink or device to record from. Set to "auto" to use the
@@ -117,9 +125,15 @@
#request setinterpolate true
/* Frame limiter, set to the frames per second (FPS) desired or
simple set to zero (or lower) to disable the frame limiter. */
simply set to zero (or lower) to disable the frame limiter. */
#request setframerate 0
/* Enable/disable fullscreen checks. This looks at the currently
focused window and halts GLava's rendering if it is
fullscreen. This prevents rendering from interfering with
other graphically intensive tasks. */
#request setfullscreencheck true
/* Enable/disable printing framerate every second. 'FPS' stands
for 'Frames Per Second', and 'UPS' stands for 'Updates Per
Second'. Updates are performed when new data is submitted
@@ -175,6 +189,21 @@
value unless you have a strange PulseAudio configuration. */
#request setsamplerate 22050
/* ** DEPRECATED **
Force window geometry (locking the window in place), useful
for some pesky WMs that try to reposition the window when
embedding in the desktop.
This routinely sends X11 events and should be avoided. */
#request setforcegeometry false
/* ** DEPRECATED **
Force window to be raised (focused in some WMs), useful for
WMs that have their own stacking order for desktop windows.
This routinely sends X11 events and should be avoided. */
#request setforceraised false
/* ** DEPRECATED **
Scale down the audio buffer before any operations are
performed on the data. Higher values are faster.

151
xwin.c
View File

@@ -8,6 +8,8 @@
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
@@ -23,11 +25,13 @@
#include "render.h"
#include "xwin.h"
static Window find_desktop(struct renderer* r) {
/* Note: currently unused */
Window* __attribute__ ((unused)) xwin_get_desktop_layer(struct gl_wcb* wcb) {
static Window desktop;
static bool searched = false;
if (!searched) {
Display* d = rd_get_wcb(r)->get_x11_display();
Display* d = wcb->get_x11_display();
Atom class = XInternAtom(d, "WM_CLASS", false);
desktop = DefaultRootWindow(d);
Window _ignored, * children;
unsigned int nret;
@@ -39,18 +43,101 @@ static Window find_desktop(struct renderer* r) {
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];
printf("Reparenting to mutter guard window instead of root window\n");
desktop = children[t];
t = nret; /* break after */
}
XFree(name);
}
unsigned long bytes;
XTextProperty text = {};
char** list;
int list_sz;
/* Get WM_CLASS property */
if (Success == XGetWindowProperty(d, children[t], class, 0, 512, false, AnyPropertyType,
&text.encoding, &text.format, &text.nitems, &bytes,
&text.value)) {
/* decode string array */
if (Success == XmbTextPropertyToTextList(d, &text, &list, &list_sz)) {
if (list_sz >= 1 && !strcmp(list[0], "plasmashell")) {
desktop = children[t];
t = nret;
}
XFreeStringList(list);
}
XFree(text.value);
}
}
XFree(children);
}
searched = true;
}
return desktop;
return &desktop;
}
void xwin_wait_for_wm(void) {
Display* d = XOpenDisplay(0);
Atom check = None;
bool exists = false;
struct timespec tv = { .tv_sec = 0, .tv_nsec = 50 * 1000000 };
do {
if (check == None) {
check = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", true);
}
if (check) {
int num_prop, idx;
Atom* props = XListProperties(d, DefaultRootWindow(d), &num_prop);
for (idx = 0; idx < num_prop; ++idx) {
if (props[idx] == check) {
exists = true;
break;
}
}
XFree(props);
}
if (!exists) nanosleep(&tv, NULL);
} while (!exists);
XCloseDisplay(d);
}
const char* xwin_detect_wm(struct gl_wcb* wcb) {
Display* d = wcb->get_x11_display();
Atom check = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", false);
Atom name = XInternAtom(d, "_NET_WM_NAME", false);
Atom type = XInternAtom(d, "UTF8_STRING", false);
union {
Atom a;
int i;
long unsigned int lui;
} ignored;
unsigned long nitems = 0;
unsigned char* wm_name = NULL;
Window* wm_check;
if (Success != XGetWindowProperty(d, DefaultRootWindow(d), check, 0, 1024, false, XA_WINDOW,
&ignored.a, &ignored.i, &nitems, &ignored.lui, (unsigned char**) &wm_check)) {
return NULL;
}
if (nitems > 0 && Success == XGetWindowProperty(d, *wm_check, name, 0, 1024, false, type,
&ignored.a, &ignored.i, &nitems, &ignored.lui, &wm_name)) {
if (nitems > 0) {
static const char* wm_name_store = NULL;
if (wm_name_store) XFree((unsigned char*) wm_name_store);
wm_name_store = (const char*) wm_name;
} else {
XFree(wm_name);
wm_name = NULL;
}
}
XFree(wm_check);
return (const char*) wm_name;
}
bool xwin_should_render(struct renderer* rd) {
@@ -107,37 +194,53 @@ bool xwin_should_render(struct renderer* rd) {
return ret;
}
static bool xwin_changeatom(struct gl_wcb* wcb, void* impl, const char* type,
/* Create string copy on stack with upcase chars */
#define S_UPPER(in, out) char out[strlen(in) + 1]; \
do { \
for (size_t t = 0; t < sizeof(out) / sizeof(char); ++t) { \
char c = in[t]; \
switch (c) { \
case 'a' ... 'z': c -= 'a' - 'A'; \
default: out[t] = c; \
} \
} \
} while (0)
static void xwin_changeatom(struct gl_wcb* wcb, void* impl, const char* type,
const char* atom, const char* fmt, int mode) {
Window w = wcb->get_x11_window(impl);
Display* d = wcb->get_x11_display();
Atom wtype = XInternAtom(d, atom, false);
size_t len = strlen(type), t;
char formatted[len + 1];
for (t = 0; t < len + 1; ++t) {
char c = type[t];
switch (c) {
case 'a' ... 'z': c -= 'a' - 'A';
default: formatted[t] = c;
}
}
bool ret = !strcmp(formatted, "DESKTOP");
char buf[256];
snprintf(buf, sizeof(buf), fmt, formatted);
snprintf(buf, sizeof(buf), fmt, type);
Atom desk = XInternAtom(d, buf, false);
XChangeProperty(d, w, wtype, XA_ATOM, 32, mode, (unsigned char*) &desk, 1);
return ret;
}
/* Set window types defined by the EWMH standard, possible values:
-> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */
bool xwin_settype(struct gl_wcb* wcb, void* impl, const char* type) {
return xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE",
"_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
bool xwin_settype(struct gl_wcb* wcb, void* impl, const char* rtype) {
S_UPPER(rtype, type);
if (type[0] != '!') {
xwin_changeatom(wcb, impl, type, "_NET_WM_WINDOW_TYPE",
"_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
}
return !strcmp(type, "DESKTOP");
}
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* state) {
xwin_changeatom(wcb, impl, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* rstate) {
S_UPPER(rstate, state);
if (strcmp(state, "PINNED"))
xwin_changeatom(wcb, impl, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
else
xwin_setdesktop(wcb, impl, XWIN_ALL_DESKTOPS);
}
void xwin_setdesktop(struct gl_wcb* wcb, void* impl, unsigned long desktop) {
Window w = wcb->get_x11_window(impl);
Display* d = wcb->get_x11_display();
Atom wtype = XInternAtom(d, "_NET_WM_DESKTOP", false);
XChangeProperty(d, w, wtype, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &desktop, 1);
}
static Drawable get_drawable(Display* d, Window w) {
@@ -175,7 +278,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
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));
Drawable src = get_drawable(d, DefaultRootWindow(d));
bool use_shm = XShmQueryExtension(d);
/* Obtain section of root pixmap */

13
xwin.h
View File

@@ -1,5 +1,18 @@
#define XWIN_ALL_DESKTOPS 0xFFFFFFFF
#ifndef XWIN_H
#define XWIN_H
typedef unsigned long int Window;
bool xwin_should_render(struct renderer* rd);
void xwin_wait_for_wm(void);
bool xwin_settype(struct gl_wcb* wcb, void* impl, const char* type);
void xwin_setdesktop(struct gl_wcb* wcb, void* impl, unsigned long desktop);
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* state);
unsigned int xwin_copyglbg(struct renderer* rd, unsigned int texture);
Window* xwin_get_desktop_layer(struct gl_wcb* wcb);
const char* xwin_detect_wm(struct gl_wcb* wcb);
#endif