Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bb7913bd3 | ||
|
|
1a1cbc9cc8 | ||
|
|
83a94e3eb4 | ||
|
|
acdbb8f3b5 | ||
|
|
829c1be370 | ||
|
|
261ba8ded5 | ||
|
|
fc9c74d256 | ||
|
|
a697af9a0a | ||
|
|
437aa146a4 | ||
|
|
1c2f362219 | ||
|
|
7dfb68fb1a | ||
|
|
353c3bd62f | ||
|
|
e4b16cdbb6 | ||
|
|
8102f99683 | ||
|
|
14747b1e75 | ||
|
|
217d5c9eea | ||
|
|
3be916f157 | ||
|
|
bc955a5b3d | ||
|
|
c7ad0a5024 | ||
|
|
83ad0d8f8a | ||
|
|
53f7352347 | ||
|
|
643b0cf3f5 | ||
|
|
11a6137370 | ||
|
|
d4b333e48c | ||
|
|
d49be40fd3 | ||
|
|
fb1d85dbf9 | ||
|
|
31ebab0549 | ||
|
|
3f621420a3 | ||
|
|
9a9c6eaa37 | ||
|
|
3e06fa683a | ||
|
|
b971c1c8f9 | ||
|
|
45b614a692 | ||
|
|
fc80db664b | ||
|
|
75a3affbc0 | ||
|
|
198596eaee | ||
|
|
e694261f4d | ||
|
|
8c3b9a5f21 | ||
|
|
ebade264ea | ||
|
|
469b8ec7ad | ||
|
|
4cc8a5e3ba | ||
|
|
309b1beeec | ||
|
|
7b72a28f19 | ||
|
|
b83c7cc59e | ||
|
|
6318c57ff7 | ||
|
|
279437dcd1 | ||
|
|
869ebba6b4 | ||
|
|
dfd16f9e22 | ||
|
|
b446ac99c9 | ||
|
|
8024e308d8 | ||
|
|
dd5586a76e | ||
|
|
20e755fbcb | ||
|
|
ccf3c7b169 | ||
|
|
4be89c3337 | ||
|
|
2220946a2f | ||
|
|
9fb80be00f | ||
|
|
283afaaaca | ||
|
|
5ac2cc4a94 | ||
|
|
273cca946e | ||
|
|
95ceadf0e1 | ||
|
|
f32f0ded0f | ||
|
|
3a1dcac8c9 | ||
|
|
8adc8fe459 | ||
|
|
e2f32a14cf | ||
|
|
318b4b3395 | ||
|
|
b89fcd2e44 | ||
|
|
baed3086d0 | ||
|
|
b4fe4b5c97 | ||
|
|
0849cb485e | ||
|
|
c086222fb2 | ||
|
|
639c6d7fe9 | ||
|
|
22bc7c446c | ||
|
|
b86870e0fd | ||
|
|
f021457abd | ||
|
|
4fd4ce4c3f | ||
|
|
e5f5671acc | ||
|
|
79d06b070e | ||
|
|
3df89da8bb | ||
|
|
79b99b09be | ||
|
|
0f4eed5e20 | ||
|
|
ae09561d11 | ||
|
|
33aa5274ae | ||
|
|
caee6a739c | ||
|
|
c8cce220d8 | ||
|
|
be63f40dc2 | ||
|
|
f5a9f801d1 | ||
|
|
53bd657226 | ||
|
|
e6f2507b53 | ||
|
|
2805528d43 | ||
|
|
1de3a5215b | ||
|
|
1db4ea3ded | ||
|
|
901cdb7d67 | ||
|
|
d82762a471 | ||
|
|
ec6d4d0c37 | ||
|
|
aea30abf67 | ||
|
|
79d391938d | ||
|
|
8c3a404f37 | ||
|
|
f539f18135 | ||
|
|
ab55156826 | ||
|
|
5e813e25a9 | ||
|
|
83f5583eeb |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,17 +0,0 @@
|
||||
|
||||
## 1.2
|
||||
|
||||
* Added support for GLFW 3.1, see issue #13
|
||||
* Added configurable solid background color, see #16
|
||||
* Fixed issue with excessive file reads from `~/.Xauthority` see #15
|
||||
|
||||
## 1.1
|
||||
|
||||
* Added `circle` module
|
||||
* Added anti-aliasing to the `radial` module, written into the shader itself
|
||||
* Default configuration values should create more appealing visualizers now
|
||||
* Massive performance improvements, see issue #10
|
||||
|
||||
## 1.0
|
||||
|
||||
* All versions of GLava with `1.0` version numbers are older development releases.
|
||||
8
LICENSE
8
LICENSE
@@ -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>.
|
||||
|
||||
47
Makefile
47
Makefile
@@ -4,12 +4,14 @@ obj = $(src:.c=.o)
|
||||
# Build type parameter
|
||||
|
||||
ifeq ($(BUILD),debug)
|
||||
CFLAGS_BUILD = -O1 -ggdb -Wall -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
CFLAGS_BUILD = -O0 -ggdb -Wall #-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
GLAD_GEN = c-debug
|
||||
ASAN = -lasan
|
||||
STRIP_CMD = $(info Skipping `strip` for debug builds)
|
||||
# ASAN = -lasan
|
||||
else
|
||||
CFLAGS_BUILD = -O2 -march=native
|
||||
CFLAGS_BUILD = -O2 -Wstringop-overflow=0
|
||||
GLAD_GEN = c
|
||||
STRIP_CMD = strip --strip-all glava
|
||||
endif
|
||||
|
||||
# Detect OS if not specified (OSX, Linux, BSD are supported)
|
||||
@@ -32,42 +34,67 @@ 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
|
||||
|
||||
ifdef ENABLE_GLFW
|
||||
CFLAGS_GLFW = -DGLAVA_GLFW
|
||||
LDFLAGS_GLFW = -lglfw
|
||||
endif
|
||||
|
||||
ifndef DISABLE_GLX
|
||||
CFLAGS_GLX = -DGLAVA_GLX
|
||||
LDFLAGS_GLX = -lXrender
|
||||
endif
|
||||
|
||||
ifeq ($(INSTALL),osx)
|
||||
CFLAGS_INSTALL = -DGLAVA_OSX
|
||||
SHADER_DIR = Library/glava
|
||||
endif
|
||||
|
||||
LDFLAGS = $(ASAN) -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext
|
||||
LDFLAGS += $(ASAN) -lpulse -lpulse-simple -pthread $(LDFLAGS_GLFW) -ldl -lm -lX11 -lXext $(LDFLAGS_GLX)
|
||||
|
||||
PYTHON = python
|
||||
|
||||
GLAVA_VERSION = \"$(shell git describe --tags 2>/dev/null)\"
|
||||
ifeq ($(GLAVA_VERSION),\"\")
|
||||
GLAVA_VERSION = \"unknown\"
|
||||
endif
|
||||
|
||||
GLAD_INSTALL_DIR = glad
|
||||
GLAD_SRCFILE = ./glad/src/glad.c
|
||||
GLAD_ARGS = --generator=$(GLAD_GEN) --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
|
||||
CFLAGS_COMMON = -I glad/include
|
||||
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS_BUILD) $(CFLAGS_INSTALL) $(CFLAGS)
|
||||
CFLAGS_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:
|
||||
|
||||
57
README.md
57
README.md
@@ -1,14 +1,14 @@
|
||||
## GLava
|
||||
|
||||
<img align="left" width="200" height="200" src="https://thumbs.gfycat.com/DefiantInformalIndianspinyloach-size_restricted.gif" />
|
||||
|
||||
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.
|
||||
**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
|
||||
$ cd glava
|
||||
$ make
|
||||
$ CFLAGS="-march=native" make
|
||||
$ sudo make install
|
||||
$ 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+
|
||||
- Linux or BSD
|
||||
|
||||
**Additional compile time requirements:**
|
||||
@@ -28,17 +27,24 @@ 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 libxext6 libxext-dev libxcomposite-dev python make gcc
|
||||
```
|
||||
|
||||
## [Configuration](https://github.com/wacossusca34/glava/wiki)
|
||||
|
||||
GLava will start by looking for an entry point in the user configuration folder (`~/.config/glava/rc.glsl`\*), and will fall back to loading from the shader installation folder (`/etc/xdg/glava`\*). The entry point will specify a module to load and should set global configuration variables. Configuration for specific modules can be done in their respective `.glsl` files, which the module itself will include.
|
||||
|
||||
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.
|
||||
|
||||
**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.
|
||||
\* On an XDG compliant Linux or BSD system.
|
||||
|
||||
## Desktop window compatibility
|
||||
|
||||
@@ -46,23 +52,24 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
|
||||
|
||||
| WM | ! | Details
|
||||
| :---: | --- | --- |
|
||||
| GNOME (on X11) |  | No notable issues
|
||||
| Openbox (LXDE or standalone) |  | [Some tweaks may be required](https://www.reddit.com/r/unixporn/comments/7vcgi4/oc_after_receiving_positive_feedback_here_i/dtrkvja/)
|
||||
| Xfwm (XFCE) |  | Untested, but should work without issues
|
||||
| Fluxbox |  | Untested, but should work without issues
|
||||
| iceWM |  | No notable issues
|
||||
| AwesomeWM |  | Requires the WM to be restarted (`Super + Ctl + R`) in order for new desktop windows to behave correctly, can still be focused, may require other changes to config depending on layout
|
||||
| Budgie Desktop |  | `"xroot"` transparency breaks with Budgie's wallpaper window
|
||||
| kwin (KDE) |  | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing
|
||||
| i3 (and i3-gaps) |  | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6)
|
||||
| Mutter (GNOME, Budgie) |  | `"native"` (default) opacity should be used
|
||||
| KWin (KDE) |  | "Show Desktop" [temporarily hides GLava](https://github.com/wacossusca34/glava/issues/4#issuecomment-419729184)
|
||||
| Openbox (LXDE or standalone) |  | No issues
|
||||
| Xfwm (XFCE) |  | No issues
|
||||
| Fluxbox |  | No issues
|
||||
| IceWM |  | No issues
|
||||
| Bspwm |  | No issues
|
||||
| Herbstluftwm |  | `hc rule windowtype~'_NET_WM_WINDOW_TYPE_DESKTOP' manage=off` can be used to unmanage desktop windows
|
||||
| Unity |  | No issues
|
||||
| AwesomeWM |  | Can still be focused, may require other changes to config depending on layout
|
||||
| i3 (and i3-gaps) |  | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6), try `#request setxwintype "!-"`
|
||||
| EXWM |  | EXWM does not have a desktop, and forces window decorations
|
||||
| Unity |  | Needs testing
|
||||
| Enlightenment |  | Needs testing
|
||||
| Bspwm |  | Needs testing
|
||||
| Herbstluftwm |  | Needs testing
|
||||
| xmonad |  | Needs testing
|
||||
| Xmonad |  | Needs testing
|
||||
| Any non EWMH-compliant WM |  | 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:
|
||||
@@ -76,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:
|
||||
|
||||
@@ -84,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
2
glad
Submodule glad updated: 0a146b6723...c33992f23c
93
glava.c
93
glava.c
@@ -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>
|
||||
@@ -18,7 +19,6 @@
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
#define GLAVA_VERSION "1.2"
|
||||
#ifdef GLAD_DEBUG
|
||||
#define GLAVA_RELEASE_TYPE_PREFIX "debug, "
|
||||
#else
|
||||
@@ -151,6 +151,8 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
#define GLAVA_VERSION_STRING "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")"
|
||||
|
||||
static const char* help_str =
|
||||
"Usage: %s [OPTIONS]...\n"
|
||||
"Opens a window with an OpenGL context to draw an audio visualizer.\n"
|
||||
@@ -158,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"
|
||||
@@ -165,20 +170,36 @@ static const char* help_str =
|
||||
"-C, --copy-config creates copies and symbolic links in the user configuration\n"
|
||||
" directory for glava, copying any files in the root directory\n"
|
||||
" of the installed shader directory, and linking any modules.\n"
|
||||
"-b, --backend specifies a window creation backend to use. By default, the most\n"
|
||||
" appropriate backend will be used for the underlying windowing\n"
|
||||
" system.\n"
|
||||
"-V, --version print application version and exit\n"
|
||||
"\n"
|
||||
"GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")\n"
|
||||
GLAVA_VERSION_STRING "\n"
|
||||
" -- Copyright (C) 2017 Levi Webb\n";
|
||||
|
||||
static const char* opt_str = "hve:Cm:";
|
||||
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'},
|
||||
{"backend", required_argument, 0, 'b'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{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 */
|
||||
@@ -186,22 +207,29 @@ int main(int argc, char** argv) {
|
||||
const char* user_path = SHADER_USER_PATH;
|
||||
const char* entry = "rc.glsl";
|
||||
const char* force = NULL;
|
||||
const char* backend = NULL;
|
||||
const char* system_shader_paths[] = { user_path, install_path, NULL };
|
||||
bool verbose = false;
|
||||
bool copy_mode = false;
|
||||
bool verbose = false, copy_mode = false, desktop = false;
|
||||
|
||||
int c, idx, n = 0;
|
||||
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;
|
||||
case '?': exit(EXIT_FAILURE); break;
|
||||
case 'V':
|
||||
puts(GLAVA_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
case 'h':
|
||||
printf(help_str, argc > 0 ? argv[0] : "glava");
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,11 +238,15 @@ int main(int argc, char** argv) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
renderer* r = rd_new(system_shader_paths, entry, force);
|
||||
|
||||
float b0[r->bufsize_request], b1[r->bufsize_request];
|
||||
rd = rd_new(system_shader_paths, entry, force, backend, desktop);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -222,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) {
|
||||
@@ -244,12 +276,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
int thread_id = pthread_create(&thread, NULL, input_pulse, (void*) &audio);
|
||||
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 */
|
||||
|
||||
@@ -257,17 +289,15 @@ int main(int argc, char** argv) {
|
||||
pthread_mutex_lock(&audio.mutex);
|
||||
modified = audio.modified;
|
||||
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));
|
||||
/* 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, 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()) {
|
||||
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
|
||||
@@ -276,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);
|
||||
}
|
||||
|
||||
146
glfw_wcb.c
Normal file
146
glfw_wcb.c
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
/* 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,
|
||||
bool clickthrough) {
|
||||
|
||||
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 void 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 bool should_render (GLFWwindow* w) { return true; }
|
||||
static bool bg_changed (GLFWwindow* w) { return false; }
|
||||
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); }
|
||||
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);
|
||||
|
||||
#endif /* GLAVA_GLFW */
|
||||
178
glsl_ext.c
178
glsl_ext.c
@@ -1,6 +1,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
@@ -51,7 +52,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
|
||||
int written;
|
||||
if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0)
|
||||
abort();
|
||||
sbuf->at += space;
|
||||
sbuf->at += written;
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@@ -68,6 +69,42 @@ struct schar {
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
bool ext_parse_color(const char* str, size_t elem_sz, float** results) {
|
||||
size_t t, len = strlen(str), i = 0, s = 0;
|
||||
uint8_t elem_bytes[elem_sz];
|
||||
/* Ignore '0x' prefix, if present */
|
||||
if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
|
||||
len -= 2;
|
||||
str += 2;
|
||||
}
|
||||
for (t = 0; t < len && t < 8; ++t) {
|
||||
char c = str[t];
|
||||
uint8_t b;
|
||||
/* obtain value from character */
|
||||
switch (c) {
|
||||
case 'a' ... 'f': b = (c - 'a') + 10; break;
|
||||
case 'A' ... 'F': b = (c - 'A') + 10; break;
|
||||
case '0' ... '9': b = c - '0'; break;
|
||||
default: return false;
|
||||
}
|
||||
elem_bytes[s] = b;
|
||||
if (s >= elem_sz - 1) { /* advance to next element */
|
||||
uint32_t e = 0; /* component storage */
|
||||
/* mask storage with input data */
|
||||
for (size_t v = 0; v < elem_sz; ++v) {
|
||||
e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4);
|
||||
}
|
||||
/* convert to [0, 1] as floating point value */
|
||||
*results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1);
|
||||
s = 0;
|
||||
++i;
|
||||
} else { /* advance character */
|
||||
++s;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* handle raw arguments for #include and #request directives */
|
||||
/* NOTE: munmap needs to be called on the result */
|
||||
static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
@@ -91,7 +128,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
parse_error(line, f, "failed to load GLSL shader source specified by #include directive '%s': %s\n",
|
||||
parse_error(line, f, "failed to load GLSL shader source "
|
||||
"specified by #include directive '%s': %s\n",
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -100,7 +138,8 @@ static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
|
||||
char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (!map) {
|
||||
parse_error(line, f, "failed to map GLSL shader source specified by #include directive '%s': %s\n",
|
||||
parse_error(line, f, "failed to map GLSL shader source "
|
||||
"specified by #include directive '%s': %s\n",
|
||||
path, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -128,7 +167,7 @@ static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
|
||||
struct request_handler* handler;
|
||||
bool found = false;
|
||||
size_t t, i;
|
||||
size_t t;
|
||||
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
|
||||
if(!strcmp(handler->name, request)) {
|
||||
found = true;
|
||||
@@ -173,10 +212,12 @@ static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
case '1': { v = true; break; }
|
||||
case '0': { v = false; break; }
|
||||
default:
|
||||
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
|
||||
parse_error_s(line, f,
|
||||
"tried to parse invalid raw string into a boolean");
|
||||
}
|
||||
} else {
|
||||
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
|
||||
parse_error_s(line, f,
|
||||
"tried to parse invalid raw string into a boolean");
|
||||
}
|
||||
processed_args[i] = malloc(sizeof(bool));
|
||||
*(bool*) processed_args[i] = v;
|
||||
@@ -211,10 +252,11 @@ static struct schar directive(struct glsl_ext* ext, char** args,
|
||||
void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
|
||||
#define LINE_START 0
|
||||
#define IGNORING 1
|
||||
#define GLSL 1
|
||||
#define MACRO 2
|
||||
#define REQUEST 3
|
||||
#define INCLUDE 4
|
||||
#define COLOR 5
|
||||
|
||||
struct sbuf sbuf = {
|
||||
.buf = malloc(256),
|
||||
@@ -226,12 +268,16 @@ void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
size_t t;
|
||||
char at;
|
||||
int state = LINE_START;
|
||||
size_t macro_start_idx, arg_start_idx;
|
||||
size_t macro_start_idx, arg_start_idx, cbuf_idx;
|
||||
size_t line = 1;
|
||||
bool quoted = false, arg_start;
|
||||
char cbuf[9];
|
||||
char** args = NULL;
|
||||
size_t args_sz = 0;
|
||||
|
||||
|
||||
bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false,
|
||||
prev_escape = false, string = false;
|
||||
|
||||
for (t = 0; t <= source_len; ++t) {
|
||||
at = source_len == t ? '\0' : ext->source[t];
|
||||
if (at == '\n')
|
||||
@@ -244,40 +290,128 @@ void ext_process(struct glsl_ext* ext, const char* f) {
|
||||
macro_start_idx = t;
|
||||
state = MACRO;
|
||||
continue; }
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
if (comment && comment_line) {
|
||||
comment = false;
|
||||
comment_line = false;
|
||||
}
|
||||
case '\t':
|
||||
case ' ':
|
||||
goto copy;
|
||||
default: {
|
||||
state = IGNORING;
|
||||
goto copy; }
|
||||
default: state = GLSL;
|
||||
/* let execution continue into next state */
|
||||
}
|
||||
}
|
||||
case IGNORING: /* copying GLSL source or unrelated preprocessor syntax */
|
||||
if (at == '\n') {
|
||||
state = LINE_START;
|
||||
case GLSL: /* copying GLSL source or unrelated preprocessor syntax */
|
||||
{
|
||||
switch (at) {
|
||||
case '"':
|
||||
if (!comment && !prev_escape)
|
||||
string = !string;
|
||||
goto normal_char;
|
||||
case '\\':
|
||||
if (!comment) {
|
||||
prev_escape = !prev_escape;
|
||||
prev_asterix = false;
|
||||
prev_slash = false;
|
||||
goto copy;
|
||||
} else goto normal_char;
|
||||
case '/':
|
||||
if (!comment) {
|
||||
if (prev_slash) {
|
||||
comment = true;
|
||||
comment_line = true;
|
||||
prev_slash = false;
|
||||
} else prev_slash = true;
|
||||
} else if (!comment_line) {
|
||||
if (prev_asterix) {
|
||||
comment = false;
|
||||
prev_asterix = false;
|
||||
}
|
||||
}
|
||||
prev_escape = false;
|
||||
goto copy;
|
||||
case '*':
|
||||
if (!comment) {
|
||||
if (prev_slash) {
|
||||
comment = true;
|
||||
prev_slash = false;
|
||||
}
|
||||
} else prev_asterix = true;
|
||||
prev_escape = false;
|
||||
goto copy;
|
||||
case '#': {
|
||||
/* handle hex color syntax */
|
||||
if (!comment && !string) {
|
||||
state = COLOR;
|
||||
cbuf_idx = 0;
|
||||
continue;
|
||||
} else goto normal_char;
|
||||
}
|
||||
case '\n':
|
||||
if (comment && comment_line) {
|
||||
comment = false;
|
||||
comment_line = false;
|
||||
}
|
||||
state = LINE_START;
|
||||
normal_char:
|
||||
default:
|
||||
prev_asterix = false;
|
||||
prev_slash = false;
|
||||
prev_escape = false;
|
||||
goto copy;
|
||||
}
|
||||
}
|
||||
case COLOR: /* parse hex color syntax (#ffffffff -> vec4(1.0, 1.0, 1.0, 1.0)) */
|
||||
{
|
||||
switch (at) {
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
case '0' ... '9': {
|
||||
cbuf[cbuf_idx] = at;
|
||||
++cbuf_idx;
|
||||
if (cbuf_idx >= 8)
|
||||
goto emit_color;
|
||||
else continue;
|
||||
}
|
||||
emit_color:
|
||||
default:
|
||||
cbuf[cbuf_idx] = '\0'; /* null terminate */
|
||||
float r = 0.0F, g = 0.0F, b = 0.0F, a = 1.0F;
|
||||
if (ext_parse_color(cbuf, 2, (float*[]) { &r, &g, &b, &a })) {
|
||||
se_append(&sbuf, 64, " vec4(%.6f, %.6f, %.6f, %.6f) ", r, g, b, a);
|
||||
} else {
|
||||
parse_error(line, f, "Invalid color format '#%s' while "
|
||||
"parsing GLSL color syntax extension", cbuf);
|
||||
}
|
||||
state = at == '\n' ? LINE_START : GLSL;
|
||||
if (cbuf_idx >= 8)
|
||||
continue;
|
||||
else goto copy; /* copy character if it ended the sequence */
|
||||
}
|
||||
}
|
||||
goto copy;
|
||||
case MACRO: /* processing start of macro */
|
||||
{
|
||||
switch (at) {
|
||||
case '\n':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\0':
|
||||
case '\0':
|
||||
{
|
||||
/* end parsing directive */
|
||||
if (!strncmp("#request", &ext->source[macro_start_idx], t - macro_start_idx)
|
||||
|| !strncmp("#REQUEST", &ext->source[macro_start_idx], t - macro_start_idx)) {
|
||||
state = REQUEST;
|
||||
goto prepare_arg_parse;
|
||||
} else if (!strncmp("#include", &ext->source[macro_start_idx], t - macro_start_idx)
|
||||
|| !strncmp("#INCLUDE", &ext->source[macro_start_idx], t - macro_start_idx)) {
|
||||
} else if (!strncmp("#include", &ext->source[macro_start_idx],
|
||||
t - macro_start_idx)
|
||||
|| !strncmp("#INCLUDE", &ext->source[macro_start_idx],
|
||||
t - macro_start_idx)) {
|
||||
state = INCLUDE;
|
||||
goto prepare_arg_parse;
|
||||
} else {
|
||||
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]);
|
||||
state = IGNORING;
|
||||
state = at == '\n' ? LINE_START : GLSL;
|
||||
goto copy;
|
||||
}
|
||||
prepare_arg_parse:
|
||||
|
||||
@@ -35,3 +35,4 @@ struct glsl_ext {
|
||||
|
||||
void ext_process(struct glsl_ext* ext, const char* f);
|
||||
void ext_free (struct glsl_ext* ext);
|
||||
bool ext_parse_color(const char* hex, size_t elem_sz, float** results);
|
||||
|
||||
580
glx_wcb.c
Normal file
580
glx_wcb.c
Normal file
@@ -0,0 +1,580 @@
|
||||
|
||||
/* 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 <dlfcn.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
typedef struct __GLXcontextRec *GLXContext;
|
||||
typedef XID GLXPixmap;
|
||||
typedef XID GLXDrawable;
|
||||
|
||||
typedef void (*__GLXextFuncPtr)(void);
|
||||
|
||||
/* GLX 1.3 and later */
|
||||
typedef struct __GLXFBConfigRec *GLXFBConfig;
|
||||
typedef XID GLXFBConfigID;
|
||||
typedef XID GLXContextID;
|
||||
typedef XID GLXWindow;
|
||||
typedef XID GLXPbuffer;
|
||||
|
||||
/*
|
||||
* Tokens for glXChooseVisual and glXGetConfig:
|
||||
*/
|
||||
#define GLX_USE_GL 1
|
||||
#define GLX_BUFFER_SIZE 2
|
||||
#define GLX_LEVEL 3
|
||||
#define GLX_RGBA 4
|
||||
#define GLX_DOUBLEBUFFER 5
|
||||
#define GLX_STEREO 6
|
||||
#define GLX_AUX_BUFFERS 7
|
||||
#define GLX_RED_SIZE 8
|
||||
#define GLX_GREEN_SIZE 9
|
||||
#define GLX_BLUE_SIZE 10
|
||||
#define GLX_ALPHA_SIZE 11
|
||||
#define GLX_DEPTH_SIZE 12
|
||||
#define GLX_STENCIL_SIZE 13
|
||||
#define GLX_ACCUM_RED_SIZE 14
|
||||
#define GLX_ACCUM_GREEN_SIZE 15
|
||||
#define GLX_ACCUM_BLUE_SIZE 16
|
||||
#define GLX_ACCUM_ALPHA_SIZE 17
|
||||
|
||||
|
||||
/*
|
||||
* Error codes returned by glXGetConfig:
|
||||
*/
|
||||
#define GLX_BAD_SCREEN 1
|
||||
#define GLX_BAD_ATTRIBUTE 2
|
||||
#define GLX_NO_EXTENSION 3
|
||||
#define GLX_BAD_VISUAL 4
|
||||
#define GLX_BAD_CONTEXT 5
|
||||
#define GLX_BAD_VALUE 6
|
||||
#define GLX_BAD_ENUM 7
|
||||
|
||||
/*
|
||||
* GLX 1.1 and later:
|
||||
*/
|
||||
#define GLX_VENDOR 1
|
||||
#define GLX_VERSION 2
|
||||
#define GLX_EXTENSIONS 3
|
||||
|
||||
|
||||
/*
|
||||
* GLX 1.3 and later:
|
||||
*/
|
||||
#define GLX_CONFIG_CAVEAT 0x20
|
||||
#define GLX_DONT_CARE 0xFFFFFFFF
|
||||
#define GLX_X_VISUAL_TYPE 0x22
|
||||
#define GLX_TRANSPARENT_TYPE 0x23
|
||||
#define GLX_TRANSPARENT_INDEX_VALUE 0x24
|
||||
#define GLX_TRANSPARENT_RED_VALUE 0x25
|
||||
#define GLX_TRANSPARENT_GREEN_VALUE 0x26
|
||||
#define GLX_TRANSPARENT_BLUE_VALUE 0x27
|
||||
#define GLX_TRANSPARENT_ALPHA_VALUE 0x28
|
||||
#define GLX_WINDOW_BIT 0x00000001
|
||||
#define GLX_PIXMAP_BIT 0x00000002
|
||||
#define GLX_PBUFFER_BIT 0x00000004
|
||||
#define GLX_AUX_BUFFERS_BIT 0x00000010
|
||||
#define GLX_FRONT_LEFT_BUFFER_BIT 0x00000001
|
||||
#define GLX_FRONT_RIGHT_BUFFER_BIT 0x00000002
|
||||
#define GLX_BACK_LEFT_BUFFER_BIT 0x00000004
|
||||
#define GLX_BACK_RIGHT_BUFFER_BIT 0x00000008
|
||||
#define GLX_DEPTH_BUFFER_BIT 0x00000020
|
||||
#define GLX_STENCIL_BUFFER_BIT 0x00000040
|
||||
#define GLX_ACCUM_BUFFER_BIT 0x00000080
|
||||
#define GLX_NONE 0x8000
|
||||
#define GLX_SLOW_CONFIG 0x8001
|
||||
#define GLX_TRUE_COLOR 0x8002
|
||||
#define GLX_DIRECT_COLOR 0x8003
|
||||
#define GLX_PSEUDO_COLOR 0x8004
|
||||
#define GLX_STATIC_COLOR 0x8005
|
||||
#define GLX_GRAY_SCALE 0x8006
|
||||
#define GLX_STATIC_GRAY 0x8007
|
||||
#define GLX_TRANSPARENT_RGB 0x8008
|
||||
#define GLX_TRANSPARENT_INDEX 0x8009
|
||||
#define GLX_VISUAL_ID 0x800B
|
||||
#define GLX_SCREEN 0x800C
|
||||
#define GLX_NON_CONFORMANT_CONFIG 0x800D
|
||||
#define GLX_DRAWABLE_TYPE 0x8010
|
||||
#define GLX_RENDER_TYPE 0x8011
|
||||
#define GLX_X_RENDERABLE 0x8012
|
||||
#define GLX_FBCONFIG_ID 0x8013
|
||||
#define GLX_RGBA_TYPE 0x8014
|
||||
#define GLX_COLOR_INDEX_TYPE 0x8015
|
||||
#define GLX_MAX_PBUFFER_WIDTH 0x8016
|
||||
#define GLX_MAX_PBUFFER_HEIGHT 0x8017
|
||||
#define GLX_MAX_PBUFFER_PIXELS 0x8018
|
||||
#define GLX_PRESERVED_CONTENTS 0x801B
|
||||
#define GLX_LARGEST_PBUFFER 0x801C
|
||||
#define GLX_WIDTH 0x801D
|
||||
#define GLX_HEIGHT 0x801E
|
||||
#define GLX_EVENT_MASK 0x801F
|
||||
#define GLX_DAMAGED 0x8020
|
||||
#define GLX_SAVED 0x8021
|
||||
#define GLX_WINDOW 0x8022
|
||||
#define GLX_PBUFFER 0x8023
|
||||
#define GLX_PBUFFER_HEIGHT 0x8040
|
||||
#define GLX_PBUFFER_WIDTH 0x8041
|
||||
#define GLX_RGBA_BIT 0x00000001
|
||||
#define GLX_COLOR_INDEX_BIT 0x00000002
|
||||
#define GLX_PBUFFER_CLOBBER_MASK 0x08000000
|
||||
|
||||
/*
|
||||
* GLX 1.4 and later:
|
||||
*/
|
||||
#define GLX_SAMPLE_BUFFERS 0x186a0 /*100000*/
|
||||
#define GLX_SAMPLES 0x186a1 /*100001*/
|
||||
|
||||
/* glXCreateContextAttribsARB extension definitions */
|
||||
|
||||
#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);
|
||||
|
||||
GLXFBConfig* (*glXChooseFBConfig) (Display* dpy, int screen, const int* attribList, int* nitems);
|
||||
XVisualInfo* (*glXGetVisualFromFBConfig)(Display* dpy, GLXFBConfig config);
|
||||
int (*glXGetFBConfigAttrib) (Display* dpy, GLXFBConfig config, int attribute, int *value );
|
||||
Bool (*glXMakeCurrent) (Display* dpy, GLXDrawable drawable, GLXContext ctx);
|
||||
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;
|
||||
|
||||
static Display* display;
|
||||
|
||||
static int swap;
|
||||
|
||||
static bool floating, decorated, focused, maximized, transparent;
|
||||
|
||||
struct glxwin {
|
||||
Window w;
|
||||
GLXContext context;
|
||||
double time;
|
||||
bool should_close, should_render, bg_changed, clickthrough;
|
||||
char override_state;
|
||||
};
|
||||
|
||||
static Atom ATOM__MOTIF_WM_HINTS, ATOM_WM_DELETE_WINDOW, ATOM_WM_PROTOCOLS, ATOM__NET_ACTIVE_WINDOW, ATOM__XROOTPMAP_ID;
|
||||
|
||||
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;
|
||||
|
||||
void* hgl = dlopen("libGL.so", RTLD_LAZY);
|
||||
void* hglx = dlopen("libGLX.so", RTLD_LAZY);
|
||||
|
||||
if (!hgl && !hglx) {
|
||||
fprintf(stderr, "Failed to load GLX functions (libGL and libGLX do not exist!)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Depending on the graphics driver, the GLX functions that we need may either be in libGL or
|
||||
libGLX. */
|
||||
void* resolve_f(const char* symbol) {
|
||||
void* s = NULL;
|
||||
if (hgl) s = dlsym(hgl, symbol);
|
||||
if (!s && hglx) s = dlsym(hglx, symbol);
|
||||
if (!s) {
|
||||
fprintf(stderr, "Failed to resolve GLX symbol: `%s`\n", symbol);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#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);
|
||||
resolve(glXGetFBConfigAttrib);
|
||||
resolve(glXMakeCurrent);
|
||||
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);
|
||||
intern(_XROOTPMAP_ID, false);
|
||||
|
||||
#undef intern
|
||||
#undef resolve
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
bool clickthrough) {
|
||||
struct glxwin* w = malloc(sizeof(struct glxwin));
|
||||
*w = (struct glxwin) {
|
||||
.override_state = '\0',
|
||||
.time = 0.0D,
|
||||
.should_close = false,
|
||||
.should_render = true,
|
||||
.bg_changed = false,
|
||||
.clickthrough = false
|
||||
};
|
||||
|
||||
XVisualInfo* vi;
|
||||
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,
|
||||
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,
|
||||
"\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) {
|
||||
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.event_mask |= PropertyChangeMask | VisibilityChangeMask;
|
||||
attr.background_pixmap = None;
|
||||
attr.border_pixel = 0;
|
||||
|
||||
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,
|
||||
vmask, &attr))) {
|
||||
fprintf(stderr, "XCreateWindow(): failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
bool desktop = false;
|
||||
|
||||
if (type)
|
||||
desktop = 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);
|
||||
|
||||
XSetWMProtocols(display, w->w, &ATOM_WM_DELETE_WINDOW, 1);
|
||||
|
||||
/* Eliminate the window's effective region */
|
||||
w->clickthrough = desktop || clickthrough;
|
||||
apply_clickthrough(w);
|
||||
|
||||
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);
|
||||
|
||||
if (!transparent)
|
||||
XSelectInput(display, DefaultRootWindow(display), 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; }
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
static bool should_close (struct glxwin* w) { return w->should_close; }
|
||||
static bool bg_changed (struct glxwin* w) { return w->bg_changed; }
|
||||
static bool should_render(struct glxwin* w) {
|
||||
/* For nearly all window managers, windows are 'minimized' by unmapping parent windows.
|
||||
VisibilityNotify events are not sent in these instances, so we have to read window
|
||||
attributes to see if our window isn't viewable. */
|
||||
XWindowAttributes attrs;
|
||||
XGetWindowAttributes(display, w->w, &attrs);
|
||||
return w->should_render && attrs.map_state == IsViewable;
|
||||
}
|
||||
|
||||
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 == ATOM_WM_PROTOCOLS
|
||||
&& ev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
|
||||
w->should_close = true;
|
||||
}
|
||||
break;
|
||||
case VisibilityNotify:
|
||||
switch (ev.xvisibility.state) {
|
||||
case VisibilityFullyObscured:
|
||||
w->should_render = false;
|
||||
break;
|
||||
case VisibilityUnobscured:
|
||||
case VisibilityPartiallyObscured:
|
||||
w->should_render = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid VisibilityNotify event state (%d)\n", ev.xvisibility.state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PropertyNotify:
|
||||
if (ev.xproperty.atom == ATOM__XROOTPMAP_ID) {
|
||||
w->bg_changed = true;
|
||||
}
|
||||
break;
|
||||
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) {
|
||||
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 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 */
|
||||
@@ -9,22 +9,22 @@
|
||||
|
||||
#include "fifo.h"
|
||||
|
||||
static pa_mainloop *m_pulseaudio_mainloop;
|
||||
static pa_mainloop* m_pulseaudio_mainloop;
|
||||
|
||||
static void cb(__attribute__((unused)) pa_context *pulseaudio_context,
|
||||
const pa_server_info *i,
|
||||
void *userdata) {
|
||||
static void cb(__attribute__((unused)) pa_context* pulseaudio_context,
|
||||
const pa_server_info* i,
|
||||
void* userdata) {
|
||||
|
||||
//getting default sink name
|
||||
struct audio_data *audio = (struct audio_data *)userdata;
|
||||
/* Obtain default sink name */
|
||||
struct audio_data* audio = (struct audio_data*) userdata;
|
||||
audio->source = malloc(sizeof(char) * 1024);
|
||||
|
||||
strcpy(audio->source,i->default_sink_name);
|
||||
|
||||
//appending .monitor suffix
|
||||
/* Append `.monitor` suffix */
|
||||
audio->source = strcat(audio->source, ".monitor");
|
||||
|
||||
//quiting mainloop
|
||||
/* Quiting mainloop */
|
||||
pa_context_disconnect(pulseaudio_context);
|
||||
pa_context_unref(pulseaudio_context);
|
||||
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||
@@ -32,10 +32,9 @@ static void cb(__attribute__((unused)) pa_context *pulseaudio_context,
|
||||
}
|
||||
|
||||
|
||||
static void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
|
||||
void *userdata) {
|
||||
static void pulseaudio_context_state_callback(pa_context* pulseaudio_context, void* userdata) {
|
||||
|
||||
// make sure loop is ready
|
||||
/* Ensure loop is ready */
|
||||
switch (pa_context_get_state(pulseaudio_context))
|
||||
{
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
@@ -54,7 +53,6 @@ static void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
// printf("PulseAudio context terminated!\n");
|
||||
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||
break;
|
||||
}
|
||||
@@ -63,29 +61,29 @@ static void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
|
||||
|
||||
void get_pulse_default_sink(struct audio_data* audio) {
|
||||
|
||||
pa_mainloop_api *mainloop_api;
|
||||
pa_context *pulseaudio_context;
|
||||
pa_mainloop_api* mainloop_api;
|
||||
pa_context* pulseaudio_context;
|
||||
int ret;
|
||||
|
||||
// Create a mainloop API and connection to the default server
|
||||
/* Create a mainloop API and connection to the default server */
|
||||
m_pulseaudio_mainloop = pa_mainloop_new();
|
||||
|
||||
mainloop_api = pa_mainloop_get_api(m_pulseaudio_mainloop);
|
||||
pulseaudio_context = pa_context_new(mainloop_api, "glava device list");
|
||||
|
||||
|
||||
// This function connects to the pulse server
|
||||
/* Connect to the PA server */
|
||||
pa_context_connect(pulseaudio_context, NULL, PA_CONTEXT_NOFLAGS,
|
||||
NULL);
|
||||
|
||||
// This function defines a callback so the server will tell us its state.
|
||||
/* Define a callback so the server will tell us its state */
|
||||
pa_context_set_state_callback(pulseaudio_context,
|
||||
pulseaudio_context_state_callback,
|
||||
(void*)audio);
|
||||
|
||||
// starting a mainloop to get default sink
|
||||
/* Start mainloop to get default sink */
|
||||
|
||||
// starting with one non blokng iteration in case pulseaudio is not able to run
|
||||
/* Start with one non blocking iteration in case pulseaudio is not able to run */
|
||||
if (!(ret = pa_mainloop_iterate(m_pulseaudio_mainloop, 0, &ret))){
|
||||
printf("Could not open pulseaudio mainloop to "
|
||||
"find default device name: %d\n"
|
||||
@@ -114,25 +112,24 @@ void get_pulse_default_sink(struct audio_data* audio) {
|
||||
#endif
|
||||
|
||||
void* input_pulse(void* data) {
|
||||
struct audio_data *audio = (struct audio_data *)data;
|
||||
struct audio_data* audio = (struct audio_data*) data;
|
||||
int i, n;
|
||||
size_t ssz = audio->sample_sz;
|
||||
float buf[ssz / 2];
|
||||
|
||||
/* The sample type to use */
|
||||
|
||||
const pa_sample_spec ss = {
|
||||
.format = FSAMPLE_FORMAT,
|
||||
.rate = audio->rate,
|
||||
.channels = 2
|
||||
};
|
||||
const pa_buffer_attr pb = {
|
||||
.maxlength = ssz * 2,
|
||||
.maxlength = (uint32_t) -1,
|
||||
.fragsize = ssz
|
||||
};
|
||||
|
||||
pa_simple *s = NULL;
|
||||
|
||||
pa_simple* s = NULL;
|
||||
int error;
|
||||
|
||||
|
||||
if (!(s = pa_simple_new(NULL, "glava", PA_STREAM_RECORD,
|
||||
audio->source, "audio for glava",
|
||||
&ss, NULL, &pb, &error))) {
|
||||
@@ -141,13 +138,13 @@ void* input_pulse(void* data) {
|
||||
audio->source, pa_strerror(error));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
n = 0;
|
||||
|
||||
|
||||
float* bl = (float*) audio->audio_out_l;
|
||||
float* br = (float*) audio->audio_out_r;
|
||||
size_t fsz = audio->audio_buf_sz;
|
||||
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Record some data ... */
|
||||
@@ -163,17 +160,21 @@ void* input_pulse(void* data) {
|
||||
memmove(bl, &bl[ssz / 4], (fsz - (ssz / 4)) * sizeof(float));
|
||||
memmove(br, &br[ssz / 4], (fsz - (ssz / 4)) * sizeof(float));
|
||||
|
||||
// sorting out channelss
|
||||
/* sorting out channels */
|
||||
|
||||
for (n = 0, i = 0; i < ssz / 2; i += 2) {
|
||||
|
||||
// size_t idx = (i / 2) + (at * (BUFSIZE / 2));
|
||||
/* size_t idx = (i / 2) + (at * (BUFSIZE / 2)); */
|
||||
|
||||
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
|
||||
/* stereo storing channels in buffer */
|
||||
if (audio->channels == 2) {
|
||||
bl[idx] = buf[i];
|
||||
br[idx] = buf[i + 1];
|
||||
@@ -183,7 +184,7 @@ void* input_pulse(void* data) {
|
||||
audio->modified = true;
|
||||
|
||||
pthread_mutex_unlock(&audio->mutex);
|
||||
|
||||
|
||||
if (audio->terminate == 1) {
|
||||
pa_simple_free(s);
|
||||
break;
|
||||
|
||||
505
render.c
505
render.c
@@ -14,7 +14,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
@@ -36,6 +35,17 @@
|
||||
#define VERTEX_SHADER_SRC \
|
||||
"layout(location = 0) in vec3 pos; void main() { gl_Position = vec4(pos.x, pos.y, 0.0F, 1.0F); }"
|
||||
|
||||
struct gl_wcb* wcbs[2] = {};
|
||||
static size_t wcbs_idx = 0;
|
||||
|
||||
static inline void register_wcb(struct gl_wcb* wcb) { wcbs[wcbs_idx++] = wcb; }
|
||||
|
||||
#define DECL_WCB(N) \
|
||||
do { \
|
||||
extern struct gl_wcb N; \
|
||||
register_wcb(&N); \
|
||||
} while (0)
|
||||
|
||||
/* GLSL bind source */
|
||||
|
||||
struct gl_bind_src {
|
||||
@@ -75,7 +85,7 @@ struct gl_bind {
|
||||
|
||||
struct gl_sfbo {
|
||||
GLuint fbo, tex, shader;
|
||||
bool valid;
|
||||
bool valid, nativeonly;
|
||||
const char* name;
|
||||
struct gl_bind* binds;
|
||||
size_t binds_sz;
|
||||
@@ -92,13 +102,15 @@ struct gl_data {
|
||||
struct overlay_data overlay;
|
||||
GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog;
|
||||
size_t stages_sz, bufscale, avg_frames;
|
||||
GLFWwindow* w;
|
||||
void* w;
|
||||
struct gl_wcb* wcb;
|
||||
int lww, lwh, lwx, lwy; /* last window dimensions */
|
||||
int rate; /* framerate */
|
||||
double tcounter;
|
||||
int fcounter, ucounter, kcounter;
|
||||
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop,
|
||||
smooth_pass, use_alpha;
|
||||
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;
|
||||
@@ -109,6 +121,8 @@ struct gl_data {
|
||||
int geometry[4];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* load shader file */
|
||||
static GLuint shaderload(const char* rpath,
|
||||
GLenum type,
|
||||
@@ -152,7 +166,8 @@ static GLuint shaderload(const char* rpath,
|
||||
"#define UNIFORM_LIMIT %d\n"
|
||||
"#define PRE_SMOOTHED_AUDIO %d\n"
|
||||
"#define SMOOTH_FACTOR %.6f\n"
|
||||
"#define USE_ALPHA %d\n";
|
||||
"#define USE_ALPHA %d\n"
|
||||
"#define PREMULTIPLY_ALPHA %d\n";
|
||||
|
||||
struct glsl_ext ext = {
|
||||
.source = raw ? NULL : map,
|
||||
@@ -167,11 +182,11 @@ static GLuint shaderload(const char* rpath,
|
||||
/* If this is raw input, skip processing */
|
||||
if (!raw) ext_process(&ext, rpath);
|
||||
|
||||
size_t blen = strlen(header_fmt) + 42;
|
||||
size_t blen = strlen(header_fmt) + 64;
|
||||
GLchar* buf = malloc((blen * sizeof(GLchar*)) + ext.p_len);
|
||||
int written = snprintf(buf, blen, header_fmt, (int) shader_version, (int) max_uniforms,
|
||||
gl->smooth_pass ? 1 : 0, (double) gl->smooth_factor,
|
||||
gl->use_alpha ? 1 : 0);
|
||||
1, gl->premultiply_alpha ? 1 : 0);
|
||||
if (written < 0) {
|
||||
fprintf(stderr, "snprintf() encoding error while prepending header to shader '%s'\n", path);
|
||||
return 0;
|
||||
@@ -312,10 +327,8 @@ static GLuint create_1d_tex() {
|
||||
}
|
||||
|
||||
static void update_1d_tex(GLuint tex, size_t w, float* data) {
|
||||
float buf[w];
|
||||
memcpy(buf, data, w * sizeof(float));
|
||||
glBindTexture(GL_TEXTURE_1D, tex);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, w, 0, GL_RED, GL_FLOAT, buf);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, w, 0, GL_RED, GL_FLOAT, data);
|
||||
}
|
||||
|
||||
#define BIND_VEC2 0
|
||||
@@ -494,7 +507,6 @@ void transform_smooth(struct gl_data* d, void** udaa, void* data) {
|
||||
for (int t = 0; t < asz; ++t) {
|
||||
float
|
||||
db = log(t), /* buffer index on log scale */
|
||||
v = b[t], /* value at this position */
|
||||
avg = 0; /* adj value averages (weighted) */
|
||||
/* Calculate real indexes for sampling at this position, since the
|
||||
distance is specified in scalar values */
|
||||
@@ -580,7 +592,7 @@ void transform_wrange(struct gl_data* d, void** _, void* data) {
|
||||
|
||||
void transform_window(struct gl_data* d, void** _, void* data) {
|
||||
struct gl_sampler_data* s = (struct gl_sampler_data*) data;
|
||||
float* b = s->buf, w;
|
||||
float* b = s->buf;
|
||||
size_t sz = s->sz, t;
|
||||
|
||||
for (t = 0; t < sz; ++t) {
|
||||
@@ -640,7 +652,7 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
|
||||
}
|
||||
mmax=istep;
|
||||
}
|
||||
|
||||
|
||||
/* abs and log scale */
|
||||
for (n = 0; n < s->sz; ++n) {
|
||||
if (data[n] < 0.0F) data[n] = -data[n];
|
||||
@@ -668,11 +680,16 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct renderer* rd_new(const char** paths, const char* entry, const char* force_mod) {
|
||||
struct renderer* rd_new(const char** paths, const char* entry,
|
||||
const char* force_mod, const char* force_backend,
|
||||
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,
|
||||
@@ -682,35 +699,82 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
struct gl_data* gl = r->gl;
|
||||
*gl = (struct gl_data) {
|
||||
.w = NULL,
|
||||
.stages = NULL,
|
||||
.rate = 0,
|
||||
.tcounter = 0.0D,
|
||||
.fcounter = 0,
|
||||
.ucounter = 0,
|
||||
.kcounter = 0,
|
||||
.fr = 1.0F,
|
||||
.ur = 1.0F,
|
||||
.print_fps = true,
|
||||
.bufscale = 1,
|
||||
.avg_frames = 6,
|
||||
.avg_window = true,
|
||||
.gravity_step = 4.2,
|
||||
.interpolate = true,
|
||||
.force_geometry = false,
|
||||
.smooth_factor = 0.025,
|
||||
.smooth_distance = 0.01,
|
||||
.smooth_ratio = 4,
|
||||
.bg_tex = 0,
|
||||
.sm_prog = 0,
|
||||
.copy_desktop = true,
|
||||
.use_alpha = true,
|
||||
.smooth_pass = true,
|
||||
.fft_scale = 10.2F,
|
||||
.fft_cutoff = 0.3F,
|
||||
.geometry = { 0, 0, 500, 400 },
|
||||
.clear_color = { 0.0F, 0.0F, 0.0F, 0.0F }
|
||||
.w = NULL,
|
||||
.wcb = NULL,
|
||||
.stages = NULL,
|
||||
.rate = 0,
|
||||
.tcounter = 0.0D,
|
||||
.fcounter = 0,
|
||||
.ucounter = 0,
|
||||
.kcounter = 0,
|
||||
.fr = 1.0F,
|
||||
.ur = 1.0F,
|
||||
.print_fps = true,
|
||||
.bufscale = 1,
|
||||
.avg_frames = 6,
|
||||
.avg_window = true,
|
||||
.gravity_step = 4.2,
|
||||
.interpolate = true,
|
||||
.force_geometry = false,
|
||||
.force_raised = false,
|
||||
.smooth_factor = 0.025,
|
||||
.smooth_distance = 0.01,
|
||||
.smooth_ratio = 4,
|
||||
.bg_tex = 0,
|
||||
.sm_prog = 0,
|
||||
.copy_desktop = true,
|
||||
.premultiply_alpha = true,
|
||||
.check_fullscreen = false,
|
||||
.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 },
|
||||
.clickthrough = false
|
||||
};
|
||||
|
||||
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("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", backend);
|
||||
abort();
|
||||
}
|
||||
|
||||
#ifdef GLAD_DEBUG
|
||||
printf("Assigning debug callback\n");
|
||||
@@ -721,26 +785,23 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!glfwInit())
|
||||
abort();
|
||||
gl->wcb->init();
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
|
||||
int shader_version = 330;
|
||||
int shader_version = 330,
|
||||
context_version_major = 3,
|
||||
context_version_minor = 3;
|
||||
const char* module = force_mod;
|
||||
char* xwintype = NULL, * wintitle = "GLava";
|
||||
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;
|
||||
|
||||
#define WINDOW_HINT(request, attr) \
|
||||
{ .name = request, .fmt = "b", \
|
||||
.handler = RHANDLER(name, args, { glfwWindowHint(attr, *(bool*) args[0]); }) }
|
||||
#define WINDOW_HINT(request) \
|
||||
{ .name = "set" #request, .fmt = "b", \
|
||||
.handler = RHANDLER(name, args, { gl->wcb->set_##request(*(bool*) args[0]); }) }
|
||||
|
||||
struct request_handler handlers[] = {
|
||||
{
|
||||
@@ -749,19 +810,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
bool native_opacity = !strcmp("native", (char*) args[0]);
|
||||
|
||||
gl->use_alpha = true;
|
||||
|
||||
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
|
||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE);
|
||||
gl->use_alpha = false;
|
||||
#elif GLFW_TRANSPARENT
|
||||
glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE);
|
||||
gl->use_alpha = false;
|
||||
#else
|
||||
if (native_opacity)
|
||||
printf("WARNING: the linked version of GLFW3 does not have transparency support"
|
||||
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
|
||||
#endif
|
||||
gl->premultiply_alpha = native_opacity;
|
||||
|
||||
gl->wcb->set_transparent(native_opacity);
|
||||
|
||||
if (!strcmp("xroot", (char*) args[0]))
|
||||
gl->copy_desktop = true;
|
||||
@@ -774,6 +825,14 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
.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, {
|
||||
@@ -783,42 +842,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
&gl->clear_color.b,
|
||||
&gl->clear_color.a
|
||||
};
|
||||
const size_t elem_sz = 2;
|
||||
char* str = (char*) args[0];
|
||||
size_t t, len = strlen(str), i = 0, s = 0;
|
||||
uint8_t elem_bytes[elem_sz];
|
||||
/* Ignore '0x' prefix, if present */
|
||||
if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
|
||||
len -= 2;
|
||||
str += 2;
|
||||
}
|
||||
for (t = 0; t < len && t < 8; ++t) {
|
||||
char c = str[t];
|
||||
uint8_t b;
|
||||
/* obtain value from character */
|
||||
switch (c) {
|
||||
case 'a' ... 'f': b = (c - 'a') + 10; break;
|
||||
case 'A' ... 'F': b = (c - 'A') + 10; break;
|
||||
case '0' ... '9': b = c - '0'; break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
elem_bytes[s] = b;
|
||||
if (s >= elem_sz - 1) { /* advance to next element */
|
||||
uint32_t e = 0; /* component storage */
|
||||
/* mask storage with input data */
|
||||
for (size_t v = 0; v < elem_sz; ++v) {
|
||||
e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4);
|
||||
}
|
||||
/* convert to [0, 1] as floating point value */
|
||||
*results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1);
|
||||
printf("[DEBUG] component %d value: %d (float: %f)\n", i, e, *results[i]);
|
||||
s = 0;
|
||||
++i;
|
||||
} else { /* advance character */
|
||||
++s;
|
||||
}
|
||||
if (!ext_parse_color((char*) args[0], 2, results)) {
|
||||
fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -835,22 +861,34 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
.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;
|
||||
}
|
||||
})
|
||||
},
|
||||
WINDOW_HINT("setfloating", GLFW_FLOATING),
|
||||
WINDOW_HINT("setdecorated", GLFW_DECORATED),
|
||||
WINDOW_HINT("setfocused", GLFW_FOCUSED),
|
||||
WINDOW_HINT("setmaximized", GLFW_MAXIMIZED),
|
||||
{
|
||||
.name = "nativeonly", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, {
|
||||
if (current)
|
||||
current->nativeonly = *(bool*) args[0];
|
||||
else {
|
||||
fprintf(stderr, "`nativeonly` request needs module context\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
})
|
||||
},
|
||||
WINDOW_HINT(floating),
|
||||
WINDOW_HINT(decorated),
|
||||
WINDOW_HINT(focused),
|
||||
WINDOW_HINT(maximized),
|
||||
{
|
||||
.name = "setversion", .fmt = "ii",
|
||||
.handler = RHANDLER(name, args, {
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, *(int*) args[0]);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, *(int*) args[1]);
|
||||
context_version_major = *(int*) args[0];
|
||||
context_version_minor = *(int*) args[1];
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -865,28 +903,39 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
{
|
||||
.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",
|
||||
.handler = RHANDLER(name, args, { glfwSwapInterval(*(int*) args[0]); }) },
|
||||
.handler = RHANDLER(name, args, { gl->wcb->set_swap(*(int*) args[0]); }) },
|
||||
{ .name = "setframerate", .fmt = "i",
|
||||
.handler = RHANDLER(name, args, { gl->rate = *(int*) args[0]; }) },
|
||||
{ .name = "setprintframes", .fmt = "b",
|
||||
.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",
|
||||
@@ -1003,12 +1052,18 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
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);
|
||||
|
||||
@@ -1020,7 +1075,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
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;
|
||||
}
|
||||
@@ -1038,6 +1093,42 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
ext_process(&ext, se_buf);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1050,16 +1141,17 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!(gl->w = glfwCreateWindow(500, 400, wintitle, NULL, NULL))) {
|
||||
glfwTerminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
|
||||
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
|
||||
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();
|
||||
|
||||
glfwMakeContextCurrent(gl->w);
|
||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
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);
|
||||
@@ -1068,8 +1160,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
if (!gl->premultiply_alpha) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
size_t m_len = strlen(module);
|
||||
size_t bsz = d_len + m_len + 2;
|
||||
@@ -1086,7 +1180,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
struct gl_sfbo* stages;
|
||||
size_t count = 0;
|
||||
|
||||
|
||||
{
|
||||
char buf[32];
|
||||
DIR* dir = opendir(shaders);
|
||||
@@ -1130,11 +1224,12 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
struct gl_sfbo* s = &stages[idx - 1];
|
||||
*s = (struct gl_sfbo) {
|
||||
.name = strdup(d->d_name),
|
||||
.shader = 0,
|
||||
.valid = false,
|
||||
.binds = malloc(1),
|
||||
.binds_sz = 0
|
||||
.name = strdup(d->d_name),
|
||||
.shader = 0,
|
||||
.valid = false,
|
||||
.nativeonly = false,
|
||||
.binds = malloc(1),
|
||||
.binds_sz = 0
|
||||
};
|
||||
|
||||
current = s;
|
||||
@@ -1149,7 +1244,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
as it can rendered directly */
|
||||
if (idx != count) {
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(gl->w, &w, &h);
|
||||
gl->wcb->get_fbsize(gl->w, &w, &h);
|
||||
setup_sfbo(&stages[idx - 1], w, h);
|
||||
}
|
||||
|
||||
@@ -1172,6 +1267,23 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
} while (found);
|
||||
}
|
||||
}
|
||||
|
||||
gl->stages = stages;
|
||||
gl->stages_sz = count;
|
||||
|
||||
{
|
||||
struct gl_sfbo* final = NULL;
|
||||
if (!gl->premultiply_alpha) {
|
||||
for (size_t t = 0; t < gl->stages_sz; ++t) {
|
||||
if (!gl->stages[t].nativeonly) {
|
||||
final = &gl->stages[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Invalidate framebuffer and use direct rendering if it was instantiated
|
||||
due to a following `nativeonly` shader pass. */
|
||||
if (final) final->valid = false;
|
||||
}
|
||||
|
||||
/* Compile smooth pass shader */
|
||||
|
||||
@@ -1182,15 +1294,11 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
char util[usz]; /* module pack path to use */
|
||||
snprintf(util, usz, "%s/%s", data, util_folder);
|
||||
loading_smooth_pass = true;
|
||||
if (!(gl->sm_prog = shaderbuild(gl, util, data, handlers, shader_version, "smooth_pass.frag"))) {
|
||||
if (!(gl->sm_prog = shaderbuild(gl, util, data, handlers, shader_version, "smooth_pass.frag")))
|
||||
abort();
|
||||
}
|
||||
loading_smooth_pass = false;
|
||||
}
|
||||
|
||||
gl->stages = stages;
|
||||
gl->stages_sz = count;
|
||||
|
||||
/* target seconds per update */
|
||||
gl->target_spu = (float) (r->samplesize_request / 4) / (float) r->rate_request;
|
||||
|
||||
@@ -1219,20 +1327,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
}
|
||||
|
||||
overlay(&gl->overlay);
|
||||
|
||||
glfwShowWindow(gl->w);
|
||||
|
||||
glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a);
|
||||
|
||||
if (xwintype) {
|
||||
xwin_settype(r, xwintype);
|
||||
free(xwintype);
|
||||
}
|
||||
|
||||
for (size_t t = 0; t < xwinstates_sz; ++t) {
|
||||
xwin_addstate(r, xwinstates[t]);
|
||||
}
|
||||
free(xwinstates);
|
||||
gl->wcb->set_visible(gl->w, true);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1240,16 +1338,25 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
void rd_time(struct renderer* r) {
|
||||
struct gl_data* gl = r->gl;
|
||||
|
||||
glfwSetTime(0.0D); /* reset time for measuring this frame */
|
||||
gl->wcb->set_time(gl->w, 0.0D); /* reset time for measuring this frame */
|
||||
}
|
||||
|
||||
void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modified) {
|
||||
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 = !glfwWindowShouldClose(gl->w);
|
||||
if (!r->alive)
|
||||
return;
|
||||
if (gl->wcb->should_close(gl->w)) {
|
||||
r->alive = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Stop rendering if the backend has some reason not to render (minimized, obscured) */
|
||||
if (!gl->wcb->should_render(gl->w))
|
||||
return false;
|
||||
|
||||
/* Stop rendering when fullscreen windows are focused */
|
||||
if (gl->check_fullscreen && !xwin_should_render(gl->wcb, gl->w))
|
||||
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 */
|
||||
@@ -1302,8 +1409,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
}
|
||||
}
|
||||
int ww, wh, wx, wy;
|
||||
glfwGetFramebufferSize(gl->w, &ww, &wh);
|
||||
glfwGetWindowPos(gl->w, &wx, &wy);
|
||||
gl->wcb->get_fbsize(gl->w, &ww, &wh);
|
||||
gl->wcb->get_pos(gl->w, &wx, &wy);
|
||||
|
||||
/* Resize screen textures if needed */
|
||||
if (ww != gl->lww || wh != gl->lwh) {
|
||||
@@ -1315,7 +1422,7 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
}
|
||||
|
||||
/* Resize and grab new background data if needed */
|
||||
if (gl->copy_desktop && (ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
|
||||
if (gl->copy_desktop && (gl->wcb->bg_changed(gl->w) || ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
|
||||
gl->bg_tex = xwin_copyglbg(r, gl->bg_tex);
|
||||
}
|
||||
|
||||
@@ -1332,10 +1439,13 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
for (t = 0; t < gl->stages_sz; ++t) {
|
||||
|
||||
bool needed[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
|
||||
bool load_flags[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
|
||||
|
||||
/* Current shader program */
|
||||
struct gl_sfbo* current = &gl->stages[t];
|
||||
|
||||
if (current->nativeonly && !gl->premultiply_alpha)
|
||||
continue;
|
||||
|
||||
/* Bind framebuffer if this is not the final pass */
|
||||
if (current->valid)
|
||||
@@ -1354,8 +1464,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
"out vec4 fragment;" "\n"
|
||||
"in vec4 gl_FragCoord;" "\n"
|
||||
"void main() {" "\n"
|
||||
" fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, " "\n"
|
||||
" (screen.y - gl_FragCoord.y) / screen.y));" "\n"
|
||||
" fragment = texelFetch(tex, ivec2(gl_FragCoord.x, " "\n"
|
||||
" screen.y - gl_FragCoord.y), 0);" "\n"
|
||||
" fragment.a = 1.0F;" "\n"
|
||||
"}" "\n";
|
||||
if (!setup) {
|
||||
@@ -1376,9 +1486,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
/* We need to disable blending, we might read in bogus alpha values due
|
||||
to how we obtain the background texture (format is four byte `rgb_`,
|
||||
where the last value is skipped) */
|
||||
glDisable(GL_BLEND);
|
||||
if (!gl->premultiply_alpha) glDisable(GL_BLEND);
|
||||
drawoverlay(&gl->overlay);
|
||||
glEnable(GL_BLEND);
|
||||
if (!gl->premultiply_alpha) glEnable(GL_BLEND);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@@ -1396,6 +1506,10 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
/* Handle transformations and bindings for 1D samplers */
|
||||
void handle_1d_tex(GLuint tex, float* buf, float* ubuf, size_t sz, int offset, bool audio) {
|
||||
|
||||
if (load_flags[offset])
|
||||
goto bind_uniform;
|
||||
load_flags[offset] = true;
|
||||
|
||||
/* Only apply transformations if the buffers we
|
||||
were given are newly copied from PA */
|
||||
@@ -1411,6 +1525,7 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
transform types are added) */
|
||||
}
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0 + offset);
|
||||
|
||||
/* Update texture with our data */
|
||||
update_1d_tex(tex, sz, gl->interpolate ? (ubuf ? ubuf : buf) : buf);
|
||||
@@ -1456,7 +1571,8 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
/* setup and bind framebuffer to texture */
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, sm->fbo);
|
||||
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_1D, sm->tex, 0);
|
||||
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,\
|
||||
GL_TEXTURE_1D, sm->tex, 0);
|
||||
|
||||
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
|
||||
case GL_FRAMEBUFFER_COMPLETE: break;
|
||||
@@ -1477,11 +1593,11 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
glUniform1i(sm_uw, sz); /* target texture width */
|
||||
glUniform1i(sm_usz, sz); /* source texture width */
|
||||
glUniform1i(sm_utex, offset);
|
||||
glDisable(GL_BLEND);
|
||||
if (!gl->premultiply_alpha) glDisable(GL_BLEND);
|
||||
glViewport(0, 0, sz, 1);
|
||||
drawoverlay(&gl->overlay);
|
||||
glViewport(0, 0, ww, wh);
|
||||
glEnable(GL_BLEND);
|
||||
if (!gl->premultiply_alpha) glEnable(GL_BLEND);
|
||||
|
||||
/* Return state */
|
||||
glUseProgram(current->shader);
|
||||
@@ -1493,11 +1609,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
tex = sm->tex; /* replace input texture with our processed one */
|
||||
}
|
||||
|
||||
if (!needed[offset]) {
|
||||
glActiveTexture(GL_TEXTURE0 + offset);
|
||||
glBindTexture(GL_TEXTURE_1D, tex);
|
||||
needed[offset] = true;
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0 + offset);
|
||||
glBindTexture(GL_TEXTURE_1D, tex);
|
||||
bind_uniform:
|
||||
glUniform1i(bind->uniform, offset);
|
||||
}
|
||||
|
||||
@@ -1542,10 +1656,9 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
}
|
||||
|
||||
/* Swap buffers, handle events, etc. (vsync is potentially included here, too) */
|
||||
glfwSwapBuffers(gl->w);
|
||||
glfwPollEvents();
|
||||
gl->wcb->swap_buffers(gl->w);
|
||||
|
||||
double duration = glfwGetTime(); /* frame execution time */
|
||||
double duration = gl->wcb->get_time(gl->w); /* frame execution time */
|
||||
|
||||
/* Handling sleeping (to meet target framerate) */
|
||||
if (gl->rate > 0) {
|
||||
@@ -1581,20 +1694,44 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
/* Refresh window position and size if we are forcing it */
|
||||
if (gl->force_geometry) {
|
||||
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
|
||||
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
|
||||
gl->wcb->set_geometry(gl->w,
|
||||
gl->geometry[0], gl->geometry[1],
|
||||
gl->geometry[2], gl->geometry[3]);
|
||||
}
|
||||
|
||||
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; }
|
||||
void* rd_get_impl_window (struct renderer* r) { return r->gl->w; }
|
||||
struct gl_wcb* rd_get_wcb (struct renderer* r) { return r->gl->wcb; }
|
||||
|
||||
void rd_destroy(struct renderer* r) {
|
||||
/* TODO: delete everything else, not really needed though (as the application exits after here) */
|
||||
glfwTerminate();
|
||||
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);
|
||||
}
|
||||
|
||||
91
render.h
91
render.h
@@ -1,15 +1,96 @@
|
||||
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
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);
|
||||
void rd_update(struct renderer*, float* lb, float* rb, size_t bsz, bool modified);
|
||||
void rd_destroy(struct renderer*);
|
||||
void rd_time(struct renderer*);
|
||||
struct renderer* rd_new (const char** paths, const char* entry,
|
||||
const char* force_mod, const char* force_backend,
|
||||
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*);
|
||||
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 clickthrough);
|
||||
bool (*should_close) (void* ptr);
|
||||
bool (*should_render) (void* ptr);
|
||||
bool (*bg_changed) (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);
|
||||
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);
|
||||
const char* (*get_environment) (void);
|
||||
#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(should_render), \
|
||||
WCB_FUNC(bg_changed), \
|
||||
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), \
|
||||
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_environment), \
|
||||
WCB_FUNC(get_x11_display), \
|
||||
WCB_FUNC(get_x11_window) \
|
||||
}
|
||||
|
||||
#endif /* RENDER_H */
|
||||
|
||||
@@ -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 */
|
||||
#define BAR_OUTLINE vec4(0.15, 0.15, 0.15, 1)
|
||||
/* outline width (in pixels, set to 0 to disable outline drawing) */
|
||||
/* Outline color */
|
||||
#define BAR_OUTLINE #262626
|
||||
/* 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 (vec4(0.2, 0.4, 0.7, 1) * ((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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* center radius (pixels) */
|
||||
#define C_RADIUS 128
|
||||
/* center line thickness (pixels) */
|
||||
#define C_LINE 2
|
||||
#define C_LINE 1.5
|
||||
/* outline color */
|
||||
#define OUTLINE vec4(0.20, 0.20, 0.20, 1)
|
||||
#define OUTLINE #333333
|
||||
/* Amplify magnitude of the results each bar displays */
|
||||
#define AMPLIFY 150
|
||||
/* Angle (in radians) for how much to rotate the visualizer */
|
||||
|
||||
1
shaders/circle/3.frag
Normal file
1
shaders/circle/3.frag
Normal file
@@ -0,0 +1 @@
|
||||
#include ":util/premultiply.frag"
|
||||
8
shaders/env_KWin.glsl
Normal file
8
shaders/env_KWin.glsl
Normal 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
2
shaders/env_Openbox.glsl
Normal file
@@ -0,0 +1,2 @@
|
||||
#request setxwintype "desktop"
|
||||
#request addxwinstate "pinned"
|
||||
3
shaders/env_Xfwm4.glsl
Normal file
3
shaders/env_Xfwm4.glsl
Normal file
@@ -0,0 +1,3 @@
|
||||
#request setxwintype "desktop"
|
||||
#request addxwinstate "pinned"
|
||||
#request addxwinstate "below"
|
||||
2
shaders/env_awesome.glsl
Normal file
2
shaders/env_awesome.glsl
Normal file
@@ -0,0 +1,2 @@
|
||||
#request setxwintype "desktop"
|
||||
#request addxwinstate "pinned"
|
||||
1
shaders/env_default.glsl
Normal file
1
shaders/env_default.glsl
Normal file
@@ -0,0 +1 @@
|
||||
#request setxwintype "desktop"
|
||||
@@ -17,7 +17,7 @@
|
||||
/* actual color definition */
|
||||
#define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1)
|
||||
/* outline color */
|
||||
#define OUTLINE vec4(0.15, 0.15, 0.15, 1)
|
||||
#define OUTLINE #262626
|
||||
/* 1 to invert (vertically), 0 otherwise */
|
||||
#define INVERT 0
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/* center line thickness (pixels) */
|
||||
#define C_LINE 2
|
||||
/* outline color */
|
||||
#define OUTLINE vec4(0.20, 0.20, 0.20, 1)
|
||||
#define OUTLINE #333333
|
||||
/* number of bars (use even values for best results) */
|
||||
#define NBARS 180
|
||||
/* width (in pixels) of each bar*/
|
||||
@@ -15,8 +15,8 @@
|
||||
#define BAR_OUTLINE_WIDTH 0
|
||||
/* Amplify magnitude of the results each bar displays */
|
||||
#define AMPLIFY 300
|
||||
/* Bar color */
|
||||
#define COLOR (vec4(0.8, 0.2, 0.2, 1) * ((d / 40) + 1))
|
||||
/* Bar color */
|
||||
#define COLOR (#cc3333 * ((d / 40) + 1))
|
||||
/* Angle (in radians) for how much to rotate the visualizer */
|
||||
#define ROTATE (PI / 2)
|
||||
/* Whether to switch left/right audio buffers */
|
||||
|
||||
@@ -29,10 +29,10 @@ out vec4 fragment;
|
||||
#define PI 3.14159265359
|
||||
|
||||
void main() {
|
||||
|
||||
|
||||
#if USE_ALPHA > 0
|
||||
#define APPLY_FRAG(f, c) f = vec4(f.rgb * f.a + c.rgb * (1 - f.a), max(c.a, f.a))
|
||||
fragment.a = 0;
|
||||
#define APPLY_FRAG(f, c) f = vec4(f.rgb * f.a + c.rgb * (1 - clamp(f.a, 0, 1)), max(c.a, f.a))
|
||||
fragment = #00000000;
|
||||
#else
|
||||
#define APPLY_FRAG(f, c) f = c
|
||||
#endif
|
||||
@@ -46,9 +46,9 @@ void main() {
|
||||
float theta = atan(dy, dx); /* fragment angle with the center of the screen as the origin */
|
||||
float d = sqrt((dx * dx) + (dy * dy)); /* distance */
|
||||
if (d > C_RADIUS - (float(C_LINE) / 2.0F) && d < C_RADIUS + (float(C_LINE) / 2.0F)) {
|
||||
fragment = OUTLINE;
|
||||
APPLY_FRAG(fragment, OUTLINE);
|
||||
#if USE_ALPHA > 0
|
||||
fragment.a *= ((float(C_LINE) / 2.0F) - abs(d - C_RADIUS)) * C_ALIAS_FACTOR;
|
||||
fragment.a *= clamp(((C_LINE / 2) - abs(C_RADIUS - d)) * C_ALIAS_FACTOR, 0, 1);
|
||||
#else
|
||||
return; /* return immediately if there is no alpha blending available */
|
||||
#endif
|
||||
|
||||
1
shaders/radial/2.frag
Normal file
1
shaders/radial/2.frag
Normal file
@@ -0,0 +1 @@
|
||||
#include ":util/premultiply.frag"
|
||||
@@ -15,23 +15,17 @@
|
||||
See documentation for more details. */
|
||||
#request mod bars
|
||||
|
||||
/* GLFW window hints */
|
||||
/* Window hints */
|
||||
#request setfloating false
|
||||
#request setdecorated false
|
||||
#request setdecorated true
|
||||
#request setfocused false
|
||||
#request setmaximized false
|
||||
|
||||
/* Force GLFW window geometry (locking the window in place),
|
||||
useful for some pesky WMs that try to reposition the window
|
||||
when embedding in the desktop. */
|
||||
#request setforcegeometry false
|
||||
|
||||
/* Set window background opacity mode. Possible values are:
|
||||
|
||||
"native" - True transparency provided by the compositor.
|
||||
Requires GLFW 3.3+ and an active compositor. Can
|
||||
reduce performance on some systems, and will not
|
||||
blend with the visualizer's alpha layer.
|
||||
"native" - True transparency provided by the compositor. Can
|
||||
reduce performance on some systems, depending on
|
||||
the compositor used.
|
||||
|
||||
"xroot" - Maintain a copy of the root window's pixmap
|
||||
(usually the desktop background) to provide a
|
||||
@@ -40,22 +34,25 @@
|
||||
Has very little performance impact.
|
||||
|
||||
"none" - Disable window opacity completely. */
|
||||
#request setopacity "xroot"
|
||||
#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
|
||||
#request setshaderversion 330
|
||||
|
||||
/* GLFW window title */
|
||||
/* Window title */
|
||||
#request settitle "GLava"
|
||||
|
||||
/* GLFW window geometry (x, y, width, height) */
|
||||
#request setgeometry 0 0 400 600
|
||||
/* Window geometry (x, y, width, height) */
|
||||
#request setgeometry 0 0 800 600
|
||||
|
||||
/* Window background color (RGB format).
|
||||
Only works with `setopacity "none"` */
|
||||
#request setbg 3C3C3C
|
||||
Does not work with `setopacity "xroot"` */
|
||||
#request setbg 00000000
|
||||
|
||||
/* (X11 only) EWMH Window type. Possible values are:
|
||||
|
||||
@@ -64,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"
|
||||
|
||||
@@ -75,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
|
||||
@@ -90,13 +90,20 @@
|
||||
// #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
|
||||
default output device. */
|
||||
#request setsource "auto"
|
||||
|
||||
/* GLFW buffer swap interval (vsync), set to '0' to prevent
|
||||
/* Buffer swap interval (vsync), set to '0' to prevent
|
||||
waiting for refresh, '1' (or more) to wait for the specified
|
||||
amount of frames. */
|
||||
#request setswap 1
|
||||
@@ -118,9 +125,18 @@
|
||||
#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
|
||||
|
||||
/* Suspends rendering if a fullscreen window is focused while
|
||||
GLava is still visible (ie. on another monitor). This prevents
|
||||
rendering from interfering with other graphically intensive
|
||||
tasks.
|
||||
|
||||
If GLava is minimized or completely obscured, it will not
|
||||
render regardless of this option. */
|
||||
#request setfullscreencheck false
|
||||
|
||||
/* 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
|
||||
@@ -176,6 +192,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.
|
||||
|
||||
15
shaders/util/premultiply.frag
Normal file
15
shaders/util/premultiply.frag
Normal 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
|
||||
}
|
||||
406
xwin.c
406
xwin.c
@@ -8,55 +8,175 @@
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
/* 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 <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLFW/glfw3native.h>
|
||||
|
||||
#define GLAVA_RDX11
|
||||
#include "render.h"
|
||||
#include "xwin.h"
|
||||
|
||||
bool xwin_should_render(void) {
|
||||
/* 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 = wcb->get_x11_display();
|
||||
Atom class = XInternAtom(d, "WM_CLASS", false);
|
||||
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("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;
|
||||
}
|
||||
|
||||
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 gl_wcb* wcb, void* impl) {
|
||||
bool ret = true, should_close = false;
|
||||
Display* d = glfwGetX11Display();
|
||||
Display* d = wcb->get_x11_display();
|
||||
if (!d) {
|
||||
d = XOpenDisplay(0);
|
||||
should_close = true;
|
||||
}
|
||||
|
||||
|
||||
Atom prop = XInternAtom(d, "_NET_ACTIVE_WINDOW", true);
|
||||
Atom fullscreen = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", true);
|
||||
|
||||
Atom actual_type;
|
||||
int actual_format, t;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char* data;
|
||||
unsigned char* data = NULL;
|
||||
|
||||
int handler(Display* d, XErrorEvent* e) { return 0; }
|
||||
|
||||
XSetErrorHandler(handler); /* dummy error handler */
|
||||
|
||||
if (Success != XGetWindowProperty(d, RootWindow(d, 0), prop, 0, 1, false, AnyPropertyType,
|
||||
if (Success != XGetWindowProperty(d, DefaultRootWindow(d), prop, 0, 1, false, AnyPropertyType,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
|
||||
goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */
|
||||
}
|
||||
|
||||
|
||||
if (!nitems)
|
||||
goto close;
|
||||
|
||||
Window active = ((Window*) data)[0];
|
||||
|
||||
prop = XInternAtom(d, "_NET_WM_STATE", true);
|
||||
|
||||
if (data) {
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
if (Success != XGetWindowProperty(d, active, prop, 0, LONG_MAX, false, AnyPropertyType,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
|
||||
goto close; /* some WMs are a little slow on creating _NET_WM_STATE, so errors may occur here */
|
||||
@@ -67,42 +187,64 @@ bool xwin_should_render(void) {
|
||||
}
|
||||
}
|
||||
close:
|
||||
if (data)
|
||||
XFree(data);
|
||||
if (should_close)
|
||||
XCloseDisplay(d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set window types defined by the EWMH standard, possible values:
|
||||
-> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */
|
||||
static void xwin_changeatom(struct renderer* rd, const char* type, const char* atom, const char* fmt, int mode) {
|
||||
Window w = glfwGetX11Window((GLFWwindow*) rd_get_impl_window(rd));
|
||||
Display* d = glfwGetX11Display();
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void xwin_settype(struct renderer* rd, const char* type) {
|
||||
xwin_changeatom(rd, type, "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_%s", PropModeReplace);
|
||||
/* 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* 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 renderer* rd, const char* state) {
|
||||
xwin_changeatom(rd, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend);
|
||||
void xwin_addstate(struct gl_wcb* wcb, void* impl, const char* 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);
|
||||
}
|
||||
|
||||
static Pixmap get_pixmap(Display* d, Window w) {
|
||||
Pixmap p;
|
||||
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) {
|
||||
Drawable p;
|
||||
Atom act_type;
|
||||
int act_format;
|
||||
unsigned long nitems, bytes_after;
|
||||
@@ -110,14 +252,14 @@ static Pixmap get_pixmap(Display* d, Window w) {
|
||||
Atom id;
|
||||
|
||||
id = XInternAtom(d, "_XROOTPMAP_ID", False);
|
||||
|
||||
|
||||
if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP,
|
||||
&act_type, &act_format, &nitems, &bytes_after,
|
||||
&data) == Success) {
|
||||
if (data) {
|
||||
p = *((Pixmap *) data);
|
||||
XFree(data);
|
||||
}
|
||||
&data) == Success && data) {
|
||||
p = *((Pixmap *) data);
|
||||
XFree(data);
|
||||
} else {
|
||||
p = w;
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -131,112 +273,122 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
GLFWwindow* gwin = (GLFWwindow*) rd_get_impl_window(rd);
|
||||
int x, y, w, h;
|
||||
glfwGetFramebufferSize(gwin, &w, &h);
|
||||
glfwGetWindowPos(gwin, &x, &y);
|
||||
rd_get_wcb(rd)->get_fbsize(rd_get_impl_window(rd), &w, &h);
|
||||
rd_get_wcb(rd)->get_pos(rd_get_impl_window(rd), &x, &y);
|
||||
XColor c;
|
||||
Display* d = glfwGetX11Display();
|
||||
Pixmap p = get_pixmap(d, RootWindow(d, DefaultScreen(d)));
|
||||
|
||||
/* Obtain section of root pixmap using XShm */
|
||||
Display* d = rd_get_wcb(rd)->get_x11_display();
|
||||
Drawable src = get_drawable(d, DefaultRootWindow(d));
|
||||
bool use_shm = XShmQueryExtension(d);
|
||||
|
||||
/* Obtain section of root pixmap */
|
||||
|
||||
XShmSegmentInfo shminfo;
|
||||
Visual* visual = DefaultVisual(d, DefaultScreen(d));
|
||||
XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) };
|
||||
int nret;
|
||||
XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret);
|
||||
XImage* image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
|
||||
&shminfo, (unsigned int) w, (unsigned int) h);
|
||||
if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777)) == -1) {
|
||||
fprintf(stderr, "shmget() failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
XImage* image;
|
||||
if (use_shm) {
|
||||
image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
|
||||
&shminfo, (unsigned int) w, (unsigned int) h);
|
||||
if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height,
|
||||
IPC_CREAT | 0777)) == -1) {
|
||||
fprintf(stderr, "shmget() failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
|
||||
shminfo.readOnly = false;
|
||||
XShmAttach(d, &shminfo);
|
||||
XShmGetImage(d, src, image, x, y, AllPlanes);
|
||||
} else {
|
||||
image = XGetImage(d, src, x, y, (unsigned int) w, (unsigned int) h,
|
||||
AllPlanes, ZPixmap);
|
||||
}
|
||||
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
|
||||
shminfo.readOnly = false;
|
||||
XShmAttach(d, &shminfo);
|
||||
XShmGetImage(d, p, image, x, y, AllPlanes);
|
||||
|
||||
/* Try to convert pixel bit depth to OpenGL storage format. The following formats\
|
||||
will need intermediate conversion before OpenGL can accept the data:
|
||||
|
||||
- 8-bit pixel formats (retro displays, low-bandwidth virtual displays)
|
||||
- 36-bit pixel formats (rare deep color displays) */
|
||||
|
||||
bool invalid = false, aligned = false;
|
||||
GLenum type;
|
||||
switch (image->bits_per_pixel) {
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
|
||||
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
|
||||
case 16: /* 16-bit, hi-color */
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
aligned = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (image->depth) {
|
||||
case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */
|
||||
case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (image->depth == 48) /* 48-bit deep color */
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else goto invalid;
|
||||
break;
|
||||
/* >64-bit formats */
|
||||
case 128:
|
||||
if (image->depth == 96)
|
||||
type = GL_UNSIGNED_INT;
|
||||
else goto invalid;
|
||||
break;
|
||||
default:
|
||||
invalid: invalid = true;
|
||||
}
|
||||
|
||||
uint8_t* buf;
|
||||
if (invalid) {
|
||||
abort();
|
||||
/* Manual reformat (slow) */
|
||||
buf = malloc(4 * w * h);
|
||||
int xi, yi;
|
||||
Colormap map = DefaultColormap(d, DefaultScreen(d));
|
||||
for (yi = 0; yi < h; ++yi) {
|
||||
for (xi = 0; xi < w; ++xi) {
|
||||
c.pixel = XGetPixel(image, xi, yi);
|
||||
XQueryColor(d, map, &c);
|
||||
size_t base = (xi + (yi * w)) * 4;
|
||||
buf[base + 0] = c.red / 256;
|
||||
buf[base + 1] = c.green / 256;
|
||||
buf[base + 2] = c.blue / 256;
|
||||
buf[base + 3] = 255;
|
||||
|
||||
if (image) {
|
||||
bool invalid = false, aligned = false;
|
||||
GLenum type;
|
||||
switch (image->bits_per_pixel) {
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
|
||||
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
|
||||
case 16: /* 16-bit, hi-color */
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
aligned = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (image->depth) {
|
||||
case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */
|
||||
case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (image->depth == 48) /* 48-bit deep color */
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else goto invalid;
|
||||
break;
|
||||
/* >64-bit formats */
|
||||
case 128:
|
||||
if (image->depth == 96)
|
||||
type = GL_UNSIGNED_INT;
|
||||
else goto invalid;
|
||||
break;
|
||||
default:
|
||||
invalid: invalid = true;
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
||||
free(buf);
|
||||
} else {
|
||||
/* Use image data directly. The alpha value is garbage/unassigned data, but
|
||||
we need to read it because X11 keeps pixel data aligned */
|
||||
buf = (uint8_t*) image->data;
|
||||
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth)
|
||||
already ensures reads will be properly aligned across scanlines */
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
GLenum format = image->bitmap_bit_order == LSBFirst ?
|
||||
(!aligned ? GL_BGRA : GL_BGR) :
|
||||
(!aligned ? GL_RGBA : GL_RGB);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
|
||||
uint8_t* buf;
|
||||
if (invalid) {
|
||||
abort();
|
||||
/* Manual reformat (slow) */
|
||||
buf = malloc(4 * w * h);
|
||||
int xi, yi;
|
||||
Colormap map = DefaultColormap(d, DefaultScreen(d));
|
||||
for (yi = 0; yi < h; ++yi) {
|
||||
for (xi = 0; xi < w; ++xi) {
|
||||
c.pixel = XGetPixel(image, xi, yi);
|
||||
XQueryColor(d, map, &c);
|
||||
size_t base = (xi + (yi * w)) * 4;
|
||||
buf[base + 0] = c.red / 256;
|
||||
buf[base + 1] = c.green / 256;
|
||||
buf[base + 2] = c.blue / 256;
|
||||
buf[base + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
||||
free(buf);
|
||||
} else {
|
||||
/* Use image data directly. The alpha value is garbage/unassigned data, but
|
||||
we need to read it because X11 keeps pixel data aligned */
|
||||
buf = (uint8_t*) image->data;
|
||||
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth)
|
||||
already ensures reads will be properly aligned across scanlines */
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
GLenum format = image->bitmap_bit_order == LSBFirst ?
|
||||
(!aligned ? GL_BGRA : GL_BGR) :
|
||||
(!aligned ? GL_RGBA : GL_RGB);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
|
||||
}
|
||||
}
|
||||
if (use_shm) {
|
||||
XShmDetach(d, &shminfo);
|
||||
shmdt(shminfo.shmaddr);
|
||||
shmctl(shminfo.shmid, IPC_RMID, NULL);
|
||||
}
|
||||
|
||||
XShmDetach(d, &shminfo);
|
||||
shmdt(shminfo.shmaddr);
|
||||
shmctl(shminfo.shmid, IPC_RMID, NULL);
|
||||
|
||||
XDestroyImage(image);
|
||||
if (image) XDestroyImage(image);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
19
xwin.h
19
xwin.h
@@ -1,5 +1,18 @@
|
||||
|
||||
bool xwin_should_render(void);
|
||||
void xwin_settype(struct renderer* rd, const char* type);
|
||||
void xwin_addstate(struct renderer* rd, const char* state);
|
||||
#define XWIN_ALL_DESKTOPS 0xFFFFFFFF
|
||||
|
||||
#ifndef XWIN_H
|
||||
#define XWIN_H
|
||||
|
||||
typedef unsigned long int Window;
|
||||
|
||||
bool xwin_should_render(struct gl_wcb* wcb, void* impl);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user