151 Commits
v1.2 ... v1.6.1

Author SHA1 Message Date
Levi Webb
b37ab5ba09 Merge pull request #106 from wacossusca34/circle-fix-undefined-fragcolor
Prevent `fragment` from being potentially undefined (Fixes #105)
2019-03-12 11:34:33 -07:00
Robin B
1a595c2f19 Prevent fragment from being potentially undefined leading to driver-specific noise
This fixes noise in the center of the circle shader, where `d >= -(float(C_LINE) / 2.0F)` is false
2019-03-12 15:54:37 +01:00
Jarcode
c9dac68bb4 Allow '--stdin' to work properly with pipes, see #77 2019-03-10 16:36:13 -07:00
Jarcode
c14c2a835a Added support for reading uniforms from stdin, see #77 2019-03-10 16:13:34 -07:00
Jarcode
6f688e8b4d Restore new smoothing parameters 2019-03-09 21:14:08 -08:00
Jarcode
bfea8e167a Load system config for 'smooth_parameters.glsl' 2019-03-09 21:13:27 -08:00
Jarcode
d07b93d54e Fix clickthrough for reparented windows, closes #80 2019-03-09 16:55:03 -08:00
Jarcode
f7170afb75 Fixed attached base frequencies in 'bars' 2019-03-09 15:27:35 -08:00
Jarcode
8ccbf22732 Merge branch 'master' of https://github.com/wacossusca34/glava 2019-03-09 14:52:58 -08:00
Jarcode
5fdf0545a8 Added GLSL sampling modes 2019-03-09 14:52:38 -08:00
Levi Webb
b77d1a7b3a Update build requirements, closes #102 2019-03-02 11:26:03 -08:00
Jarcode
f87c70af2b Disable second 'graph' pass if nessecary (& other formatting), see #97 2019-02-05 17:45:17 -08:00
Jarcode
7c30c4a78a Added support for disabling shader passes, see #97 2019-02-04 22:07:26 -08:00
Levi Webb
bc2db4a415 Merge pull request #97 from arch1t3cht30/graph_tweaks
Anti-aliasing and better joining for the graph module
2019-02-02 14:58:33 -08:00
Theo Müller
3ff1097941 Fix inverting in antialias pass 2019-02-01 22:40:46 +01:00
Theo Müller
041bfdfd55 Make anti-aliasing work with invert 2019-02-01 20:19:15 +01:00
Theo Müller
8cbf7bc579 Cubic interpolation for joining graph channels 2019-01-31 12:05:05 +01:00
Theo Müller
91530dfc49 Make joining optional 2019-01-30 20:26:35 +01:00
Theo Müller
a39a1324e1 Add anti-aliasing option for graph 2019-01-30 19:59:51 +01:00
Theo Müller
140d98b404 Join middle borders instead of clamping them down 2019-01-30 19:59:51 +01:00
Levi Webb
f235362f44 Merge pull request #94 from dan-santana/musl-build
Fix musl build
2019-01-04 21:45:19 -08:00
Levi Webb
32853b73d8 Merge pull request #93 from dan-santana/master
Use SONAMEs when loading GLX libs dinamically
2019-01-04 21:43:29 -08:00
Daniel Santana
4705699bb6 Fix musl build 2019-01-04 22:23:52 -02:00
Daniel Santana
3ef23f0db8 Use SONAMEs when loading GLX libs dinamically 2019-01-04 21:57:52 -02:00
Levi Webb
4d51ccbd54 Update README.md 2019-01-01 14:00:11 -08:00
Levi Webb
6c02d15c80 Force bash in Makefile, see #91 2019-01-01 12:47:57 -08:00
Levi Webb
40dac829cc Merge pull request #92 from Aaahh/patch-7
Check SHADERDIR exist
2019-01-01 12:46:06 -08:00
Aaahh Ahh
bde3101c42 Check SHADERDIR exist
Unity has incorrectly set XDG_CONFIG_DIRS to a path that does not exist
2018-12-30 16:49:40 -05:00
Levi Webb
f2857e5f21 Merge pull request #83 from jubalh/readme
Add install instructions
2018-12-17 13:09:01 -08:00
Levi Webb
66a9b09b10 Merge pull request #82 from jubalh/giti
Add gitignore
2018-11-30 18:15:16 -08:00
Michael Vetter
31a39b6ecd Add install instructions
Some distros have packages for glava. Let's mention them.
2018-11-30 14:49:13 +01:00
Michael Vetter
dd0f5bf0f9 Add gitignore
Add files that we don't want to have in version control so devs have it
easier.
2018-11-30 14:42:58 +01:00
Jarcode
1337253257 Added audio backends, fifo support, see #78 2018-11-17 21:03:02 -08:00
Jarcode
76325cb126 Fixed uninitialized GLSL output variable, closes #70 2018-11-13 17:04:12 -08:00
Jarcode
426f70f579 Added --request parameter, fixed reading directives at end of file, limited most output to --verbose option 2018-11-02 16:27:54 -07:00
Jarcode
1d2b014da7 Fix leak with 'xroot' transparency 2018-11-02 13:09:48 -07:00
Jarcode
6670c54827 Cleaned up and optimized 'graph' module and its settings, fixes #76 2018-10-29 20:16:40 -07:00
Jarcode
f4ad41df32 Added '@' include specifier, #define workaround, closes #74, closes #75 2018-10-26 21:40:31 -07:00
Jarcode
93df114308 update wording 2018-10-26 18:42:01 -07:00
Jarcode
d21edcfb29 update contribution guidelines 2018-10-26 18:41:00 -07:00
Jarcode
e0b4f7d6c7 Merge branch 'master' of https://github.com/wacossusca34/glava 2018-10-26 18:35:44 -07:00
Jarcode
386d32076c Style changes and contribution guidelines 2018-10-26 18:35:29 -07:00
Robin B
52af6ac173 radial: configurable offsets (#73)
* Add: Offset radial visualizer (config)

* Add: Offset radial visualizer
2018-10-25 18:56:59 +02:00
Jarcode
3da1fb34dd stopped using centered coords for radial module, see #69 2018-10-21 12:13:56 -07:00
Jarcode
2c99124556 Fixed various static leaks 2018-10-21 10:28:06 -07:00
Jarcode
9d1c3a177c Fixed critical bug that caused GLava to stop rendering if it was obstructed 2018-10-09 17:43:15 -07:00
Jarcode
db625cf00c Add flags for i3 2018-10-08 17:52:04 -07:00
Jarcode
1845fad7fd update README.md 2018-10-08 17:51:18 -07:00
Jarcode
25c1701ce6 Update --desktop flags for i3 and awesome 2018-10-08 17:49:17 -07:00
Jarcode
b627219109 Updated makefile with various improvements 2018-10-08 17:36:17 -07:00
Jarcode
3dfbdb5127 Removed hard dependency on glad submodule 2018-10-08 17:32:44 -07:00
Jarcode
7bb7913bd3 Removed -march=native from Makefile 2018-10-08 12:43:18 -07:00
Jarcode
1a1cbc9cc8 Fixed changes to _XROOTPMAP_ID not updating background with "xroot" transparency 2018-10-08 11:53:58 -07:00
Jarcode
83a94e3eb4 Handle window map_state and VisibilityNotify events, closes #68 2018-10-08 11:18:45 -07:00
Jarcode
acdbb8f3b5 updated README.md 2018-10-07 12:04:00 -07:00
Jarcode
829c1be370 Merge branch 'master' of https://github.com/wacossusca34/glava 2018-10-07 08:20:33 -07:00
Jarcode
261ba8ded5 Added more meaningful error messages to the GLX backend 2018-10-07 08:20:19 -07:00
Levi Webb
fc9c74d256 Merge pull request #66 from coderobe/patch-readme
README: Add required X extensions
2018-10-06 15:21:11 -07:00
Levi Webb
a697af9a0a Merge pull request #67 from coderobe/patch-readme-archlinux
README: Mention Arch Linux binary release (community)
2018-10-06 15:20:40 -07:00
Robin B
437aa146a4 README: Mention Arch Linux binary release (community)
GLava is now part of the official Arch Linux [community] repository, maintained by me
2018-10-06 20:41:11 +02:00
Robin B
1c2f362219 README: Add required X extensions 2018-10-06 19:28:32 +02:00
Levi Webb
7dfb68fb1a Update README.md 2018-10-05 21:38:40 -07:00
Jarcode
353c3bd62f Added support for unmanaged windows, see #65 and #4 2018-10-04 20:14:37 -07:00
Jarcode
e4b16cdbb6 Added option to render bars on left/right sides, see #64 2018-10-03 17:45:48 -07:00
Jarcode
8102f99683 Added option to vertically flip bars output, see #64 2018-10-03 17:21:53 -07:00
Jarcode
14747b1e75 Use default PulseAudio buffer sizes when reading input from server, see #63 2018-09-30 14:50:10 -07:00
Levi Webb
217d5c9eea Fix spelling error 2018-09-26 19:04:44 -07:00
Jarcode
3be916f157 Fixed clickthrough issue on Openbox and XFCE, closes #61 2018-09-17 19:23:06 -07:00
Jarcode
bc955a5b3d Merge branch 'master' of https://github.com/wacossusca34/glava 2018-09-12 21:01:46 -07:00
Jarcode
c7ad0a5024 Added signal handler for SIGINT and SIGTERM 2018-09-12 21:01:12 -07:00
Levi Webb
83ad0d8f8a Merge pull request #60 from Smarthard/master
bars COLOR refactored & alpha channel support added
2018-09-11 21:31:25 -07:00
Smarthard
53f7352347 bars COLOR refactored & alpha channel support added 2018-09-11 19:01:24 +03:00
Jarcode
643b0cf3f5 Added detection for variable changes in Makefile, see #59 2018-09-09 19:26:32 -07:00
Jarcode
11a6137370 Added graceful renderer and audio thread destruction, fixes #47 2018-09-09 07:54:51 -07:00
Jarcode
d4b333e48c removed pointless message 2018-09-08 21:25:11 -07:00
Jarcode
d49be40fd3 Added --desktop flag with presets, added setclickthrough option, removed kde previous kde changes, updated readme 2018-09-08 21:06:18 -07:00
Jarcode
fb1d85dbf9 Added potential fixes for KDE 2018-09-08 14:05:40 -07:00
Levi Webb
31ebab0549 Merge pull request #56 from Aaahh/patch-5
Stray letter
2018-08-15 22:13:24 -07:00
Aaahh Ahh
3f621420a3 Stray letter 2018-08-15 23:14:54 -04:00
Levi Webb
9a9c6eaa37 Update README.md 2018-08-13 19:28:06 -07:00
Jarcode
3e06fa683a Updated readme with XFCE4 instructions 2018-08-06 09:12:26 -07:00
Jarcode
b971c1c8f9 Updated readme with XFCE4 instructions 2018-08-05 22:30:26 -07:00
Jarcode
45b614a692 Added configuration option for fullscreen check 2018-08-05 21:24:18 -07:00
Jarcode
fc80db664b fix startup resource leak 2018-08-05 19:47:12 -07:00
Jarcode
75a3affbc0 changed defaults to disable mirroring 2018-08-05 19:27:50 -07:00
Jarcode
198596eaee Wait for EWMH WM to add _NET_SUPPORTING_WM_CHECK property, addresses #54 2018-08-05 18:22:07 -07:00
Jarcode
e694261f4d Merge branch 'master' of https://github.com/wacossusca34/glava 2018-08-05 17:39:58 -07:00
Jarcode
8c3b9a5f21 Added audio buffer mirroring, closes #53 2018-08-05 17:38:59 -07:00
Levi Webb
ebade264ea Fix hyperlink text in license
GitHub displays license information at the head of the repostiory contents for certain licenses, however this breaks when license text is slightly altered. In this case, github only works with the GPLv3 when the license text contains `http:` instead of `https:`!
2018-06-02 09:42:07 -07:00
Jarcode
469b8ec7ad Merge branch 'master' of https://github.com/wacossusca34/glava 2018-06-02 09:29:51 -07:00
Jarcode
4cc8a5e3ba Added "pinned" option for setxwinstate, addresses #4 2018-06-02 09:28:13 -07:00
Levi Webb
309b1beeec Merge pull request #44 from coderobe/patch-1
Fix fatal make issue when using XDG_CONFIG_DIRS
2018-05-07 14:04:14 -07:00
Robin B
7b72a28f19 Fix fatal make issue when using XDG_CONFIG_DIRS 2018-05-07 11:48:17 +02:00
Levi Webb
b83c7cc59e Merge pull request #43 from Aaahh/patch-3
Update README.md
2018-05-02 06:33:29 -07:00
Aaahh Ahh
6318c57ff7 Update README.md 2018-04-27 17:30:07 -04:00
Jarcode
279437dcd1 Update README with accurate instructions for Ubuntu/Debian 2018-04-06 17:05:58 -07:00
Jarcode
869ebba6b4 Added dynamic symbol loading for GLX functions, closes #41 2018-04-06 17:02:43 -07:00
Jarcode
dfd16f9e22 Properly compare desktop window types, closes #37 2018-03-19 21:45:19 -07:00
Jarcode
b446ac99c9 Fixed resource leak associated with xlib usage, see #33 2018-03-18 22:01:03 -07:00
Jarcode
8024e308d8 fixed some code style issues 2018-03-18 17:27:36 -07:00
Jarcode
dd5586a76e cleaning up comment formatting & spelling 2018-03-18 17:23:45 -07:00
Jarcode
20e755fbcb Switched to using git tags for versioning 2018-03-18 17:00:14 -07:00
Jarcode
ccf3c7b169 updated version 2018-03-18 16:26:52 -07:00
Jarcode
4be89c3337 force GLX backend even on XWayland sessions 2018-03-18 16:22:23 -07:00
Jarcode
2220946a2f Updated readme with compatibility information 2018-03-18 16:19:25 -07:00
Jarcode
9fb80be00f Merge branch 'unstable' 2018-03-18 12:26:31 -07:00
Jarcode
283afaaaca Cleanup & implementation of #32 2018-03-18 12:20:20 -07:00
Jarcode
5ac2cc4a94 Removed changelog in favour of GitHub releases 2018-03-04 13:39:39 -08:00
Jarcode
273cca946e Merge branch 'master' of https://github.com/wacossusca34/glava 2018-03-04 12:49:51 -08:00
Jarcode
95ceadf0e1 Merge branch 'unstable' 2018-03-04 12:49:23 -08:00
Jarcode
f32f0ded0f Version increase 2018-03-04 12:41:51 -08:00
Jarcode
3a1dcac8c9 Fixed texture load order, closes #28 2018-03-04 12:40:17 -08:00
Levi Webb
8adc8fe459 Merge pull request #26 from coderobe/master
Fix LDFLAGS
2018-03-02 17:41:49 -08:00
Robin Broda
e2f32a14cf Fix LDFLAGS 2018-03-02 18:40:35 +01:00
Levi Webb
318b4b3395 Update README.md 2018-02-20 19:05:00 -08:00
Levi Webb
b89fcd2e44 Merge pull request #23 from Aaahh/patch-3
Update Package names
2018-02-17 17:18:15 -08:00
Aaahh Ahh
baed3086d0 Update Package names
libpulse and libxext6-dev don't exist, also add libglx0 as it's not installed by default
2018-02-17 15:25:36 -05:00
Jarcode
b4fe4b5c97 stopped using the word currently frequently 2018-02-12 17:00:07 -08:00
Jarcode
0849cb485e Merge branch 'unstable' 2018-02-12 16:50:29 -08:00
Jarcode
c086222fb2 updated changelog 2018-02-12 16:50:08 -08:00
Jarcode
639c6d7fe9 Merge branch 'unstable' 2018-02-12 16:41:18 -08:00
Jarcode
22bc7c446c Fixed window class not being set by glx backend, + other fixes 2018-02-12 16:36:27 -08:00
Jarcode
b86870e0fd Added passes for premultiplied alpha, fixed alpha blending for native transparency 2018-02-12 16:25:32 -08:00
Jarcode
f021457abd Added GLX window creation backend, addresses #18 2018-02-12 12:47:59 -08:00
Jarcode
4fd4ce4c3f added 'glfw_wcb.c' 2018-02-12 07:17:33 -08:00
Jarcode
e5f5671acc Added gl_wcb interface to abstract out glfw and glx usage 2018-02-11 20:43:52 -08:00
Jarcode
79d06b070e Fixed potetial segmentation fault for xwin_should_render, addresses #19 2018-02-11 12:02:26 -08:00
Jarcode
3df89da8bb fixed alpha blending for 'radial' inner circle 2018-02-11 11:42:26 -08:00
Jarcode
79b99b09be refactored X11 code in 'render.c' and 'xwin.c' 2018-02-11 09:38:23 -08:00
Jarcode
0f4eed5e20 Updated readme with list of compatibility issues 2018-02-08 17:25:57 -08:00
Jarcode
ae09561d11 added test case for mutter gaurd window, addresses #11 and #18 2018-02-08 17:13:38 -08:00
Jarcode
33aa5274ae Merge branch 'master' into unstable 2018-02-08 16:52:31 -08:00
Jarcode
caee6a739c added case for mutter gaurd window 2018-02-08 16:50:52 -08:00
Jarcode
c8cce220d8 fixed GLFW_MAXIMIZED macro for GLFW 3.1 2018-02-08 06:36:40 -08:00
Jarcode
be63f40dc2 Merge branch 'unstable' 2018-02-07 20:57:00 -08:00
Jarcode
f5a9f801d1 Backported for GLFW 3.1, fixes #13, *again* 2018-02-07 20:56:41 -08:00
Jarcode
53bd657226 Added debian(-based) compile instructions 2018-02-07 20:42:05 -08:00
Jarcode
e6f2507b53 Merge branch 'unstable' 2018-02-07 20:23:15 -08:00
Jarcode
2805528d43 fixed directive issue 2018-02-07 20:22:54 -08:00
Jarcode
1de3a5215b updated changelog 2018-02-07 20:18:05 -08:00
Jarcode
1db4ea3ded updated changelog 2018-02-07 20:17:43 -08:00
Jarcode
901cdb7d67 Merge branch 'unstable' 2018-02-07 20:06:09 -08:00
Jarcode
d82762a471 changed version, added -V (--version) flag 2018-02-07 20:02:24 -08:00
Jarcode
ec6d4d0c37 exempted glsl color syntax from string contents 2018-02-07 19:53:23 -08:00
Jarcode
aea30abf67 added RGBA hex color parsing for GLSL, fixed alpha blending bug 2018-02-07 19:29:56 -08:00
Jarcode
79d391938d moved hex color parsing to ext_parse_color, removed debug message 2018-02-07 17:46:34 -08:00
Jarcode
8c3a404f37 fixed include order in xwin.c, fixes #13 2018-02-07 17:17:58 -08:00
Jarcode
f539f18135 Always define GLFW_EXPOSE_NATIVE_GLX 2018-02-07 17:12:45 -08:00
Jarcode
ab55156826 Set window types and state ealier during initialization, fixed #17 2018-02-07 16:58:39 -08:00
Jarcode
5e813e25a9 Changed position of 'glfwShowWindow' call, related to #17 2018-02-07 16:41:12 -08:00
Jarcode
83f5583eeb Updated version 2018-02-06 19:04:25 -08:00
47 changed files with 11222 additions and 1050 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.o
build_state
glava

View File

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

44
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,44 @@
## Code Style
GLava uses a bastardized version of the [linux kernel style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html), with the following modifications:
* Opening braces are _always_ on the same line as the token it is associated with (`if`, `while`, labels, functions). The only time this is not honoured is when a set of braces has no associated token (ie. scope usage).
* Indentation is 4 spaces, and tabs are forbidden
* The content of a `switch` statement, including `case` labels, are indented.
* Preprocessor directives should inherit the same intentation level as the code it resides in.
* Align tokens in repetitious lines by padding spacing between tokens.
The following rules of the linux style are **ignored**:
* Function size and control flow recommendations
* Comment formatting rules
* Any other rules regarding preprocessor directives
Naming rules and the usage of `typedef` is strictly honoured from the Linux style. Anything not mentioned here is probably subjective and won't hurt your chances of getting a PR accepted.
If you use GNU Emacs, the above style can be configured via the following elisp:
```emacs
(setq-default c-basic-offset 4)
(setq c-default-style "linux")
(setq tab-stop-list (number-sequence 4 200 4))
(c-set-offset (quote cpp-macro) 0 nil)
(c-set-offset 'case-label '+)
```
## Shaders
If you author and maintain your own shader module for GLava, you are free to use your preferred code style. Otherwise, shaders follow the same style as GLava's C sources.
## Pull Requests
You are free to make pull requests for any change, even if you are not sure if the proposed changes are appropriate. @wacossusca34 and/or @coderobe will be able to suggest changes or commentary on the PR if there is a reason it is not acceptable.
## Conduct
Engagement in the issue tracker and pull requests simply requires participants remain rational and on-topic.

View File

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

View File

@@ -1,15 +1,18 @@
SHELL := /bin/bash
src = $(wildcard *.c)
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 -DGLAVA_DEBUG
GLAD_GEN = c-debug
ASAN = -lasan
STRIP_CMD = $(info Skipping `strip` for debug builds)
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)
@@ -23,6 +26,10 @@ ifndef INSTALL
endif
endif
ifndef EXECDIR
EXECDIR = /usr/bin/
endif
# Install type parameter
ifeq ($(INSTALL),standalone)
@@ -31,52 +38,95 @@ endif
ifeq ($(INSTALL),unix)
CFLAGS_INSTALL = -DGLAVA_UNIX
ifdef XDG_CONFIG_DIRS
SHADER_DIR = $(firstword $(subst :, ,$XDG_CONFIG_DIR))/glava
else
SHADER_DIR = etc/xdg/glava
ifndef SHADERDIR
ifdef XDG_CONFIG_DIRS
SHADERDIR = /$(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava/
ifeq ($(wildcard $(SHADERDIR)/..),)
SHADERDIR = /etc/xdg/glava/
endif
else
SHADERDIR = /etc/xdg/glava/
endif
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
ifndef SHADERDIR
SHADERDIR = /Library/glava
endif
endif
LDFLAGS = $(ASAN) -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext
LDFLAGS += $(ASAN) -lpulse -lpulse-simple -pthread $(LDFLAGS_GLFW) -ldl -lm -lX11 -lXext $(LDFLAGS_GLX)
PYTHON = python
GLAVA_VERSION = \"$(shell git describe --tags 2>/dev/null)\"
ifeq ($(GLAVA_VERSION),\"\")
GLAVA_VERSION = \"unknown\"
endif
ifdef DESTDIR
DESTDIR += /
endif
GLAD_INSTALL_DIR = glad
GLAD_SRCFILE = ./glad/src/glad.c
GLAD_SRCFILE = 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 = -DGLAVA_VERSION="$(GLAVA_VERSION)" -DSHADER_INSTALL_PATH="\"$(SHADERDIR)\""
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),$(PREFIX),$(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
$(CC) $(CFLAGS_USE) -o $@ -c $(firstword $<)
%.o: %.c build_state
@$(CC) $(CFLAGS_USE) -o $@ -c $(firstword $<)
@echo "CC $@"
glava: $(obj)
$(CC) -o glava $(obj) glad.o $(LDFLAGS)
@$(CC) -o glava $(obj) $(LDFLAGS)
@echo "CC glava"
$(STRIP_CMD)
glad.o:
cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS) --out-path=.
$(CC) $(CFLAGS_USE) -o glad.o $(GLAD_SRCFILE) -c
.PHONY: glad
glad: build_state
@cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS) --local-files --out-path=.
@cp glad/*.h .
@cp glad/glad.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
EXECTARGET = $(shell readlink -m "$(DESTDIR)$(EXECDIR)/glava")
SHADERTARGET = $(shell readlink -m "$(DESTDIR)$(SHADERDIR)")
.PHONY: install
install:
install -Dm755 glava "$(DESTDIR)/usr/bin/glava"
install -d "$(DESTDIR)/$(SHADER_DIR)"
cp -Rv shaders/* "$(DESTDIR)/$(SHADER_DIR)"
install -Dm755 glava $(EXECTARGET)
install -d $(SHADERTARGET)
cp -Rv shaders/* $(SHADERTARGET)
.PHONY: uninstall
uninstall:
rm /usr/bin/glava
rm -rf $(SHADER_DIR)/glava
rm $(EXECTARGET)
rm -rf $(SHADERTARGET)

View File

@@ -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:**
```bash
$ git clone --recursive https://github.com/wacossusca34/glava
$ git clone https://github.com/wacossusca34/glava
$ cd glava
$ make
$ CFLAGS="-march=native" make
$ sudo make install
$ glava
```
@@ -17,28 +17,39 @@ 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:**
- glad (included as a submodule)
- 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 libxext6 libxext-dev libxrender-dev libxcomposite-dev make gcc
```
## Installation
Some distributions have a package for `glava`. If your distribution is not listed please use the compilation instructions above.
- Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/)
- NixOS [package](https://github.com/NixOS/nixpkgs/blob/release-18.09/pkgs/applications/misc/glava/default.nix)
- openSUSE [package](https://build.opensuse.org/package/show/X11:Utilities/glava)
## [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,26 +57,42 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
| WM | ! | Details
| :---: | --- | --- |
| GNOME (on X11) | ![-](https://placehold.it/15/118932/000000?text=+) | No notable issues
| Openbox (LXDE or standalone) | ![-](https://placehold.it/15/118932/000000?text=+) | [Some tweaks may be required](https://www.reddit.com/r/unixporn/comments/7vcgi4/oc_after_receiving_positive_feedback_here_i/dtrkvja/)
| Xfwm (XFCE) | ![-](https://placehold.it/15/118932/000000?text=+) | Untested, but should work without issues
| Fluxbox | ![-](https://placehold.it/15/118932/000000?text=+) | Untested, but should work without issues
| iceWM | ![-](https://placehold.it/15/118932/000000?text=+) | No notable issues
| AwesomeWM | ![-](https://placehold.it/15/f09c00/000000?text=+) | Requires the WM to be restarted (`Super + Ctl + R`) in order for new desktop windows to behave correctly, can still be focused, may require other changes to config depending on layout
| Budgie Desktop | ![-](https://placehold.it/15/f09c00/000000?text=+) | `"xroot"` transparency breaks with Budgie's wallpaper window
| kwin (KDE) | ![-](https://placehold.it/15/f09c00/000000?text=+) | [Issues with workspaces and stacking](https://github.com/wacossusca34/glava/issues/4), needs further testing
| i3 (and i3-gaps) | ![-](https://placehold.it/15/f03c15/000000?text=+) | [i3 does not respect the `"desktop"` window type](https://github.com/wacossusca34/glava/issues/6)
| Mutter (GNOME, Budgie) | ![-](https://placehold.it/15/118932/000000?text=+) | `"native"` (default) opacity should be used
| KWin (KDE) | ![-](https://placehold.it/15/118932/000000?text=+) | "Show Desktop" [temporarily hides GLava](https://github.com/wacossusca34/glava/issues/4#issuecomment-419729184)
| Openbox (LXDE or standalone) | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Xfwm (XFCE) | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Fluxbox | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| IceWM | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Bspwm | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| Herbstluftwm | ![-](https://placehold.it/15/118932/000000?text=+) | `hc rule windowtype~'_NET_WM_WINDOW_TYPE_DESKTOP' manage=off` can be used to unmanage desktop windows
| Unity | ![-](https://placehold.it/15/118932/000000?text=+) | No issues
| AwesomeWM | ![-](https://placehold.it/15/118932/000000?text=+) | Defaults to unmanaged
| i3 (and i3-gaps) | ![-](https://placehold.it/15/118932/000000?text=+) | Defaults to unmanaged
| EXWM | ![-](https://placehold.it/15/f03c15/000000?text=+) | EXWM does not have a desktop, and forces window decorations
| Unity | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Enlightenment | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Bspwm | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Herbstluftwm | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| xmonad | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Xmonad | ![-](https://placehold.it/15/1589F0/000000?text=+) | Needs testing
| Any non EWMH-compliant WM | ![-](https://placehold.it/15/f03c15/000000?text=+) | Window types and hints will not work if the window manager does not support the EWMH standards.
Note that some WMs listed without issues have specific overrides when using the `--desktop` flag. See `shaders/env_*.glsl` files for details.
## Reading from MPD's FIFO output
Add the following to your `~/.config/mpd.conf`:
```
audio_output {
type "fifo"
name "glava_fifo"
path "/tmp/mpd.fifo"
format "22050:16:2"
}
```
Note the `22050` sample rate -- this is the reccommended setting for GLava. Restart MPD (if nessecary) and start GLava with `glava --audio=fifo`.
## 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:
GLava is licensed under the terms of the GPLv3, with the exemption of `khrplatform.h`, which is licensed under the terms in its header. 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:
- `[cava]/input/fifo.c -> [glava]/fifo.c`
- `[cava]/input/fifo.h -> [glava]/fifo.h`
@@ -76,12 +103,8 @@ 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:
`Copyright (c) 2017 Levi Webb`
## 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.

183
fifo.c
View File

@@ -7,80 +7,123 @@
#include <fcntl.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include "fifo.h"
//input: FIFO
void* input_fifo(void* data)
{
struct audio_data *audio = (struct audio_data *)data;
int fd;
int n = 0;
signed char buf[1024];
int tempr, templ, lo;
int q, i;
int t = 0;
int size = 1024;
int bytes = 0;
int flags;
struct timespec req = { .tv_sec = 0, .tv_nsec = 10000000 };
fd = open(audio->source, O_RDONLY);
flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
/* Implementation struct storage */
while (1) {
typeof(*audio_impls) audio_impls[sizeof(audio_impls) / sizeof(struct audio_impl*)] = {};
size_t audio_impls_idx = 0;
bytes = read(fd, buf, sizeof(buf));
/* FIFO backend */
if (bytes == -1) { //if no bytes read sleep 10ms and zero shared buffer
nanosleep (&req, NULL);
t++;
if (t > 10) {
for (i = 0; i < 2048; i++)audio->audio_out_l[i] = 0;
for (i = 0; i < 2048; i++)audio->audio_out_r[i] = 0;
t = 0;
}
} else { //if bytes read go ahead
t = 0;
for (q = 0; q < (size / 4); q++) {
tempr = ( buf[ 4 * q + 3] << 2);
lo = ( buf[4 * q + 2] >> 6);
if (lo < 0)lo = abs(lo) + 1;
if (tempr >= 0)tempr = tempr + lo;
else tempr = tempr - lo;
templ = ( buf[ 4 * q + 1] << 2);
lo = ( buf[ 4 * q] >> 6);
if (lo < 0)lo = abs(lo) + 1;
if (templ >= 0)templ = templ + lo;
else templ = templ - lo;
if (audio->channels == 1) audio->audio_out_l[n] = (tempr +
templ) /
2;
//stereo storing channels in buffer
if (audio->channels == 2) {
audio->audio_out_l[n] = templ;
audio->audio_out_r[n] = tempr;
}
n++;
if (n == 2048 - 1)n = 0;
}
}
if (audio->terminate == 1) {
close(fd);
break;
}
}
return 0;
static void init(struct audio_data* audio) {
if (!audio->source) {
audio->source = strdup("/tmp/mpd.fifo");
}
}
static void* entry(void* data) {
struct audio_data* audio = (struct audio_data *) data;
float* bl = (float*) audio->audio_out_l;
float* br = (float*) audio->audio_out_r;
size_t fsz = audio->audio_buf_sz;
size_t ssz = audio->sample_sz;
int fd;
int16_t buf[ssz / 2];
size_t q;
int timeout = 50;
struct timespec tv_last = {}, tv;
bool measured = false;
if ((fd = open(audio->source, O_RDONLY)) == -1) {
fprintf(stderr, "failed to open FIFO audio source \"%s\": %s\n", audio->source, strerror(errno));
exit(EXIT_FAILURE);
}
struct pollfd pfd = {
.fd = fd,
.events = POLLIN
};
size_t buffer_offset = (fsz - (ssz / 4));
while (true) {
/* The poll timeout is set to accommodate an approximate UPS, but has little purpose except
for effectively setting the rate of empty samples in the event of the FIFO descriptor
blocking for long periods of time. */
switch (poll(&pfd, 1, timeout)) {
case -1:
fprintf(stderr, "FIFO backend: poll() failed (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
case 0:
pthread_mutex_lock(&audio->mutex);
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
for (q = 0; q < (ssz / 4); ++q) bl[buffer_offset + q] = 0;
for (q = 0; q < (ssz / 4); ++q) br[buffer_offset + q] = 0;
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
break;
default: {
read(fd, buf, sizeof(buf));
clock_gettime(CLOCK_REALTIME, measured ? &tv : &tv_last);
if (measured) {
/* Set the timeout slightly higher than the delay between samples to prevent empty writes */
timeout = (((tv.tv_sec - tv_last.tv_sec) * 1000) + ((tv.tv_nsec - tv_last.tv_nsec) / 1000000)) + 1;
tv_last = tv;
} else measured = true;
pthread_mutex_lock(&audio->mutex);
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
for (size_t n = 0, q = 0; q < (ssz / 2); q += 2) {
size_t idx = (fsz - (ssz / 4)) + n;
if (audio->channels == 1) {
float sample = ((buf[q] + buf[q + 1]) / 2) / (float) 65535;
bl[idx] = sample;
br[idx] = sample;
}
if (audio->channels == 2) {
bl[idx] = buf[q] / (float) 65535;
br[idx] = buf[q + 1] / (float) 65535;
}
n++;
}
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
break;
}
}
if (audio->terminate == 1) {
close(fd);
break;
}
}
return 0;
}
AUDIO_ATTACH(fifo);

24
fifo.h
View File

@@ -14,4 +14,26 @@ struct audio_data {
pthread_mutex_t mutex;
};
void* input_fifo(void* data);
struct audio_impl {
const char* name;
void (*init)(struct audio_data* data);
void* (*entry)(void* data);
};
#define AUDIO_FUNC(F) \
.F = (typeof(((struct audio_impl*) NULL)->F)) &F
extern struct audio_impl* audio_impls[4];
extern size_t audio_impls_idx;
static inline void register_audio_impl(struct audio_impl* impl) { audio_impls[audio_impls_idx++] = impl; }
#define AUDIO_ATTACH(N) \
static struct audio_impl N##_var = { \
.name = #N, \
AUDIO_FUNC(init), \
AUDIO_FUNC(entry), \
}; \
void __attribute__((constructor)) _##N##_construct(void) { \
register_audio_impl(&N##_var); \
}

2
glad

Submodule glad updated: 0a146b6723...c33992f23c

2541
glad.c Normal file

File diff suppressed because it is too large Load Diff

5221
glad.h Normal file

File diff suppressed because it is too large Load Diff

236
glava.c
View File

@@ -5,6 +5,7 @@
#include <string.h>
#include <pthread.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
@@ -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
@@ -55,16 +55,24 @@
#define SHADER_USER_PATH "userconf"
/* FHS compliant systems */
#elif defined(__unix__) || defined(GLAVA_UNIX)
#ifndef SHADER_INSTALL_PATH
#define SHADER_INSTALL_PATH "/etc/xdg/glava"
#endif
#define SHADER_USER_PATH FORMAT("%s/glava", ENV("XDG_CONFIG_HOME", "%s/.config", ENV("HOME", "/home")))
/* OSX */
#elif (defined(__APPLE__) && defined(__MACH__)) || defined(GLAVA_OSX)
#ifndef SHADER_INSTALL_PATH
#define SHADER_INSTALL_PATH "/Library/glava"
#endif
#define SHADER_USER_PATH FORMAT("%s/Library/Preferences/glava", ENV("HOME", "/"))
#else
#error "Unsupported target system"
#endif
#ifndef ACCESSPERMS
#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
#endif
/* Copy installed shaders/configuration from the installed location
(usually /etc/xdg). Modules (folders) will be linked instead of
copied. */
@@ -109,8 +117,7 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
}
switch (type) {
case 1:
{
case 1: {
int source = -1, dest = -1;
uint8_t buf[pgsz];
ssize_t r, t, w, a;
@@ -140,17 +147,19 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) {
if (dest > 0) close(dest);
}
break;
case 2:
if (symlink(p, f) && errno != EEXIST)
fprintf(stderr, "failed to symlink '%s' -> '%s': %s\n", p, f, strerror(errno));
else if (verbose)
printf("symlink '%s' -> '%s'\n", p, f);
break;
case 2:
if (symlink(p, f) && errno != EEXIST)
fprintf(stderr, "failed to symlink '%s' -> '%s': %s\n", p, f, strerror(errno));
else if (verbose)
printf("symlink '%s' -> '%s'\n", p, f);
break;
}
}
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,50 +167,134 @@ 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"
"-r, --request=REQUEST evaluates the specified request after loading \"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"
"-e, --entry=FILE specifies the name of the file to look for when loading shaders,\n"
" by default this is \"rc.glsl\".\n"
"-C, --copy-config creates copies and symbolic links in the user configuration\n"
" directory for glava, copying any files in the root directory\n"
" 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"
"-a, --audio=BACKEND specifies an audio input backend to use.\n"
"-i, --stdin[=FORMAT] specifies a format for input to be read from stdin. The input\n"
" may be read from the STDIN macro from within shader sources.\n"
" A stream of inputs (each overriding the previous) must be\n"
" separated by newline (\'\\n\') characters.\n"
"-V, --version print application version and exit\n"
"\n"
"GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")\n"
" -- Copyright (C) 2017 Levi Webb\n";
"The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n"
"in GLSL files.\n"
"\n"
"The DEFINE argument is appended to the associated file before it is processed. It is\n"
"evaluated identically to the \'#define' preprocessor directive.\n"
"\n"
"The FILE argument may be any file path. All specified file paths are relative to the\n"
"active configuration root (usually ~/.config/glava if present).\n"
"\n"
"The BACKEND argument may be any of the following strings (for this particular build):\n"
"%s"
"\n"
"The FORMAT argument must be a valid GLSL type. If `--stdin` is used without an argument,\n"
"the default type is `vec4` (type used for RGBA colors)\n"
"\n"
GLAVA_VERSION_STRING "\n";
static const char* opt_str = "hve:Cm:";
static const char* opt_str = "dhvVe:Cm:b:r:a:i::";
static struct option p_opts[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"desktop", no_argument, 0, 'd'},
{"audio", required_argument, 0, 'a'},
{"request", required_argument, 0, 'r'},
{"entry", required_argument, 0, 'e'},
{"force-mod", required_argument, 0, 'm'},
{"copy-config", no_argument, 0, 'C'},
{"backend", required_argument, 0, 'b'},
{"stdin", optional_argument, 0, 'i'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0 }
};
static renderer* rd = NULL;
static void handle_term(int signum) {
if (rd->alive) {
puts("\nInterrupt recieved, closing...");
rd->alive = false;
}
}
static inline void append_buf(char** buf, size_t* sz_store, char* str) {
buf = realloc(buf, ++(*sz_store) * sizeof(char*));
buf[*sz_store - 1] = str;
}
int main(int argc, char** argv) {
/* Evaluate these macros only once, since they allocate */
const char* install_path = SHADER_INSTALL_PATH;
const char* user_path = SHADER_USER_PATH;
const char* entry = "rc.glsl";
const char* force = NULL;
const char
* install_path = SHADER_INSTALL_PATH,
* user_path = SHADER_USER_PATH,
* entry = "rc.glsl",
* force = NULL,
* backend = NULL,
* audio_impl_name = "pulseaudio";
const char* system_shader_paths[] = { user_path, install_path, NULL };
bool verbose = false;
bool copy_mode = false;
int stdin_type = STDIN_TYPE_NONE;
int c, idx, n = 0;
char** requests = malloc(1);
size_t requests_sz = 0;
bool verbose = false, copy_mode = false, desktop = false;
int c, idx;
while ((c = getopt_long(argc, argv, opt_str, p_opts, &idx)) != -1) {
switch (c) {
case 'v': verbose = true; break;
case 'C': copy_mode = true; break;
case 'e': entry = optarg; break;
case 'm': force = optarg; break;
case '?': exit(EXIT_FAILURE); break;
default:
case 'h':
printf(help_str, argc > 0 ? argv[0] : "glava");
exit(EXIT_SUCCESS);
case 'v': verbose = true; break;
case 'C': copy_mode = true; break;
case 'd': desktop = true; break;
case 'r': append_buf(requests, &requests_sz, optarg); break;
case 'e': entry = optarg; break;
case 'm': force = optarg; break;
case 'b': backend = optarg; break;
case 'a': audio_impl_name = optarg; break;
case '?': exit(EXIT_FAILURE); break;
case 'V':
puts(GLAVA_VERSION_STRING);
exit(EXIT_SUCCESS);
break;
default:
case 'h': {
char buf[2048];
size_t bsz = 0;
for (size_t t = 0; t < audio_impls_idx; ++t)
bsz += snprintf(buf + bsz, sizeof(buf) - bsz, "\t\"%s\"%s\n", audio_impls[t]->name,
!strcmp(audio_impls[t]->name, audio_impl_name) ? " (default)" : "");
printf(help_str, argc > 0 ? argv[0] : "glava", buf);
exit(EXIT_SUCCESS);
break;
}
case 'i': {
stdin_type = -1;
for (size_t t = 0 ; stdin_types[t].n != NULL; ++t) {
if (optarg == NULL) {
stdin_type = STDIN_TYPE_VEC4;
} else if (!strcmp(stdin_types[t].n, optarg)) {
stdin_type = stdin_types[t].i;
break;
}
}
if (stdin_type == -1) {
fprintf(stderr, "Unsupported `--stdin` GLSL type: \"%s\"\n", optarg);
exit(EXIT_FAILURE);
}
}
}
}
@@ -210,11 +303,27 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
renderer* r = rd_new(system_shader_paths, entry, force);
/* Handle `--force` argument as a request override */
if (force) {
const size_t bsz = 5 + strlen(force);
char* force_req_buf = malloc(bsz);
snprintf(force_req_buf, bsz, "mod %s", force);
append_buf(requests, &requests_sz, force_req_buf);
}
float b0[r->bufsize_request], b1[r->bufsize_request];
/* Null terminate array arguments */
append_buf(requests, &requests_sz, NULL);
rd = rd_new(system_shader_paths, entry, (const char**) requests,
backend, stdin_type, desktop, verbose);
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,34 +331,48 @@ 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) {
get_pulse_default_sink(&audio);
printf("Using default PulseAudio sink: %s\n", audio.source);
struct audio_impl* impl = NULL;
for (size_t t = 0; t < audio_impls_idx; ++t) {
if (!strcmp(audio_impls[t]->name, audio_impl_name)) {
impl = audio_impls[t];
break;
}
}
if (!impl) {
fprintf(stderr, "The specified audio backend (\"%s\") is not available.\n", audio_impl_name);
exit(EXIT_FAILURE);
}
pthread_t thread;
int thread_id = pthread_create(&thread, NULL, input_pulse, (void*) &audio);
impl->init(&audio);
float lb[r->bufsize_request], rb[r->bufsize_request];
while (r->alive) {
if (verbose) printf("Using audio source: %s\n", audio.source);
pthread_t thread;
pthread_create(&thread, NULL, impl->entry, (void*) &audio);
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 +380,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 +397,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
View 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 */

View File

@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
@@ -14,6 +15,14 @@
#include "glsl_ext.h"
#define LINE_START 0
#define GLSL 1
#define MACRO 2
#define REQUEST 3
#define INCLUDE 4
#define COLOR 5
#define DEFINE 6
struct sbuf {
char* buf;
size_t at; /* index of final null character */
@@ -51,16 +60,16 @@ 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);
}
#define parse_error(line, f, fmt, ...) \
#define parse_error(line, f, fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \
abort()
#define parse_error_s(line, f, s) \
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
#define parse_error_s(line, f, s) \
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
abort()
struct schar {
@@ -68,153 +77,224 @@ 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;
}
static void free_after(struct glsl_ext* ext, void* ptr) {
++ext->destruct_sz;
ext->destruct = realloc(ext->destruct, sizeof(void*) * ext->destruct_sz);
ext->destruct[ext->destruct_sz - 1] = ptr;
}
static void inherit(struct glsl_ext* parent, struct glsl_ext* child) {
free_after(parent, child->processed);
parent->destruct = realloc(parent->destruct, sizeof(void*) * (parent->destruct_sz + child->destruct_sz));
memcpy(parent->destruct + parent->destruct_sz, child->destruct, sizeof(void*) * child->destruct_sz);
parent->destruct_sz += child->destruct_sz;
free(child->destruct);
}
/* 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,
size_t args_sz, bool include,
size_t line, const char* f) {
if (include) {
if (args_sz == 0) {
parse_error_s(line, f, "No arguments provided to #include directive!");
size_t args_sz, int state,
size_t line, const char* f) {
switch (state) {
case DEFINE: {
/* Workaround for re-defining macros in GLSL. By default this is generally an error in most
compilers/drivers, but we would prefer to override (non-function) definitions instead.
Due to how this directive is parsed, the macro itself is still emitted afterwards. */
if (args_sz == 0) {
parse_error_s(line, f, "No arguments provided to #define directive!");
}
size_t bsz = (strlen(args[0]) * 3) + 64;
struct schar ret = { .buf = malloc(bsz) };
int r = snprintf(ret.buf, bsz, "#ifdef %1$s\n#undef %1$s\n#endif\n", args[0]);
if (r < 0) abort();
ret.sz = r;
free_after(ext, ret.buf);
return ret;
}
char* target = args[0];
case INCLUDE: {
if (args_sz == 0) {
parse_error_s(line, f, "No arguments provided to #include directive!");
}
char* target = args[0];
/* Handle `:` config specifier */
size_t tsz = strlen(target);
if (tsz && target[0] == ':' && ext->cfd) {
target = &target[1];
ext->cd = ext->cfd;
}
char path[strlen(ext->cd) + tsz + 2];
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
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",
path, strerror(errno));
}
struct stat st;
fstat(fd, &st);
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",
path, strerror(errno));
/* Handle `:` config specifier */
size_t tsz = strlen(target);
if (tsz && target[0] == ':' && ext->cfd) {
target = &target[1];
ext->cd = ext->cfd;
}
/* Handle `@` default specifier */
if (tsz && target[0] == '@') {
if (!ext->dd) {
parse_error_s(line, f, "encountered '@' path specifier while no default "
"directory is available in the current context");
}
target = &target[1];
ext->cd = ext->dd;
}
char path[strlen(ext->cd) + tsz + 2];
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
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",
path, strerror(errno));
}
struct stat st;
fstat(fd, &st);
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",
path, strerror(errno));
}
struct glsl_ext next = {
.source = map,
.source_len = st.st_size,
.cd = ext->cd,
.cfd = ext->cfd,
.dd = ext->dd,
.handlers = ext->handlers
};
/* recursively process */
ext_process(&next, target);
inherit(ext, &next);
munmap(map, st.st_size);
struct schar ret = {
.buf = next.processed,
.sz = next.p_len
};
return ret;
}
case REQUEST: {
if (args_sz > 0) {
char* request = args[0];
struct glsl_ext next = {
.source = map,
.source_len = st.st_size,
.cd = ext->cd,
.cfd = ext->cfd,
.handlers = ext->handlers
};
/* recursively process */
ext_process(&next, target);
struct schar ret = {
.buf = next.processed,
.sz = next.p_len
};
return ret;
} else {
if (args_sz > 0) {
char* request = args[0];
struct request_handler* handler;
bool found = false;
size_t t, i;
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
if(!strcmp(handler->name, request)) {
found = true;
void** processed_args = malloc(strlen(handler->fmt) * sizeof(void*));
struct request_handler* handler;
bool found = false;
size_t t;
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
if(!strcmp(handler->name, request)) {
found = true;
void** processed_args = malloc(strlen(handler->fmt) * sizeof(void*));
char c;
size_t i;
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
if (args_sz <= 1 + i) {
parse_error(line, f,
"failed to execute request '%s': expected format '%s'\n",
request, handler->fmt);
}
char* raw = args[1 + i];
switch (c) {
case 'i':
{
int v = (int) strtol(raw, NULL, 0);
processed_args[i] = malloc(sizeof(int));
*(int*) processed_args[i] = v;
break;
char c;
size_t i;
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
if (args_sz <= 1 + i) {
parse_error(line, f,
"failed to execute request '%s': expected format '%s'\n",
request, handler->fmt);
}
case 'f':
{
float f = strtof(raw, NULL);
processed_args[i] = malloc(sizeof(float));
*(float*) processed_args[i] = f;
break;
}
case 's': { *(char**) &processed_args[i] = raw; break; }
case 'b':
{
bool v;
if (!strcmp(raw, "true")) {
v = true;
} else if (!strcmp(raw, "false")) {
v = false;
} else if (strlen(raw) == 1) {
switch (raw[0]) {
case 't': { v = true; break; }
case 'f': { v = false; break; }
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");
}
} else {
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
char* raw = args[1 + i];
switch (c) {
case 'i': {
int v = (int) strtol(raw, NULL, 0);
processed_args[i] = malloc(sizeof(int));
*(int*) processed_args[i] = v;
break;
}
case 'f': {
float f = strtof(raw, NULL);
processed_args[i] = malloc(sizeof(float));
*(float*) processed_args[i] = f;
break;
}
case 's': { *(char**) &processed_args[i] = raw; break; }
case 'b': {
bool v;
if (!strcmp(raw, "true")) {
v = true;
} else if (!strcmp(raw, "false")) {
v = false;
} else if (strlen(raw) == 1) {
switch (raw[0]) {
case 't': { v = true; break; }
case 'f': { v = false; break; }
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");
}
} else {
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;
break;
}
processed_args[i] = malloc(sizeof(bool));
*(bool*) processed_args[i] = v;
break;
}
}
}
handler->handler(request, processed_args);
handler->handler(request, processed_args);
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i)
if (c != 's')
free(processed_args[i]);
free(processed_args);
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i)
if (c != 's')
free(processed_args[i]);
free(processed_args);
}
}
if (!found) {
parse_error(line, f, "unknown request type '%s'", request);
}
}
if (!found) {
parse_error(line, f, "unknown request type '%s'", request);
}
}
struct schar ret = {
.buf = NULL,
.sz = 0
};
return ret;
default: return (struct schar) { .buf = NULL, .sz = 0 };
}
}
/* state machine parser */
void ext_process(struct glsl_ext* ext, const char* f) {
#define LINE_START 0
#define IGNORING 1
#define MACRO 2
#define REQUEST 3
#define INCLUDE 4
ext->destruct = malloc(1);
ext->destruct_sz = 0;
struct sbuf sbuf = {
.buf = malloc(256),
@@ -226,136 +306,235 @@ 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** args = NULL;
char cbuf[9];
char** args = malloc(sizeof(char*));
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')
++line;
switch (state) {
case LINE_START: /* processing start of line */
{
case LINE_START: { /* processing start of line */
switch (at) {
case '#': {
macro_start_idx = t;
state = MACRO;
continue; }
case ' ':
case '\t':
case '\n':
goto copy;
default: {
state = IGNORING;
goto copy; }
case '#': {
macro_start_idx = t;
state = MACRO;
continue;
}
case '\n':
if (comment && comment_line) {
comment = false;
comment_line = false;
}
case '\t':
case ' ':
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;
}
goto copy;
case MACRO: /* processing start of macro */
{
case GLSL: { /* copying GLSL source or unrelated preprocessor syntax */
switch (at) {
case '\n':
case ' ':
case '\t':
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)) {
state = INCLUDE;
goto prepare_arg_parse;
} else {
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]);
state = IGNORING;
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 */
}
}
/* emit contents from start of macro to current index and resume regular parsing*/
#define skip_macro() \
do { \
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]); \
state = at == '\n' ? LINE_START : GLSL; \
goto copy; \
} while (0)
case MACRO: { /* processing start of macro */
switch (at) {
case '\n':
case ' ':
case '\t':
case '\0': { /* end parsing directive */
#define DIRECTIVE_CMP(lower, upper) \
(!strncmp("#" lower, &ext->source[macro_start_idx], t - macro_start_idx) \
|| !strncmp("#" upper, &ext->source[macro_start_idx], t - macro_start_idx))
#define DIRECTIVE_CASE(lower, upper) \
do { if (state == MACRO && DIRECTIVE_CMP(#lower, #upper)) { state = upper; goto prepare_arg_parse; } } while (0)
DIRECTIVE_CASE(request, REQUEST);
DIRECTIVE_CASE(include, INCLUDE);
DIRECTIVE_CASE(define, DEFINE);
/* no match */
if (state == MACRO) skip_macro();
#undef DIRECTIVE_CMP
#undef DIRECTIVE_CASE
prepare_arg_parse:
{
arg_start_idx = t + 1;
arg_start = true;
args = malloc(sizeof(char*));
args_sz = 0;
*args = NULL;
}
}
case 'a' ... 'z':
case 'A' ... 'Z':
continue;
default:
/* invalid char, malformed! */
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
case 'a' ... 'z':
case 'A' ... 'Z':
continue;
default:
/* invalid char, malformed! */
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
}
}
/* scope-violating macro to copy the result of the currently parsed argument */
#define copy_arg(end) \
do { if (end - arg_start_idx > 0) { \
++args_sz; \
args = realloc(args, sizeof(char*) * args_sz); \
args[args_sz - 1] = malloc((end - arg_start_idx) + 1); \
memcpy(args[args_sz - 1], &ext->source[arg_start_idx], end - arg_start_idx); \
args[args_sz - 1][end - arg_start_idx] = '\0'; \
} } while (0)
case REQUEST:
case INCLUDE:
{
/* scope-violating macro to copy the result of the currently parsed argument */
#define copy_arg(end) \
do { if (end - arg_start_idx > 0) { \
++args_sz; \
args = realloc(args, sizeof(char*) * args_sz); \
args[args_sz - 1] = malloc((end - arg_start_idx) + 1); \
memcpy(args[args_sz - 1], &ext->source[arg_start_idx], end - arg_start_idx); \
args[args_sz - 1][end - arg_start_idx] = '\0'; \
} } while (0)
case REQUEST:
case INCLUDE:
case DEFINE: {
switch (at) {
case ' ':
case '\t':
case '\n':
case '\0':
if (!quoted) {
/* end arg */
copy_arg(t);
arg_start = true;
arg_start_idx = t + 1;
} else arg_start = false;
if (at == '\n') {
/* end directive */
size_t a;
struct schar r = directive(ext, args, args_sz, state == INCLUDE, line, f);
for (a = 0; a < args_sz; ++a) {
free(args[a]);
case ' ':
case '\t':
case '\n':
case '\0':
if (!quoted) {
/* end arg */
copy_arg(t);
arg_start = true;
arg_start_idx = t + 1;
} else arg_start = false;
if (at == '\n' || at == '\0' || state == DEFINE) {
/* end directive */
size_t a;
struct schar r = directive(ext, args, args_sz, state, line, f);
for (a = 0; a < args_sz; ++a) {
free(args[a]);
}
args_sz = 0;
/* if something was returned (ie. included file), paste the results */
if (r.buf) {
n_append(&sbuf, r.sz, r.buf);
append(&sbuf, "\n");
}
if (state == DEFINE) skip_macro();
else state = LINE_START;
}
args_sz = 0;
/* if something was returned (ie. included file), paste the results */
if (r.buf) {
n_append(&sbuf, r.sz, r.buf);
append(&sbuf, "\n");
}
munmap(r.buf, r.sz);
state = LINE_START;
break;
case '(':
if (state != DEFINE || args_sz != 0) goto arg; /* only handle first arg of #define */
skip_macro(); /* ignore macro functions */
case '"':
if (state == DEFINE) goto arg; /* do not handle quoting for #define */
if (quoted) {
/* end arg */
copy_arg(t);
quoted = false;
arg_start = true;
arg_start_idx = t + 1;
} else if (arg_start) {
++arg_start_idx;
quoted = true;
} else arg_start = false;
break;
default: {
arg: arg_start = false;
}
break;
case '"':
if (quoted) {
/* end arg */
copy_arg(t);
quoted = false;
arg_start = true;
arg_start_idx = t + 1;
} else if (arg_start) {
++arg_start_idx;
quoted = true;
} else arg_start = false;
break;
default:
arg_start = false;
}
continue;
}
#undef copy_arg
}
copy:
if (at != '\0')
@@ -363,10 +542,20 @@ void ext_process(struct glsl_ext* ext, const char* f) {
}
ext->processed = sbuf.buf;
ext->p_len = sbuf.at;
if (args)
if (args) {
for (t = 0; t < args_sz; ++t) {
free(args[t]);
}
free(args);
}
}
void ext_free(struct glsl_ext* ext) {
free(ext->processed);
size_t t;
for (t = 0; t < ext->destruct_sz; ++t) {
free(ext->destruct[t]);
}
free(ext->destruct);
}

View File

@@ -21,12 +21,15 @@ struct request_handler {
};
struct glsl_ext {
char* processed; /* OUT: null terminated processed source */
size_t p_len; /* OUT: length of processed buffer, excluding null char */
const char* source; /* IN: raw data passed via ext_process */
size_t source_len; /* IN: raw source len */
const char* cd; /* IN: current directory */
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
char* processed; /* OUT: null terminated processed source */
size_t p_len; /* OUT: length of processed buffer, excluding null char */
const char* source; /* IN: raw data passed via ext_process */
size_t source_len; /* IN: raw source len */
const char* cd; /* IN: current directory */
const char* cfd; /* IN: config directory, if NULL it is assumed to cd */
const char* dd; /* IN: default directory */
void** destruct; /* internal */
size_t destruct_sz; /* internal */
/* IN: NULL (where the last element's 'name' member is NULL) terminated
array of request handlers */
@@ -35,3 +38,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);

610
glx_wcb.c Normal file
View File

@@ -0,0 +1,610 @@
/* 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.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.1", RTLD_LAZY);
void* hglx = dlopen("libGLX.so.0", 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 bool find_parent(Window w, Window* parent) {
Window root, *children = NULL;
unsigned int num_children;
if(!XQueryTree(display, w, &root, parent, &children, &num_children))
return false;
if (children)
XFree(children);
return *parent != None;
}
static void apply_clickthrough(struct glxwin* w) {
if (w->clickthrough) {
int ignored;
if (XShapeQueryExtension(display, &ignored, &ignored)) {
Window root = DefaultRootWindow(display);
Window win = w->w;
while (win != None) {
Region region;
if ((region = XCreateRegion())) {
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
Window parent;
find_parent(win, &parent);
win = (parent == root ? None : parent);
}
} else {
fprintf(stderr, "Warning: XShape extension not available\n");
}
}
}
static void process_events(struct glxwin* 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 MapNotify:
apply_clickthrough(w);
XFlush(display);
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* 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);
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);
process_events(w);
return w->should_render && attrs.map_state == IsViewable;
}
static void swap_buffers(struct glxwin* w) {
glXSwapBuffers(display, w->w);
process_events(w);
}
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);
free(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 */

282
khrplatform.h Normal file
View File

@@ -0,0 +1,282 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

View File

@@ -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,20 +32,14 @@ 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
switch (pa_context_get_state(pulseaudio_context))
{
case PA_CONTEXT_UNCONNECTED:
break;
case PA_CONTEXT_CONNECTING:
break;
case PA_CONTEXT_AUTHORIZING:
break;
case PA_CONTEXT_SETTING_NAME:
break;
/* Ensure loop is ready */
switch (pa_context_get_state(pulseaudio_context)) {
case PA_CONTEXT_UNCONNECTED: break;
case PA_CONTEXT_CONNECTING: break;
case PA_CONTEXT_AUTHORIZING: break;
case PA_CONTEXT_SETTING_NAME: break;
case PA_CONTEXT_READY: /* extract default sink name */
pa_operation_unref(pa_context_get_server_info(pulseaudio_context, cb, userdata));
break;
@@ -54,38 +48,39 @@ 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;
}
}
}
void get_pulse_default_sink(struct audio_data* audio) {
static void init(struct audio_data* audio) {
if (audio->source) return;
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"
@@ -113,26 +108,25 @@ void get_pulse_default_sink(struct audio_data* audio) {
#error "Unsupported float format (requires 32 bit IEEE (little or big endian) floating point support)"
#endif
void* input_pulse(void* data) {
struct audio_data *audio = (struct audio_data *)data;
static void* entry(void* 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 +135,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 +157,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 +181,7 @@ void* input_pulse(void* data) {
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
if (audio->terminate == 1) {
pa_simple_free(s);
break;
@@ -192,3 +190,5 @@ void* input_pulse(void* data) {
return 0;
}
AUDIO_ATTACH(pulseaudio);

994
render.c

File diff suppressed because it is too large Load Diff

105
render.h
View File

@@ -1,15 +1,110 @@
#ifndef RENDER_H
#define RENDER_H
extern const struct {
const char* n;
int i;
} stdin_types[];
#define STDIN_TYPE_NONE 0
#define STDIN_TYPE_INT 1
#define STDIN_TYPE_FLOAT 2
#define STDIN_TYPE_BOOL 3
#define STDIN_TYPE_VEC2 4
#define STDIN_TYPE_VEC3 5
#define STDIN_TYPE_VEC4 6
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** requests, const char* force_backend,
int stdin_type, bool auto_desktop,
bool verbose);
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 */

View File

@@ -1,21 +1,31 @@
/* 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

View File

@@ -6,6 +6,7 @@ uniform ivec2 screen;
#request uniform "audio_sz" audio_sz
uniform int audio_sz;
#include "@bars.glsl"
#include ":bars.glsl"
#request uniform "audio_l" audio_l
@@ -29,17 +30,35 @@ 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 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 */
float nbars = floor((AREA_WIDTH * 0.5F) / section) * 2;
float p, s;
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 */
s = dx / section;
p = (sign(s) == 1.0 ? ceil(s) : floor(s)) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
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;
@@ -49,7 +68,7 @@ void main() {
return;
}
/* handle user options and store result of indexing in 'v' */
if (p >= 0.0F) {
if (p > 0.0F) {
#if DIRECTION == 1
p = 1.0F - p;
#endif

View File

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

View File

@@ -7,6 +7,7 @@ uniform ivec2 screen;
uniform int audio_sz;
#include ":util/smooth.glsl"
#include "@circle.glsl"
#include ":circle.glsl"
#request uniform "audio_l" audio_l
@@ -31,7 +32,6 @@ out vec4 fragment;
/* This shader is based on radial.glsl, refer to it for more commentary */
float apply_smooth(float theta) {
float idx = theta + ROTATE;
float dir = mod(abs(idx), TWOPI);
if (dir > PI)
@@ -50,6 +50,7 @@ float apply_smooth(float theta) {
}
void main() {
fragment = vec4(0, 0, 0, 0);
float
dx = gl_FragCoord.x - (screen.x / 2),
dy = gl_FragCoord.y - (screen.y / 2);

View File

@@ -6,6 +6,7 @@ uniform sampler2D tex; /* screen texture */
out vec4 fragment; /* output */
#include "@circle.glsl"
#include ":circle.glsl"
void main() {

1
shaders/circle/3.frag Normal file
View File

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

8
shaders/env_KWin.glsl Normal file
View File

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

2
shaders/env_Openbox.glsl Normal file
View File

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

3
shaders/env_Xfwm4.glsl Normal file
View File

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

1
shaders/env_awesome.glsl Normal file
View File

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

1
shaders/env_default.glsl Normal file
View File

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

1
shaders/env_i3.glsl Normal file
View File

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

View File

@@ -4,24 +4,27 @@
/* Rendering direction, either -1 (outwards) or 1 (inwards). */
#define DIRECTION 1
/* The `RCOL_OFF`, `LCOL_OFF` AND `LSTEP` definitions are used to calculate
the `COLOR` macro definition for output. You can remove all these values
any simply define the `COLOR` macro yourself. */
/* right color offset */
#define RCOL_OFF (gl_FragCoord.x / 3000)
/* left color offset */
#define LCOL_OFF ((screen.x - gl_FragCoord.x) / 3000)
/* vertical color step */
#define LSTEP (pos / 170)
/* actual color definition */
#define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1)
/* Color gradient scale, (optionally) used in `COLOR` macro */
#define GRADIENT_SCALE 75
/* Color definition. By default this is a gradient formed by mixing two colors.
`pos` represents the pixel position relative to the visualizer baseline. */
#define COLOR mix(#802A2A, #4F4F92, clamp(pos / GRADIENT_SCALE, 0, 1))
/* 1 to draw outline, 0 to disable */
#define DRAW_OUTLINE 0
/* 1 to draw edge highlight, 0 to disable */
#define DRAW_HIGHLIGHT 1
/* Whether to anti-alias the border of the graph, creating a smoother curve.
This may have a small impact on performance.
Note: requires `xroot` or `none` opacity to be set */
#define ANTI_ALIAS 0
/* outline color */
#define OUTLINE vec4(0.15, 0.15, 0.15, 1)
#define OUTLINE #262626
/* 1 to join the two channels together in the middle, 0 to clamp both down to zero */
#define JOIN_CHANNELS 0
/* 1 to invert (vertically), 0 otherwise */
#define INVERT 0
/* Gravity step, overrude frin `smooth_parameters.glsl` */
/* Gravity step, overrude from `smooth_parameters.glsl` */
#request setgravitystep 2.4
/* Smoothing factor, override from `smooth_parameters.glsl` */

View File

@@ -40,6 +40,7 @@ uniform int audio_sz;
*/
#include ":util/smooth.glsl"
#include "@graph.glsl"
#include ":graph.glsl"
#request uniform "audio_l" audio_l
@@ -80,16 +81,31 @@ out vec4 fragment;
#define TWOPI 6.28318530718
float half_w;
float middle;
highp float pixel = 1.0F / float(screen.x);
void render_side(in sampler1D tex, float idx) {
highp float pixel = 1.0F / float(screen.x);
float get_line_height(in sampler1D tex, float idx) {
float s = smooth_audio_adj(tex, audio_sz, idx / half_w, pixel);
/* scale the data upwards so we can see it */
s *= VSCALE;
/* clamp far ends of the screen down to make the ends of the graph smoother */
s *= clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
float fact = clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
#if JOIN_CHANNELS > 0
fact = -2 * pow(fact, 3) + 3 * pow(fact, 2); /* To avoid spikes */
s = fact * s + (1 - fact) * middle;
#else
s *= fact;
#endif
s *= clamp((min(gl_FragCoord.x, screen.x - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
return s;
}
void render_side(in sampler1D tex, float idx) {
float s = get_line_height(tex, idx);
/* and finally set fragment color if we are in range */
#if INVERT > 0
float pos = float(screen.y) - gl_FragCoord.y;
@@ -105,6 +121,9 @@ void render_side(in sampler1D tex, float idx) {
void main() {
half_w = (screen.x / 2);
middle = VSCALE * (smooth_audio_adj(audio_l, audio_sz, 1, pixel) + smooth_audio_adj(audio_r, audio_sz, 0, pixel)) / 2;
if (gl_FragCoord.x < half_w) {
render_side(audio_l, LEFT_IDX);
} else {

View File

@@ -1,44 +1,44 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
in vec4 gl_FragCoord;
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
out vec4 fragment; /* output */
#include "@graph.glsl"
#include ":graph.glsl"
#if DRAW_OUTLINE == 0 && DRAW_HIGHLIGHT == 0
#error __disablestage
#endif
void main() {
fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, gl_FragCoord.y / screen.y));
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
vec4
a0 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)),
a1 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 1) / screen.y)),
a2 = texture(tex, vec2((gl_FragCoord.x + 0) / screen.x, (gl_FragCoord.y + 1) / screen.y)),
a3 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)),
a0 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 0)), 0),
a1 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 1)), 0),
a2 = texelFetch(tex, ivec2((gl_FragCoord.x + 0), (gl_FragCoord.y + 1)), 0),
a3 = texelFetch(tex, ivec2((gl_FragCoord.x + 1), (gl_FragCoord.y + 0)), 0),
a4 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)),
a5 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 1) / screen.y)),
a6 = texture(tex, vec2((gl_FragCoord.x - 0) / screen.x, (gl_FragCoord.y - 1) / screen.y)),
a7 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y));
a4 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 0)), 0),
a5 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 1)), 0),
a6 = texelFetch(tex, ivec2((gl_FragCoord.x - 0), (gl_FragCoord.y - 1)), 0),
a7 = texelFetch(tex, ivec2((gl_FragCoord.x - 1), (gl_FragCoord.y - 0)), 0);
vec4 avg = (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) / 8.0;
if (avg.a > 0){
#if INVERT > 0
#define EDGE_CHECK (gl_FragCoord.y != 0)
#define TEDGE_CHECK (gl_FragCoord.y != screen.y - 1)
#else
#define EDGE_CHECK (gl_FragCoord.y != screen.y - 1)
#define TEDGE_CHECK (gl_FragCoord.y != 0)
#endif
if (fragment.a <= 0 && EDGE_CHECK) {
if (fragment.a <= 0) {
/* outline */
#if DRAW_OUTLINE > 0
fragment = OUTLINE;
} else if (avg.a < 1 && TEDGE_CHECK) {
/* creates a nice 'glint' along the edge of the spectrum */
#endif
} else if (avg.a < 1) {
/* creates a highlight along the edge of the spectrum */
#if DRAW_HIGHLIGHT > 0
fragment.rgb *= avg.a * 2;
#endif
}
}
}

104
shaders/graph/3.frag Normal file
View File

@@ -0,0 +1,104 @@
in vec4 gl_FragCoord;
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
out vec4 fragment; /* output */
#include "@graph.glsl"
#include ":graph.glsl"
#if ANTI_ALIAS == 0
#error __disablestage
#endif
/* Moves toward the border of the graph, gives the
y coordinate of the last colored pixel */
float get_col_height_up(float x, float oy) {
float y = oy;
#if INVERT > 0
while (y >= 0) {
#else
while (y < screen.y) {
#endif
vec4 f = texelFetch(tex, ivec2(x, y), 0);
if (f.a <= 0) {
#if INVERT > 0
y += 1;
#else
y -= 1;
#endif
break;
}
#if INVERT > 0
y -= 1;
#else
y += 1;
#endif
}
return y;
}
/* Moves toward the base of the graph, gives the
y coordinate of the first colored pixel */
float get_col_height_down(float x, float oy) {
float y = oy;
#if INVERT > 0
while (y < screen.y) {
#else
while (y >= 0) {
#endif
vec4 f = texelFetch(tex, ivec2(x, y), 0);
if (f.a > 0) {
break;
}
#if INVERT > 0
y += 1;
#else
y -= 1;
#endif
}
return y;
}
void main() {
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
#if ANTI_ALIAS > 0
if (fragment.a <= 0) {
bool left_done = false;
float h2;
float a_fact = 0;
if (texelFetch(tex, ivec2(gl_FragCoord.x - 1, gl_FragCoord.y), 0).a > 0) {
float h1 = get_col_height_up(gl_FragCoord.x - 1, gl_FragCoord.y);
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
a_fact = clamp(abs((h1 - gl_FragCoord.y) / (h2 - h1)), 0.0, 1.0);
left_done = true;
}
if (texelFetch(tex, ivec2(gl_FragCoord.x + 1, gl_FragCoord.y), 0).a > 0) {
if (!left_done) {
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
}
float h3 = get_col_height_up(gl_FragCoord.x + 1, gl_FragCoord.y);
a_fact = max(a_fact, clamp(abs((h3 - gl_FragCoord.y) / (h2 - h3)), 0.0, 1.0));
}
fragment.a *= a_fact;
}
#endif
}

View File

@@ -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 */
@@ -27,8 +27,12 @@
the background. */
#define BAR_ALIAS_FACTOR 1.2
#define C_ALIAS_FACTOR 1.8
/* Offset (Y) of the visualization */
#define CENTER_OFFSET_Y 0
/* Offset (X) of the visualization */
#define CENTER_OFFSET_X 0
/* Gravity step, overrude frin `smooth_parameters.glsl` */
/* Gravity step, override from `smooth_parameters.glsl` */
#request setgravitystep 5.0
/* Smoothing factor, override from `smooth_parameters.glsl` */

View File

@@ -1,4 +1,4 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
in vec4 gl_FragCoord;
#request uniform "screen" screen
uniform ivec2 screen;
@@ -7,6 +7,7 @@ uniform ivec2 screen;
uniform int audio_sz;
#include ":util/smooth.glsl"
#include "@radial.glsl"
#include ":radial.glsl"
#request uniform "audio_l" audio_l
@@ -29,10 +30,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
@@ -41,14 +42,14 @@ void main() {
Alpha layer blending is only applied when `xroot` transparency is enabled. */
float /* translate (x, y) to use (0, 0) as the center of the screen */
dx = gl_FragCoord.x - (screen.x / 2),
dy = gl_FragCoord.y - (screen.y / 2);
dx = gl_FragCoord.x - (screen.x / 2) + CENTER_OFFSET_X,
dy = gl_FragCoord.y - (screen.y / 2) + CENTER_OFFSET_Y;
float theta = atan(dy, dx); /* fragment angle with the center of the screen as the origin */
float d = sqrt((dx * dx) + (dy * dy)); /* distance */
if (d > C_RADIUS - (float(C_LINE) / 2.0F) && d < C_RADIUS + (float(C_LINE) / 2.0F)) {
fragment = OUTLINE;
APPLY_FRAG(fragment, OUTLINE);
#if USE_ALPHA > 0
fragment.a *= ((float(C_LINE) / 2.0F) - abs(d - C_RADIUS)) * C_ALIAS_FACTOR;
fragment.a *= clamp(((C_LINE / 2) - abs(C_RADIUS - d)) * C_ALIAS_FACTOR, 0, 1);
#else
return; /* return immediately if there is no alpha blending available */
#endif

1
shaders/radial/2.frag Normal file
View File

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

View File

@@ -15,23 +15,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,25 @@
// #request addxwinstate "skip_taskbar"
// #request addxwinstate "skip_pager"
// #request addxwinstate "above"
// #request addxwinstate "pinned"
/* 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. */
/* (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
/* Audio source
When the "pulseaudio" backend is set, this 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.
When the "fifo" backend is set, "auto" is interpreted as
"/tmp/mpd.fifo". Otherwise, a valid path should be provided. */
#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 +130,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
@@ -173,9 +194,28 @@
Lower sample rates also can make output more choppy, when
not using interpolation. It's generally OK to leave this
value unless you have a strange PulseAudio configuration. */
value unless you have a strange PulseAudio configuration.
This option does nothing when using the "fifo" audio
backend. Instead, an ideal rate should be be configured
in the application generating the output. */
#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.

View File

@@ -13,10 +13,25 @@
- circular heavily rounded points
- sinusoidal rounded at both low and high weighted values
like a sine wave
- linear not rounded at all, just use linear distance
- linear not rounded at all; linear distance
*/
#define ROUND_FORMULA sinusoidal
/* The sampling mode for processing raw FFT input:
- average averages all the inputs in the sample range for
a given point. Produces smooth output, but peaks
are not well represented
- maximum obtains the best value from the closest peak in
the sample range. Very accurate peaks, but
output is jagged and sporadic.
- hybrid uses the results from both `average` and `maximum`
with the weight provided in `SAMPLE_HYBRID_WEIGHT` */
#define SAMPLE_MODE average
/* Weight should be provided in the range (0, 1). Higher values favour
averaged results. `hybrid` mode only. */
#define SAMPLE_HYBRID_WEIGHT 0.65
/* Factor used to scale frequencies. Lower values allows lower
frequencies to occupy more space. */
#define SAMPLE_SCALE 8

View File

@@ -0,0 +1,15 @@
#request nativeonly true
#request uniform "prev" tex
uniform sampler2D tex;
out vec4 fragment;
in vec4 gl_FragCoord;
void main() {
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
#if PREMULTIPLY_ALPHA > 0
fragment.rgb *= fragment.a;
#endif
}

View File

@@ -10,6 +10,7 @@
#define PI 3.14159265359
#endif
#include "@smooth_parameters.glsl"
#include ":smooth_parameters.glsl"
/* window value t that resides in range [0, sz)*/
@@ -21,6 +22,10 @@
/* take value x that scales linearly between [0, 1) and return its circlar curve */
#define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1)))
#define average 0
#define maximum 1
#define hybrid 2
float scale_audio(float idx) {
return -log((-(SAMPLE_RANGE) * idx) + 1) / (SAMPLE_SCALE);
}
@@ -32,20 +37,43 @@ float iscale_audio(float idx) {
/* Note: the SMOOTH_FACTOR macro is defined by GLava itself, from `#request setsmoothfactor`*/
float smooth_audio(in sampler1D tex, int tex_sz, highp float idx) {
#if PRE_SMOOTHED_AUDIO < 1
float
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz,
avg = 0, s, weight = 0;
float m = ((smax - smin) / 2.0F);
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz;
float m = ((smax - smin) / 2.0F), s, w;
float rm = smin + m; /* middle */
#if SAMPLE_MODE == average
float avg = 0, weight = 0;
for (s = smin; s <= smax; s += 1.0F) {
float w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
weight += w;
avg += texelFetch(tex, int(round(s)), 0).r * w;
}
avg /= weight;
return avg;
#elif SAMPLE_MODE == hybrid
float vmax = 0, avg = 0, weight = 0, v;
for (s = smin; s < smax; s += 1.0F) {
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
weight += w;
v = texelFetch(tex, int(round(s)), 0).r * w;
avg += v;
if (vmax < v)
vmax = v;
}
return (vmax * (1 - SAMPLE_HYBRID_WEIGHT)) + ((avg / weight) * SAMPLE_HYBRID_WEIGHT);
#elif SAMPLE_MODE == maximum
float vmax = 0, v;
for (s = smin; s < smax; s += 1.0F) {
w = texelFetch(tex, int(round(s)), 0).r * ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
if (vmax < w)
vmax = w;
}
return vmax;
#endif
#else
return texelFetch(tex, int(round(idx * tex_sz)), 0).r;
#endif

View File

@@ -11,6 +11,7 @@ uniform sampler1D audio_l;
out vec4 fragment;
#include "@wave.glsl"
#include ":wave.glsl"
#define index(offset) ((texture(audio_l, (gl_FragCoord.x + offset) / screen.x).r - 0.5) * AMPLIFY) + 0.5F

View File

@@ -8,6 +8,7 @@ uniform ivec2 screen; /* screen dimensions */
out vec4 fragment; /* output */
#include "@wave.glsl"
#include ":wave.glsl"
void main() {

411
xwin.c
View File

@@ -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>
#include "glad.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,
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
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 */
@@ -66,43 +186,65 @@ bool xwin_should_render(void) {
ret = false;
}
}
close:
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,123 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFWwindow* gwin = (GLFWwindow*) rd_get_impl_window(rd);
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;
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;
}
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;
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 */
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);
XFree(info);
return texture;
}

19
xwin.h
View File

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