├── config └── config.vibe ├── examples └── shaders │ ├── test_conflict.glsl │ ├── test_assignment.glsl │ ├── star_next.glsl │ ├── starship_reentry.glsl │ ├── creepy_mountains.glsl │ ├── mandelbrot.glsl │ ├── 2d_clouds.glsl │ ├── windows.glsl │ ├── matrix_rain.glsl │ ├── sunrise.glsl │ ├── saturday_torus.glsl │ ├── paper_scroll.glsl │ ├── fractal_land.glsl │ ├── a_lone_waters.glsl │ ├── cross_galactic_ocean.glsl │ ├── time.glsl.glsl │ └── synthwave2.glsl ├── meson_options.txt ├── .gitignore ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── workflows │ ├── release.yml │ └── build.yml ├── TOPICS.md └── ISSUE_TEMPLATE │ ├── feature_request.yml │ └── bug_report.yml ├── packaging ├── systemd │ └── neowall.service ├── neowall.install ├── README.md ├── PKGBUILD └── neowall.svg ├── src ├── config │ └── config.h ├── image │ └── image.h ├── shader_lib │ ├── shader.h │ ├── shader_log.h │ └── platform_compat.h ├── render │ └── render.h ├── textures │ ├── gray_noise.c │ ├── rgba_noise.c │ ├── wood.c │ ├── blue_noise.c │ └── abstract.c └── transitions │ ├── fade.c │ └── slide.c ├── include ├── config_access.h ├── textures.h ├── egl │ └── egl_core.h ├── transitions.h ├── version.h.in ├── constants.h ├── compositor │ └── backends │ │ ├── wayland.h │ │ └── x11.h ├── reload_metrics.h └── neowall.h ├── LICENSE ├── scripts └── generate-protocols.sh ├── protocols ├── plasma-shell-client-protocol.c ├── viewporter-client-protocol.c ├── tearing-control-v1-client-protocol.c ├── xdg-output-unstable-v1-client-protocol.c ├── wlr-layer-shell-unstable-v1-client-protocol.c ├── tearing-control-v1.xml └── xdg-shell-client-protocol.c └── README.md /config/config.vibe: -------------------------------------------------------------------------------- 1 | default { 2 | shader train_journey.glsl 3 | shader_speed 1.0 4 | } 5 | -------------------------------------------------------------------------------- /examples/shaders/test_conflict.glsl: -------------------------------------------------------------------------------- 1 | uniform float iTime; 2 | uniform vec2 iResolution; 3 | 4 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 5 | vec2 uv = fragCoord / iResolution.xy; 6 | vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4)); 7 | fragColor = vec4(col, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /examples/shaders/test_assignment.glsl: -------------------------------------------------------------------------------- 1 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 2 | float t = iTime; 3 | t += 0.1; // This is OK - local variable 4 | vec2 uv = fragCoord / iResolution.xy; 5 | vec3 col = 0.5 + 0.5 * cos(t + uv.xyx + vec3(0, 2, 4)); 6 | fragColor = vec4(col, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | # NeoWall Build Options 2 | 3 | # Backend options - can be set to 'enabled', 'disabled', or 'auto' 4 | option('wayland_backend', 5 | type: 'feature', 6 | value: 'auto', 7 | description: 'Build Wayland backend (requires wayland-client, wayland-egl, wayland-protocols)' 8 | ) 9 | 10 | option('x11_backend', 11 | type: 'feature', 12 | value: 'auto', 13 | description: 'Build X11 backend (requires libx11, libxrandr)' 14 | ) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | build/ 3 | *.o 4 | *.a 5 | *.so 6 | *.so.* 7 | staticwall 8 | REFACTORING_PLAN.md 9 | .cache 10 | 11 | # Generated files 12 | include/version.h 13 | 14 | # Backup files 15 | *.old 16 | *.bak 17 | *~ 18 | .*.swp 19 | .*.swo 20 | *.orig 21 | *.rej 22 | 23 | # IDE files 24 | .vscode/ 25 | .idea/ 26 | *.sublime-* 27 | .DS_Store 28 | 29 | # Temporary files 30 | COMMIT_MSG.txt 31 | *.tmp 32 | *.log 33 | 34 | # Core dumps 35 | core 36 | core.* 37 | vgcore.* 38 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code Owners for Staticwall 2 | # "Sets wallpapers until it... doesn't" - and these folks make sure it does 3 | 4 | # Default owners for everything 5 | * @1ay1 6 | 7 | # Core source files 8 | /src/ @1ay1 9 | /include/ @1ay1 10 | 11 | # Build system 12 | /Makefile @1ay1 13 | 14 | # Documentation 15 | /docs/ @1ay1 16 | README.md @1ay1 17 | CONTRIBUTING.md @1ay1 18 | 19 | # Configuration examples 20 | /config/ @1ay1 21 | 22 | # GitHub workflows and actions 23 | /.github/ @1ay1 24 | 25 | # Protocol definitions (auto-generated, but still worth reviewing) 26 | /protocols/ @1ay1 27 | 28 | # Assets and media 29 | /assets/ @1ay1 30 | -------------------------------------------------------------------------------- /packaging/systemd/neowall.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NeoWall - Wayland Wallpaper Daemon 3 | Documentation=https://github.com/1ay1/neowall 4 | After=graphical-session.target 5 | PartOf=graphical-session.target 6 | ConditionEnvironment=WAYLAND_DISPLAY 7 | 8 | [Service] 9 | Type=simple 10 | ExecStart=%h/.local/bin/neowall 11 | ExecReload=/bin/kill -HUP $MAINPID 12 | Restart=on-failure 13 | RestartSec=5 14 | 15 | # Security hardening 16 | PrivateTmp=true 17 | ProtectSystem=strict 18 | ProtectHome=read-only 19 | NoNewPrivileges=true 20 | RestrictRealtime=true 21 | 22 | # Resource limits 23 | MemoryHigh=128M 24 | MemoryMax=256M 25 | 26 | [Install] 27 | WantedBy=graphical-session.target 28 | -------------------------------------------------------------------------------- /src/config/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include 5 | #include 6 | 7 | /* Forward declarations */ 8 | struct neowall_state; 9 | struct wallpaper_config; 10 | 11 | /* Configuration parsing */ 12 | bool config_load(struct neowall_state *state, const char *config_path); 13 | bool config_parse_wallpaper(struct wallpaper_config *config, const char *output_name); 14 | void config_free_wallpaper(struct wallpaper_config *config); 15 | const char *config_get_default_path(void); 16 | char **load_images_from_directory(const char *dir_path, size_t *count); 17 | char **load_shaders_from_directory(const char *dir_path, size_t *count); 18 | 19 | #endif /* CONFIG_H */ 20 | -------------------------------------------------------------------------------- /include/config_access.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_ACCESS_H 2 | #define CONFIG_ACCESS_H 3 | 4 | #include "neowall.h" 5 | 6 | /* ============================================================================ 7 | * CONFIG ACCESS - Simplified (No Hot-Reload) 8 | * ============================================================================ 9 | * 10 | * With hot-reload removed, config access is now straightforward: 11 | * - Each output has a single config pointer allocated at creation 12 | * - No double-buffering, no slot swapping, no complex synchronization 13 | * - Config can only be changed by restarting the daemon 14 | * 15 | * USAGE: 16 | * output->config->type 17 | * output->config->shader_path 18 | * etc. 19 | * ============================================================================ */ 20 | 21 | #endif /* CONFIG_ACCESS_H */ 22 | -------------------------------------------------------------------------------- /src/image/image.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_H 2 | #define IMAGE_H 3 | 4 | #include 5 | 6 | /* Image format types */ 7 | enum image_format { 8 | FORMAT_PNG, 9 | FORMAT_JPEG, 10 | FORMAT_UNKNOWN, 11 | }; 12 | 13 | /* Image data structure */ 14 | struct image_data { 15 | uint8_t *pixels; /* RGBA pixel data */ 16 | uint32_t width; 17 | uint32_t height; 18 | uint32_t channels; /* Number of channels (3 for RGB, 4 for RGBA) */ 19 | enum image_format format; 20 | char path[4096]; /* OUTPUT_MAX_PATH_LENGTH */ 21 | }; 22 | 23 | /* Image loading functions */ 24 | struct image_data *image_load(const char *path, int32_t display_width, int32_t display_height, int mode); 25 | void image_free(struct image_data *img); 26 | void image_free_pixels(struct image_data *img); /* Free pixel data only (after GPU upload) */ 27 | enum image_format image_detect_format(const char *path); 28 | 29 | /* Image loaders for specific formats */ 30 | struct image_data *image_load_png(const char *path); 31 | struct image_data *image_load_jpeg(const char *path); 32 | 33 | #endif /* IMAGE_H */ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 NeoWall Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packaging/neowall.install: -------------------------------------------------------------------------------- 1 | # NeoWall post-install script for Arch Linux packages 2 | 3 | post_install() { 4 | echo "" 5 | echo "==> NeoWall installed successfully!" 6 | echo "" 7 | echo " Quick Start:" 8 | echo " 1. Run 'neowall' (Matrix rain starts automatically)" 9 | echo " 2. Edit ~/.config/neowall/config.vibe" 10 | echo "" 11 | echo " Installed files:" 12 | echo " /usr/share/neowall/config.vibe (default)" 13 | echo " /usr/share/neowall/neowall.vibe (example)" 14 | echo " /usr/share/neowall/shaders/ (12+ shaders)" 15 | echo "" 16 | echo " GPU shaders as wallpapers." 17 | echo "" 18 | } 19 | 20 | post_upgrade() { 21 | echo "" 22 | echo "==> NeoWall upgraded to $(pacman -Q neowall-git | awk '{print $2}')" 23 | echo "" 24 | echo " Restart if running:" 25 | echo " pkill neowall && neowall" 26 | echo "" 27 | } 28 | 29 | post_remove() { 30 | echo "" 31 | echo "==> NeoWall removed" 32 | echo "" 33 | echo " Config preserved at ~/.config/neowall/" 34 | echo "" 35 | echo " To remove:" 36 | echo " rm -rf ~/.config/neowall" 37 | echo "" 38 | } 39 | -------------------------------------------------------------------------------- /packaging/README.md: -------------------------------------------------------------------------------- 1 | # Packaging Files for NeoWall 2 | 3 | This directory contains files needed by package maintainers. 4 | 5 | ## What's Here 6 | 7 | - **`systemd/neowall.service`** - Systemd user service file 8 | - **`neowall.install`** - Optional Arch Linux post-install script 9 | 10 | ## For Package Maintainers 11 | 12 | If you're packaging NeoWall for a distribution, see: 13 | - **[docs/PACKAGING.md](../docs/PACKAGING.md)** - Complete packaging guide 14 | 15 | ### Quick Reference 16 | 17 | **Build:** 18 | ```bash 19 | meson setup build --prefix=/usr 20 | ninja -C build 21 | DESTDIR="$pkgdir" ninja -C build install 22 | ``` 23 | 24 | **Systemd service install location:** 25 | ``` 26 | /usr/lib/systemd/user/neowall.service 27 | ``` 28 | 29 | **Arch .install file usage:** 30 | Add to PKGBUILD: 31 | ```bash 32 | install=neowall.install 33 | ``` 34 | 35 | ## Creating AUR Package 36 | 37 | PKGBUILD files are **NOT** kept in this repo. Create them in a separate AUR git repository: 38 | 39 | ```bash 40 | git clone ssh://aur@aur.archlinux.org/neowall-git.git 41 | # Create PKGBUILD there 42 | ``` 43 | 44 | See [docs/PACKAGING.md](../docs/PACKAGING.md) for a complete PKGBUILD example. 45 | 46 | ## Notes 47 | 48 | - This repo does NOT contain distribution-specific packaging files (PKGBUILD, .deb, .rpm, etc.) 49 | - Package maintainers create those in their own repositories 50 | - We only provide generic files that are useful across distributions 51 | 52 | --- 53 | 54 | **Maintaining a package?** Let us know and we'll link to it in the README! -------------------------------------------------------------------------------- /src/shader_lib/shader.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_H 2 | #define SHADER_H 3 | 4 | #include "platform_compat.h" 5 | #include 6 | 7 | /** 8 | * Creates a shader program from source code. 9 | * Shared utility function used by all transitions. 10 | * 11 | * @param vertex_src Vertex shader source code 12 | * @param fragment_src Fragment shader source code 13 | * @param program Pointer to store the created program ID 14 | * @return true on success, false on failure 15 | */ 16 | bool shader_create_program_from_sources(const char *vertex_src, 17 | const char *fragment_src, 18 | GLuint *program); 19 | 20 | /** 21 | * Destroys a shader program. 22 | * @param program The program ID to destroy 23 | */ 24 | void shader_destroy_program(GLuint program); 25 | 26 | /* Transition-specific shader creation functions (defined in transition files) */ 27 | bool shader_create_fade_program(GLuint *program); 28 | bool shader_create_slide_program(GLuint *program); 29 | bool shader_create_glitch_program(GLuint *program); 30 | bool shader_create_pixelate_program(GLuint *program); 31 | 32 | /** 33 | * Get the last detailed error log from shader compilation/linking 34 | * 35 | * @return Pointer to static error log buffer (valid until next compilation) 36 | */ 37 | const char *shader_get_last_error_log(void); 38 | 39 | /** 40 | * Load shader source from file 41 | * 42 | * @param path Path to shader file (absolute or relative to config dir) 43 | * @return Shader source string (caller must free) or NULL on failure 44 | */ 45 | char *shader_load_file(const char *path); 46 | 47 | #endif /* SHADER_H */ -------------------------------------------------------------------------------- /packaging/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Your Name 2 | pkgname=neowall-git 3 | pkgver=0.4.4.r0.ga382c50 4 | pkgrel=1 5 | pkgdesc="GPU shader wallpapers for Wayland and X11" 6 | arch=('x86_64' 'aarch64') 7 | url="https://github.com/1ay1/neowall" 8 | license=('MIT') 9 | depends=( 10 | 'wayland' 11 | 'mesa' 12 | 'libpng' 13 | 'libjpeg-turbo' 14 | 'libx11' 15 | 'libxrandr' 16 | ) 17 | makedepends=( 18 | 'git' 19 | 'meson' 20 | 'ninja' 21 | 'wayland-protocols' 22 | 'pkgconf' 23 | ) 24 | provides=('neowall') 25 | conflicts=('neowall') 26 | source=("git+https://github.com/1ay1/neowall.git") 27 | sha256sums=('SKIP') 28 | 29 | pkgver() { 30 | cd "$srcdir/neowall" 31 | # Try to get version from git tags 32 | if git describe --long --tags 2>/dev/null | grep -q "^v"; then 33 | git describe --long --tags 2>/dev/null | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' 34 | else 35 | # Fallback: use meson.build version + commit count + short hash 36 | local meson_ver=$(grep "version:" meson.build 2>/dev/null | head -1 | sed "s/.*'\([^']*\)'.*/\1/") 37 | local commit_count=$(git rev-list --count HEAD) 38 | local short_hash=$(git rev-parse --short HEAD) 39 | printf "%s.r%s.g%s" "${meson_ver:-0.4.4}" "$commit_count" "$short_hash" 40 | fi 41 | } 42 | 43 | build() { 44 | cd "$srcdir/neowall" 45 | meson setup build --prefix=/usr --buildtype=release 46 | ninja -C build 47 | } 48 | 49 | package() { 50 | cd "$srcdir/neowall" 51 | DESTDIR="$pkgdir" ninja -C build install 52 | 53 | # Install license 54 | install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" 55 | } 56 | -------------------------------------------------------------------------------- /include/textures.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURES_H 2 | #define TEXTURES_H 3 | 4 | #include 5 | 6 | /* Procedural texture generators for iChannel inputs 7 | * These are the most commonly used textures in Shadertoy shaders 8 | */ 9 | 10 | /* RGBA noise texture - most common Shadertoy texture 11 | * Contains independent noise in all 4 channels 12 | * Typical size: 256x256 or 512x512 13 | */ 14 | GLuint texture_create_rgba_noise(int width, int height); 15 | 16 | /* Grayscale noise texture 17 | * High quality multi-octave noise in grayscale 18 | * Useful for displacement maps and effects 19 | * Typical size: 256x256 20 | */ 21 | GLuint texture_create_gray_noise(int width, int height); 22 | 23 | /* Blue noise texture 24 | * Better distributed than white noise, ideal for dithering 25 | * Reduces banding artifacts in shaders 26 | * Typical size: 128x128 or 256x256 27 | */ 28 | GLuint texture_create_blue_noise(int width, int height); 29 | 30 | /* Wood grain texture 31 | * Procedural wood pattern with realistic grain 32 | * Useful for natural backgrounds 33 | * Typical size: 256x256 34 | */ 35 | GLuint texture_create_wood(int width, int height); 36 | 37 | /* Abstract colorful texture 38 | * Voronoi-based pattern with colors 39 | * Good for artistic/abstract backgrounds 40 | * Typical size: 256x256 41 | */ 42 | GLuint texture_create_abstract(int width, int height); 43 | 44 | /* Texture name constants for configuration */ 45 | #define TEXTURE_NAME_RGBA_NOISE "rgba_noise" 46 | #define TEXTURE_NAME_GRAY_NOISE "gray_noise" 47 | #define TEXTURE_NAME_BLUE_NOISE "blue_noise" 48 | #define TEXTURE_NAME_WOOD "wood" 49 | #define TEXTURE_NAME_ABSTRACT "abstract" 50 | 51 | /* Default texture sizes */ 52 | #define DEFAULT_TEXTURE_SIZE 256 53 | 54 | #endif /* TEXTURES_H */ -------------------------------------------------------------------------------- /include/egl/egl_core.h: -------------------------------------------------------------------------------- 1 | #ifndef EGL_CORE_H 2 | #define EGL_CORE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Forward declarations */ 9 | struct neowall_state; 10 | struct output_state; 11 | 12 | /** 13 | * EGL Core - Simple Desktop OpenGL 3.3 Context Management 14 | * 15 | * Uses desktop OpenGL 3.3 Core profile for Shadertoy compatibility. 16 | */ 17 | 18 | /** 19 | * Initialize EGL with desktop OpenGL 3.3 context 20 | * 21 | * @param state NeoWall global state 22 | * @return true on success, false on failure 23 | */ 24 | bool egl_core_init(struct neowall_state *state); 25 | 26 | /** 27 | * Cleanup EGL resources 28 | * 29 | * @param state NeoWall global state 30 | */ 31 | void egl_core_cleanup(struct neowall_state *state); 32 | 33 | /** 34 | * Make EGL context current for an output 35 | * 36 | * @param state NeoWall global state 37 | * @param output Output state (can be NULL to unbind) 38 | * @return true on success, false on failure 39 | */ 40 | bool egl_core_make_current(struct neowall_state *state, 41 | struct output_state *output); 42 | 43 | /** 44 | * Swap buffers for an output 45 | * 46 | * @param state NeoWall global state 47 | * @param output Output state 48 | * @return true on success, false on failure 49 | */ 50 | bool egl_core_swap_buffers(struct neowall_state *state, 51 | struct output_state *output); 52 | 53 | /** 54 | * Get human-readable error string 55 | * 56 | * @param error EGL error code 57 | * @return Error string 58 | */ 59 | const char *egl_error_string(EGLint error); 60 | 61 | /** 62 | * Check for EGL errors and log them 63 | * 64 | * @param context Context string for error message 65 | * @return true if error occurred, false otherwise 66 | */ 67 | bool egl_check_error(const char *context); 68 | 69 | #endif /* EGL_CORE_H */ -------------------------------------------------------------------------------- /examples/shaders/star_next.glsl: -------------------------------------------------------------------------------- 1 | // Star Nest by Pablo Roman Andrioli 2 | // License: MIT 3 | 4 | #define iterations 17 5 | #define formuparam 0.53 6 | 7 | #define volsteps 20 8 | #define stepsize 0.1 9 | 10 | #define zoom 0.800 11 | #define tile 0.850 12 | #define speed 0.010 13 | 14 | #define brightness 0.0015 15 | #define darkmatter 0.300 16 | #define distfading 0.730 17 | #define saturation 0.850 18 | 19 | 20 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 21 | { 22 | //get coords and direction 23 | vec2 uv=fragCoord.xy/iResolution.xy-.5; 24 | uv.y*=iResolution.y/iResolution.x; 25 | vec3 dir=vec3(uv*zoom,1.); 26 | float time=iTime*speed+.25; 27 | 28 | //mouse rotation 29 | float a1=.5+iMouse.x/iResolution.x*2.; 30 | float a2=.8+iMouse.y/iResolution.y*2.; 31 | mat2 rot1=mat2(cos(a1),sin(a1),-sin(a1),cos(a1)); 32 | mat2 rot2=mat2(cos(a2),sin(a2),-sin(a2),cos(a2)); 33 | dir.xz*=rot1; 34 | dir.xy*=rot2; 35 | vec3 from=vec3(1.,.5,0.5); 36 | from+=vec3(time*2.,time,-2.); 37 | from.xz*=rot1; 38 | from.xy*=rot2; 39 | 40 | //volumetric rendering 41 | float s=0.1,fade=1.; 42 | vec3 v=vec3(0.); 43 | for (int r=0; r6) fade*=1.-dm; // dark matter, don't render near 55 | //v+=vec3(dm,dm*.5,0.); 56 | v+=fade; 57 | v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance 58 | fade*=distfading; // distance fading 59 | s+=stepsize; 60 | } 61 | v=mix(vec3(length(v)),v,saturation); //color adjust 62 | fragColor = vec4(v*.01,1.); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /examples/shaders/starship_reentry.glsl: -------------------------------------------------------------------------------- 1 | //modified from @XorDev 2 | 3 | #define NUM_OCTAVES 5 4 | 5 | float rand(vec2 n) { 6 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 7 | } 8 | 9 | float noise(vec2 p){ 10 | vec2 ip = floor(p); 11 | vec2 u = fract(p); 12 | u = u*u*(3.0-2.0*u); 13 | 14 | float res = mix( 15 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 16 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 17 | return res*res; 18 | } 19 | 20 | float fbm(vec2 x) { 21 | float v = 0.0; 22 | float a = 0.5; 23 | vec2 shift = vec2(100); 24 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 25 | for (int i = 0; i < NUM_OCTAVES; ++i) { 26 | v += a * noise(x); 27 | x = rot * x * 2.0 + shift; 28 | a *= 0.5; 29 | } 30 | return v; 31 | } 32 | 33 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 34 | { 35 | 36 | vec2 shake = vec2(sin(iTime * 1.5) * 0.01, cos(iTime * 2.7) * 0.01); 37 | 38 | 39 | vec2 p = ((fragCoord.xy + shake * iResolution.xy) - iResolution.xy * 0.5) / iResolution.y * mat2(8.0, -6.0, 6.0, 8.0); 40 | vec2 v; 41 | vec4 o = vec4(0.0); 42 | 43 | float f = 3.0 + fbm(p + vec2(iTime * 7.0, 0.0)); 44 | 45 | for(float i = 0.0; i++ < 50.0;) 46 | { 47 | v = p + cos(i * i + (iTime + p.x * 0.1) * 0.03 + i * vec2(11.0, 9.0)) * 5.0 + vec2(sin(iTime * 4.0 + i) * 0.005, cos(iTime * 4.5 - i) * 0.005); 48 | 49 | float tailNoise = fbm(v + vec2(iTime, i)) * (1.0 - (i / 50.0)); 50 | vec4 currentContribution = (cos(sin(i) * vec4(1.0, 2.0, 3.0, 1.0)) + 1.0) * exp(sin(i * i + iTime)) / length(max(v, vec2(v.x * f * 0.02, v.y))); 51 | 52 | 53 | float thinnessFactor = smoothstep(0.0, 1.0, i / 50.0); 54 | o += currentContribution * (1.0 + tailNoise * 2.0) * thinnessFactor; 55 | } 56 | 57 | o = tanh(pow(o / 1e2, vec4(1.5))); 58 | fragColor = o; 59 | } 60 | -------------------------------------------------------------------------------- /src/render/render.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_H 2 | #define RENDER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Forward declarations */ 9 | struct output_state; 10 | struct wallpaper_config; 11 | struct neowall_state; 12 | 13 | /* Texture creation from raw pixel data - render module doesn't need to know about image_data */ 14 | GLuint render_create_texture_from_pixels(const uint8_t *pixels, uint32_t width, uint32_t height, uint32_t channels); 15 | GLuint render_create_texture_from_pixels_flipped(const uint8_t *pixels, uint32_t width, uint32_t height, uint32_t channels); 16 | void render_destroy_texture(GLuint texture); 17 | 18 | /* Legacy API - deprecated, use render_create_texture_from_pixels() instead */ 19 | struct image_data; /* Only for legacy functions */ 20 | GLuint render_create_texture(struct image_data *img); 21 | 22 | /* Rendering */ 23 | bool render_init_output(struct output_state *output); 24 | void render_cleanup_output(struct output_state *output); 25 | bool render_frame(struct output_state *output); 26 | bool render_frame_shader(struct output_state *output); 27 | bool render_frame_transition(struct output_state *output, float progress); 28 | GLuint render_create_texture(struct image_data *img); 29 | void render_destroy_texture(GLuint texture); 30 | bool render_load_channel_textures(struct output_state *output, struct wallpaper_config *config); 31 | bool render_update_channel_texture(struct output_state *output, size_t channel_index, const char *image_path); 32 | 33 | /* GL shader programs */ 34 | bool shader_create_program(GLuint *program); 35 | void shader_destroy_program(GLuint program); 36 | const char *get_glsl_version_string(struct neowall_state *state); 37 | char *adapt_shader_for_version(struct neowall_state *state, const char *shader_code, bool is_fragment_shader); 38 | char *adapt_vertex_shader(struct neowall_state *state, const char *shader_code); 39 | char *adapt_fragment_shader(struct neowall_state *state, const char *shader_code); 40 | 41 | #endif /* RENDER_H */ 42 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | ## Type of Change 5 | 6 | 7 | - [ ] Bug fix (non-breaking change which fixes an issue) 8 | - [ ] New feature (non-breaking change which adds functionality) 9 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 10 | - [ ] Documentation update 11 | - [ ] Code refactoring 12 | - [ ] Performance improvement 13 | - [ ] Other (please describe): 14 | 15 | ## Related Issues 16 | 17 | 18 | 19 | Fixes # 20 | 21 | ## Testing 22 | 23 | 24 | - [ ] Tested on compositor: 25 | - [ ] Single wallpaper works 26 | - [ ] Multi-monitor setup works 27 | - [ ] Wallpaper cycling works 28 | - [ ] Config hot-reload works (SIGHUP) 29 | - [ ] All display modes tested (center, fill, fit, stretch, tile) 30 | - [ ] Transitions work (if applicable) 31 | - [ ] No memory leaks (tested with valgrind) 32 | - [ ] Builds without warnings 33 | 34 | ### Test Configuration 35 | 36 | ```vibe 37 | # Your test configuration here 38 | ``` 39 | 40 | ## Screenshots/Videos 41 | 42 | 43 | ## Checklist 44 | 45 | 46 | - [ ] My code follows the project's code style 47 | - [ ] I have performed a self-review of my code 48 | - [ ] I have commented my code, particularly in hard-to-understand areas 49 | - [ ] I have updated the documentation accordingly 50 | - [ ] My changes generate no new warnings 51 | - [ ] I have added tests that prove my fix is effective or that my feature works 52 | - [ ] New and existing unit tests pass locally with my changes 53 | - [ ] Any dependent changes have been merged and published 54 | 55 | ## Additional Notes 56 | -------------------------------------------------------------------------------- /include/transitions.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_H 2 | #define TRANSITIONS_H 3 | 4 | #include "neowall.h" 5 | 6 | /* Transition rendering function signature */ 7 | typedef bool (*transition_render_func)(struct output_state *output, float progress); 8 | 9 | /* Transition interface - each transition implements this */ 10 | struct transition { 11 | enum transition_type type; 12 | const char *name; 13 | transition_render_func render; 14 | }; 15 | 16 | /* Transition registry functions */ 17 | void transitions_init(void); 18 | bool transition_render(struct output_state *output, enum transition_type type, float progress); 19 | 20 | /* Transition context for managing OpenGL state across draws */ 21 | typedef struct { 22 | struct output_state *output; 23 | GLuint program; 24 | GLint pos_attrib; 25 | GLint tex_attrib; 26 | float vertices[16]; 27 | bool blend_enabled; 28 | bool error_occurred; 29 | } transition_context_t; 30 | 31 | /* High-level transition API - abstracts OpenGL state management */ 32 | bool transition_begin(transition_context_t *ctx, struct output_state *output, GLuint program); 33 | bool transition_draw_textured_quad(transition_context_t *ctx, GLuint texture, 34 | float alpha, const float *custom_vertices); 35 | void transition_end(transition_context_t *ctx); 36 | 37 | /* Low-level helper functions (for advanced use) */ 38 | void transition_setup_fullscreen_quad(GLuint vbo, float vertices[16]); 39 | void transition_bind_texture_for_transition(GLuint texture, GLenum texture_unit); 40 | void transition_setup_common_attributes(GLuint program, GLuint vbo); 41 | 42 | /* Individual transition implementations */ 43 | bool transition_fade_render(struct output_state *output, float progress); 44 | bool transition_slide_left_render(struct output_state *output, float progress); 45 | bool transition_slide_right_render(struct output_state *output, float progress); 46 | bool transition_glitch_render(struct output_state *output, float progress); 47 | bool transition_pixelate_render(struct output_state *output, float progress); 48 | 49 | #endif /* TRANSITIONS_H */ 50 | -------------------------------------------------------------------------------- /scripts/generate-protocols.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generate Wayland protocol bindings for Staticwall 3 | 4 | set -e 5 | 6 | PROTO_DIR="protocols" 7 | PROTO_BASE="/usr/share/wayland-protocols" 8 | 9 | # Create protocols directory if it doesn't exist 10 | mkdir -p "$PROTO_DIR" 11 | 12 | echo "Generating Wayland protocol bindings..." 13 | 14 | # wlr-layer-shell 15 | if [ -f "$PROTO_BASE/wlr-unstable/wlr-layer-shell-unstable-v1.xml" ]; then 16 | echo " - wlr-layer-shell-unstable-v1" 17 | wayland-scanner client-header \ 18 | "$PROTO_BASE/wlr-unstable/wlr-layer-shell-unstable-v1.xml" \ 19 | "$PROTO_DIR/wlr-layer-shell-unstable-v1-client-protocol.h" 20 | 21 | wayland-scanner private-code \ 22 | "$PROTO_BASE/wlr-unstable/wlr-layer-shell-unstable-v1.xml" \ 23 | "$PROTO_DIR/wlr-layer-shell-unstable-v1-client-protocol.c" 24 | else 25 | echo " ! wlr-layer-shell protocol not found (required)" 26 | echo " Install wayland-protocols or wlroots development package" 27 | fi 28 | 29 | # xdg-shell 30 | if [ -f "$PROTO_BASE/stable/xdg-shell/xdg-shell.xml" ]; then 31 | echo " - xdg-shell" 32 | wayland-scanner client-header \ 33 | "$PROTO_BASE/stable/xdg-shell/xdg-shell.xml" \ 34 | "$PROTO_DIR/xdg-shell-client-protocol.h" 35 | 36 | wayland-scanner private-code \ 37 | "$PROTO_BASE/stable/xdg-shell/xdg-shell.xml" \ 38 | "$PROTO_DIR/xdg-shell-client-protocol.c" 39 | else 40 | echo " ! xdg-shell protocol not found (optional)" 41 | fi 42 | 43 | # viewporter 44 | if [ -f "$PROTO_BASE/stable/viewporter/viewporter.xml" ]; then 45 | echo " - viewporter" 46 | wayland-scanner client-header \ 47 | "$PROTO_BASE/stable/viewporter/viewporter.xml" \ 48 | "$PROTO_DIR/viewporter-client-protocol.h" 49 | 50 | wayland-scanner private-code \ 51 | "$PROTO_BASE/stable/viewporter/viewporter.xml" \ 52 | "$PROTO_DIR/viewporter-client-protocol.c" 53 | else 54 | echo " ! viewporter protocol not found (optional)" 55 | fi 56 | 57 | echo "Protocol generation complete!" 58 | echo "" 59 | echo "Generated files in $PROTO_DIR/:" 60 | ls -lh "$PROTO_DIR/" 2>/dev/null || echo " (no files generated)" 61 | -------------------------------------------------------------------------------- /src/shader_lib/shader_log.h: -------------------------------------------------------------------------------- 1 | /* Shader Library Logging Shim 2 | * Provides logging macros that work with neowall's logging system when available, 3 | * or standalone logging when used in other projects like gleditor 4 | */ 5 | 6 | #ifndef SHADER_LIB_LOG_H 7 | #define SHADER_LIB_LOG_H 8 | 9 | /* Check if we're being compiled within neowall (which defines its own logging) */ 10 | #ifdef NEOWALL_VERSION 11 | 12 | /* Use neowall's logging system - just declare the functions as extern */ 13 | /* The actual log_* functions are defined in neowall's utils.c */ 14 | /* Do NOT redefine LOG_LEVEL_* or log_* macros here */ 15 | 16 | #else 17 | 18 | /* Standalone mode - provide our own logging implementation */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /* Log levels for standalone mode */ 25 | #define LOG_LEVEL_ERROR 0 26 | #define LOG_LEVEL_WARN 1 27 | #define LOG_LEVEL_INFO 2 28 | #define LOG_LEVEL_DEBUG 3 29 | 30 | /* Default log level (can be overridden) */ 31 | #ifndef SHADER_LIB_LOG_LEVEL 32 | #define SHADER_LIB_LOG_LEVEL LOG_LEVEL_INFO 33 | #endif 34 | 35 | /* Get current timestamp for logging */ 36 | static inline void shader_log_timestamp(char *buf, size_t size) { 37 | struct timeval tv; 38 | gettimeofday(&tv, NULL); 39 | time_t now = tv.tv_sec; 40 | struct tm *tm_info = localtime(&now); 41 | strftime(buf, size, "%Y-%m-%d %H:%M:%S", tm_info); 42 | } 43 | 44 | /* Core logging function */ 45 | static inline void shader_log(int level, const char *prefix, const char *fmt, ...) { 46 | if (level > SHADER_LIB_LOG_LEVEL) { 47 | return; 48 | } 49 | 50 | char timestamp[32]; 51 | shader_log_timestamp(timestamp, sizeof(timestamp)); 52 | 53 | fprintf(stderr, "[%s] [ShaderLib] [%s] ", timestamp, prefix); 54 | 55 | va_list args; 56 | va_start(args, fmt); 57 | vfprintf(stderr, fmt, args); 58 | va_end(args); 59 | 60 | fprintf(stderr, "\n"); 61 | } 62 | 63 | /* Logging macros for standalone mode */ 64 | #define log_error(...) shader_log(LOG_LEVEL_ERROR, "ERROR", __VA_ARGS__) 65 | #define log_warn(...) shader_log(LOG_LEVEL_WARN, "WARN", __VA_ARGS__) 66 | #define log_info(...) shader_log(LOG_LEVEL_INFO, "INFO", __VA_ARGS__) 67 | #define log_debug(...) shader_log(LOG_LEVEL_DEBUG, "DEBUG", __VA_ARGS__) 68 | 69 | #endif /* NEOWALL_VERSION */ 70 | 71 | #endif /* SHADER_LIB_LOG_H */ -------------------------------------------------------------------------------- /protocols/plasma-shell-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * SPDX-FileCopyrightText: 2013-2014 Pier Luigi Fiorini 5 | * 6 | * SPDX-License-Identifier: LGPL-2.1-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "wayland-util.h" 13 | 14 | #ifndef __has_attribute 15 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 16 | #endif 17 | 18 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 19 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 20 | #else 21 | #define WL_PRIVATE 22 | #endif 23 | 24 | extern const struct wl_interface org_kde_plasma_surface_interface; 25 | extern const struct wl_interface wl_output_interface; 26 | extern const struct wl_interface wl_surface_interface; 27 | 28 | static const struct wl_interface *plasma_shell_types[] = { 29 | NULL, 30 | NULL, 31 | &org_kde_plasma_surface_interface, 32 | &wl_surface_interface, 33 | &wl_output_interface, 34 | }; 35 | 36 | static const struct wl_message org_kde_plasma_shell_requests[] = { 37 | { "get_surface", "no", plasma_shell_types + 2 }, 38 | }; 39 | 40 | WL_PRIVATE const struct wl_interface org_kde_plasma_shell_interface = { 41 | "org_kde_plasma_shell", 8, 42 | 1, org_kde_plasma_shell_requests, 43 | 0, NULL, 44 | }; 45 | 46 | static const struct wl_message org_kde_plasma_surface_requests[] = { 47 | { "destroy", "", plasma_shell_types + 0 }, 48 | { "set_output", "o", plasma_shell_types + 4 }, 49 | { "set_position", "ii", plasma_shell_types + 0 }, 50 | { "set_role", "u", plasma_shell_types + 0 }, 51 | { "set_panel_behavior", "u", plasma_shell_types + 0 }, 52 | { "set_skip_taskbar", "2u", plasma_shell_types + 0 }, 53 | { "panel_auto_hide_hide", "4", plasma_shell_types + 0 }, 54 | { "panel_auto_hide_show", "4", plasma_shell_types + 0 }, 55 | { "set_panel_takes_focus", "4u", plasma_shell_types + 0 }, 56 | { "set_skip_switcher", "5u", plasma_shell_types + 0 }, 57 | { "open_under_cursor", "7", plasma_shell_types + 0 }, 58 | }; 59 | 60 | static const struct wl_message org_kde_plasma_surface_events[] = { 61 | { "auto_hidden_panel_hidden", "4", plasma_shell_types + 0 }, 62 | { "auto_hidden_panel_shown", "4", plasma_shell_types + 0 }, 63 | }; 64 | 65 | WL_PRIVATE const struct wl_interface org_kde_plasma_surface_interface = { 66 | "org_kde_plasma_surface", 8, 67 | 11, org_kde_plasma_surface_requests, 68 | 2, org_kde_plasma_surface_events, 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /include/version.h.in: -------------------------------------------------------------------------------- 1 | #ifndef NEOWALL_VERSION_H 2 | #define NEOWALL_VERSION_H 3 | 4 | /** 5 | * NeoWall Version Information 6 | * 7 | * This file is auto-generated from version.h.in by Meson. 8 | * DO NOT EDIT THIS FILE DIRECTLY - edit version.h.in instead. 9 | */ 10 | 11 | /* Version string from Meson */ 12 | #define NEOWALL_VERSION_STRING "@VERSION@" 13 | #define NEOWALL_VERSION_FULL "NeoWall v@VERSION@" 14 | 15 | /* Project metadata */ 16 | #define NEOWALL_PROJECT_NAME "NeoWall" 17 | #define NEOWALL_PROJECT_DESC "A reliable Wayland wallpaper daemon with Multi-Version EGL/OpenGL ES Support" 18 | #define NEOWALL_COPYRIGHT "Copyright (C) 2025" 19 | #define NEOWALL_LICENSE "GPL-3.0" 20 | 21 | /* Build information */ 22 | #ifndef NEOWALL_BUILD_DATE 23 | #define NEOWALL_BUILD_DATE __DATE__ 24 | #endif 25 | 26 | #ifndef NEOWALL_BUILD_TIME 27 | #define NEOWALL_BUILD_TIME __TIME__ 28 | #endif 29 | 30 | /** 31 | * Get version string 32 | * @return Version string (e.g., "0.4.0") 33 | */ 34 | static inline const char *neowall_get_version(void) { 35 | return NEOWALL_VERSION_STRING; 36 | } 37 | 38 | /** 39 | * Get full version string with project name 40 | * @return Full version string (e.g., "NeoWall v0.4.0") 41 | */ 42 | static inline const char *neowall_get_version_full(void) { 43 | return NEOWALL_VERSION_FULL; 44 | } 45 | 46 | /** 47 | * Get project name 48 | * @return Project name 49 | */ 50 | static inline const char *neowall_get_project_name(void) { 51 | return NEOWALL_PROJECT_NAME; 52 | } 53 | 54 | /** 55 | * Get project description 56 | * @return Project description 57 | */ 58 | static inline const char *neowall_get_project_desc(void) { 59 | return NEOWALL_PROJECT_DESC; 60 | } 61 | 62 | /** 63 | * Get copyright notice 64 | * @return Copyright string 65 | */ 66 | static inline const char *neowall_get_copyright(void) { 67 | return NEOWALL_COPYRIGHT; 68 | } 69 | 70 | /** 71 | * Get license 72 | * @return License identifier 73 | */ 74 | static inline const char *neowall_get_license(void) { 75 | return NEOWALL_LICENSE; 76 | } 77 | 78 | /** 79 | * Get build date 80 | * @return Build date string 81 | */ 82 | static inline const char *neowall_get_build_date(void) { 83 | return NEOWALL_BUILD_DATE; 84 | } 85 | 86 | /** 87 | * Get build time 88 | * @return Build time string 89 | */ 90 | static inline const char *neowall_get_build_time(void) { 91 | return NEOWALL_BUILD_TIME; 92 | } 93 | 94 | #endif /* NEOWALL_VERSION_H */ 95 | -------------------------------------------------------------------------------- /include/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef NEOWALL_CONSTANTS_H 2 | #define NEOWALL_CONSTANTS_H 3 | 4 | /* ============================================================================ 5 | * NeoWall Constants - Single Source of Truth 6 | * ============================================================================ 7 | * Centralized constants to eliminate magic numbers and improve maintainability 8 | * ============================================================================ */ 9 | 10 | /* ============================================================================ 11 | * Time Constants (in milliseconds) 12 | * ============================================================================ */ 13 | #define MS_PER_SECOND 1000ULL 14 | #define NS_PER_MS 1000000ULL 15 | #define MS_PER_NANOSECOND 1000000ULL 16 | 17 | /* Animation and transition timings */ 18 | #define FPS_TARGET 60 19 | #define FRAME_TIME_MS 16 /* ~60 FPS (1000/60) - Smooth animations */ 20 | #define DEFAULT_TRANSITION_MS 300 21 | #define SHADER_FADE_IN_MS 600 22 | #define SHADER_FADE_OUT_MS 400 23 | 24 | /* Polling and sleep intervals */ 25 | #define POLL_TIMEOUT_INFINITE -1 26 | #define SLEEP_100MS_NS 100000000 /* 100ms in nanoseconds */ 27 | #define STATS_INTERVAL_MS 10000 /* Print stats every 10 seconds */ 28 | 29 | /* ============================================================================ 30 | * Limits and Thresholds 31 | * ============================================================================ */ 32 | #define MAX_NEXT_REQUESTS 100 /* Maximum queued 'next' wallpaper requests */ 33 | #define DAEMON_SHUTDOWN_TIMEOUT 50 /* Max attempts to wait for daemon shutdown */ 34 | #define ALPHA_OPAQUE 255 /* Fully opaque alpha value */ 35 | 36 | /* ============================================================================ 37 | * OpenGL/Shader Version 38 | * ============================================================================ */ 39 | #define GLSL_VERSION_STRING "#version 100\n" /* OpenGL ES 2.0 */ 40 | #define GLSL_VERSION_LINE "#version 100\\n" /* For concatenated strings */ 41 | 42 | /* ============================================================================ 43 | * Default Values 44 | * ============================================================================ */ 45 | #define DEFAULT_SHADER_SPEED 1.0f 46 | #define MIN_SHADER_SPEED 0.1f 47 | #define SHADER_SPEED_INCREMENT 1.0f 48 | 49 | #endif /* NEOWALL_CONSTANTS_H */ 50 | -------------------------------------------------------------------------------- /protocols/viewporter-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * Copyright © 2013-2016 Collabora, Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | * DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "wayland-util.h" 30 | 31 | #ifndef __has_attribute 32 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 33 | #endif 34 | 35 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 36 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 37 | #else 38 | #define WL_PRIVATE 39 | #endif 40 | 41 | extern const struct wl_interface wl_surface_interface; 42 | extern const struct wl_interface wp_viewport_interface; 43 | 44 | static const struct wl_interface *viewporter_types[] = { 45 | NULL, 46 | NULL, 47 | NULL, 48 | NULL, 49 | &wp_viewport_interface, 50 | &wl_surface_interface, 51 | }; 52 | 53 | static const struct wl_message wp_viewporter_requests[] = { 54 | { "destroy", "", viewporter_types + 0 }, 55 | { "get_viewport", "no", viewporter_types + 4 }, 56 | }; 57 | 58 | WL_PRIVATE const struct wl_interface wp_viewporter_interface = { 59 | "wp_viewporter", 1, 60 | 2, wp_viewporter_requests, 61 | 0, NULL, 62 | }; 63 | 64 | static const struct wl_message wp_viewport_requests[] = { 65 | { "destroy", "", viewporter_types + 0 }, 66 | { "set_source", "ffff", viewporter_types + 0 }, 67 | { "set_destination", "ii", viewporter_types + 0 }, 68 | }; 69 | 70 | WL_PRIVATE const struct wl_interface wp_viewport_interface = { 71 | "wp_viewport", 1, 72 | 3, wp_viewport_requests, 73 | 0, NULL, 74 | }; 75 | 76 | -------------------------------------------------------------------------------- /src/shader_lib/platform_compat.h: -------------------------------------------------------------------------------- 1 | /* Platform Compatibility Header 2 | * Provides compatibility layer for shader library 3 | * Uses desktop OpenGL for Shadertoy compatibility 4 | */ 5 | 6 | #ifndef PLATFORM_COMPAT_H 7 | #define PLATFORM_COMPAT_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* ============================================ 17 | * Unified OpenGL Header Include 18 | * ============================================ */ 19 | 20 | /* Use desktop OpenGL for full Shadertoy shader compatibility. 21 | * Desktop OpenGL 3.3 Core supports #version 330 core shaders 22 | * which is what Shadertoy uses. */ 23 | #include 24 | #include 25 | 26 | /* Platform detection */ 27 | #ifdef _WIN32 28 | #define PLATFORM_WINDOWS 29 | #else 30 | #define PLATFORM_UNIX 31 | #endif 32 | 33 | /* When building within neowall, NEOWALL_VERSION is defined by meson. 34 | * In that case, we use neowall's logging functions declared in neowall.h. 35 | * When building standalone (gleditor), we use shader_log.h's implementation. 36 | */ 37 | #ifdef NEOWALL_VERSION 38 | /* Neowall context - logging functions are declared elsewhere (neowall.h/utils.c) */ 39 | /* Just declare them as extern so shader_lib can use them */ 40 | extern void log_error(const char *format, ...); 41 | extern void log_warn(const char *format, ...); 42 | extern void log_info(const char *format, ...); 43 | extern void log_debug(const char *format, ...); 44 | 45 | /* 46 | * log_debug_once - logs only the first N times (default 3) 47 | * Useful for per-frame debugging without spamming logs 48 | * Usage: log_debug_once(count, "format", args...) 49 | * where count is a static counter variable 50 | */ 51 | #define LOG_DEBUG_ONCE_MAX 3 52 | 53 | #define log_debug_once(counter, ...) do { \ 54 | if ((counter) < LOG_DEBUG_ONCE_MAX) { \ 55 | log_debug(__VA_ARGS__); \ 56 | (counter)++; \ 57 | } \ 58 | } while(0) 59 | 60 | /* 61 | * log_debug_frame - logs only during first N frames of shader execution 62 | * Pass the frame_count from the shader context 63 | */ 64 | #define LOG_DEBUG_FRAME_MAX 3 65 | 66 | #define log_debug_frame(frame_count, ...) do { \ 67 | if ((frame_count) < LOG_DEBUG_FRAME_MAX) { \ 68 | log_debug(__VA_ARGS__); \ 69 | } \ 70 | } while(0) 71 | 72 | #else 73 | /* Standalone context - use shader_log.h's implementation */ 74 | #include "shader_log.h" 75 | #endif 76 | 77 | /* Cross-platform time function */ 78 | static inline double platform_get_time(void) { 79 | struct timeval tv; 80 | gettimeofday(&tv, NULL); 81 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; 82 | } 83 | 84 | #endif /* PLATFORM_COMPAT_H */ -------------------------------------------------------------------------------- /protocols/tearing-control-v1-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * Copyright © 2021 Xaver Hugl 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | * DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "wayland-util.h" 30 | 31 | #ifndef __has_attribute 32 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 33 | #endif 34 | 35 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 36 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 37 | #else 38 | #define WL_PRIVATE 39 | #endif 40 | 41 | extern const struct wl_interface wl_surface_interface; 42 | extern const struct wl_interface wp_tearing_control_v1_interface; 43 | 44 | static const struct wl_interface *tearing_control_v1_types[] = { 45 | NULL, 46 | &wp_tearing_control_v1_interface, 47 | &wl_surface_interface, 48 | }; 49 | 50 | static const struct wl_message wp_tearing_control_manager_v1_requests[] = { 51 | { "destroy", "", tearing_control_v1_types + 0 }, 52 | { "get_tearing_control", "no", tearing_control_v1_types + 1 }, 53 | }; 54 | 55 | WL_PRIVATE const struct wl_interface wp_tearing_control_manager_v1_interface = { 56 | "wp_tearing_control_manager_v1", 1, 57 | 2, wp_tearing_control_manager_v1_requests, 58 | 0, NULL, 59 | }; 60 | 61 | static const struct wl_message wp_tearing_control_v1_requests[] = { 62 | { "set_presentation_hint", "u", tearing_control_v1_types + 0 }, 63 | { "destroy", "", tearing_control_v1_types + 0 }, 64 | }; 65 | 66 | WL_PRIVATE const struct wl_interface wp_tearing_control_v1_interface = { 67 | "wp_tearing_control_v1", 1, 68 | 2, wp_tearing_control_v1_requests, 69 | 0, NULL, 70 | }; 71 | 72 | -------------------------------------------------------------------------------- /include/compositor/backends/wayland.h: -------------------------------------------------------------------------------- 1 | #ifndef WAYLAND_H 2 | #define WAYLAND_H 3 | 4 | /** 5 | * ============================================================================ 6 | * WAYLAND - Backend-Specific Wayland State 7 | * ============================================================================ 8 | * 9 | * This header defines the Wayland-specific global state that was previously 10 | * stored directly in neowall_state. Moving these types here achieves: 11 | * 12 | * 1. True compositor abstraction - core neowall_state is platform-agnostic 13 | * 2. Clean separation - Wayland types only in Wayland backend code 14 | * 3. X11 equality - X11 backend doesn't need to know about Wayland types 15 | * 16 | * This struct is managed by wayland_core.c and accessed by Wayland compositor 17 | * backend implementations (wlr_layer_shell, kde_plasma, gnome_shell, fallback). 18 | * 19 | * NOTE: This header should ONLY be included by Wayland backend code! 20 | * Core neowall code should NOT include this header. 21 | */ 22 | 23 | #include 24 | #include "xdg-output-unstable-v1-client-protocol.h" 25 | #include "tearing-control-v1-client-protocol.h" 26 | 27 | /* Forward declaration */ 28 | struct neowall_state; 29 | 30 | /** 31 | * Wayland state - platform-specific objects 32 | * 33 | * These are the Wayland-specific objects that were previously in neowall_state. 34 | * Now they're encapsulated in this structure, managed by wayland_core.c. 35 | */ 36 | typedef struct wayland { 37 | struct wl_display *display; 38 | struct wl_registry *registry; 39 | struct wl_compositor *compositor; 40 | struct wl_shm *shm; 41 | struct zxdg_output_manager_v1 *xdg_output_manager; 42 | struct wp_tearing_control_manager_v1 *tearing_control_manager; 43 | 44 | /* Back-pointer to main neowall state */ 45 | struct neowall_state *state; 46 | 47 | /* Initialization flag */ 48 | bool initialized; 49 | } wayland_t; 50 | 51 | /** 52 | * Get the Wayland state 53 | * 54 | * @return Pointer to the Wayland state, or NULL if Wayland not initialized 55 | * 56 | * This function is implemented in wayland_core.c and provides access to the 57 | * Wayland objects for the Wayland backend implementations. 58 | */ 59 | wayland_t *wayland_get(void); 60 | 61 | /** 62 | * Initialize Wayland 63 | * 64 | * @param state The main neowall state 65 | * @return true on success, false on failure 66 | * 67 | * Connects to the Wayland display and initializes all global objects. 68 | */ 69 | bool wayland_init(struct neowall_state *state); 70 | 71 | /** 72 | * Cleanup Wayland 73 | * 74 | * Disconnects from the Wayland display and cleans up all global objects. 75 | */ 76 | void wayland_cleanup(void); 77 | 78 | /** 79 | * Check if Wayland is initialized 80 | * 81 | * @return true if Wayland is initialized and available 82 | */ 83 | bool wayland_available(void); 84 | 85 | #endif /* WAYLAND_H */ 86 | -------------------------------------------------------------------------------- /examples/shaders/creepy_mountains.glsl: -------------------------------------------------------------------------------- 1 | 2 | float noise( vec2 p ) 3 | { 4 | return texture(iChannel0,p).x; 5 | } 6 | 7 | float fnoise(vec2 uv, vec4 sc) { 8 | float f = sc.x*noise( uv ); uv = 2.*uv+.11532185; 9 | f += sc.y*noise( uv ); uv = 2.*uv+.23548563; 10 | f += sc.z*noise( uv ); uv = 2.*uv+.12589452; 11 | f += sc.w*noise( uv ); uv = 2.*uv+.26489542; 12 | return f; 13 | } 14 | 15 | 16 | float terrain(float x) { 17 | float w=0.; 18 | float a=1.; 19 | x*=20.; 20 | w+=sin(x*.3521)*4.; 21 | for (int i=0; i<5; i++) { 22 | x*=1.53562; 23 | x+=7.56248; 24 | w+=sin(x)*a; 25 | a*=.5; 26 | } 27 | return .2+w*.015; 28 | } 29 | 30 | float bird(vec2 p) { 31 | p.x+=iTime*.05; 32 | float t=iTime*.05+noise(vec2(floor(p.x/.4-.2)*.7213548))*.7; 33 | p.x=mod(p.x,.4)-.2; 34 | p*=2.-mod(t,1.)*2.; 35 | p.y+=.6-mod(t,1.); 36 | p.y+=pow(abs(p.x),2.)*20.*(.2+sin(iTime*20.)); 37 | float s=step(0.003-abs(p.x)*.1,abs(p.y)); 38 | return min(s,step(0.005,length(p+vec2(0.,.0015)))); 39 | } 40 | 41 | float tree(vec2 p, float tx) { 42 | float noisev=noise(p.xx*.1+.552121)*.25; 43 | p.x=mod(p.x,.2)-.1; 44 | p*=15.+noise(vec2(tx*1.72561))*10.; 45 | float ot=1000.; 46 | float a=radians(-60.+noise(vec2(tx))*30.); 47 | for (int i=0; i<7; i++) { 48 | ot=min(ot,length(max(vec2(0.),abs(p)-vec2(-a*.15,.9)))); 49 | float s=(sign(p.x)+1.)*.25; 50 | p.x=abs(p.x); 51 | p=p*1.3-vec2(0.,1.+noisev); 52 | a*=.8; 53 | a-=(noise(vec2(float(i+2)*.55170275+tx,s))-.5)*.2; 54 | mat2 rot=mat2(cos(a),sin(a),-sin(a),cos(a)); 55 | p*=rot; 56 | } 57 | return step(0.05,ot); 58 | } 59 | 60 | 61 | float scene(vec2 p) { 62 | float t=terrain(p.x); 63 | float s=step(0.,p.y+t); 64 | float tx=floor(p.x/.2)*.2+.1; 65 | if (noise(vec2(tx*3.75489))>.55) s=min(s,tree(p+vec2(0.,terrain(tx)),.42+tx*4.5798523)); 66 | s=min(s,bird(p)); 67 | return s; 68 | } 69 | 70 | float aascene(vec2 p) { 71 | vec2 pix=vec2(0.,max(.25,6.-iTime)/iResolution.x); 72 | float aa=scene(p); 73 | aa+=scene(p+pix.xy); 74 | aa+=scene(p+pix.yy); 75 | aa+=scene(p+pix.yx); 76 | return aa*.25; 77 | } 78 | 79 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 80 | { 81 | vec2 uv = fragCoord.xy / iResolution.xy-.5; 82 | uv.x*=iResolution.x/iResolution.y; 83 | float v=0., l; 84 | float t=iTime*.05; 85 | vec2 c=vec2(-t,0.); 86 | vec2 p; 87 | float sc=clamp(t*t*.5,.05,.15); 88 | uv.y-=.25; 89 | uv.x-=.2; 90 | for (int i=0; i<50; i++) { 91 | p=uv*sc; 92 | l=pow(max(0.,1.-length(p)*2.),15.); 93 | l=.02+l*.8; 94 | v+=scene(p+c)*pow(float(i+1)/30.,2.)*l; 95 | sc+=.006; 96 | } 97 | float clo=fnoise((uv-vec2(t,0.))*vec2(.03,.15),vec4(.8,.6,.3,.1))*max(0.,1.-uv.y*3.); 98 | float tx=uv.x-t*.5; 99 | float ter=.5+step(0.,uv.y-fnoise(vec2(tx)*.015, 100 | vec4(1.,.5,.3,.1))*(.23*(1.+sin(tx*3.2342)*.25))+.5); 101 | float s=aascene(p+c)*(ter+clo*.4); 102 | v*=.025; 103 | float col=min(1.,.05+v+s*l); 104 | col=sqrt(col)*2.05-.5; 105 | fragColor = vec4(col*min(1.,iTime*.2)); 106 | } 107 | -------------------------------------------------------------------------------- /protocols/xdg-output-unstable-v1-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * Copyright © 2017 Red Hat Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | * DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "wayland-util.h" 30 | 31 | #ifndef __has_attribute 32 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 33 | #endif 34 | 35 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 36 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 37 | #else 38 | #define WL_PRIVATE 39 | #endif 40 | 41 | extern const struct wl_interface wl_output_interface; 42 | extern const struct wl_interface zxdg_output_v1_interface; 43 | 44 | static const struct wl_interface *xdg_output_unstable_v1_types[] = { 45 | NULL, 46 | NULL, 47 | &zxdg_output_v1_interface, 48 | &wl_output_interface, 49 | }; 50 | 51 | static const struct wl_message zxdg_output_manager_v1_requests[] = { 52 | { "destroy", "", xdg_output_unstable_v1_types + 0 }, 53 | { "get_xdg_output", "no", xdg_output_unstable_v1_types + 2 }, 54 | }; 55 | 56 | WL_PRIVATE const struct wl_interface zxdg_output_manager_v1_interface = { 57 | "zxdg_output_manager_v1", 3, 58 | 2, zxdg_output_manager_v1_requests, 59 | 0, NULL, 60 | }; 61 | 62 | static const struct wl_message zxdg_output_v1_requests[] = { 63 | { "destroy", "", xdg_output_unstable_v1_types + 0 }, 64 | }; 65 | 66 | static const struct wl_message zxdg_output_v1_events[] = { 67 | { "logical_position", "ii", xdg_output_unstable_v1_types + 0 }, 68 | { "logical_size", "ii", xdg_output_unstable_v1_types + 0 }, 69 | { "done", "", xdg_output_unstable_v1_types + 0 }, 70 | { "name", "2s", xdg_output_unstable_v1_types + 0 }, 71 | { "description", "2s", xdg_output_unstable_v1_types + 0 }, 72 | }; 73 | 74 | WL_PRIVATE const struct wl_interface zxdg_output_v1_interface = { 75 | "zxdg_output_v1", 3, 76 | 1, zxdg_output_v1_requests, 77 | 5, zxdg_output_v1_events, 78 | }; 79 | 80 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | **"Sets wallpapers until it... doesn't (securely)"** 4 | 5 | ## Supported Versions 6 | 7 | We actively maintain and provide security updates for the following versions: 8 | 9 | | Version | Supported | 10 | | ------- | ------------------ | 11 | | 0.1.x | :white_check_mark: | 12 | | < 0.1 | :x: | 13 | 14 | ## Reporting a Vulnerability 15 | 16 | We take security seriously. If you discover a security vulnerability in NeoWall, please report it responsibly. 17 | 18 | ### How to Report 19 | 20 | **DO NOT** open a public GitHub issue for security vulnerabilities. 21 | 22 | Instead, please: 23 | 24 | 1. **Email:** Send details to the maintainer (check GitHub profile for contact) 25 | 2. **GitHub Security Advisory:** Use the [private vulnerability reporting feature](https://github.com/1ay1/neowall/security/advisories/new) 26 | 27 | ### What to Include 28 | 29 | Please provide: 30 | 31 | - Description of the vulnerability 32 | - Steps to reproduce the issue 33 | - Affected versions 34 | - Potential impact 35 | - Any suggested fixes (if you have them) 36 | 37 | ### Response Timeline 38 | 39 | - **Initial Response:** Within 48 hours 40 | - **Status Update:** Within 7 days 41 | - **Fix Timeline:** Depends on severity 42 | - Critical: 1-3 days 43 | - High: 1-2 weeks 44 | - Medium: 2-4 weeks 45 | - Low: Next release cycle 46 | 47 | ### Security Considerations 48 | 49 | NeoWall is designed with security in mind: 50 | 51 | - **No Root Privileges:** Runs as a normal user 52 | - **No Network Access:** Purely local, no external connections 53 | - **Limited Attack Surface:** Minimal dependencies 54 | - **Input Validation:** All user inputs are validated 55 | - **Safe File Operations:** Proper bounds checking on file I/O 56 | - **Memory Safety:** Careful memory management with proper cleanup 57 | 58 | ### Known Security Boundaries 59 | 60 | What NeoWall does NOT protect against: 61 | 62 | - **Malicious Images:** We load PNG/JPEG files - ensure your image sources are trusted 63 | - **Config Injection:** Don't allow untrusted users to modify your config file 64 | - **Symlink Attacks:** Be cautious with symbolic links in wallpaper directories 65 | - **File Permissions:** Ensure your wallpaper files have appropriate permissions 66 | 67 | ### Security Best Practices 68 | 69 | When using NeoWall: 70 | 71 | 1. ✅ Keep NeoWall updated to the latest version 72 | 2. ✅ Use wallpapers from trusted sources 73 | 3. ✅ Set appropriate file permissions on config files (`chmod 600 ~/.config/neowall/config.vibe`) 74 | 4. ✅ Verify image files before using them as wallpapers 75 | 5. ✅ Don't run NeoWall with elevated privileges (unnecessary and dangerous) 76 | 6. ✅ Review config changes if using auto-reload (`--watch`) 77 | 78 | ### Past Security Issues 79 | 80 | None reported yet. We'll maintain a list here if any are discovered. 81 | 82 | ### Acknowledgments 83 | 84 | We appreciate responsible disclosure and will credit security researchers who report vulnerabilities (unless they prefer to remain anonymous). 85 | 86 | --- 87 | 88 | **Remember:** Even wallpaper daemons deserve secure code. If you find something, let us know! -------------------------------------------------------------------------------- /examples/shaders/mandelbrot.glsl: -------------------------------------------------------------------------------- 1 | // Created by inigo quilez - iq/2013 2 | // https://www.youtube.com/c/InigoQuilez 3 | // https://iquilezles.org 4 | // I share this piece (art and code) here in Shadertoy and through its Public API, only for educational purposes. 5 | // You cannot use, sell, share or host this piece or modifications of it as part of your own commercial or non-commercial product, website or project. 6 | // You can share a link to it or an unmodified screenshot of it provided you attribute "by Inigo Quilez, @iquilezles and iquilezles.org". 7 | // If you are a teacher, lecturer, educator or similar and these conditions are too restrictive for your needs, please contact me and we'll work it out. 8 | 9 | 10 | // See here for more information on smooth iteration count: 11 | // 12 | // https://iquilezles.org/articles/msetsmooth 13 | 14 | 15 | // increase this if you have a very fast GPU 16 | #define AA 2 17 | 18 | 19 | 20 | 21 | float mandelbrot( in vec2 c ) 22 | { 23 | #if 1 24 | { 25 | float c2 = dot(c, c); 26 | // skip computation inside M1 - https://iquilezles.org/articles/mset1bulb 27 | if( 256.0*c2*c2 - 96.0*c2 + 32.0*c.x - 3.0 < 0.0 ) return 0.0; 28 | // skip computation inside M2 - https://iquilezles.org/articles/mset2bulb 29 | if( 16.0*(c2+2.0*c.x+1.0) - 1.0 < 0.0 ) return 0.0; 30 | } 31 | #endif 32 | 33 | const float B = 256.0; 34 | float n = 0.0; 35 | vec2 z = vec2(0.0); 36 | for( int i=0; i<512; i++ ) 37 | { 38 | z = vec2( z.x*z.x - z.y*z.y, 2.0*z.x*z.y ) + c; 39 | if( dot(z,z)>(B*B) ) break; 40 | n += 1.0; 41 | } 42 | 43 | if( n>511.0 ) return 0.0; 44 | 45 | // ------------------------------------------------------ 46 | // smooth interation count 47 | //float sl = l - log(log(length(z))/log(B))/log(2.0); 48 | 49 | // equivalent optimized smooth interation count 50 | float sn = n - log2(log2(dot(z,z))) + 4.0; 51 | 52 | float al = smoothstep( -0.1, 0.0, sin(0.5*6.2831*iTime ) ); 53 | return mix( n, sn, al ); 54 | } 55 | 56 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 57 | { 58 | vec3 col = vec3(0.0); 59 | 60 | #if AA>1 61 | for( int m=0; m1 84 | } 85 | col /= float(AA*AA); 86 | #endif 87 | 88 | fragColor = vec4( col, 1.0 ); 89 | } 90 | -------------------------------------------------------------------------------- /src/textures/gray_noise.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "neowall.h" 5 | 6 | /* Generate grayscale noise texture 7 | * Single channel noise that's useful for many effects 8 | */ 9 | 10 | static float hash_gray(float n) { 11 | return fmodf(sinf(n) * 43758.5453123f, 1.0f); 12 | } 13 | 14 | static float fract_gray(float x) { 15 | return x - floorf(x); 16 | } 17 | 18 | static float noise_gray(float x, float y) { 19 | float px = floorf(x); 20 | float py = floorf(y); 21 | float fx = fract_gray(x); 22 | float fy = fract_gray(y); 23 | 24 | // Smoothstep interpolation 25 | fx = fx * fx * (3.0f - 2.0f * fx); 26 | fy = fy * fy * (3.0f - 2.0f * fy); 27 | 28 | float n = px + py * 157.0f; 29 | 30 | float a = hash_gray(n + 0.0f); 31 | float b = hash_gray(n + 1.0f); 32 | float c = hash_gray(n + 157.0f); 33 | float d = hash_gray(n + 158.0f); 34 | 35 | float res = a * (1.0f - fx) * (1.0f - fy) + 36 | b * fx * (1.0f - fy) + 37 | c * (1.0f - fx) * fy + 38 | d * fx * fy; 39 | 40 | return res; 41 | } 42 | 43 | static float fbm_gray(float x, float y, int octaves) { 44 | float value = 0.0f; 45 | float amplitude = 0.5f; 46 | float frequency = 1.0f; 47 | 48 | for (int i = 0; i < octaves; i++) { 49 | value += amplitude * noise_gray(x * frequency, y * frequency); 50 | frequency *= 2.0f; 51 | amplitude *= 0.5f; 52 | } 53 | 54 | return value; 55 | } 56 | 57 | GLuint texture_create_gray_noise(int width, int height) { 58 | unsigned char *data = malloc(width * height * 4); 59 | if (!data) { 60 | return 0; 61 | } 62 | 63 | // Generate grayscale noise 64 | for (int y = 0; y < height; y++) { 65 | for (int x = 0; x < width; x++) { 66 | int idx = (y * width + x) * 4; 67 | 68 | float u = (float)x / (float)width; 69 | float v = (float)y / (float)height; 70 | 71 | // High quality multi-octave noise 72 | float gray = fbm_gray(u * 10.0f, v * 10.0f, 5); 73 | 74 | unsigned char value = (unsigned char)(gray * 255.0f); 75 | 76 | data[idx + 0] = value; 77 | data[idx + 1] = value; 78 | data[idx + 2] = value; 79 | data[idx + 3] = 255; 80 | } 81 | } 82 | 83 | GLuint texture; 84 | glGenTextures(1, &texture); 85 | glBindTexture(GL_TEXTURE_2D, texture); 86 | 87 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 88 | GL_RGBA, GL_UNSIGNED_BYTE, data); 89 | 90 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 91 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 92 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 93 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 94 | 95 | glGenerateMipmap(GL_TEXTURE_2D); 96 | 97 | free(data); 98 | 99 | return texture; 100 | } -------------------------------------------------------------------------------- /include/reload_metrics.h: -------------------------------------------------------------------------------- 1 | #ifndef RELOAD_METRICS_H 2 | #define RELOAD_METRICS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Configuration reload metrics for monitoring and debugging */ 9 | typedef struct { 10 | /* Counters */ 11 | uint64_t total_reloads_attempted; 12 | uint64_t total_reloads_succeeded; 13 | uint64_t total_reloads_failed; 14 | uint64_t total_reloads_throttled; 15 | uint64_t total_changes_detected; 16 | uint64_t total_changes_ignored; /* debounce, empty file, etc. */ 17 | 18 | /* Timing statistics */ 19 | uint64_t last_reload_start_time_ms; 20 | uint64_t last_reload_duration_ms; 21 | uint64_t fastest_reload_ms; 22 | uint64_t slowest_reload_ms; 23 | uint64_t average_reload_ms; 24 | 25 | /* Error tracking */ 26 | uint64_t file_not_found_errors; 27 | uint64_t permission_errors; 28 | uint64_t parse_errors; 29 | uint64_t deadlock_preventions; 30 | uint64_t concurrent_reload_preventions; 31 | 32 | /* Debouncing metrics */ 33 | uint64_t debounce_hits; /* Changes that disappeared after debounce */ 34 | uint64_t debounce_passes; /* Changes that survived debounce */ 35 | 36 | /* File system anomalies */ 37 | uint64_t empty_file_detections; 38 | uint64_t oversized_file_detections; 39 | uint64_t invalid_file_type_detections; 40 | uint64_t file_disappeared_during_read; 41 | 42 | /* Recovery metrics */ 43 | uint64_t rollbacks_performed; 44 | uint64_t rollbacks_succeeded; 45 | uint64_t rollbacks_failed; 46 | 47 | /* Timestamps */ 48 | time_t first_reload_timestamp; 49 | time_t last_successful_reload_timestamp; 50 | time_t last_failed_reload_timestamp; 51 | 52 | /* Configuration state */ 53 | char last_loaded_path[256]; 54 | time_t last_loaded_mtime; 55 | size_t last_loaded_size; 56 | 57 | } reload_metrics_t; 58 | 59 | /* Initialize reload metrics */ 60 | void reload_metrics_init(reload_metrics_t *metrics); 61 | 62 | /* Update metrics on reload attempt */ 63 | void reload_metrics_record_attempt(reload_metrics_t *metrics); 64 | 65 | /* Update metrics on reload completion */ 66 | void reload_metrics_record_result(reload_metrics_t *metrics, bool success, uint64_t duration_ms); 67 | 68 | /* Update metrics on reload throttling */ 69 | void reload_metrics_record_throttle(reload_metrics_t *metrics); 70 | 71 | /* Update metrics on file system anomaly */ 72 | void reload_metrics_record_anomaly(reload_metrics_t *metrics, const char *anomaly_type); 73 | 74 | /* Update metrics on debounce */ 75 | void reload_metrics_record_debounce(reload_metrics_t *metrics, bool survived); 76 | 77 | /* Update metrics on rollback */ 78 | void reload_metrics_record_rollback(reload_metrics_t *metrics, bool success); 79 | 80 | /* Print metrics summary (for debugging) */ 81 | void reload_metrics_print(const reload_metrics_t *metrics); 82 | 83 | /* Reset metrics (for testing) */ 84 | void reload_metrics_reset(reload_metrics_t *metrics); 85 | 86 | /* Check if reload performance is degrading */ 87 | bool reload_metrics_is_slow(const reload_metrics_t *metrics); 88 | 89 | /* Check if reload is unstable (many failures) */ 90 | bool reload_metrics_is_unstable(const reload_metrics_t *metrics); 91 | 92 | #endif /* RELOAD_METRICS_H */ -------------------------------------------------------------------------------- /src/textures/rgba_noise.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "neowall.h" 5 | 6 | /* Generate RGBA noise texture - most common Shadertoy texture 7 | * This creates a tileable RGBA noise texture similar to what's used in many Shadertoy shaders 8 | */ 9 | 10 | static float fract(float x) { 11 | return x - floorf(x); 12 | } 13 | 14 | static float hash(float n) { 15 | return fract(sin(n) * 43758.5453123f); 16 | } 17 | 18 | static float noise(float x, float y) { 19 | float px = floorf(x); 20 | float py = floorf(y); 21 | float fx = fract(x); 22 | float fy = fract(y); 23 | 24 | fx = fx * fx * (3.0f - 2.0f * fx); 25 | fy = fy * fy * (3.0f - 2.0f * fy); 26 | 27 | float n = px + py * 157.0f; 28 | 29 | float a = hash(n + 0.0f); 30 | float b = hash(n + 1.0f); 31 | float c = hash(n + 157.0f); 32 | float d = hash(n + 158.0f); 33 | 34 | float res = a * (1.0f - fx) * (1.0f - fy) + 35 | b * fx * (1.0f - fy) + 36 | c * (1.0f - fx) * fy + 37 | d * fx * fy; 38 | 39 | return res; 40 | } 41 | 42 | static float fbm(float x, float y, int octaves) { 43 | float value = 0.0f; 44 | float amplitude = 0.5f; 45 | float frequency = 1.0f; 46 | 47 | for (int i = 0; i < octaves; i++) { 48 | value += amplitude * noise(x * frequency, y * frequency); 49 | frequency *= 2.0f; 50 | amplitude *= 0.5f; 51 | } 52 | 53 | return value; 54 | } 55 | 56 | GLuint texture_create_rgba_noise(int width, int height) { 57 | unsigned char *data = malloc(width * height * 4); 58 | if (!data) { 59 | return 0; 60 | } 61 | 62 | // Generate RGBA noise with different frequencies per channel 63 | for (int y = 0; y < height; y++) { 64 | for (int x = 0; x < width; x++) { 65 | int idx = (y * width + x) * 4; 66 | 67 | float u = (float)x / (float)width; 68 | float v = (float)y / (float)height; 69 | 70 | // Different noise patterns for each channel 71 | float r = fbm(u * 8.0f, v * 8.0f, 4); 72 | float g = fbm(u * 6.5f + 123.4f, v * 6.5f + 456.7f, 4); 73 | float b = fbm(u * 7.3f + 789.1f, v * 7.3f + 234.5f, 4); 74 | float a = fbm(u * 5.7f + 567.8f, v * 5.7f + 890.1f, 4); 75 | 76 | data[idx + 0] = (unsigned char)(r * 255.0f); 77 | data[idx + 1] = (unsigned char)(g * 255.0f); 78 | data[idx + 2] = (unsigned char)(b * 255.0f); 79 | data[idx + 3] = (unsigned char)(a * 255.0f); 80 | } 81 | } 82 | 83 | GLuint texture; 84 | glGenTextures(1, &texture); 85 | glBindTexture(GL_TEXTURE_2D, texture); 86 | 87 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 88 | GL_RGBA, GL_UNSIGNED_BYTE, data); 89 | 90 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 91 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 92 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 93 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 94 | 95 | glGenerateMipmap(GL_TEXTURE_2D); 96 | 97 | free(data); 98 | 99 | return texture; 100 | } -------------------------------------------------------------------------------- /include/compositor/backends/x11.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPOSITOR_BACKENDS_X11_H 2 | #define COMPOSITOR_BACKENDS_X11_H 3 | 4 | #include "compositor.h" 5 | 6 | /* 7 | * ============================================================================ 8 | * X11 BACKEND FOR TILING WINDOW MANAGERS 9 | * ============================================================================ 10 | * 11 | * Header file for the X11 compositor backend targeting tiling window managers. 12 | * 13 | * This backend provides wallpaper functionality for X11-based systems, 14 | * particularly optimized for tiling window managers that respect EWMH hints. 15 | * 16 | * SUPPORTED WINDOW MANAGERS: 17 | * - i3/i3-gaps 18 | * - bspwm 19 | * - dwm 20 | * - awesome 21 | * - xmonad 22 | * - qtile 23 | * - herbstluftwm 24 | * 25 | * FEATURES: 26 | * - Desktop window type (_NET_WM_WINDOW_TYPE_DESKTOP) 27 | * - Proper stacking below all windows (_NET_WM_STATE_BELOW) 28 | * - Multi-monitor support via XRandR 29 | * - EGL rendering via EGL_PLATFORM_X11_KHR 30 | * - Sticky windows across all workspaces 31 | * - Skip taskbar/pager hints 32 | * 33 | * USAGE: 34 | * The backend is automatically registered and selected when: 35 | * 1. DISPLAY environment variable is set 36 | * 2. X11 display connection succeeds 37 | * 3. No Wayland compositor is detected (or Wayland fails) 38 | */ 39 | 40 | /* Forward declarations */ 41 | struct neowall_state; 42 | struct compositor_backend; 43 | 44 | /** 45 | * Initialize X11 backend 46 | * 47 | * This function checks if X11 is available and initializes the backend. 48 | * It will return NULL if X11 is not available or initialization fails. 49 | * 50 | * @param state Global NeoWall state 51 | * @return Compositor backend handle, or NULL on failure 52 | */ 53 | struct compositor_backend *compositor_backend_x11_init(struct neowall_state *state); 54 | 55 | /** 56 | * Check if X11 is available 57 | * 58 | * Quick check to see if X11 display can be opened. 59 | * Useful for backend selection logic. 60 | * 61 | * @return true if X11 is available, false otherwise 62 | */ 63 | bool compositor_backend_x11_available(void); 64 | 65 | /** 66 | * Get native X11 window from compositor surface 67 | * 68 | * Helper function to get the underlying X11 Window from a compositor surface. 69 | * Returns 0 if the surface is not an X11 surface. 70 | * 71 | * @param surface Compositor surface 72 | * @return X11 Window handle, or 0 if not X11 73 | */ 74 | unsigned long compositor_surface_get_x11_window(struct compositor_surface *surface); 75 | 76 | /** 77 | * Get X11 connection file descriptor 78 | * 79 | * Returns the file descriptor for the X11 connection, which can be used 80 | * with poll/select for event-driven X11 event processing. 81 | * 82 | * @param backend Compositor backend (must be X11 backend) 83 | * @return X11 connection file descriptor, or -1 on error 84 | */ 85 | int x11_backend_get_fd(struct compositor_backend *backend); 86 | 87 | /** 88 | * Handle pending X11 events 89 | * 90 | * Process all pending X11 events including mouse events (button press/release, 91 | * motion), keyboard events, expose events, and structure notifications. 92 | * This should be called when the X11 file descriptor is ready for reading. 93 | * 94 | * @param backend Compositor backend (must be X11 backend) 95 | * @return true on success, false on error 96 | */ 97 | bool x11_backend_handle_events(struct compositor_backend *backend); 98 | 99 | #endif /* COMPOSITOR_BACKENDS_X11_H */ -------------------------------------------------------------------------------- /examples/shaders/2d_clouds.glsl: -------------------------------------------------------------------------------- 1 | const float cloudscale = 1.1; 2 | const float speed = 0.03; 3 | const float clouddark = 0.5; 4 | const float cloudlight = 0.3; 5 | const float cloudcover = 0.2; 6 | const float cloudalpha = 8.0; 7 | const float skytint = 0.5; 8 | const vec3 skycolour1 = vec3(0.2, 0.4, 0.6); 9 | const vec3 skycolour2 = vec3(0.4, 0.7, 1.0); 10 | 11 | const mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 ); 12 | 13 | vec2 hash( vec2 p ) { 14 | p = vec2(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3))); 15 | return -1.0 + 2.0*fract(sin(p)*43758.5453123); 16 | } 17 | 18 | float noise( in vec2 p ) { 19 | const float K1 = 0.366025404; // (sqrt(3)-1)/2; 20 | const float K2 = 0.211324865; // (3-sqrt(3))/6; 21 | vec2 i = floor(p + (p.x+p.y)*K1); 22 | vec2 a = p - i + (i.x+i.y)*K2; 23 | vec2 o = (a.x>a.y) ? vec2(1.0,0.0) : vec2(0.0,1.0); //vec2 of = 0.5 + 0.5*vec2(sign(a.x-a.y), sign(a.y-a.x)); 24 | vec2 b = a - o + K2; 25 | vec2 c = a - 1.0 + 2.0*K2; 26 | vec3 h = max(0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); 27 | vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0))); 28 | return dot(n, vec3(70.0)); 29 | } 30 | 31 | float fbm(vec2 n) { 32 | float total = 0.0, amplitude = 0.1; 33 | for (int i = 0; i < 7; i++) { 34 | total += noise(n) * amplitude; 35 | n = m * n; 36 | amplitude *= 0.4; 37 | } 38 | return total; 39 | } 40 | 41 | // ----------------------------------------------- 42 | 43 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 44 | vec2 p = fragCoord.xy / iResolution.xy; 45 | vec2 uv = p*vec2(iResolution.x/iResolution.y,1.0); 46 | float time = iTime * speed; 47 | float q = fbm(uv * cloudscale * 0.5); 48 | 49 | //ridged noise shape 50 | float r = 0.0; 51 | uv *= cloudscale; 52 | uv -= q - time; 53 | float weight = 0.8; 54 | for (int i=0; i<8; i++){ 55 | r += abs(weight*noise( uv )); 56 | uv = m*uv + time; 57 | weight *= 0.7; 58 | } 59 | 60 | //noise shape 61 | float f = 0.0; 62 | uv = p*vec2(iResolution.x/iResolution.y,1.0); 63 | uv *= cloudscale; 64 | uv -= q - time; 65 | weight = 0.7; 66 | for (int i=0; i<8; i++){ 67 | f += weight*noise( uv ); 68 | uv = m*uv + time; 69 | weight *= 0.6; 70 | } 71 | 72 | f *= r + f; 73 | 74 | //noise colour 75 | float c = 0.0; 76 | time = iTime * speed * 2.0; 77 | uv = p*vec2(iResolution.x/iResolution.y,1.0); 78 | uv *= cloudscale*2.0; 79 | uv -= q - time; 80 | weight = 0.4; 81 | for (int i=0; i<7; i++){ 82 | c += weight*noise( uv ); 83 | uv = m*uv + time; 84 | weight *= 0.6; 85 | } 86 | 87 | //noise ridge colour 88 | float c1 = 0.0; 89 | time = iTime * speed * 3.0; 90 | uv = p*vec2(iResolution.x/iResolution.y,1.0); 91 | uv *= cloudscale*3.0; 92 | uv -= q - time; 93 | weight = 0.4; 94 | for (int i=0; i<7; i++){ 95 | c1 += abs(weight*noise( uv )); 96 | uv = m*uv + time; 97 | weight *= 0.6; 98 | } 99 | 100 | c += c1; 101 | 102 | vec3 skycolour = mix(skycolour2, skycolour1, p.y); 103 | vec3 cloudcolour = vec3(1.1, 1.1, 0.9) * clamp((clouddark + cloudlight*c), 0.0, 1.0); 104 | 105 | f = cloudcover + cloudalpha*f*r; 106 | 107 | vec3 result = mix(skycolour, clamp(skytint * skycolour + cloudcolour, 0.0, 1.0), clamp(f + c, 0.0, 1.0)); 108 | 109 | fragColor = vec4( result, 1.0 ); 110 | } 111 | -------------------------------------------------------------------------------- /protocols/wlr-layer-shell-unstable-v1-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * Copyright © 2017 Drew DeVault 5 | * 6 | * Permission to use, copy, modify, distribute, and sell this 7 | * software and its documentation for any purpose is hereby granted 8 | * without fee, provided that the above copyright notice appear in 9 | * all copies and that both that copyright notice and this permission 10 | * notice appear in supporting documentation, and that the name of 11 | * the copyright holders not be used in advertising or publicity 12 | * pertaining to distribution of the software without specific, 13 | * written prior permission. The copyright holders make no 14 | * representations about the suitability of this software for any 15 | * purpose. It is provided "as is" without express or implied 16 | * warranty. 17 | * 18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 19 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 23 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 24 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 25 | * THIS SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "wayland-util.h" 32 | 33 | #ifndef __has_attribute 34 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 35 | #endif 36 | 37 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 38 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 39 | #else 40 | #define WL_PRIVATE 41 | #endif 42 | 43 | extern const struct wl_interface wl_output_interface; 44 | extern const struct wl_interface wl_surface_interface; 45 | extern const struct wl_interface xdg_popup_interface; 46 | extern const struct wl_interface zwlr_layer_surface_v1_interface; 47 | 48 | static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = { 49 | NULL, 50 | NULL, 51 | NULL, 52 | NULL, 53 | &zwlr_layer_surface_v1_interface, 54 | &wl_surface_interface, 55 | &wl_output_interface, 56 | NULL, 57 | NULL, 58 | &xdg_popup_interface, 59 | }; 60 | 61 | static const struct wl_message zwlr_layer_shell_v1_requests[] = { 62 | { "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 }, 63 | { "destroy", "3", wlr_layer_shell_unstable_v1_types + 0 }, 64 | }; 65 | 66 | WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = { 67 | "zwlr_layer_shell_v1", 5, 68 | 2, zwlr_layer_shell_v1_requests, 69 | 0, NULL, 70 | }; 71 | 72 | static const struct wl_message zwlr_layer_surface_v1_requests[] = { 73 | { "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 }, 74 | { "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 }, 75 | { "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 }, 76 | { "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 }, 77 | { "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 }, 78 | { "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 }, 79 | { "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 }, 80 | { "destroy", "", wlr_layer_shell_unstable_v1_types + 0 }, 81 | { "set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0 }, 82 | { "set_exclusive_edge", "5u", wlr_layer_shell_unstable_v1_types + 0 }, 83 | }; 84 | 85 | static const struct wl_message zwlr_layer_surface_v1_events[] = { 86 | { "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 }, 87 | { "closed", "", wlr_layer_shell_unstable_v1_types + 0 }, 88 | }; 89 | 90 | WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = { 91 | "zwlr_layer_surface_v1", 5, 92 | 10, zwlr_layer_surface_v1_requests, 93 | 2, zwlr_layer_surface_v1_events, 94 | }; 95 | 96 | -------------------------------------------------------------------------------- /src/textures/wood.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "neowall.h" 5 | 6 | /* Generate wood grain texture 7 | * Creates a realistic wood grain pattern useful for backgrounds 8 | */ 9 | 10 | static float fract_wood(float x) { 11 | return x - floorf(x); 12 | } 13 | 14 | static float hash_wood(float n) { 15 | return fract_wood(sinf(n) * 43758.5453123f); 16 | } 17 | 18 | static float noise_wood(float x, float y) { 19 | float px = floorf(x); 20 | float py = floorf(y); 21 | float fx = fract_wood(x); 22 | float fy = fract_wood(y); 23 | 24 | fx = fx * fx * (3.0f - 2.0f * fx); 25 | fy = fy * fy * (3.0f - 2.0f * fy); 26 | 27 | float n = px + py * 157.0f; 28 | 29 | float a = hash_wood(n + 0.0f); 30 | float b = hash_wood(n + 1.0f); 31 | float c = hash_wood(n + 157.0f); 32 | float d = hash_wood(n + 158.0f); 33 | 34 | return a * (1.0f - fx) * (1.0f - fy) + 35 | b * fx * (1.0f - fy) + 36 | c * (1.0f - fx) * fy + 37 | d * fx * fy; 38 | } 39 | 40 | static float fbm_wood(float x, float y, int octaves) { 41 | float value = 0.0f; 42 | float amplitude = 0.5f; 43 | float frequency = 1.0f; 44 | 45 | for (int i = 0; i < octaves; i++) { 46 | value += amplitude * noise_wood(x * frequency, y * frequency); 47 | frequency *= 2.0f; 48 | amplitude *= 0.5f; 49 | } 50 | 51 | return value; 52 | } 53 | 54 | static float wood_pattern(float x, float y) { 55 | // Create wood grain pattern using concentric circles with noise 56 | float dist = sqrtf(x * x + y * y); 57 | 58 | // Add some warping 59 | dist += fbm_wood(x * 2.0f, y * 2.0f, 3) * 0.5f; 60 | 61 | // Create rings 62 | float rings = sinf(dist * 20.0f) * 0.5f + 0.5f; 63 | 64 | // Add fine grain details 65 | float grain = fbm_wood(x * 40.0f, y * 40.0f, 4) * 0.3f; 66 | 67 | return rings * 0.7f + grain * 0.3f; 68 | } 69 | 70 | GLuint texture_create_wood(int width, int height) { 71 | unsigned char *data = malloc(width * height * 4); 72 | if (!data) { 73 | return 0; 74 | } 75 | 76 | // Generate wood texture 77 | for (int y = 0; y < height; y++) { 78 | for (int x = 0; x < width; x++) { 79 | int idx = (y * width + x) * 4; 80 | 81 | float u = ((float)x / (float)width - 0.5f) * 2.0f; 82 | float v = ((float)y / (float)height - 0.5f) * 2.0f; 83 | 84 | // Generate wood pattern 85 | float wood = wood_pattern(u, v); 86 | 87 | // Wood color variations (brown tones) 88 | float base = 0.3f + wood * 0.4f; 89 | 90 | unsigned char r = (unsigned char)(base * 180.0f + 40.0f); 91 | unsigned char g = (unsigned char)(base * 120.0f + 30.0f); 92 | unsigned char b = (unsigned char)(base * 60.0f + 20.0f); 93 | 94 | data[idx + 0] = r; 95 | data[idx + 1] = g; 96 | data[idx + 2] = b; 97 | data[idx + 3] = 255; 98 | } 99 | } 100 | 101 | GLuint texture; 102 | glGenTextures(1, &texture); 103 | glBindTexture(GL_TEXTURE_2D, texture); 104 | 105 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 106 | GL_RGBA, GL_UNSIGNED_BYTE, data); 107 | 108 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 109 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 110 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 111 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 112 | 113 | glGenerateMipmap(GL_TEXTURE_2D); 114 | 115 | free(data); 116 | 117 | return texture; 118 | } -------------------------------------------------------------------------------- /src/transitions/fade.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "neowall.h" 4 | #include "constants.h" 5 | #include "transitions.h" 6 | #include "shader.h" 7 | 8 | /* External function from render.c for calculating vertices */ 9 | extern void calculate_vertex_coords_for_image(struct output_state *output, 10 | struct image_data *image, 11 | float vertices[16]); 12 | 13 | /* Vertex shader for fade transition */ 14 | static const char *fade_vertex_shader_source = 15 | GLSL_VERSION_STRING 16 | "attribute vec2 position;\n" 17 | "attribute vec2 texcoord;\n" 18 | "varying vec2 v_texcoord;\n" 19 | "void main() {\n" 20 | " gl_Position = vec4(position, 0.0, 1.0);\n" 21 | " v_texcoord = texcoord;\n" 22 | "}\n"; 23 | 24 | /* Fragment shader for fade transition */ 25 | static const char *fade_fragment_shader_source = 26 | GLSL_VERSION_STRING 27 | "precision mediump float;\n" 28 | "varying vec2 v_texcoord;\n" 29 | "uniform sampler2D texture0;\n" 30 | "uniform float alpha;\n" 31 | "void main() {\n" 32 | " vec4 color = texture2D(texture0, v_texcoord);\n" 33 | " gl_FragColor = vec4(color.rgb, color.a * alpha);\n" 34 | "}\n"; 35 | 36 | /** 37 | * Create shader program for fade transition 38 | * @param program Pointer to store the created program ID 39 | * @return true on success, false on failure 40 | */ 41 | bool shader_create_fade_program(GLuint *program) { 42 | return shader_create_program_from_sources( 43 | fade_vertex_shader_source, 44 | fade_fragment_shader_source, 45 | program 46 | ); 47 | } 48 | 49 | /** 50 | * Fade Transition 51 | * 52 | * Classic crossfade effect where the new wallpaper gradually appears 53 | * over the old wallpaper. Both images maintain their display mode 54 | * throughout the transition. 55 | * 56 | * @param output Output state containing images and textures 57 | * @param progress Transition progress (0.0 to 1.0) 58 | * @return true on success, false on error 59 | */ 60 | bool transition_fade_render(struct output_state *output, float progress) { 61 | if (!output || !output->current_image || !output->next_image) { 62 | log_error("Fade transition: invalid parameters (output=%p)", (void*)output); 63 | return false; 64 | } 65 | 66 | if (output->texture == 0 || output->next_texture == 0) { 67 | log_error("Fade transition: missing textures (texture=%u, next_texture=%u)", 68 | output->texture, output->next_texture); 69 | return false; 70 | } 71 | 72 | if (output->program == 0) { 73 | log_error("Fade transition: program not initialized"); 74 | return false; 75 | } 76 | 77 | log_debug("Fade transition rendering: progress=%.2f, program=%u", 78 | progress, output->program); 79 | 80 | /* Initialize transition context - handles all OpenGL state setup */ 81 | transition_context_t ctx; 82 | if (!transition_begin(&ctx, output, output->program)) { 83 | return false; 84 | } 85 | 86 | /* Draw old image at full opacity */ 87 | if (!transition_draw_textured_quad(&ctx, output->next_texture, 1.0f, NULL)) { 88 | transition_end(&ctx); 89 | return false; 90 | } 91 | 92 | /* Draw new image with alpha based on progress (crossfade effect) */ 93 | if (!transition_draw_textured_quad(&ctx, output->texture, progress, NULL)) { 94 | transition_end(&ctx); 95 | return false; 96 | } 97 | 98 | /* Disable alpha channel writes - force opaque output */ 99 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); 100 | 101 | /* Re-enable alpha channel writes after transition */ 102 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 103 | 104 | /* Clean up and return - handles all OpenGL state cleanup */ 105 | transition_end(&ctx); 106 | return true; 107 | } -------------------------------------------------------------------------------- /examples/shaders/windows.glsl: -------------------------------------------------------------------------------- 1 | #define PI 3.1415926535897921284 2 | #define REP 8 3 | #define d2r(x) (x * PI / 180.0) 4 | #define WBCOL (vec3(0.5, 0.7, 1.7)) 5 | #define WBCOL2 (vec3(0.15, 0.8, 1.7)) 6 | #define ZERO (min(iFrame, 0)) 7 | 8 | float hash(vec2 p) { 9 | float h = dot(p, vec2(127.1, 311.7)); 10 | return fract(sin(h) * 458.325421) * 2.0 - 1.0; 11 | } 12 | 13 | float noise(vec2 p) { 14 | vec2 i = floor(p); 15 | vec2 f = fract(p); 16 | f = f * f * (3.0 - 2.0 * f); 17 | return mix( 18 | mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x), 19 | mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), 20 | f.y 21 | ); 22 | } 23 | 24 | // Fractal brownian motion for mist texture 25 | float fbm(vec2 p) { 26 | float v = 0.0; 27 | float a = 0.5; 28 | for (int i = 0; i < 5; i++) { 29 | v += a * noise(p); 30 | p *= 2.03; 31 | a *= 0.55; 32 | } 33 | return v; 34 | } 35 | 36 | vec2 rot(vec2 p, float a) { 37 | float c = cos(a), s = sin(a); 38 | return vec2(p.x * c - p.y * s, p.x * s + p.y * c); 39 | } 40 | 41 | float nac(vec3 p, vec2 F, vec3 o) { 42 | p += o; 43 | return length(max(abs(p.xy) - F, 0.0)) - 0.0001; 44 | } 45 | 46 | float recta(vec3 p, vec3 F, vec3 o) { 47 | p += o; 48 | return length(max(abs(p) - F, 0.0)) - 0.0001; 49 | } 50 | 51 | float map1(vec3 p, float scale) { 52 | float G = 0.50; 53 | float F = 0.50 * scale; 54 | float t = nac(p, vec2(F), vec3(G, G, 0.0)); 55 | t = min(t, nac(p, vec2(F), vec3(G, -G, 0.0))); 56 | t = min(t, nac(p, vec2(F), vec3(-G, G, 0.0))); 57 | t = min(t, nac(p, vec2(F), vec3(-G, -G, 0.0))); 58 | return t; 59 | } 60 | 61 | float map2(vec3 p) { 62 | float t = map1(p, 0.9); 63 | return max(t, recta(p, vec3(1.0, 1.0, 0.02), vec3(0.0))); 64 | } 65 | 66 | float gennoise(vec2 p) { 67 | return 0.5 * noise(p * 5.0 + iTime) + 0.25 * noise(p * 8.0 + iTime); 68 | } 69 | 70 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 71 | vec2 uv = -1.0 + 2.0 * (fragCoord.xy / iResolution.xy); 72 | uv *= 1.4; 73 | 74 | vec3 dir = normalize(vec3(uv * vec2(iResolution.x / iResolution.y, 1.0), 1.0 + sin(iTime) * 0.01)); 75 | dir.xz = rot(dir.xz, d2r(70.0)); 76 | dir.xy = rot(dir.xy, d2r(90.0)); 77 | 78 | vec3 pos = vec3(-0.1 + sin(iTime * 0.3) * 0.1, 2.0 + cos(iTime * 0.4) * 0.1, -3.5); 79 | vec3 col = vec3(0.0); 80 | float t = 0.0; 81 | float M = 1.002; 82 | float bsh = 0.01; 83 | float dens = 0.0; 84 | 85 | for (int i = ZERO; i < REP * 12; i++) { 86 | vec3 p = pos + dir * t; 87 | float temp = map1(p, 0.6); 88 | if (temp < 0.2) { 89 | col += WBCOL * 0.005 * dens; 90 | } 91 | 92 | // --- Thick mist layer --- 93 | float fogLayer = fbm(p.xz * 0.25 + iTime * 0.05 + vec2(0.0, iTime * 0.02)); 94 | float fogDepth = smoothstep(0.2, 0.8, fogLayer); 95 | float fogDistance = clamp(t * 0.08, 0.0, 1.0); 96 | float dynamicFog = fogDepth * fogDistance; 97 | vec3 fogColor = vec3(0.15, 0.22, 0.35) * (1.2 + 0.5 * sin(iTime * 0.3 + p.x * 0.7)); 98 | col += fogColor * dynamicFog * 0.1; 99 | // ------------------------ 100 | 101 | t += bsh * M; 102 | bsh *= M; 103 | dens += 0.025; 104 | } 105 | 106 | // Windows pass 107 | t = 0.0; 108 | float y = 0.0; 109 | for (int i = ZERO; i < REP; i++) { 110 | float temp = map2(pos + dir * t); 111 | if (temp < 0.025) { 112 | col += WBCOL2 * 0.5; 113 | } 114 | t += max(temp, 0.01); 115 | y++; 116 | } 117 | 118 | col += ((2.0 + uv.x) * WBCOL2) + (y / 625.0); 119 | col += gennoise(dir.xz) * 0.5; 120 | col *= 1.0 - uv.y * 0.5; 121 | 122 | // Lift brightness for fog visibility 123 | col *= 0.1; 124 | 125 | // Soft gamma 126 | col = pow(col, vec3(0.717)); 127 | 128 | fragColor = vec4(col, 1.0); 129 | } 130 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: "Version number (e.g., 0.4.4)" 11 | required: true 12 | type: string 13 | 14 | jobs: 15 | release: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: write 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Set version 27 | id: version 28 | run: | 29 | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then 30 | echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT 31 | echo "TAG=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT 32 | else 33 | TAG=${GITHUB_REF#refs/tags/} 34 | VERSION=${TAG#v} 35 | echo "VERSION=$VERSION" >> $GITHUB_OUTPUT 36 | echo "TAG=$TAG" >> $GITHUB_OUTPUT 37 | fi 38 | 39 | - name: Install dependencies 40 | run: | 41 | sudo apt-get update 42 | sudo apt-get install -y \ 43 | meson \ 44 | ninja-build \ 45 | wayland-protocols \ 46 | libwayland-dev \ 47 | libpng-dev \ 48 | libjpeg-dev \ 49 | libgles2-mesa-dev \ 50 | libegl1-mesa-dev \ 51 | libx11-dev \ 52 | libxrandr-dev \ 53 | pkg-config \ 54 | gcc 55 | 56 | - name: Build neowall 57 | run: | 58 | meson setup build 59 | ninja -C build 60 | 61 | - name: Create tarball 62 | run: | 63 | mkdir -p release 64 | tar -czf release/neowall-${{ steps.version.outputs.VERSION }}.tar.gz \ 65 | --exclude='.git' \ 66 | --exclude='release' \ 67 | --exclude='.github' \ 68 | --exclude='build' \ 69 | --transform "s,^,neowall-${{ steps.version.outputs.VERSION }}/," \ 70 | * 71 | 72 | - name: Generate changelog 73 | id: changelog 74 | run: | 75 | if [ "${{ github.event_name }}" = "push" ]; then 76 | # Get previous tag 77 | PREV_TAG=$(git describe --abbrev=0 --tags ${{ steps.version.outputs.TAG }}^ 2>/dev/null || echo "") 78 | if [ -n "$PREV_TAG" ]; then 79 | echo "CHANGELOG<> $GITHUB_OUTPUT 80 | git log $PREV_TAG..${{ steps.version.outputs.TAG }} --pretty=format:"- %s (%h)" >> $GITHUB_OUTPUT 81 | echo "" >> $GITHUB_OUTPUT 82 | echo "EOF" >> $GITHUB_OUTPUT 83 | else 84 | echo "CHANGELOG=Initial release" >> $GITHUB_OUTPUT 85 | fi 86 | else 87 | echo "CHANGELOG=Manual release of version ${{ steps.version.outputs.VERSION }}" >> $GITHUB_OUTPUT 88 | fi 89 | 90 | - name: Create GitHub Release 91 | uses: softprops/action-gh-release@v1 92 | with: 93 | tag_name: ${{ steps.version.outputs.TAG }} 94 | name: Release ${{ steps.version.outputs.TAG }} 95 | body: | 96 | ## NeoWall ${{ steps.version.outputs.VERSION }} 97 | 98 | ### Changes 99 | ${{ steps.changelog.outputs.CHANGELOG }} 100 | 101 | ### Installation 102 | 103 | #### Arch Linux (AUR) 104 | ```bash 105 | yay -S neowall-git 106 | ``` 107 | 108 | #### From source 109 | ```bash 110 | tar -xzf neowall-${{ steps.version.outputs.VERSION }}.tar.gz 111 | cd neowall-${{ steps.version.outputs.VERSION }} 112 | meson setup build 113 | ninja -C build 114 | sudo ninja -C build install 115 | ``` 116 | files: | 117 | release/neowall-${{ steps.version.outputs.VERSION }}.tar.gz 118 | draft: false 119 | prerelease: false 120 | env: 121 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 122 | -------------------------------------------------------------------------------- /.github/TOPICS.md: -------------------------------------------------------------------------------- 1 | # GitHub Topics for NeoWall 2 | 3 | ## How to Add Topics 4 | 5 | 1. Go to your repository on GitHub 6 | 2. Click the ⚙️ gear icon next to "About" (top right) 7 | 3. Add these topics in the "Topics" field 8 | 4. Click "Save changes" 9 | 10 | ## Recommended Topics (Add All 20) 11 | 12 | ### Core Functionality 13 | - `wayland` - Primary platform 14 | - `wallpaper` - Main feature 15 | - `wallpaper-manager` - Category 16 | - `daemon` - Architecture type 17 | - `background` - Related feature 18 | 19 | ### Supported Compositors 20 | - `sway` - Sway window manager 21 | - `hyprland` - Hyprland compositor 22 | - `wlroots` - Base protocol library 23 | - `compositor` - General category 24 | 25 | ### Technical Stack 26 | - `c` - Programming language 27 | - `opengl` - Graphics API 28 | - `egl` - Graphics interface 29 | - `wayland-protocols` - Protocol implementation 30 | 31 | ### User Categories 32 | - `linux` - Operating system 33 | - `desktop-customization` - Use case 34 | - `ricing` - Community term 35 | - `unix` - Platform family 36 | 37 | ### Features 38 | - `multi-monitor` - Key feature 39 | - `hot-reload` - Key feature 40 | - `transitions` - Key feature 41 | 42 | ## Additional Topics (If Space Available) 43 | 44 | ### More Specific Tags 45 | - `wayland-compositor` 46 | - `wlr-layer-shell` 47 | - `desktop-environment` 48 | - `screensaver` 49 | - `display-manager` 50 | - `system-daemon` 51 | - `image-viewer` 52 | - `opengl-es` 53 | 54 | ### Alternative/Related Tools 55 | - `wpaperd-alternative` 56 | - `swaybg-alternative` 57 | - `hyprpaper-alternative` 58 | 59 | ### Development 60 | - `systems-programming` 61 | - `low-level` 62 | - `performance` 63 | - `minimal-dependencies` 64 | 65 | ### Platform Specific 66 | - `arch-linux` 67 | - `aur` 68 | - `nixos` 69 | - `gentoo` 70 | 71 | ## Topic Selection Strategy 72 | 73 | **Priority 1 (MUST ADD):** wayland, wallpaper, sway, hyprland, c, daemon, linux 74 | 75 | **Priority 2 (HIGHLY RECOMMENDED):** wlroots, compositor, multi-monitor, ricing, desktop-customization 76 | 77 | **Priority 3 (NICE TO HAVE):** opengl, egl, unix, hot-reload, transitions 78 | 79 | **Why Topics Matter:** 80 | - GitHub search algorithm heavily weights topics 81 | - Each topic = another search result pathway 82 | - Users browse by topics to discover new projects 83 | - Topics appear in "related repositories" suggestions 84 | - Better categorization = better discoverability 85 | 86 | ## Topic SEO Impact 87 | 88 | ### High Impact Topics (Most Searched) 89 | 1. `wayland` - 10,000+ searches/month 90 | 2. `wallpaper` - 8,000+ searches/month 91 | 3. `linux` - 50,000+ searches/month 92 | 4. `sway` - 2,000+ searches/month 93 | 5. `hyprland` - 3,000+ searches/month (growing fast) 94 | 95 | ### Niche Topics (Targeted Users) 96 | 1. `ricing` - Highly engaged community 97 | 2. `multi-monitor` - Specific pain point 98 | 3. `wlroots` - Technical audience 99 | 4. `compositor` - Developer audience 100 | 101 | ### Long-tail Topics (Discovery) 102 | 1. `desktop-customization` - Casual users 103 | 2. `wayland-protocols` - Educational searches 104 | 3. `hot-reload` - Feature-specific searches 105 | 106 | ## Competitor Analysis 107 | 108 | **wpaperd topics:** wayland, wallpaper, rust, daemon 109 | **swaybg topics:** wayland, sway, wallpaper 110 | **hyprpaper topics:** wayland, hyprland, wallpaper 111 | 112 | **Your advantage:** More comprehensive tagging = better discoverability 113 | 114 | ## Action Items 115 | 116 | - [ ] Add all Priority 1 topics immediately 117 | - [ ] Add Priority 2 topics 118 | - [ ] Fill remaining slots with Priority 3 119 | - [ ] Monitor GitHub Insights to see which topics drive traffic 120 | - [ ] Adjust topics based on analytics after 2-4 weeks 121 | 122 | ## Notes 123 | 124 | - GitHub allows up to 20 topics per repository 125 | - Topics are case-insensitive and use hyphens for spaces 126 | - Popular topics get auto-suggested in search 127 | - Topics appear in your repo's metadata for SEO crawlers 128 | - Topics are clickable - users can explore all repos with that topic 129 | 130 | --- 131 | 132 | **Last Updated:** 2024-10-21 133 | **Max Topics Allowed:** 20 134 | **Current Recommendation:** Use all 20 slots for maximum visibility -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: ✨ Feature Request 2 | description: Suggest a new feature or enhancement for Staticwall 3 | title: "[Feature]: " 4 | labels: ["enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for suggesting a feature! 10 | 11 | Remember: Staticwall sets wallpapers until it doesn't. Let's make sure it keeps doing that (with style). 12 | 13 | - type: textarea 14 | id: problem 15 | attributes: 16 | label: Problem Statement 17 | description: What problem does this feature solve? Is your feature request related to a problem? 18 | placeholder: "I'm frustrated when... / It would be helpful if..." 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: solution 24 | attributes: 25 | label: Proposed Solution 26 | description: Describe the feature or enhancement you'd like to see 27 | placeholder: "Add support for... / Implement a way to..." 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | id: alternatives 33 | attributes: 34 | label: Alternatives Considered 35 | description: Have you considered any alternative solutions or workarounds? 36 | placeholder: "I've tried... / Another approach could be..." 37 | 38 | - type: textarea 39 | id: examples 40 | attributes: 41 | label: Examples or Mockups 42 | description: | 43 | If applicable, add examples, mockups, or references to similar features in other tools 44 | (You can drag and drop images here) 45 | placeholder: "Similar to how wpaperd does X... / Example config snippet..." 46 | 47 | - type: dropdown 48 | id: category 49 | attributes: 50 | label: Feature Category 51 | description: Which category does this feature fall under? 52 | options: 53 | - Display Modes (new ways to show wallpapers) 54 | - Transitions (new animation effects) 55 | - Image Formats (support for new file types) 56 | - Configuration (config options or syntax) 57 | - Performance (optimization) 58 | - Multi-Monitor (monitor-specific features) 59 | - CLI (command-line interface) 60 | - Integration (with other tools/services) 61 | - Documentation 62 | - Other 63 | validations: 64 | required: true 65 | 66 | - type: dropdown 67 | id: priority 68 | attributes: 69 | label: Priority (Your Opinion) 70 | description: How important is this feature to you? 71 | options: 72 | - Critical (blocking my workflow) 73 | - High (would significantly improve my experience) 74 | - Medium (nice to have) 75 | - Low (minor improvement) 76 | validations: 77 | required: true 78 | 79 | - type: textarea 80 | id: usecase 81 | attributes: 82 | label: Use Case 83 | description: How would you use this feature? What's your workflow? 84 | placeholder: | 85 | I use Staticwall with a 3-monitor setup where... 86 | This feature would help me... 87 | 88 | - type: checkboxes 89 | id: contribution 90 | attributes: 91 | label: Contribution 92 | options: 93 | - label: I would be willing to implement this feature (with guidance) 94 | - label: I would be willing to test this feature 95 | - label: I would be willing to help with documentation 96 | 97 | - type: textarea 98 | id: additional 99 | attributes: 100 | label: Additional Context 101 | description: Any other context, screenshots, or references about the feature request 102 | placeholder: "Links to discussions, similar implementations, technical considerations..." 103 | 104 | - type: checkboxes 105 | id: checks 106 | attributes: 107 | label: Pre-submission Checklist 108 | options: 109 | - label: I have searched existing issues and discussions 110 | required: true 111 | - label: This feature aligns with Staticwall's goals (reliable, minimal, Wayland-focused) 112 | required: true 113 | - label: I have clearly described the problem and proposed solution 114 | required: true 115 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: Report a bug or crash in Staticwall 3 | title: "[Bug]: " 4 | labels: ["bug", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to report a bug! 10 | 11 | **"Sets wallpapers until it... doesn't"** - Well, it looks like we hit the "doesn't" part. Let's fix it. 12 | 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Bug Description 17 | description: A clear description of what went wrong 18 | placeholder: "Staticwall crashed when I tried to..." 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: reproduction 24 | attributes: 25 | label: Steps to Reproduce 26 | description: How can we reproduce this issue? 27 | placeholder: | 28 | 1. Start staticwall with `staticwall -d` 29 | 2. Change wallpaper to... 30 | 3. See error 31 | validations: 32 | required: true 33 | 34 | - type: textarea 35 | id: expected 36 | attributes: 37 | label: Expected Behavior 38 | description: What did you expect to happen? 39 | placeholder: "Wallpaper should change smoothly" 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | id: actual 45 | attributes: 46 | label: Actual Behavior 47 | description: What actually happened? 48 | placeholder: "Staticwall crashed / froze / displayed wrong wallpaper" 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | id: logs 54 | attributes: 55 | label: Logs 56 | description: | 57 | Run with verbose logging: `staticwall -v` 58 | Or check logs: `journalctl --user -u staticwall.service -n 50` 59 | render: shell 60 | placeholder: | 61 | Paste relevant logs here... 62 | 63 | - type: input 64 | id: version 65 | attributes: 66 | label: Staticwall Version 67 | description: Run `staticwall --version` 68 | placeholder: "v0.1.0" 69 | validations: 70 | required: true 71 | 72 | - type: dropdown 73 | id: compositor 74 | attributes: 75 | label: Wayland Compositor 76 | description: Which compositor are you using? 77 | options: 78 | - Sway 79 | - Hyprland 80 | - River 81 | - Wayfire 82 | - Newm 83 | - Other (specify below) 84 | validations: 85 | required: true 86 | 87 | - type: input 88 | id: compositor-version 89 | attributes: 90 | label: Compositor Version 91 | description: "e.g., `sway --version` or `hyprctl version`" 92 | placeholder: "sway version 1.8" 93 | 94 | - type: dropdown 95 | id: os 96 | attributes: 97 | label: Operating System 98 | options: 99 | - Arch Linux 100 | - Ubuntu 101 | - Debian 102 | - Fedora 103 | - NixOS 104 | - Gentoo 105 | - Void Linux 106 | - Other (specify below) 107 | validations: 108 | required: true 109 | 110 | - type: input 111 | id: os-version 112 | attributes: 113 | label: OS Version 114 | placeholder: "e.g., Ubuntu 24.04, Arch (2024-01-15)" 115 | 116 | - type: textarea 117 | id: config 118 | attributes: 119 | label: Configuration File 120 | description: Share your `~/.config/staticwall/config.vibe` (remove sensitive paths if needed) 121 | render: vibe 122 | placeholder: | 123 | default { 124 | path ~/Pictures/wallpaper.png 125 | mode fill 126 | } 127 | 128 | - type: dropdown 129 | id: installation 130 | attributes: 131 | label: Installation Method 132 | options: 133 | - Built from source (make) 134 | - AUR (staticwall-git) 135 | - Package manager 136 | - Other 137 | 138 | - type: textarea 139 | id: additional 140 | attributes: 141 | label: Additional Context 142 | description: | 143 | - Number of monitors? 144 | - Using transitions? 145 | - Does it happen consistently or randomly? 146 | - Any recent system updates? 147 | placeholder: "Any other context about the problem..." 148 | 149 | - type: checkboxes 150 | id: checks 151 | attributes: 152 | label: Pre-submission Checklist 153 | options: 154 | - label: I have searched existing issues for duplicates 155 | required: true 156 | - label: I have included the output of `staticwall --version` 157 | required: true 158 | - label: I have included relevant logs from `staticwall -v` 159 | required: true 160 | - label: I have included my configuration file 161 | required: false 162 | -------------------------------------------------------------------------------- /src/textures/blue_noise.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "neowall.h" 5 | 6 | /* Generate blue noise texture 7 | * Blue noise has better distribution than white noise - useful for dithering 8 | * and better visual quality in shaders 9 | */ 10 | 11 | static unsigned int xorshift32(unsigned int *state) { 12 | unsigned int x = *state; 13 | x ^= x << 13; 14 | x ^= x >> 17; 15 | x ^= x << 5; 16 | *state = x; 17 | return x; 18 | } 19 | 20 | static float random_float(unsigned int *state) { 21 | return (float)xorshift32(state) / (float)0xFFFFFFFF; 22 | } 23 | 24 | /* Simple blue noise approximation using void-and-cluster algorithm */ 25 | static void generate_blue_noise(unsigned char *data, int width, int height) { 26 | int total_pixels = width * height; 27 | unsigned char *binary = calloc(total_pixels, 1); 28 | float *energy = malloc(total_pixels * sizeof(float)); 29 | 30 | if (!binary || !energy) { 31 | free(binary); 32 | free(energy); 33 | return; 34 | } 35 | 36 | // Initialize with random state 37 | unsigned int seed = 12345; 38 | 39 | // Simplified blue noise generation 40 | // Calculate energy for each pixel based on distance to other set pixels 41 | for (int i = 0; i < total_pixels; i++) { 42 | energy[i] = random_float(&seed); 43 | } 44 | 45 | // Sort-based approach for better distribution 46 | for (int threshold = 0; threshold < 256; threshold++) { 47 | for (int i = 0; i < total_pixels; i++) { 48 | float noise_val = random_float(&seed); 49 | 50 | // Add spatial correlation 51 | int x = i % width; 52 | int y = i / width; 53 | 54 | for (int dy = -2; dy <= 2; dy++) { 55 | for (int dx = -2; dx <= 2; dx++) { 56 | int nx = (x + dx + width) % width; 57 | int ny = (y + dy + height) % height; 58 | int ni = ny * width + nx; 59 | 60 | if (binary[ni]) { 61 | float dist = sqrtf((float)(dx*dx + dy*dy)); 62 | if (dist > 0.0f) { 63 | noise_val += 0.3f / (dist * dist); 64 | } 65 | } 66 | } 67 | } 68 | 69 | energy[i] = noise_val; 70 | } 71 | 72 | // Find pixel with lowest energy and set it 73 | float min_energy = 1e10f; 74 | int min_idx = 0; 75 | for (int i = 0; i < total_pixels; i++) { 76 | if (!binary[i] && energy[i] < min_energy) { 77 | min_energy = energy[i]; 78 | min_idx = i; 79 | } 80 | } 81 | 82 | if (threshold < 255) { 83 | binary[min_idx] = 1; 84 | } 85 | 86 | // Convert binary pattern to grayscale value 87 | for (int i = 0; i < total_pixels; i++) { 88 | int count = 0; 89 | int x = i % width; 90 | int y = i / width; 91 | 92 | for (int dy = -1; dy <= 1; dy++) { 93 | for (int dx = -1; dx <= 1; dx++) { 94 | int nx = (x + dx + width) % width; 95 | int ny = (y + dy + height) % height; 96 | int ni = ny * width + nx; 97 | count += binary[ni]; 98 | } 99 | } 100 | 101 | data[i * 4 + 0] = (unsigned char)((count * 255) / 9); 102 | data[i * 4 + 1] = (unsigned char)((count * 255) / 9); 103 | data[i * 4 + 2] = (unsigned char)((count * 255) / 9); 104 | data[i * 4 + 3] = 255; 105 | } 106 | } 107 | 108 | free(binary); 109 | free(energy); 110 | } 111 | 112 | GLuint texture_create_blue_noise(int width, int height) { 113 | unsigned char *data = malloc(width * height * 4); 114 | if (!data) { 115 | return 0; 116 | } 117 | 118 | generate_blue_noise(data, width, height); 119 | 120 | GLuint texture; 121 | glGenTextures(1, &texture); 122 | glBindTexture(GL_TEXTURE_2D, texture); 123 | 124 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 125 | GL_RGBA, GL_UNSIGNED_BYTE, data); 126 | 127 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 128 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 129 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 130 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 131 | 132 | glGenerateMipmap(GL_TEXTURE_2D); 133 | 134 | free(data); 135 | 136 | return texture; 137 | } -------------------------------------------------------------------------------- /packaging/neowall.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | Neo 82 | 83 | 84 | 85 | 86 | 87 | 94 | Wall 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /examples/shaders/matrix_rain.glsl: -------------------------------------------------------------------------------- 1 | // Matrix Digital Rain - Textured Version 2 | // Works with iChannel0 texture or procedurally generated fallback 3 | // GLSL ES 1.0 compatible 4 | 5 | #define RAIN_SPEED 1.75 // Speed of rain droplets 6 | #define DROP_SIZE 3.0 // Higher value lowers the size of individual droplets 7 | 8 | // Simple hash function for randomness 9 | float rand(vec2 co) { 10 | return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); 11 | } 12 | 13 | // Generate random character appearance 14 | float rchar(vec2 outer, vec2 inner, float globalTime) { 15 | vec2 seed = floor(inner * 4.0) + outer.y; 16 | 17 | // Some columns update faster 18 | if (rand(vec2(outer.y, 23.0)) > 0.98) { 19 | seed += floor((globalTime + rand(vec2(outer.y, 49.0))) * 3.0); 20 | } 21 | 22 | return float(rand(seed) > 0.5); 23 | } 24 | 25 | // Procedural noise fallback for when texture is dark/empty 26 | float noise(vec2 p) { 27 | vec2 i = floor(p); 28 | vec2 f = fract(p); 29 | f = f * f * (3.0 - 2.0 * f); 30 | 31 | float a = rand(i); 32 | float b = rand(i + vec2(1.0, 0.0)); 33 | float c = rand(i + vec2(0.0, 1.0)); 34 | float d = rand(i + vec2(1.0, 1.0)); 35 | 36 | return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); 37 | } 38 | 39 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 40 | vec2 position = fragCoord.xy / iResolution.xy; 41 | vec2 uv = vec2(position.x, position.y); 42 | position.x /= iResolution.x / iResolution.y; 43 | float globalTime = iTime * RAIN_SPEED; 44 | 45 | float scaledown = DROP_SIZE; 46 | vec4 result = vec4(0.0); 47 | 48 | // First rain layer 49 | float rx = fragCoord.x / (40.0 * scaledown); 50 | float mx = 40.0 * scaledown * fract(position.x * 30.0 * scaledown); 51 | 52 | if (mx > 12.0 * scaledown) { 53 | result = vec4(0.0); 54 | } else { 55 | float x = floor(rx); 56 | float r1x = floor(fragCoord.x / 15.0); 57 | 58 | float ry = position.y * 600.0 + rand(vec2(x, x * 3.0)) * 100000.0 + 59 | globalTime * rand(vec2(r1x, 23.0)) * 120.0; 60 | float my = mod(ry, 15.0); 61 | 62 | if (my > 12.0 * scaledown) { 63 | result = vec4(0.0); 64 | } else { 65 | float y = floor(ry / 15.0); 66 | float b = rchar(vec2(rx, floor(ry / 15.0)), vec2(mx, my) / 12.0, globalTime); 67 | float col = max(mod(-y, 24.0) - 4.0, 0.0) / 20.0; 68 | 69 | vec3 c; 70 | if (col < 0.8) { 71 | c = vec3(0.0, col / 0.8, 0.0); 72 | } else { 73 | c = mix(vec3(0.0, 1.0, 0.0), vec3(1.0), (col - 0.8) / 0.2); 74 | } 75 | 76 | result = vec4(c * b, 1.0); 77 | } 78 | } 79 | 80 | // Second rain layer (offset for depth) 81 | position.x += 0.05; 82 | 83 | scaledown = DROP_SIZE; 84 | rx = fragCoord.x / (40.0 * scaledown); 85 | mx = 40.0 * scaledown * fract(position.x * 30.0 * scaledown); 86 | 87 | if (mx > 12.0 * scaledown) { 88 | result += vec4(0.0); 89 | } else { 90 | float x = floor(rx); 91 | float r1x = floor(fragCoord.x / 12.0); 92 | 93 | float ry = position.y * 700.0 + rand(vec2(x, x * 3.0)) * 100000.0 + 94 | globalTime * rand(vec2(r1x, 23.0)) * 120.0; 95 | float my = mod(ry, 15.0); 96 | 97 | if (my > 12.0 * scaledown) { 98 | result += vec4(0.0); 99 | } else { 100 | float y = floor(ry / 15.0); 101 | float b = rchar(vec2(rx, floor(ry / 15.0)), vec2(mx, my) / 12.0, globalTime); 102 | float col = max(mod(-y, 24.0) - 4.0, 0.0) / 20.0; 103 | 104 | vec3 c; 105 | if (col < 0.8) { 106 | c = vec3(0.0, col / 0.8, 0.0); 107 | } else { 108 | c = mix(vec3(0.0, 1.0, 0.0), vec3(1.0), (col - 0.8) / 0.2); 109 | } 110 | 111 | result += vec4(c * b, 1.0); 112 | } 113 | } 114 | 115 | // Sample texture from iChannel0 116 | vec3 texColor = texture2D(iChannel0, uv).rgb; 117 | float texBrightness = length(texColor); 118 | 119 | // If texture is too dark (like night_glow), use procedural noise instead 120 | if (texBrightness < 0.15) { 121 | // Use procedural noise 122 | float texNoise = noise(uv * 50.0 + vec2(0.0, iTime * 0.5)); 123 | texColor = vec3(0.0, texNoise, 0.0); 124 | texBrightness = texNoise; 125 | } 126 | 127 | // Combine rain with texture 128 | result.rgb = result.rgb * texBrightness + 0.22 * vec3(0.0, texColor.g, 0.0); 129 | 130 | // Add blue tint to shadows 131 | if (result.b < 0.5) { 132 | result.b = result.g * 0.5; 133 | } 134 | 135 | // Add subtle glow 136 | float glow = length(result.rgb) * 0.1; 137 | result.rgb += vec3(0.0, glow, glow * 0.3); 138 | 139 | fragColor = vec4(result.rgb, 1.0); 140 | } 141 | -------------------------------------------------------------------------------- /src/textures/abstract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "neowall.h" 5 | 6 | /* Generate abstract colorful texture 7 | * Creates a Voronoi-based abstract pattern useful for artistic backgrounds 8 | */ 9 | 10 | static float fract_abstract(float x) { 11 | return x - floorf(x); 12 | } 13 | 14 | static float hash_abstract(float n) __attribute__((unused)); 15 | static float hash_abstract(float n) { 16 | return fract_abstract(sinf(n) * 43758.5453123f); 17 | } 18 | 19 | static void hash22_abstract(float x, float y, float *out_x, float *out_y) { 20 | float n = x + y * 157.0f; 21 | *out_x = fract_abstract(sinf(n) * 43758.5453123f); 22 | *out_y = fract_abstract(cosf(n) * 73156.8493217f); 23 | } 24 | 25 | static float hash13_abstract(float x, float y, float z) { 26 | float n = x + y * 57.0f + z * 113.0f; 27 | return fract_abstract(sinf(n) * 43758.5453123f); 28 | } 29 | 30 | /* Voronoi distance field */ 31 | static float voronoi(float x, float y, float *cell_id) { 32 | float px = floorf(x); 33 | float py = floorf(y); 34 | float fx = fract_abstract(x); 35 | float fy = fract_abstract(y); 36 | 37 | float min_dist = 10.0f; 38 | float closest_id = 0.0f; 39 | 40 | for (int j = -1; j <= 1; j++) { 41 | for (int i = -1; i <= 1; i++) { 42 | float cell_x = px + (float)i; 43 | float cell_y = py + (float)j; 44 | 45 | float point_x, point_y; 46 | hash22_abstract(cell_x, cell_y, &point_x, &point_y); 47 | 48 | float dx = (float)i + point_x - fx; 49 | float dy = (float)j + point_y - fy; 50 | float dist = sqrtf(dx * dx + dy * dy); 51 | 52 | if (dist < min_dist) { 53 | min_dist = dist; 54 | closest_id = hash13_abstract(cell_x, cell_y, 0.0f); 55 | } 56 | } 57 | } 58 | 59 | *cell_id = closest_id; 60 | return min_dist; 61 | } 62 | 63 | /* Generate colorful abstract pattern */ 64 | static void abstract_pattern(float u, float v, unsigned char *r, unsigned char *g, unsigned char *b) { 65 | float cell_id; 66 | float scale = 6.0f; 67 | 68 | // Primary voronoi pattern 69 | float dist1 = voronoi(u * scale, v * scale, &cell_id); 70 | 71 | // Secondary pattern at different scale 72 | float cell_id2; 73 | float dist2 = voronoi(u * scale * 2.3f + 100.0f, v * scale * 2.3f + 200.0f, &cell_id2); 74 | 75 | // Color based on cell ID 76 | float hue = cell_id * 6.28318f; 77 | float sat = 0.6f + cell_id2 * 0.4f; 78 | float val = 0.5f + dist1 * 0.5f; 79 | 80 | // HSV to RGB conversion 81 | float c = val * sat; 82 | float x = c * (1.0f - fabsf(fmodf(hue / 1.047198f, 2.0f) - 1.0f)); 83 | float m = val - c; 84 | 85 | float r_f = 0.0f, g_f = 0.0f, b_f = 0.0f; 86 | 87 | if (hue < 1.047198f) { 88 | r_f = c; g_f = x; b_f = 0.0f; 89 | } else if (hue < 2.094395f) { 90 | r_f = x; g_f = c; b_f = 0.0f; 91 | } else if (hue < 3.141593f) { 92 | r_f = 0.0f; g_f = c; b_f = x; 93 | } else if (hue < 4.188790f) { 94 | r_f = 0.0f; g_f = x; b_f = c; 95 | } else if (hue < 5.235988f) { 96 | r_f = x; g_f = 0.0f; b_f = c; 97 | } else { 98 | r_f = c; g_f = 0.0f; b_f = x; 99 | } 100 | 101 | // Add some variation with second pattern 102 | r_f = r_f * 0.7f + dist2 * 0.3f; 103 | g_f = g_f * 0.7f + (1.0f - dist2) * 0.3f; 104 | b_f = b_f * 0.7f + cell_id2 * 0.3f; 105 | 106 | *r = (unsigned char)((r_f + m) * 255.0f); 107 | *g = (unsigned char)((g_f + m) * 255.0f); 108 | *b = (unsigned char)((b_f + m) * 255.0f); 109 | } 110 | 111 | GLuint texture_create_abstract(int width, int height) { 112 | unsigned char *data = malloc(width * height * 4); 113 | if (!data) { 114 | return 0; 115 | } 116 | 117 | // Generate abstract pattern 118 | for (int y = 0; y < height; y++) { 119 | for (int x = 0; x < width; x++) { 120 | int idx = (y * width + x) * 4; 121 | 122 | float u = (float)x / (float)width; 123 | float v = (float)y / (float)height; 124 | 125 | unsigned char r, g, b; 126 | abstract_pattern(u, v, &r, &g, &b); 127 | 128 | data[idx + 0] = r; 129 | data[idx + 1] = g; 130 | data[idx + 2] = b; 131 | data[idx + 3] = 255; 132 | } 133 | } 134 | 135 | GLuint texture; 136 | glGenTextures(1, &texture); 137 | glBindTexture(GL_TEXTURE_2D, texture); 138 | 139 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 140 | GL_RGBA, GL_UNSIGNED_BYTE, data); 141 | 142 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 143 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 144 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 145 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 146 | 147 | glGenerateMipmap(GL_TEXTURE_2D); 148 | 149 | free(data); 150 | 151 | return texture; 152 | } -------------------------------------------------------------------------------- /src/transitions/slide.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "neowall.h" 4 | #include "constants.h" 5 | #include "transitions.h" 6 | #include "shader.h" 7 | 8 | /* External function from render.c for calculating vertices */ 9 | extern void calculate_vertex_coords_for_image(struct output_state *output, 10 | struct image_data *image, 11 | float vertices[16]); 12 | 13 | /* Vertex shader for slide transition */ 14 | static const char *slide_vertex_shader_source = 15 | GLSL_VERSION_STRING 16 | "attribute vec2 position;\n" 17 | "attribute vec2 texcoord;\n" 18 | "varying vec2 v_texcoord;\n" 19 | "void main() {\n" 20 | " gl_Position = vec4(position, 0.0, 1.0);\n" 21 | " v_texcoord = texcoord;\n" 22 | "}\n"; 23 | 24 | /* Fragment shader for slide transition */ 25 | static const char *slide_fragment_shader_source = 26 | GLSL_VERSION_STRING 27 | "precision mediump float;\n" 28 | "varying vec2 v_texcoord;\n" 29 | "uniform sampler2D texture0;\n" 30 | "uniform float alpha;\n" 31 | "void main() {\n" 32 | " vec4 color = texture2D(texture0, v_texcoord);\n" 33 | " gl_FragColor = vec4(color.rgb, color.a * alpha);\n" 34 | "}\n"; 35 | 36 | /** 37 | * Create shader program for slide transition 38 | * @param program Pointer to store the created program ID 39 | * @return true on success, false on failure 40 | */ 41 | bool shader_create_slide_program(GLuint *program) { 42 | return shader_create_program_from_sources( 43 | slide_vertex_shader_source, 44 | slide_fragment_shader_source, 45 | program 46 | ); 47 | } 48 | 49 | /** 50 | * Helper function to render slide transition 51 | * 52 | * @param output Output state containing images and textures 53 | * @param progress Transition progress (0.0 to 1.0) 54 | * @param slide_left true for left slide, false for right slide 55 | * @return true on success, false on error 56 | */ 57 | static bool render_slide_transition(struct output_state *output, float progress, bool slide_left) { 58 | if (!output || !output->current_image || !output->next_image) { 59 | return false; 60 | } 61 | 62 | if (output->texture == 0 || output->next_texture == 0) { 63 | return false; 64 | } 65 | 66 | /* Initialize transition context */ 67 | transition_context_t ctx; 68 | if (!transition_begin(&ctx, output, output->program)) { 69 | return false; 70 | } 71 | 72 | /* Calculate slide offset */ 73 | float offset = slide_left ? progress : -progress; 74 | 75 | /* === DRAW OLD IMAGE (sliding out) === */ 76 | 77 | /* Create custom vertices for old image sliding out */ 78 | float old_vertices[16]; 79 | for (int i = 0; i < 16; i++) { 80 | old_vertices[i] = ctx.vertices[i]; 81 | } 82 | 83 | /* Adjust X positions based on slide direction */ 84 | for (int i = 0; i < 4; i++) { 85 | old_vertices[i * 4] = old_vertices[i * 4] - (offset * 2.0f); 86 | } 87 | 88 | /* Disable alpha channel writes - force opaque output for old image */ 89 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); 90 | 91 | /* Draw old image */ 92 | if (!transition_draw_textured_quad(&ctx, output->next_texture, 1.0f, old_vertices)) { 93 | transition_end(&ctx); 94 | return false; 95 | } 96 | 97 | /* === DRAW NEW IMAGE (sliding in) === */ 98 | 99 | /* Create custom vertices for new image sliding in */ 100 | float new_vertices[16]; 101 | for (int i = 0; i < 16; i++) { 102 | new_vertices[i] = ctx.vertices[i]; 103 | } 104 | 105 | /* Adjust position to slide in from opposite side */ 106 | float slide_in_offset = slide_left ? (1.0f - progress) : -(1.0f - progress); 107 | 108 | for (int i = 0; i < 4; i++) { 109 | new_vertices[i * 4] = new_vertices[i * 4] + (slide_in_offset * 2.0f); 110 | } 111 | 112 | /* Draw new image */ 113 | if (!transition_draw_textured_quad(&ctx, output->texture, 1.0f, new_vertices)) { 114 | transition_end(&ctx); 115 | return false; 116 | } 117 | 118 | /* Re-enable alpha channel writes */ 119 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 120 | 121 | /* Clean up and return */ 122 | transition_end(&ctx); 123 | return true; 124 | } 125 | 126 | /** 127 | * Slide Left Transition 128 | * 129 | * New wallpaper slides in from right to left while old wallpaper slides out. 130 | * Both images maintain their display mode throughout the transition. 131 | * 132 | * @param output Output state containing images and textures 133 | * @param progress Transition progress (0.0 to 1.0) 134 | * @return true on success, false on error 135 | */ 136 | bool transition_slide_left_render(struct output_state *output, float progress) { 137 | return render_slide_transition(output, progress, true); 138 | } 139 | 140 | /** 141 | * Slide Right Transition 142 | * 143 | * New wallpaper slides in from left to right while old wallpaper slides out. 144 | * Both images maintain their display mode throughout the transition. 145 | * 146 | * @param output Output state containing images and textures 147 | * @param progress Transition progress (0.0 to 1.0) 148 | * @return true on success, false on error 149 | */ 150 | bool transition_slide_right_render(struct output_state *output, float progress) { 151 | return render_slide_transition(output, progress, false); 152 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | NeoWall 3 |

4 | 5 |

6 | Live GPU shaders as your wallpaper. Yes, really. 7 |

8 | 9 |

10 | Build 11 | License 12 | Release 13 |

14 | 15 |

16 | 17 | 18 |

19 | 20 | --- 21 | 22 | ## What is this? 23 | 24 | NeoWall renders **Shadertoy shaders** directly on your desktop. Wayland, X11, multi-monitor, 60fps, ~2% CPU. 25 | 26 | ```bash 27 | neowall # That's it. You now have an animated wallpaper. 28 | ``` 29 | 30 | ## Install 31 | 32 | **Arch (AUR):** 33 | ```bash 34 | yay -S neowall-git 35 | ``` 36 | 37 | **Build it yourself:** 38 | ```bash 39 | git clone https://github.com/1ay1/neowall && cd neowall 40 | meson setup build && ninja -C build 41 | sudo ninja -C build install 42 | ``` 43 | 44 |
45 | Dependencies 46 | 47 | ```bash 48 | # Debian/Ubuntu 49 | sudo apt install build-essential meson ninja-build libwayland-dev \ 50 | libgles2-mesa-dev libpng-dev libjpeg-dev wayland-protocols \ 51 | libx11-dev libxrandr-dev 52 | 53 | # Arch 54 | sudo pacman -S base-devel meson ninja wayland mesa libpng libjpeg-turbo \ 55 | wayland-protocols libx11 libxrandr 56 | 57 | # Fedora 58 | sudo dnf install gcc meson ninja-build wayland-devel mesa-libGLES-devel \ 59 | libpng-devel libjpeg-turbo-devel wayland-protocols-devel \ 60 | libX11-devel libXrandr-devel 61 | ``` 62 |
63 | 64 | ## Config 65 | 66 | Lives at `~/.config/neowall/config.vibe` 67 | 68 | **Shader wallpaper:** 69 | ``` 70 | default { 71 | shader retro_wave.glsl 72 | shader_speed 0.8 73 | } 74 | ``` 75 | 76 | **Image slideshow:** 77 | ``` 78 | default { 79 | path ~/Pictures/Wallpapers/ 80 | duration 300 81 | transition glitch 82 | } 83 | ``` 84 | 85 | **Multi-monitor:** 86 | ``` 87 | output { 88 | DP-1 { shader matrix_rain.glsl } 89 | HDMI-A-1 { path ~/Pictures/ duration 600 } 90 | } 91 | ``` 92 | 93 | ## Commands 94 | 95 | ```bash 96 | neowall # start 97 | neowall kill # stop 98 | neowall next # next wallpaper 99 | neowall pause # pause 100 | neowall resume # resume 101 | neowall list # show cycle 102 | neowall set 3 # jump to index 3 103 | neowall current # what's playing? 104 | ``` 105 | 106 | ## Shaders 107 | 108 | 30+ included. Some highlights: 109 | 110 | | Vibe | Shaders | 111 | |------|---------| 112 | | 🌆 Synthwave | `retro_wave` `synthwave` `neonwave_sunrise` | 113 | | 🌊 Nature | `ocean_waves` `aurora` `sunrise` `moon_ocean` | 114 | | 💻 Cyber | `matrix_rain` `matrix_real` `glowing_triangles` | 115 | | 🔮 Abstract | `fractal_land` `plasma` `mandelbrot` | 116 | | 🌌 Space | `star_next` `starship_reentry` `cross_galactic_ocean` | 117 | 118 | **Use any Shadertoy shader:** 119 | 1. Copy code from shadertoy.com 120 | 2. Save to `~/.config/neowall/shaders/cool.glsl` 121 | 3. Config: `shader cool.glsl` 122 | 4. Done 123 | 124 | ## GLEditor 125 | 126 | [**GLEditor**](https://github.com/1ay1/gleditor) — live shader editor that exports directly to NeoWall. Write, preview, one-click install. 127 | 128 | ```bash 129 | yay -S gleditor-git 130 | ``` 131 | 132 | ## How it works 133 | 134 | ``` 135 | ┌────────────────────────────────────────┐ 136 | │ NeoWall Daemon │ 137 | ├────────────────────────────────────────┤ 138 | │ Config Parser → Event Loop → Shaders │ 139 | ├────────────────────────────────────────┤ 140 | │ Wayland (layer-shell) │ X11 (EWMH) │ 141 | ├────────────────────────────────────────┤ 142 | │ EGL / OpenGL 3.3 │ 143 | └────────────────────────────────────────┘ 144 | ``` 145 | 146 | - Pure C, single binary 147 | - GPU does the work, CPU chills 148 | - timerfd/signalfd — no busy loops 149 | - Same code runs everywhere 150 | 151 | ## vs Others 152 | 153 | | | NeoWall | swww | mpvpaper | hyprpaper | 154 | |-|---------|------|----------|-----------| 155 | | Live shaders | ✅ | ❌ | ❌ | ❌ | 156 | | Shadertoy | ✅ | ❌ | ❌ | ❌ | 157 | | Videos | ❌ | GIFs | ✅ | ❌ | 158 | | Images | ✅ | ✅ | ❌ | ✅ | 159 | | X11 | ✅ | ❌ | ❌ | ❌ | 160 | | Wayland | ✅ | ✅ | ✅ | ✅ | 161 | | Interactive | ✅ | ❌ | ❌ | ❌ | 162 | 163 | **NeoWall = only Linux tool for live GPU shader wallpapers.** 164 | 165 | ## Caveats 166 | 167 | - **KDE Plasma**: Desktop icons might hide. Use a dock. 168 | - **No video wallpapers**: Use mpvpaper for that. 169 | 170 | ## Contributing 171 | 172 | ```bash 173 | meson setup build --buildtype=debug 174 | ninja -C build 175 | ./build/neowall -f -v 176 | ``` 177 | 178 | PRs welcome: shaders, bug fixes, docs, testing. 179 | 180 | ## License 181 | 182 | MIT — do whatever you want. 183 | 184 | --- 185 | 186 |

187 | Bugs · Chat · ⭐ Star if cool 188 |

189 | -------------------------------------------------------------------------------- /examples/shaders/sunrise.glsl: -------------------------------------------------------------------------------- 1 | // Man has demonstrated that he is master of everything except his own nature 2 | //--------------------------------------------------------------------------- 3 | 4 | // One morning we woke up when the sky was still dark. 5 | // We walked half an hour through the forest, 6 | // to reach the other side of the island, 7 | // where the beach is facing the rising sun. 8 | // The sun was already there, one half over the horizon. 9 | // The sky was on fire. 10 | // We swum in the sea, staring at the rising sun. 11 | 12 | // visual parameters ------------------- 13 | const vec3 sunColor = vec3(1.5,.9,.7); 14 | const vec3 lightColor = vec3(1.,.8,.7); 15 | const vec3 darkColor = vec3(.2,.2,.3); 16 | const vec3 baseSkyColor = vec3(.6,.7,.8); 17 | const vec3 seaColor = vec3(.1,.3,.5); 18 | const vec3 seaLight = vec3(.1,.45,.55); 19 | //--------------------------------------- 20 | 21 | vec3 gamma( vec3 col, float g){ 22 | return pow(col,vec3(g)); 23 | } 24 | 25 | 26 | // clouds layered noise 27 | float noiseLayer(vec2 uv){ 28 | float t = (iTime+iMouse.x)/5.; 29 | uv.y -= t/60.; // clouds pass by 30 | float e = 0.; 31 | for(float j=1.; j<9.; j++){ 32 | // shift each layer in different directions 33 | float timeOffset = t*mod(j,2.989)*.02 - t*.015; 34 | e += 1.-texture(iChannel0, uv * (j*1.789) + j*159.45 + timeOffset).r / j ; 35 | } 36 | e /= 3.5; 37 | return e; 38 | } 39 | 40 | // waves layered noise 41 | float waterHeight(vec2 uv){ 42 | float t = (iTime+iMouse.x); 43 | float e = 0.; 44 | for(float j=1.; j<6.; j++){ 45 | // shift each layer in different directions 46 | float timeOffset = t*mod(j,.789)*.1 - t*.05; 47 | e += texture(iChannel1, uv * (j*1.789) + j*159.45 + timeOffset).r / j ; 48 | } 49 | e /= 6.; 50 | return e; 51 | } 52 | 53 | vec3 waterNormals(vec2 uv){ 54 | uv.x *= .25; 55 | float eps = 0.008; 56 | vec3 n=vec3( waterHeight(uv) - waterHeight(uv+vec2(eps,0.)), 57 | 1., 58 | waterHeight(uv) - waterHeight(uv+vec2(0.,eps))); 59 | return normalize(n); 60 | } 61 | 62 | 63 | vec3 drawSky( vec2 uv, vec2 uvInit){ 64 | 65 | float clouds = noiseLayer(uv); 66 | 67 | // clouds normals 68 | float eps = 0.1; 69 | vec3 n = vec3( clouds - noiseLayer(uv+vec2(eps,0.)), 70 | -.3, 71 | clouds - noiseLayer(uv+vec2(0.,eps))); 72 | n = normalize(n); 73 | 74 | // fake lighting 75 | float l = dot(n, normalize(vec3(uv.x,-.2,uv.y+.5))); 76 | l = clamp(l,0.,1.); 77 | 78 | // clouds color (color gradient from light) 79 | vec3 cloudColor = mix(baseSkyColor, darkColor, length(uvInit)*1.7); 80 | cloudColor = mix( cloudColor,sunColor, l ); 81 | 82 | // sky color (color gradient on Y) 83 | vec3 skyColor = mix(lightColor , baseSkyColor, clamp(uvInit.y*2.,0.,1.) ); 84 | skyColor = mix ( skyColor, darkColor, clamp(uvInit.y*3.-.8,0.,1.) ); 85 | skyColor = mix ( skyColor, sunColor, clamp(-uvInit.y*10.+1.1,0.,1.) ); 86 | 87 | // draw sun 88 | if(length(uvInit-vec2(0.,.04) )<.03){ 89 | skyColor += vec3(2.,1.,.8); 90 | } 91 | 92 | // mix clouds and sky 93 | float cloudMix = clamp(0.,1.,clouds*4.-8.); 94 | vec3 color = mix( cloudColor, skyColor, clamp(cloudMix,0.,1.) ); 95 | 96 | // draw islands on horizon 97 | uvInit.y = abs(uvInit.y); 98 | float islandHeight = texture(iChannel1, uvInit.xx/2.+.67).r/15. - uvInit.y + .978; 99 | islandHeight += texture(iChannel1, uvInit.xx*2.).r/60.; 100 | islandHeight = clamp(floor(islandHeight),0.,1.); 101 | vec3 landColor = mix(baseSkyColor, darkColor, length(uvInit)*1.5); 102 | color = mix(color, landColor, islandHeight); 103 | 104 | return color; 105 | } 106 | 107 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 108 | { 109 | // center uv around horizon and manage ratio 110 | vec2 uvInit = fragCoord.xy / iResolution.xy; 111 | uvInit.x -= .5; 112 | uvInit.x *= iResolution.x/iResolution.y; 113 | uvInit.y -= 0.35; 114 | 115 | // perspective deform 116 | vec2 uv = uvInit; 117 | uv.y -=.01; 118 | uv.y = abs(uv.y); 119 | uv.y = log(uv.y)/2.; 120 | uv.x *= 1.-uv.y; 121 | uv *= .2; 122 | 123 | vec3 col = vec3(1.,1.,1.); 124 | 125 | // draw water 126 | if(uvInit.y < 0.){ 127 | 128 | vec3 n = waterNormals(uv); 129 | 130 | // draw reflection of sky into water 131 | vec3 waterReflections = drawSky(uv+n.xz, uvInit+n.xz); 132 | 133 | // mask for fore-ground green light effect in water 134 | float transparency = dot(n, vec3(0.,.2,1.5)); 135 | transparency -= length ( (uvInit - vec2(0.,-.35)) * vec2(.2,1.) ); 136 | transparency = (transparency*12.+1.5); 137 | 138 | // add foreground water effect 139 | waterReflections = mix( waterReflections, seaColor, clamp(transparency,0.,1.) ); 140 | waterReflections = mix( waterReflections, seaLight, max(0.,transparency-1.5) ); 141 | 142 | col = waterReflections; 143 | 144 | // darken sea near horizon 145 | col = mix(col, col*vec3(.6,.8,1.), -uv.y); 146 | 147 | //sun specular 148 | col += max(0.,.02-abs(uv.x+n.x))* 8000. * vec3(1.,.7,.3) * -uv.y * max(0.,-n.z); 149 | 150 | }else{ 151 | 152 | // sky 153 | col = drawSky(uv, uvInit); 154 | } 155 | 156 | // sun flare & vignette 157 | col += vec3(1.,.8,.6) * (0.55-length(uvInit)) ; 158 | 159 | // "exposure" adjust 160 | col *= .75; 161 | col = gamma(col,1.3); 162 | 163 | fragColor = vec4(col,1.); 164 | } 165 | 166 | //-------------------------------------------------------------------- 167 | //There is no salvation in becoming adapted to a world which is crazy. 168 | -------------------------------------------------------------------------------- /include/neowall.h: -------------------------------------------------------------------------------- 1 | #ifndef NEOWALL_H 2 | #define NEOWALL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "version.h" 12 | #include "../src/output/output.h" 13 | #include "../src/config/config.h" 14 | 15 | /* Thread-safe atomic types for flags accessed from multiple threads */ 16 | typedef atomic_bool atomic_bool_t; 17 | typedef atomic_int atomic_int_t; 18 | 19 | #define MAX_PATH_LENGTH OUTPUT_MAX_PATH_LENGTH /* Compatibility alias */ 20 | #define MAX_OUTPUTS 16 21 | #define MAX_WALLPAPERS 256 22 | 23 | 24 | 25 | /* Forward declarations */ 26 | struct neowall_state; 27 | struct compositor_backend; 28 | 29 | /* Global application state */ 30 | struct neowall_state { 31 | /* ===== COMPOSITOR ABSTRACTION - ONLY INTERFACE ===== */ 32 | struct compositor_backend *compositor_backend; 33 | 34 | /* ===== EGL CONTEXT (PLATFORM-AGNOSTIC) ===== */ 35 | EGLDisplay egl_display; 36 | EGLContext egl_context; 37 | EGLConfig egl_config; 38 | 39 | /* ===== OUTPUTS ===== */ 40 | struct output_state *outputs; 41 | uint32_t output_count; 42 | 43 | /* ===== CONFIGURATION ===== */ 44 | char config_path[MAX_PATH_LENGTH]; 45 | 46 | /* ===== RUNTIME STATE ===== */ 47 | /* ALL flags must be atomic for thread safety */ 48 | atomic_bool_t running; /* Main loop running flag - accessed from signal handlers */ 49 | atomic_bool_t paused; /* Pause wallpaper cycling - set by signal handlers */ 50 | atomic_bool_t outputs_need_init; /* Flag when new outputs need initialization */ 51 | atomic_int_t next_requested; /* Counter for skip to next wallpaper requests */ 52 | atomic_int_t set_index_requested; /* Requested wallpaper index (-1 = no request) */ 53 | pthread_mutex_t state_mutex; /* Protects output list and config data */ 54 | pthread_rwlock_t output_list_lock; /* Read-write lock for output linked list traversal */ 55 | pthread_mutex_t state_file_lock; /* Mutex for state file I/O operations */ 56 | 57 | /* BUG FIX #9: LOCK ORDERING POLICY (to prevent deadlock) 58 | * ======================================================== 59 | * Always acquire locks in this order: 60 | * 1. output_list_lock (rwlock) 61 | * 2. state_mutex 62 | * 63 | * NEVER acquire them in reverse order! 64 | * 65 | * Correct example: 66 | * pthread_rwlock_rdlock(&state->output_list_lock); // 1st 67 | * pthread_mutex_lock(&state->state_mutex); // 2nd - OK 68 | * // ... critical section ... 69 | * pthread_mutex_unlock(&state->state_mutex); 70 | * pthread_rwlock_unlock(&state->output_list_lock); 71 | * 72 | * WRONG (will cause deadlock): 73 | * pthread_mutex_lock(&state->state_mutex); // 2nd first 74 | * pthread_rwlock_rdlock(&state->output_list_lock); // 1st second - DEADLOCK! 75 | * 76 | * Rationale: output_list_lock is the coarser-grained lock (protects the 77 | * entire list structure), while state_mutex is fine-grained (protects 78 | * individual fields). Acquiring coarse-grained locks first prevents 79 | * deadlock scenarios. 80 | *========================================================*/ 81 | 82 | /* ===== EVENT-DRIVEN TIMERS ===== */ 83 | int timer_fd; /* timerfd for next wallpaper cycle */ 84 | int wakeup_fd; /* eventfd for waking poll on internal events */ 85 | int signal_fd; /* signalfd for race-free signal handling */ 86 | 87 | /* ===== STATISTICS ===== */ 88 | uint64_t frames_rendered; 89 | uint64_t errors_count; 90 | }; 91 | 92 | /* Note: Compositor initialization is now handled via compositor_backend_init() 93 | * in compositor.h. Wayland/X11 specific code has been moved to their respective backends. */ 94 | 95 | /* EGL initialization */ 96 | bool egl_init(struct neowall_state *state); 97 | void egl_cleanup(struct neowall_state *state); 98 | void detect_gl_capabilities(struct neowall_state *state); 99 | 100 | /* Main loop */ 101 | void event_loop_run(struct neowall_state *state); 102 | void event_loop_stop(struct neowall_state *state); 103 | 104 | /* Utility functions */ 105 | uint64_t get_time_ms(void); 106 | const char *wallpaper_mode_to_string(enum wallpaper_mode mode); 107 | enum wallpaper_mode wallpaper_mode_from_string(const char *str); 108 | const char *transition_type_to_string(enum transition_type type); 109 | enum transition_type transition_type_from_string(const char *str); 110 | 111 | /* Logging */ 112 | #define LOG_LEVEL_ERROR 0 113 | #define LOG_LEVEL_WARN 1 114 | #define LOG_LEVEL_INFO 2 115 | #define LOG_LEVEL_DEBUG 3 116 | 117 | void log_error(const char *format, ...); 118 | void log_warn(const char *format, ...); 119 | void log_info(const char *format, ...); 120 | void log_debug(const char *format, ...); 121 | void log_set_level(int level); 122 | float ease_in_out_cubic(float t); 123 | 124 | /* State file functions */ 125 | const char *get_state_file_path(void); 126 | const char *get_cycle_list_file_path(void); 127 | bool write_wallpaper_state(const char *output_name, const char *wallpaper_path, 128 | const char *mode, int cycle_index, int cycle_total, 129 | const char *status); 130 | bool read_wallpaper_state(void); 131 | int restore_cycle_index_from_state(const char *output_name); 132 | bool write_cycle_list(const char *output_name, char **paths, size_t count, size_t current_index); 133 | bool read_cycle_list(void); 134 | 135 | /* Signal handling */ 136 | void signal_handler_init(struct neowall_state *state); 137 | void signal_handler_cleanup(void); 138 | 139 | #endif /* NEOWALL_H */ 140 | -------------------------------------------------------------------------------- /examples/shaders/saturday_torus.glsl: -------------------------------------------------------------------------------- 1 | // License CC0: Saturday Torus 2 | // Inspired by: https://www.istockphoto.com/photo/black-and-white-stripes-projection-on-torus-gm488221403-39181884 3 | 4 | #define PI 3.141592654 5 | #define TAU (2.0*PI) 6 | #define TIME iTime 7 | #define TTIME (TAU*TIME) 8 | #define RESOLUTION iResolution 9 | #define ROT(a) mat2(cos(a), sin(a), -sin(a), cos(a)) 10 | #define PCOS(x) (0.5+0.5*cos(x)) 11 | 12 | // License: MIT, author: Inigo Quilez, found: https://iquilezles.org/articles/intersectors 13 | float rayTorus(vec3 ro, vec3 rd, vec2 tor) { 14 | float po = 1.0; 15 | 16 | float Ra2 = tor.x*tor.x; 17 | float ra2 = tor.y*tor.y; 18 | 19 | float m = dot(ro,ro); 20 | float n = dot(ro,rd); 21 | 22 | // bounding sphere 23 | { 24 | float h = n*n - m + (tor.x+tor.y)*(tor.x+tor.y); 25 | if(h<0.0) return -1.0; 26 | //float t = -n-sqrt(h); // could use this to compute intersections from ro+t*rd 27 | } 28 | 29 | // find quartic equation 30 | float k = (m - ra2 - Ra2)/2.0; 31 | float k3 = n; 32 | float k2 = n*n + Ra2*rd.z*rd.z + k; 33 | float k1 = k*n + Ra2*ro.z*rd.z; 34 | float k0 = k*k + Ra2*ro.z*ro.z - Ra2*ra2; 35 | 36 | #ifndef TORUS_REDUCE_PRECISION 37 | // prevent |c1| from being too close to zero 38 | if(abs(k3*(k3*k3 - k2) + k1) < 0.01) 39 | { 40 | po = -1.0; 41 | float tmp=k1; k1=k3; k3=tmp; 42 | k0 = 1.0/k0; 43 | k1 = k1*k0; 44 | k2 = k2*k0; 45 | k3 = k3*k0; 46 | } 47 | #endif 48 | 49 | float c2 = 2.0*k2 - 3.0*k3*k3; 50 | float c1 = k3*(k3*k3 - k2) + k1; 51 | float c0 = k3*(k3*(-3.0*k3*k3 + 4.0*k2) - 8.0*k1) + 4.0*k0; 52 | 53 | 54 | c2 /= 3.0; 55 | c1 *= 2.0; 56 | c0 /= 3.0; 57 | 58 | float Q = c2*c2 + c0; 59 | float R = 3.0*c0*c2 - c2*c2*c2 - c1*c1; 60 | 61 | float h = R*R - Q*Q*Q; 62 | float z = 0.0; 63 | if(h < 0.0) { 64 | // 4 intersections 65 | float sQ = sqrt(Q); 66 | z = 2.0*sQ*cos(acos(R/(sQ*Q)) / 3.0); 67 | } else { 68 | // 2 intersections 69 | float sQ = pow(sqrt(h) + abs(R), 1.0/3.0); 70 | z = sign(R)*abs(sQ + Q/sQ); 71 | } 72 | z = c2 - z; 73 | 74 | float d1 = z - 3.0*c2; 75 | float d2 = z*z - 3.0*c0; 76 | if(abs(d1) < 1.0e-4) { 77 | if(d2 < 0.0) return -1.0; 78 | d2 = sqrt(d2); 79 | } else { 80 | if(d1 < 0.0) return -1.0; 81 | d1 = sqrt(d1/2.0); 82 | d2 = c1/d1; 83 | } 84 | 85 | //---------------------------------- 86 | 87 | float result = 1e20; 88 | 89 | h = d1*d1 - z + d2; 90 | if(h > 0.0) { 91 | h = sqrt(h); 92 | float t1 = -d1 - h - k3; t1 = (po<0.0)?2.0/t1:t1; 93 | float t2 = -d1 + h - k3; t2 = (po<0.0)?2.0/t2:t2; 94 | if(t1 > 0.0) result=t1; 95 | if(t2 > 0.0) result=min(result,t2); 96 | } 97 | 98 | h = d1*d1 - z - d2; 99 | if(h > 0.0) { 100 | h = sqrt(h); 101 | float t1 = d1 - h - k3; t1 = (po<0.0)?2.0/t1:t1; 102 | float t2 = d1 + h - k3; t2 = (po<0.0)?2.0/t2:t2; 103 | if(t1 > 0.0) result=min(result,t1); 104 | if(t2 > 0.0) result=min(result,t2); 105 | } 106 | 107 | return result; 108 | } 109 | 110 | // License: MIT, author: Inigo Quilez, found: https://iquilezles.org/articles/intersectors 111 | vec3 torusNormal(vec3 pos, vec2 tor) { 112 | return normalize(pos*(dot(pos,pos)- tor.y*tor.y - tor.x*tor.x*vec3(1.0,1.0,-1.0))); 113 | } 114 | 115 | // License: Unknown, author: Unknown, found: don't remember 116 | float tanh_approx(float x) { 117 | // Found this somewhere on the interwebs 118 | // return tanh(x); 119 | float x2 = x*x; 120 | return clamp(x*(27.0 + x2)/(27.0+9.0*x2), -1.0, 1.0); 121 | } 122 | 123 | vec3 color(vec2 p, vec2 q) { 124 | const float rdd = 2.0; 125 | vec3 ro = 1.*vec3(0., 0.75, -0.2); 126 | vec3 la = vec3(0.0, 0.0, 0.2); 127 | vec3 up = vec3(0.3, 0.0, 1.0); 128 | vec3 lp1 = ro; 129 | lp1.xy *= ROT(0.85); 130 | lp1.xz *= ROT(-0.5); 131 | 132 | vec3 ww = normalize(la - ro); 133 | vec3 uu = normalize(cross(up, ww)); 134 | vec3 vv = normalize(cross(ww,uu)); 135 | vec3 rd = normalize(p.x*uu + p.y*vv + rdd*ww); 136 | 137 | const vec2 tor = 0.55*vec2(1.0, 0.75); 138 | float td = rayTorus(ro, rd, tor); 139 | vec3 tpos = ro + rd*td; 140 | vec3 tnor = -torusNormal(tpos, tor); 141 | vec3 tref = reflect(rd, tnor); 142 | 143 | vec3 ldif1 = lp1 - tpos; 144 | float ldd1 = dot(ldif1, ldif1); 145 | float ldl1 = sqrt(ldd1); 146 | vec3 ld1 = ldif1/ldl1; 147 | vec3 sro = tpos+0.05*tnor; 148 | float sd = rayTorus(sro, ld1, tor); 149 | vec3 spos = sro+ld1*sd; 150 | vec3 snor = -torusNormal(spos, tor); 151 | 152 | float dif1 = max(dot(tnor, ld1), 0.0); 153 | float spe1 = pow(max(dot(tref, ld1), 0.0), 10.0); 154 | float r = length(tpos.xy); 155 | float a = atan(tpos.y, tpos.x)-PI*tpos.z/(r+0.5*abs(tpos.z))-TTIME/45.0; 156 | float s = mix(0.05, 0.5, tanh_approx(2.0*abs(td-0.75))); 157 | vec3 bcol0 = vec3(0.3); 158 | vec3 bcol1 = vec3(0.025); 159 | vec3 tcol = mix(bcol0, bcol1, smoothstep(-s, s, sin(9.0*a))); 160 | 161 | vec3 col = vec3(0.0); 162 | 163 | if (td > -1.0) { 164 | col += tcol*mix(0.2, 1.0, dif1/ldd1)+0.25*spe1; 165 | col *= sqrt(abs(dot(rd, tnor))); 166 | } 167 | 168 | if (sd < ldl1) { 169 | col *= mix(1.0, 0.0, pow(abs(dot(ld1, snor)), 3.0*tanh_approx(sd))); 170 | } 171 | 172 | return col; 173 | } 174 | 175 | // License: MIT, author: Inigo Quilez, found: https://iquilezles.org/www/index.htm 176 | vec3 postProcess(vec3 col, vec2 q) { 177 | col = clamp(col, 0.0, 1.0); 178 | col = pow(col, 1.0/vec3(2.2)); 179 | col = col*0.6+0.4*col*col*(3.0-2.0*col); 180 | col = mix(col, vec3(dot(col, vec3(0.33))), -0.4); 181 | col *=0.5+0.5*pow(19.0*q.x*q.y*(1.0-q.x)*(1.0-q.y),0.7); 182 | return col; 183 | } 184 | 185 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 186 | vec2 q = fragCoord/iResolution.xy; 187 | vec2 p = -1. + 2. * q; 188 | p.x *= RESOLUTION.x/RESOLUTION.y; 189 | vec3 col = color(p, q); 190 | col = postProcess(col, q); 191 | fragColor = vec4(col, 1.0); 192 | } 193 | -------------------------------------------------------------------------------- /protocols/tearing-control-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2021 Xaver Hugl 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice (including the next 14 | paragraph) shall be included in all copies or substantial portions of the 15 | Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | 27 | 28 | For some use cases like games or drawing tablets it can make sense to 29 | reduce latency by accepting tearing with the use of asynchronous page 30 | flips. This global is a factory interface, allowing clients to inform 31 | which type of presentation the content of their surfaces is suitable for. 32 | 33 | Graphics APIs like EGL or Vulkan, that manage the buffer queue and commits 34 | of a wl_surface themselves, are likely to be using this extension 35 | internally. If a client is using such an API for a wl_surface, it should 36 | not directly use this extension on that surface, to avoid raising a 37 | tearing_control_exists protocol error. 38 | 39 | Warning! The protocol described in this file is currently in the testing 40 | phase. Backward compatible changes may be added together with the 41 | corresponding interface version bump. Backward incompatible changes can 42 | only be done by creating a new major version of the extension. 43 | 44 | 45 | 46 | 47 | Destroy this tearing control factory object. Other objects, including 48 | wp_tearing_control_v1 objects created by this factory, are not affected 49 | by this request. 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | Instantiate an interface extension for the given wl_surface to request 61 | asynchronous page flips for presentation. 62 | 63 | If the given wl_surface already has a wp_tearing_control_v1 object 64 | associated, the tearing_control_exists protocol error is raised. 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | An additional interface to a wl_surface object, which allows the client 74 | to hint to the compositor if the content on the surface is suitable for 75 | presentation with tearing. 76 | The default presentation hint is vsync. See presentation_hint for more 77 | details. 78 | 79 | If the associated wl_surface is destroyed, this object becomes inert and 80 | should be destroyed. 81 | 82 | 83 | 84 | 85 | This enum provides information for if submitted frames from the client 86 | may be presented with tearing. 87 | 88 | 89 | 90 | The content of this surface is meant to be synchronized to the 91 | vertical blanking period. This should not result in visible tearing 92 | and may result in a delay before a surface commit is presented. 93 | 94 | 95 | 96 | 97 | The content of this surface is meant to be presented with minimal 98 | latency and tearing is acceptable. 99 | 100 | 101 | 102 | 103 | 104 | 105 | Set the presentation hint for the associated wl_surface. This state is 106 | double-buffered, see wl_surface.commit. 107 | 108 | The compositor is free to dynamically respect or ignore this hint based 109 | on various conditions like hardware capabilities, surface state and 110 | user preferences. 111 | 112 | 113 | 114 | 115 | 116 | 117 | Destroy this surface tearing object and revert the presentation hint to 118 | vsync. The change will be applied on the next wl_surface.commit. 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /examples/shaders/paper_scroll.glsl: -------------------------------------------------------------------------------- 1 | /** 2 | * Ping 2.0 14/06/2019 3 | * 4 | * TODO : Cloud rocking 5 | **/ 6 | const vec3 c0 = vec3(.042,0.530,0.159); 7 | const vec3 c1 = vec3(.142,0.630,0.259); 8 | const vec3 c2 = vec3(0.242,0.730, 0.359); 9 | const vec3 c3 = vec3(0.342,0.830,0.459); 10 | const vec3 c4 = vec3(0.442,0.930,0.559); 11 | 12 | const vec3 c5 =vec3(1); 13 | const vec3 c6 = vec3(0.95, 0.95 ,1.0); 14 | const vec3 c7 = vec3(0.9, 0.9,1.0); 15 | //const vec3 c8 = vec3(0.85, 0.85 ,1.0); 16 | //const vec3 c9 = vec3(0.8,0.85, 0.95); 17 | 18 | // min dist 2 circles (or ellipsis) 19 | #define GRND1 min(length(fract(op)*vec2(1, 3) - vec2(0.5,0.18)) - 0.3, length(fract(op+vec2(0.5, 0))*vec2(1, 2) - vec2(0.5,0.09)) - 0.35) 20 | #define GRND2 min(length(fract(op)*vec2(1.2, 2.5) - vec2(0.5,0.45)) - 0.4, length(fract(op+vec2(0.65, 0))*vec2(1, 1.4) - vec2(0.5,0.25)) - 0.35) 21 | #define GRND3 min(length(fract(op)-vec2(0.5,0.3))-0.35, length(fract(op+vec2(0.5, 0))-vec2(0.5,0.25))-0.3) 22 | #define GRND4 min(length(fract(op)-vec2(0.5,0.1))-0.3, length(fract(op+vec2(0.5, 0))-vec2(0.5,0.1))-0.4) 23 | #define GRND5 min(length(fract(op)-vec2(0.5,0.2))-0.5, length(fract(op+vec2(0.5, 0))-vec2(0.5,0.2))-0.5) 24 | 25 | #define txc(c, n, f) c*n + (1.0125-n)*texture(iChannel0, op*f).r 26 | 27 | vec3 ground(in vec2 u, in vec3 c, in float shadow_pos) 28 | { 29 | if(u.y<0.4) 30 | { 31 | const float b = 0.005; //blur 32 | vec2 op = u*2.0; 33 | op.x += iTime*0.05; 34 | c=mix(c, txc(c0, 0.98, vec2(2.5,2.5)), smoothstep(b*5.0, -b*5.0, GRND5)); 35 | 36 | op = vec2(u.x*3.0 + iTime*0.1 - shadow_pos, u.y*3.0-0.5); 37 | c=mix(c, c*0.75, smoothstep(b*30.0, -b*30.0, GRND4)); 38 | op.x += shadow_pos; 39 | c=mix(c, txc(c1, 0.98, vec2(1.33,1.33)), smoothstep(b*3.0, -b*3.0, GRND4)); 40 | 41 | op = vec2(u.x*4.0 + iTime*0.2 - shadow_pos, u.y*3.0-0.2); 42 | c=mix(c, c*0.9, smoothstep(b*10.0, -b*10.0, GRND3)); 43 | op.x += shadow_pos; 44 | c=mix(c, txc(c2, 0.98, vec2(0.75, 1.0)), smoothstep(b*0.5, -b*0.5, GRND3)); 45 | 46 | op = vec2(u.x*5.0 + iTime*0.4 - shadow_pos, u.y*2.0); 47 | c=mix(c, c*0.82, smoothstep(b*20.0, -b*20.0, GRND2)); 48 | op.x += shadow_pos; 49 | c=mix(c, txc(c3, 0.98, vec2(0.4,1.0)), smoothstep(b*3.0, -b*3.0, GRND2)); 50 | 51 | op = vec2(u.x*8.0 + iTime -shadow_pos, u.y*2.0+0.02); 52 | c=mix(c, c*0.75, smoothstep(b*30.0, -b*30.0, GRND1)); 53 | op += vec2(shadow_pos, -0.02); 54 | c=mix(c, txc(c4, 0.96, vec2(0.5, 1.0)), smoothstep(b*5.0, -b*5.0, GRND1)); 55 | } 56 | return c; 57 | } 58 | 59 | // https://iquilezles.org/articles/distfunctions2d 60 | float sdLine( in vec2 p, in vec2 a, in vec2 b ) 61 | { 62 | vec2 pa = p-a, ba = b-a; 63 | float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); 64 | return length( pa - ba*h ); 65 | } 66 | 67 | vec3 cloud(in vec2 u, in vec2 p, in float iscale,in vec3 c, const in vec3 cloud_color, in vec2 shadow_pos, in float shadow_factor, in float blur, in float shadow_blur) 68 | { 69 | u *= iscale; 70 | p *= iscale; 71 | 72 | // thread shadow 73 | c=mix(c, c*.95, smoothstep(shadow_blur*0.2, -shadow_blur*0.2, sdLine(u, p+vec2(shadow_pos.x,0.07), vec2(p.x + shadow_pos.x, iscale)))); 74 | 75 | // cloud shadow 76 | vec2 st = u - p -shadow_pos ; 77 | float d = length(st) - 0.07; 78 | d = min(d, length((st -vec2(0.06, 0))) - 0.055); 79 | d = min(d, length((st +vec2(0.06, 0))) - 0.055); 80 | c=mix(c, c*shadow_factor, smoothstep(shadow_blur, -shadow_blur, d)); 81 | 82 | // cloud 83 | st += shadow_pos; 84 | d = length(st) - 0.07; 85 | d = min(d, length((st -vec2(0.06, 0))) - 0.055); 86 | d = min(d, length((st +vec2(0.06, 0))) - 0.055); 87 | vec2 op = st; 88 | c=mix(c, cloud_color*0.98 + (1.0-0.98)*texture(iChannel0, st*2.5).r, smoothstep(blur, -blur, d)); 89 | 90 | // thread 91 | c=mix(c, cloud_color*0.65, smoothstep(blur / (iscale*iscale), -(blur*0.5)/(iscale*iscale), sdLine(u, p + vec2(0,0.065), vec2(p.x, iscale)))); 92 | return c; 93 | } 94 | 95 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 96 | { 97 | vec2 uv = fragCoord/iResolution.xy; 98 | uv.x *= 4.0/3.0; // dev in 4/3 screen 99 | 100 | // beautiful sky 101 | float d = length(uv-vec2(0.25,0.5)); // -0.5; 102 | vec3 c = mix(vec3(.4,0.4,.8), vec3(0.55,0.8,0.8), smoothstep(1.7, 0., d)); 103 | 104 | // gorgeous ground 105 | float shadow_pos = - smoothstep(1.0, 0.0, uv.x)*0.06 - 0.1 ; 106 | c = ground(uv, c, shadow_pos); 107 | 108 | // wonderful clouds 109 | vec2 np = vec2(1.4-fract((iTime+50.0)*0.005) *1.5 , 0.8); 110 | c = cloud(uv, np, 2.0, c, c7, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.01, 0.03); 111 | 112 | np = vec2(1.4-fract((iTime)*0.0055) *1.5 , 0.75+ sin(iTime*0.1)*0.01); // x : -1 1 113 | c = cloud(uv, np, 2.0, c, c7, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.01, 0.03); 114 | 115 | np = vec2(1.4-fract((iTime + 100.0)*0.0045) *1.5 , 0.8+ sin(0.5+iTime*0.01)*0.02); // x : -1 1 116 | c = cloud(uv, np, 2.0, c, c7, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.01, 0.03); 117 | 118 | np = vec2(1.4-fract((iTime + 0.75)*0.0045) *1.5 , 0.88+ sin(0.75+iTime*0.01)*0.03); // x : -1 1 119 | c = cloud(uv, np, 2.0, c, c7, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.01, 0.03); 120 | 121 | 122 | np = vec2(1.41-fract((iTime+75.0)*0.007) *1.5 , 0.88+ sin(iTime*0.05)*0.01); // x : -1 1 123 | c = cloud(uv, np, 1.5, c, c6, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.005, 0.04); 124 | 125 | np = vec2(1.41-fract((iTime+50.0)*0.0071) *1.5 , 0.85+ sin(0.5+iTime*0.042)*0.0095); // x : -1 1 126 | c = cloud(uv, np, 1.5, c, c6, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.005, 0.04); 127 | 128 | np = vec2(1.41-fract((iTime+35.0)*0.0067) *1.5 , 0.82+ sin(0.9+iTime*0.035)*0.012); // x : -1 1 129 | c = cloud(uv, np, 1.5, c, c6, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.005, 0.04); 130 | 131 | 132 | np = vec2(1.50-fract(iTime*0.011) *1.75 , 0.85 + sin(iTime*0.2)*0.025); // x : -1 1 133 | c = cloud(uv , np, 1.0, c, c5, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.002, 0.04); 134 | 135 | np = vec2(1.50-fract((iTime+50.0)*0.01) *1.75 , 0.85 + sin(1.5+iTime*0.08)*0.0125); // x : -1 1 136 | c = cloud(uv , np, 1.0, c, c5, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.002, 0.04); 137 | 138 | np = vec2(1.50-fract((iTime+35.0)*0.009) *1.75 , 0.8 + sin(0.5+iTime*0.05)*0.025); // x : -1 1 139 | c = cloud(uv , np, 1.0, c, c5, vec2(shadow_pos, -0.1)*0.2, 0.8, 0.002, 0.04); 140 | 141 | 142 | // Output to screen 143 | fragColor = vec4(c,1.0); 144 | } 145 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build-arch-full: 11 | name: Arch Linux (Wayland + X11) 12 | runs-on: ubuntu-latest 13 | container: 14 | image: archlinux:latest 15 | 16 | steps: 17 | - name: Update system and install dependencies 18 | run: | 19 | pacman -Syu --noconfirm 20 | pacman -S --noconfirm base-devel git meson ninja wayland wayland-protocols \ 21 | egl-wayland mesa libpng libjpeg-turbo libx11 libxrandr 22 | 23 | - name: Checkout code 24 | uses: actions/checkout@v4 25 | 26 | - name: Setup build 27 | run: meson setup build 28 | 29 | - name: Build 30 | run: ninja -C build 31 | 32 | - name: Check binary 33 | run: | 34 | test -f build/neowall 35 | ./build/neowall --version 36 | ./build/neowall --help 37 | 38 | - name: Upload artifact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: neowall-arch-full 42 | path: build/neowall 43 | 44 | build-arch-wayland-only: 45 | name: Arch Linux (Wayland only) 46 | runs-on: ubuntu-latest 47 | container: 48 | image: archlinux:latest 49 | 50 | steps: 51 | - name: Update system and install dependencies (no X11) 52 | run: | 53 | pacman -Syu --noconfirm 54 | pacman -S --noconfirm base-devel git meson ninja wayland wayland-protocols \ 55 | egl-wayland mesa libpng libjpeg-turbo 56 | 57 | - name: Checkout code 58 | uses: actions/checkout@v4 59 | 60 | - name: Setup build (Wayland only) 61 | run: meson setup build -Dx11_backend=disabled 62 | 63 | - name: Build 64 | run: ninja -C build 65 | 66 | - name: Verify build configuration 67 | run: | 68 | meson configure build | grep -E "wayland_backend|x11_backend" 69 | 70 | - name: Check binary 71 | run: | 72 | test -f build/neowall 73 | ./build/neowall --version 74 | 75 | build-arch-x11-only: 76 | name: Arch Linux (X11 only) 77 | runs-on: ubuntu-latest 78 | container: 79 | image: archlinux:latest 80 | 81 | steps: 82 | - name: Update system and install dependencies (no Wayland) 83 | run: | 84 | pacman -Syu --noconfirm 85 | pacman -S --noconfirm base-devel git meson ninja mesa libpng libjpeg-turbo \ 86 | libx11 libxrandr 87 | 88 | - name: Checkout code 89 | uses: actions/checkout@v4 90 | 91 | - name: Setup build (X11 only) 92 | run: meson setup build -Dwayland_backend=disabled 93 | 94 | - name: Build 95 | run: ninja -C build 96 | 97 | - name: Verify build configuration 98 | run: | 99 | meson configure build | grep -E "wayland_backend|x11_backend" 100 | 101 | - name: Check binary 102 | run: | 103 | test -f build/neowall 104 | ./build/neowall --version 105 | 106 | build-debian-full: 107 | name: Debian (Wayland + X11) 108 | runs-on: ubuntu-latest 109 | container: 110 | image: debian:bookworm 111 | 112 | steps: 113 | - name: Install dependencies 114 | run: | 115 | apt-get update 116 | apt-get install -y build-essential git pkg-config meson ninja-build \ 117 | libwayland-dev wayland-protocols libwayland-egl1-mesa \ 118 | libx11-dev libxrandr-dev \ 119 | libegl1-mesa-dev libgles2-mesa-dev \ 120 | libpng-dev libjpeg-dev 121 | 122 | - name: Checkout code 123 | uses: actions/checkout@v4 124 | 125 | - name: Setup build 126 | run: meson setup build 127 | 128 | - name: Build 129 | run: ninja -C build 130 | 131 | - name: Check binary 132 | run: | 133 | test -f build/neowall 134 | ./build/neowall --version 135 | 136 | - name: Upload artifact 137 | uses: actions/upload-artifact@v4 138 | with: 139 | name: neowall-debian-full 140 | path: build/neowall 141 | 142 | build-debian-x11-only: 143 | name: Debian (X11 only - i3 style) 144 | runs-on: ubuntu-latest 145 | container: 146 | image: debian:bookworm 147 | 148 | steps: 149 | - name: Install dependencies (pure X11, no Wayland) 150 | run: | 151 | apt-get update 152 | apt-get install -y build-essential git pkg-config meson ninja-build \ 153 | libx11-dev libxrandr-dev \ 154 | libegl1-mesa-dev libgles2-mesa-dev \ 155 | libpng-dev libjpeg-dev 156 | 157 | - name: Checkout code 158 | uses: actions/checkout@v4 159 | 160 | - name: Setup build (X11 only) 161 | run: meson setup build -Dwayland_backend=disabled 162 | 163 | - name: Build 164 | run: ninja -C build 165 | 166 | - name: Verify build configuration 167 | run: | 168 | meson configure build | grep -E "wayland_backend|x11_backend" 169 | 170 | - name: Check binary 171 | run: | 172 | test -f build/neowall 173 | ./build/neowall --version 174 | 175 | build-ubuntu: 176 | name: Ubuntu (Wayland + X11) 177 | runs-on: ubuntu-latest 178 | 179 | steps: 180 | - name: Install dependencies 181 | run: | 182 | sudo apt-get update 183 | sudo apt-get install -y build-essential git pkg-config meson ninja-build \ 184 | libwayland-dev wayland-protocols libwayland-egl1-mesa \ 185 | libx11-dev libxrandr-dev \ 186 | libegl1-mesa-dev libgles2-mesa-dev \ 187 | libpng-dev libjpeg-dev 188 | 189 | - name: Checkout code 190 | uses: actions/checkout@v4 191 | 192 | - name: Setup build 193 | run: meson setup build 194 | 195 | - name: Build 196 | run: ninja -C build 197 | 198 | - name: Check binary 199 | run: | 200 | test -f build/neowall 201 | ./build/neowall --version 202 | ./build/neowall --help 203 | 204 | - name: Test install 205 | run: | 206 | DESTDIR=/tmp/neowall-test ninja -C build install 207 | test -f /tmp/neowall-test/usr/local/bin/neowall 208 | test -d /tmp/neowall-test/usr/local/share/neowall/shaders 209 | test -f /tmp/neowall-test/usr/local/share/neowall/config.vibe 210 | 211 | - name: Upload artifact 212 | uses: actions/upload-artifact@v4 213 | with: 214 | name: neowall-ubuntu 215 | path: build/neowall 216 | -------------------------------------------------------------------------------- /protocols/xdg-shell-client-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.24.0 */ 2 | 3 | /* 4 | * Copyright © 2008-2013 Kristian Høgsberg 5 | * Copyright © 2013 Rafael Antognolli 6 | * Copyright © 2013 Jasper St. Pierre 7 | * Copyright © 2010-2013 Intel Corporation 8 | * Copyright © 2015-2017 Samsung Electronics Co., Ltd 9 | * Copyright © 2015-2017 Red Hat Inc. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a 12 | * copy of this software and associated documentation files (the "Software"), 13 | * to deal in the Software without restriction, including without limitation 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 | * and/or sell copies of the Software, and to permit persons to whom the 16 | * Software is furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice (including the next 19 | * paragraph) shall be included in all copies or substantial portions of the 20 | * Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include "wayland-util.h" 35 | 36 | #ifndef __has_attribute 37 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 38 | #endif 39 | 40 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 41 | #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 42 | #else 43 | #define WL_PRIVATE 44 | #endif 45 | 46 | extern const struct wl_interface wl_output_interface; 47 | extern const struct wl_interface wl_seat_interface; 48 | extern const struct wl_interface wl_surface_interface; 49 | extern const struct wl_interface xdg_popup_interface; 50 | extern const struct wl_interface xdg_positioner_interface; 51 | extern const struct wl_interface xdg_surface_interface; 52 | extern const struct wl_interface xdg_toplevel_interface; 53 | 54 | static const struct wl_interface *xdg_shell_types[] = { 55 | NULL, 56 | NULL, 57 | NULL, 58 | NULL, 59 | &xdg_positioner_interface, 60 | &xdg_surface_interface, 61 | &wl_surface_interface, 62 | &xdg_toplevel_interface, 63 | &xdg_popup_interface, 64 | &xdg_surface_interface, 65 | &xdg_positioner_interface, 66 | &xdg_toplevel_interface, 67 | &wl_seat_interface, 68 | NULL, 69 | NULL, 70 | NULL, 71 | &wl_seat_interface, 72 | NULL, 73 | &wl_seat_interface, 74 | NULL, 75 | NULL, 76 | &wl_output_interface, 77 | &wl_seat_interface, 78 | NULL, 79 | &xdg_positioner_interface, 80 | NULL, 81 | }; 82 | 83 | static const struct wl_message xdg_wm_base_requests[] = { 84 | { "destroy", "", xdg_shell_types + 0 }, 85 | { "create_positioner", "n", xdg_shell_types + 4 }, 86 | { "get_xdg_surface", "no", xdg_shell_types + 5 }, 87 | { "pong", "u", xdg_shell_types + 0 }, 88 | }; 89 | 90 | static const struct wl_message xdg_wm_base_events[] = { 91 | { "ping", "u", xdg_shell_types + 0 }, 92 | }; 93 | 94 | WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { 95 | "xdg_wm_base", 7, 96 | 4, xdg_wm_base_requests, 97 | 1, xdg_wm_base_events, 98 | }; 99 | 100 | static const struct wl_message xdg_positioner_requests[] = { 101 | { "destroy", "", xdg_shell_types + 0 }, 102 | { "set_size", "ii", xdg_shell_types + 0 }, 103 | { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, 104 | { "set_anchor", "u", xdg_shell_types + 0 }, 105 | { "set_gravity", "u", xdg_shell_types + 0 }, 106 | { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, 107 | { "set_offset", "ii", xdg_shell_types + 0 }, 108 | { "set_reactive", "3", xdg_shell_types + 0 }, 109 | { "set_parent_size", "3ii", xdg_shell_types + 0 }, 110 | { "set_parent_configure", "3u", xdg_shell_types + 0 }, 111 | }; 112 | 113 | WL_PRIVATE const struct wl_interface xdg_positioner_interface = { 114 | "xdg_positioner", 7, 115 | 10, xdg_positioner_requests, 116 | 0, NULL, 117 | }; 118 | 119 | static const struct wl_message xdg_surface_requests[] = { 120 | { "destroy", "", xdg_shell_types + 0 }, 121 | { "get_toplevel", "n", xdg_shell_types + 7 }, 122 | { "get_popup", "n?oo", xdg_shell_types + 8 }, 123 | { "set_window_geometry", "iiii", xdg_shell_types + 0 }, 124 | { "ack_configure", "u", xdg_shell_types + 0 }, 125 | }; 126 | 127 | static const struct wl_message xdg_surface_events[] = { 128 | { "configure", "u", xdg_shell_types + 0 }, 129 | }; 130 | 131 | WL_PRIVATE const struct wl_interface xdg_surface_interface = { 132 | "xdg_surface", 7, 133 | 5, xdg_surface_requests, 134 | 1, xdg_surface_events, 135 | }; 136 | 137 | static const struct wl_message xdg_toplevel_requests[] = { 138 | { "destroy", "", xdg_shell_types + 0 }, 139 | { "set_parent", "?o", xdg_shell_types + 11 }, 140 | { "set_title", "s", xdg_shell_types + 0 }, 141 | { "set_app_id", "s", xdg_shell_types + 0 }, 142 | { "show_window_menu", "ouii", xdg_shell_types + 12 }, 143 | { "move", "ou", xdg_shell_types + 16 }, 144 | { "resize", "ouu", xdg_shell_types + 18 }, 145 | { "set_max_size", "ii", xdg_shell_types + 0 }, 146 | { "set_min_size", "ii", xdg_shell_types + 0 }, 147 | { "set_maximized", "", xdg_shell_types + 0 }, 148 | { "unset_maximized", "", xdg_shell_types + 0 }, 149 | { "set_fullscreen", "?o", xdg_shell_types + 21 }, 150 | { "unset_fullscreen", "", xdg_shell_types + 0 }, 151 | { "set_minimized", "", xdg_shell_types + 0 }, 152 | }; 153 | 154 | static const struct wl_message xdg_toplevel_events[] = { 155 | { "configure", "iia", xdg_shell_types + 0 }, 156 | { "close", "", xdg_shell_types + 0 }, 157 | { "configure_bounds", "4ii", xdg_shell_types + 0 }, 158 | { "wm_capabilities", "5a", xdg_shell_types + 0 }, 159 | }; 160 | 161 | WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { 162 | "xdg_toplevel", 7, 163 | 14, xdg_toplevel_requests, 164 | 4, xdg_toplevel_events, 165 | }; 166 | 167 | static const struct wl_message xdg_popup_requests[] = { 168 | { "destroy", "", xdg_shell_types + 0 }, 169 | { "grab", "ou", xdg_shell_types + 22 }, 170 | { "reposition", "3ou", xdg_shell_types + 24 }, 171 | }; 172 | 173 | static const struct wl_message xdg_popup_events[] = { 174 | { "configure", "iiii", xdg_shell_types + 0 }, 175 | { "popup_done", "", xdg_shell_types + 0 }, 176 | { "repositioned", "3u", xdg_shell_types + 0 }, 177 | }; 178 | 179 | WL_PRIVATE const struct wl_interface xdg_popup_interface = { 180 | "xdg_popup", 7, 181 | 3, xdg_popup_requests, 182 | 3, xdg_popup_events, 183 | }; 184 | 185 | -------------------------------------------------------------------------------- /examples/shaders/fractal_land.glsl: -------------------------------------------------------------------------------- 1 | // "Fractal Cartoon" - former "DE edge detection" by Kali 2 | 3 | // There are no lights and no AO, only color by normals and dark edges. 4 | 5 | // update: Nyan Cat cameo, thanks to code from mu6k: https://www.shadertoy.com/view/4dXGWH 6 | 7 | 8 | //#define SHOWONLYEDGES 9 | #define NYAN 10 | #define WAVES 11 | #define BORDER 12 | 13 | #define RAY_STEPS 150 14 | 15 | #define BRIGHTNESS 1.2 16 | #define GAMMA 1.4 17 | #define SATURATION .65 18 | 19 | 20 | #define detail .001 21 | #define t iTime*.5 22 | 23 | 24 | const vec3 origin=vec3(-1.,.7,0.); 25 | float det=0.0; 26 | 27 | 28 | // 2D rotation function 29 | mat2 rot(float a) { 30 | return mat2(cos(a),sin(a),-sin(a),cos(a)); 31 | } 32 | 33 | // "Amazing Surface" fractal 34 | vec4 formula(vec4 p) { 35 | p.xz = abs(p.xz+1.)-abs(p.xz-1.)-p.xz; 36 | p.y-=.25; 37 | p.xy*=rot(radians(35.)); 38 | p=p*2./clamp(dot(p.xyz,p.xyz),.2,1.); 39 | return p; 40 | } 41 | 42 | // Distance function 43 | float de(vec3 pos) { 44 | #ifdef WAVES 45 | pos.y+=sin(pos.z-t*6.)*.15; //waves! 46 | #endif 47 | float hid=0.; 48 | vec3 tpos=pos; 49 | tpos.z=abs(3.-mod(tpos.z,6.)); 50 | vec4 p=vec4(tpos,1.); 51 | for (int i=0; i<4; i++) {p=formula(p);} 52 | float fr=(length(max(vec2(0.),p.yz-1.5))-1.)/p.w; 53 | float ro=max(abs(pos.x+1.)-.3,pos.y-.35); 54 | ro=max(ro,-max(abs(pos.x+1.)-.1,pos.y-.5)); 55 | pos.z=abs(.25-mod(pos.z,.5)); 56 | ro=max(ro,-max(abs(pos.z)-.2,pos.y-.3)); 57 | ro=max(ro,-max(abs(pos.z)-.01,-pos.y+.32)); 58 | float d=min(fr,ro); 59 | return d; 60 | } 61 | 62 | 63 | // Camera path 64 | vec3 path(float ti) { 65 | ti*=1.5; 66 | vec3 p=vec3(sin(ti),(1.-sin(ti*2.))*.5,-ti*5.)*.5; 67 | return p; 68 | } 69 | 70 | // Calc normals, and here is edge detection, set to variable "edge" 71 | 72 | float edge=0.; 73 | vec3 normal(vec3 p) { 74 | vec3 e = vec3(0.0,det*5.,0.0); 75 | 76 | float d1=de(p-e.yxx),d2=de(p+e.yxx); 77 | float d3=de(p-e.xyx),d4=de(p+e.xyx); 78 | float d5=de(p-e.xxy),d6=de(p+e.xxy); 79 | float d=de(p); 80 | edge=abs(d-0.5*(d2+d1))+abs(d-0.5*(d4+d3))+abs(d-0.5*(d6+d5));//edge finder 81 | edge=min(1.,pow(edge,.55)*15.); 82 | return normalize(vec3(d1-d2,d3-d4,d5-d6)); 83 | } 84 | 85 | 86 | // Used Nyan Cat code by mu6k, with some mods 87 | 88 | vec4 rainbow(vec2 p) 89 | { 90 | float q = max(p.x,-0.1); 91 | float s = sin(p.x*7.0+t*70.0)*0.08; 92 | p.y+=s; 93 | p.y*=1.1; 94 | 95 | vec4 c; 96 | if (p.x>0.0) c=vec4(0,0,0,0); else 97 | if (0.0/6.00.2) color.a=0.0; 120 | return color; 121 | } 122 | 123 | 124 | // Raymarching and 2D graphics 125 | 126 | vec3 raymarch(in vec3 from, in vec3 dir) 127 | 128 | { 129 | edge=0.; 130 | vec3 p, norm; 131 | float d=100.; 132 | float totdist=0.; 133 | for (int i=0; idet && totdist<25.0) { 135 | p=from+totdist*dir; 136 | d=de(p); 137 | det=detail*exp(.13*totdist); 138 | totdist+=d; 139 | } 140 | } 141 | vec3 col=vec3(0.); 142 | p-=(det-d)*dir; 143 | norm=normal(p); 144 | #ifdef SHOWONLYEDGES 145 | col=1.-vec3(edge); // show wireframe version 146 | #else 147 | col=(1.-abs(norm))*max(0.,1.-edge*.8); // set normal as color with dark edges 148 | #endif 149 | totdist=clamp(totdist,0.,26.); 150 | dir.y-=.02; 151 | float sunsize=7.-max(0.,texture(iChannel0,vec2(.6,.2)).x)*5.; // responsive sun size 152 | float an=atan(dir.x,dir.y)+iTime*1.5; // angle for drawing and rotating sun 153 | float s=pow(clamp(1.0-length(dir.xy)*sunsize-abs(.2-mod(an,.4)),0.,1.),.1); // sun 154 | float sb=pow(clamp(1.0-length(dir.xy)*(sunsize-.2)-abs(.2-mod(an,.4)),0.,1.),.1); // sun border 155 | float sg=pow(clamp(1.0-length(dir.xy)*(sunsize-4.5)-.5*abs(.2-mod(an,.4)),0.,1.),3.); // sun rays 156 | float y=mix(.45,1.2,pow(smoothstep(0.,1.,.75-dir.y),2.))*(1.-sb*.5); // gradient sky 157 | 158 | // set up background with sky and sun 159 | vec3 backg=vec3(0.5,0.,1.)*((1.-s)*(1.-sg)*y+(1.-sb)*sg*vec3(1.,.8,0.15)*3.); 160 | backg+=vec3(1.,.9,.1)*s; 161 | backg=max(backg,sg*vec3(1.,.9,.5)); 162 | 163 | col=mix(vec3(1.,.9,.3),col,exp(-.004*totdist*totdist));// distant fading to sun color 164 | if (totdist>25.) col=backg; // hit background 165 | col=pow(col,vec3(GAMMA))*BRIGHTNESS; 166 | col=mix(vec3(length(col)),col,SATURATION); 167 | #ifdef SHOWONLYEDGES 168 | col=1.-vec3(length(col)); 169 | #else 170 | col*=vec3(1.,.9,.85); 171 | #ifdef NYAN 172 | dir.yx*=rot(dir.x); 173 | vec2 ncatpos=(dir.xy+vec2(-3.+mod(-t,6.),-.27)); 174 | vec4 ncat=nyan(ncatpos*5.); 175 | vec4 rain=rainbow(ncatpos*10.+vec2(.8,.5)); 176 | if (totdist>8.) col=mix(col,max(vec3(.2),rain.xyz),rain.a*.9); 177 | if (totdist>8.) col=mix(col,max(vec3(.2),ncat.xyz),ncat.a*.9); 178 | #endif 179 | #endif 180 | return col; 181 | } 182 | 183 | // get camera position 184 | vec3 move(inout vec3 dir) { 185 | vec3 go=path(t); 186 | vec3 adv=path(t+.7); 187 | float hd=de(adv); 188 | vec3 advec=normalize(adv-go); 189 | float an=adv.x-go.x; an*=min(1.,abs(adv.z-go.z))*sign(adv.z-go.z)*.7; 190 | dir.xy*=mat2(cos(an),sin(an),-sin(an),cos(an)); 191 | an=advec.y*1.7; 192 | dir.yz*=mat2(cos(an),sin(an),-sin(an),cos(an)); 193 | an=atan(advec.x,advec.z); 194 | dir.xz*=mat2(cos(an),sin(an),-sin(an),cos(an)); 195 | return go; 196 | } 197 | 198 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 199 | { 200 | vec2 uv = fragCoord.xy / iResolution.xy*2.-1.; 201 | vec2 oriuv=uv; 202 | uv.y*=iResolution.y/iResolution.x; 203 | vec2 mouse=(iMouse.xy/iResolution.xy-.5)*3.; 204 | if (iMouse.z<1.) mouse=vec2(0.,-0.05); 205 | float fov=.9-max(0.,.7-iTime*.3); 206 | vec3 dir=normalize(vec3(uv*fov,1.)); 207 | dir.yz*=rot(mouse.y); 208 | dir.xz*=rot(mouse.x); 209 | vec3 from=origin+move(dir); 210 | vec3 color=raymarch(from,dir); 211 | #ifdef BORDER 212 | color=mix(vec3(0.),color,pow(max(0.,.95-length(oriuv*oriuv*oriuv*vec2(1.05,1.1))),.3)); 213 | #endif 214 | fragColor = vec4(color,1.); 215 | } 216 | -------------------------------------------------------------------------------- /examples/shaders/a_lone_waters.glsl: -------------------------------------------------------------------------------- 1 | //Set to 2.0 for AA 2 | #define AA 1.0 3 | 4 | #define STEPS 80.0 5 | #define MDIST 35.0 6 | #define pi 3.1415926535 7 | #define rot(a) mat2(cos(a),sin(a),-sin(a),cos(a)) 8 | #define sat(a) clamp(a,0.0,1.0) 9 | 10 | #define ITERS_TRACE 9 11 | #define ITERS_NORM 20 12 | 13 | #define HOR_SCALE 1.1 14 | #define OCC_SPEED 1.4 15 | #define DX_DET 0.65 16 | 17 | #define FREQ 0.6 18 | #define HEIGHT_DIV 2.5 19 | #define WEIGHT_SCL 0.8 20 | #define FREQ_SCL 1.2 21 | #define TIME_SCL 1.095 22 | #define WAV_ROT 1.21 23 | #define DRAG 0.9 24 | #define SCRL_SPEED 1.5 25 | vec2 scrollDir = vec2(1,1); 26 | 27 | 28 | //Built with some ideas from 29 | //https://www.shadertoy.com/view/wldBRf 30 | //https://www.shadertoy.com/view/ssG3Wt 31 | //https://www.shadertoy.com/view/4dBcRD 32 | //https://www.shadertoy.com/view/Xdlczl 33 | vec2 wavedx(vec2 wavPos, int iters, float t){ 34 | vec2 dx = vec2(0); 35 | vec2 wavDir = vec2(1,0); 36 | float wavWeight = 1.0; 37 | wavPos+= t*SCRL_SPEED*scrollDir; 38 | wavPos*= HOR_SCALE; 39 | float wavFreq = FREQ; 40 | float wavTime = OCC_SPEED*t; 41 | for(int i=0;i0.) foam+=result*0.3; 46 | result*=wavWeight; 47 | dx+= result*wavDir/pow(wavWeight,DX_DET); 48 | wavFreq*= FREQ_SCL; 49 | wavTime*= TIME_SCL; 50 | wavPos-= wavDir*result*DRAG; 51 | wavWeight*= WEIGHT_SCL; 52 | } 53 | float wavSum = -(pow(WEIGHT_SCL,float(iters))-1.)*HEIGHT_DIV; 54 | return dx/pow(wavSum,1.-DX_DET); 55 | } 56 | 57 | float wave(vec2 wavPos, int iters, float t){ 58 | float wav = 0.0; 59 | vec2 wavDir = vec2(1,0); 60 | float wavWeight = 1.0; 61 | wavPos+= t*SCRL_SPEED*scrollDir; 62 | wavPos*= HOR_SCALE; 63 | float wavFreq = FREQ; 64 | float wavTime = OCC_SPEED*t; 65 | for(int i=0;i10.;s*=0.8){ 128 | p.xz*=rot(s); 129 | p+=s; 130 | e+=abs(dot(sin(p*s+iTime*0.02)/s,vec3(1.65))); 131 | } 132 | e*=smoothstep(0.5,0.4,e-0.095); 133 | 134 | col += (e)*smoothstep(-0.02,0.3,rdo.y)*0.8*(1.0-sun*3.75)*mix(sc,vec3(1),0.4); 135 | 136 | return (col); 137 | } 138 | void render( out vec4 fragColor, in vec2 fragCoord ){ 139 | vec2 uv = (fragCoord-0.5*iResolution.xy)/min(iResolution.y,iResolution.x); 140 | vec3 col = vec3(0); 141 | vec3 ro = vec3(0,2.25,-3)*1.1; 142 | bool click = iMouse.z>0.; 143 | if(click){ 144 | ro.yz*=rot(2.0*min(iMouse.y/iResolution.y-0.5,0.15)); 145 | ro.zx*=rot(-7.0*(iMouse.x/iResolution.x-0.5)); 146 | } 147 | vec3 lk = vec3(0,2,0); 148 | vec3 f = normalize(lk-ro); 149 | vec3 r = normalize(cross(vec3(0,1,0),f)); 150 | vec3 rd = normalize(f*(0.9)+uv.x*r+uv.y*cross(f,r)); 151 | 152 | float dO = 0.; 153 | bool hit = false; 154 | float d = 0.; 155 | vec3 p = ro; 156 | 157 | float tPln = -(ro.y-1.86)/rd.y; 158 | if(tPln>0.||click){ 159 | if(!click)dO+=tPln; 160 | for(float i = 0.; iSTEPS-2.0){ 163 | hit = true; 164 | break; 165 | } 166 | if(dO>MDIST){ 167 | dO = MDIST; 168 | break; 169 | } 170 | } 171 | } 172 | vec3 skyrd = sky(rd); 173 | if(hit){ 174 | vec3 n = norm(p); 175 | vec3 rfl = reflect(rd,n); 176 | rfl.y = abs(rfl.y); 177 | vec3 rf = refract(rd,n,1./1.33); 178 | vec3 sd = normalize(vec3(0,0.3,-1.0)); 179 | float fres = clamp((pow(1. - max(0.0, dot(-n, rd)), 5.0)),0.0,1.0); 180 | 181 | vec3 sunDir = vec3(0,0.15,1.0); 182 | sunDir.xz*=rot(-sunrot.x); 183 | col += sky(rfl)*fres*0.9; 184 | float subRefract =pow(max(0.0, dot(rf,sunDir)),35.0); 185 | //This effect is exaggerated much more than is realistic because I like it :) 186 | col += pow(spc(spec-0.1,0.5),vec3(2.2))*subRefract*2.5; 187 | vec3 rd2 = rd; 188 | rd2.xz*=rot(sunrot.x); 189 | vec3 waterCol = min(sat(spc(spec-0.1,0.4))*0.05*pow(min(p.y+0.5,1.8),4.0)*length(skyrd)*(rd2.z*0.3+0.7),1.0); 190 | 191 | waterCol = sat(spc(spec-0.1,0.4))*(0.4*pow(min(p.y*0.7+0.9,1.8),4.)*length(skyrd)*(rd2.z*0.15+0.85)); 192 | col += waterCol*0.17; 193 | //col+=smoothstep(0.95,1.55,wave(p.xz,25,iTime))*mix(waterCol*0.3,vec3(1),0.2)*0.2; 194 | 195 | col = mix(col,skyrd,dO/MDIST); 196 | } 197 | else{ 198 | col += skyrd; 199 | } 200 | col = sat(col); 201 | col = pow(col,vec3(0.87)); 202 | col *=1.0-0.8*pow(length(uv*vec2(0.8,1.)),2.7); 203 | fragColor = vec4(col,1.0); 204 | 205 | } 206 | 207 | #define ZERO min(0.0,iTime) 208 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 209 | { 210 | float px = 1.0/AA; 211 | vec4 col = vec4(0); 212 | 213 | if(AA==1.0) {render(col,fragCoord); fragColor = col; return;} 214 | 215 | for(float i = ZERO; i 0.0) { 164 | color = getBackgroundColor(dir); 165 | } else { 166 | // project the starting position to z = 0 so we ccan lower the raymarch count 167 | float totdist = from.z / -dir.z; 168 | for (int steps = 0 ; steps < STEPS ; steps++) { 169 | vec3 p = from + totdist * dir; 170 | vec3 projClosest; 171 | vec3 projSecondClosest; 172 | p.x -= iTime * 2.7; 173 | float dist = de(p, projClosest, projSecondClosest); 174 | totdist += dist; 175 | if ( dist < 0.01 || steps == STEPS-1 ) { 176 | color = getWaveColor(p, projClosest, projSecondClosest, 177 | dir, totdist, fragCoord); 178 | break; 179 | } 180 | } 181 | } 182 | 183 | fragColor = vec4(color, 1); 184 | 185 | } 186 | -------------------------------------------------------------------------------- /examples/shaders/time.glsl.glsl: -------------------------------------------------------------------------------- 1 | // Author: Rigel rui@gil.com 2 | // licence: https://creativecommons.org/licenses/by/4.0/ 3 | // link: https://www.shadertoy.com/view/lljfRD 4 | 5 | 6 | /* 7 | This was a study on circles, inspired by this artwork 8 | http://www.dailymail.co.uk/news/article-1236380/Worlds-largest-artwork-etched-desert-sand.html 9 | 10 | and implemented with the help of this article 11 | http://www.ams.org/samplings/feature-column/fcarc-kissing 12 | 13 | The structure is called an apollonian packing (or gasket) 14 | https://en.m.wikipedia.org/wiki/Apollonian_gasket 15 | 16 | There is a lot of apollonians in shadertoy, but not many quite like the image above. 17 | This one by klems is really cool. He uses a technique called a soddy circle. 18 | https://www.shadertoy.com/view/4s2czK 19 | 20 | This shader uses another technique called a Descartes Configuration. 21 | The only thing that makes this technique interesting is that it can be generalized to higher dimensions. 22 | */ 23 | 24 | 25 | // a few utility functions 26 | // a signed distance function for a rectangle 27 | float sdfRect(vec2 uv, vec2 s) {vec2 auv = abs(uv); return max(auv.x-s.x,auv.y-s.y); } 28 | // a signed distance function for a circle 29 | float sdfCircle(vec2 uv, vec2 c, float r) { return length(uv-c)-r; } 30 | // fills an sdf in 2d 31 | float fill(float d, float s, float i) { return abs(smoothstep(0.,s,d) - i); } 32 | // makes a stroke of an sdf at the zero boundary 33 | float stroke(float d, float w, float s, float i) { return abs(smoothstep(0.,s,abs(d)-(w*.5)) - i); } 34 | // a simple palette 35 | vec3 pal(float d) { return .5*(cos(6.283*d*vec3(2.,2.,1.)+vec3(.0,1.4,.0))+1.);} 36 | // 2d rotation matrix 37 | mat2 uvRotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } 38 | // circle inversion 39 | vec2 inversion(vec2 uv, float r) { return (r*r*uv)/vec2(dot(uv,uv)); } 40 | // seeded random number 41 | float hash(vec2 s) { return fract(sin(dot(s,vec2(12.9898,78.2333)))*43758.5453123); } 42 | 43 | // this is an algorithm to construct an apollonian packing with a descartes configuration 44 | // remaps the plane to a circle at the origin and a specific radius. vec3(x,y,radius) 45 | vec3 apollonian(vec2 uv) { 46 | // the algorithm is recursive and must start with a initial descartes configuration 47 | // each vec3 represents a circle with the form vec3(centerx, centery, 1./radius) 48 | // the signed inverse radius is also called the bend (refer to the article above) 49 | vec3 dec[4]; 50 | // a DEC is a configuration of 4 circles tangent to each other 51 | // the easiest way to build the initial one it to construct a symetric Steiner Chain. 52 | // http://mathworld.wolfram.com/SteinerChain.html 53 | float a = 6.283/3.; 54 | float ra = 1.+sin(a*.5); 55 | float rb = 1.-sin(a*.5); 56 | dec[0] = vec3(0.,0.,-1./ra); 57 | float radius = .5*(ra-rb); 58 | float bend = 1./radius; 59 | for (int i=1; i<4; i++) { 60 | dec[i] = vec3(cos(float(i)*a),sin(float(i)*a),bend); 61 | // if the point is in one of the starting circles we have already found our solution 62 | if (length(uv-dec[i].xy) < radius) return vec3(uv-dec[i].xy,radius); 63 | } 64 | 65 | // Now that we have a starting DEC we are going to try to 66 | // find the solution for the current point 67 | for(int i=0; i<7; i++) { 68 | // find the circle that is further away from the point uv, using euclidean distance 69 | int fi = 0; 70 | float d = distance(uv,dec[0].xy)-abs(1./dec[0].z); 71 | // for some reason, the euclidean distance doesn't work for the circle with negative bend 72 | // can anyone with proper math skills, explain me why? 73 | d *= dec[0].z < 0. ? -.5 : 1.; // just scale it to make it work... 74 | for(int i=1; i<4; i++) { 75 | float fd = distance(uv,dec[i].xy)-abs(1./dec[i].z); 76 | fd *= dec[i].z < 0. ? -.5: 1.; 77 | if (fd>d) {fi = i;d=fd;} 78 | } 79 | // put the cicle found in the last slot, to generate a solution 80 | // in the "direction" of the point 81 | vec3 c = dec[3]; 82 | dec[3] = dec[fi]; 83 | dec[fi] = c; 84 | // generate a new solution 85 | float bend = (2.*(dec[0].z+dec[1].z+dec[2].z))-dec[3].z; 86 | vec2 center = vec2((2.*(dec[0].z*dec[0].xy 87 | +dec[1].z*dec[1].xy 88 | +dec[2].z*dec[2].xy) 89 | -dec[3].z*dec[3].xy)/bend); 90 | 91 | vec3 solution = vec3(center,bend); 92 | // is the solution radius is to small, quit 93 | if (abs(1./bend) < 0.01) break; 94 | // if the solution contains the point return the circle 95 | if (length(uv-solution.xy) < 1./bend) return vec3(uv-solution.xy,1./bend); 96 | // else update the descartes configuration, 97 | dec[3] = solution; 98 | // and repeat... 99 | } 100 | // if nothing is found we return by default the inner circle of the Steiner chain 101 | return vec3(uv,rb); 102 | } 103 | 104 | 105 | vec3 scene(vec2 uv, vec4 ms) { 106 | 107 | vec2 ci = vec2(.0); 108 | 109 | // drag your mouse to apply circle inversion 110 | if (ms.y != -2. && ms.z > -2.) { 111 | uv = inversion(uv,cos(radians(60.))); 112 | ci = ms.xy; 113 | } 114 | 115 | // remap uv to appolonian packing 116 | vec3 uvApo = apollonian(uv-ci); 117 | 118 | float d = 6.2830/360.; 119 | float a = atan(uvApo.y,uvApo.x); 120 | float r = length(uvApo.xy); 121 | 122 | float circle = sdfCircle(uv,uv-uvApo.xy,uvApo.z); 123 | 124 | // background 125 | vec3 c = length(uv)*pal(.7)*.2; 126 | 127 | // drawing the clocks 128 | if (uvApo.z > .3) { 129 | c = mix(c,pal(.75-r*.1)*.8,fill(circle+.02,.01,1.)); // clock 130 | c = mix(c,pal(.4+r*.1),stroke(circle+(uvApo.z*.03),uvApo.z*.01,.005,1.));// dial 131 | 132 | float h = stroke(mod(a+d*15.,d*30.)-d*15.,.02,0.01,1.); 133 | c = mix(c,pal(.4+r*.1),h*stroke(circle+(uvApo.z*.16),uvApo.z*.25,.005,1.0));// hours 134 | 135 | float m = stroke(mod(a+d*15.,d*6.)-d*3.,.005,0.01,1.); 136 | c = mix(c,pal(.45+r*.1),(1.-h)*m*stroke(circle+(uvApo.z*.15),uvApo.z*.1,.005,1.0));// minutes, 137 | 138 | // needles rotation 139 | vec2 uvrh = uvApo.xy*uvRotate(sign(cos(hash(vec2(uvApo.z))*d*180.))*d*iTime*(1./uvApo.z*10.)-d*90.); 140 | vec2 uvrm = uvApo.xy*uvRotate(sign(cos(hash(vec2(uvApo.z)*4.)*d*180.))*d*iTime*(1./uvApo.z*120.)-d*90.); 141 | // draw needles 142 | c = mix(c,pal(.85),stroke(sdfRect(uvrh+vec2(uvApo.z-(uvApo.z*.8),.0),uvApo.z*vec2(.4,.03)),uvApo.z*.01,0.005,1.)); 143 | c = mix(c,pal(.9),fill(sdfRect(uvrm+vec2(uvApo.z-(uvApo.z*.65),.0),uvApo.z*vec2(.5,.002)),0.005,1.)); 144 | c = mix(c,pal(.5+r*10.),fill(circle+uvApo.z-.02,0.005,1.)); // center 145 | // drawing the gears 146 | } else if (uvApo.z > .05) { 147 | vec2 uvrg = uvApo.xy*uvRotate(sign(cos(hash(vec2(uvApo.z+2.))*d*180.))*d*iTime*(1./uvApo.z*20.)); 148 | float g = stroke(mod(atan(uvrg.y,uvrg.x)+d*22.5,d*45.)-d*22.5,.3,.05,1.0); 149 | vec2 size = uvApo.z*vec2(.45,.08); 150 | c = mix(c,pal(.55-r*.6),fill(circle+g*(uvApo.z*.2)+.01,.001,1.)*fill(circle+(uvApo.z*.6),.005,.0)); 151 | c = mix(c,pal(.55-r*.6),fill(min(sdfRect(uvrg,size.xy),sdfRect(uvrg,size.yx)),.005,1.)); 152 | // drawing the screws 153 | } else { 154 | vec2 size = uvApo.z * vec2(.5,.1); 155 | c = mix(c, pal(.85-(uvApo.z*2.)), fill(circle + 0.01,.007,1.)); 156 | c = mix(c, pal(.8-(uvApo.z*3.)), fill(min(sdfRect(uvApo.xy,size.xy),sdfRect(uvApo.xy,size.yx)), .002, 1.)); 157 | } 158 | return c; 159 | } 160 | 161 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 162 | vec2 uv = (fragCoord.xy - iResolution.xy * .5) / iResolution.y; 163 | vec4 ms = (iMouse - iResolution.xyxy * .5 ) / iResolution.y ; 164 | fragColor = vec4(scene(uv*4., ms*4.),1.0); 165 | } 166 | -------------------------------------------------------------------------------- /examples/shaders/synthwave2.glsl: -------------------------------------------------------------------------------- 1 | //#define AA 2 2 | //#define VAPORWAVE 3 | //#define stereo 1. // -1. for cross-eyed (defaults to parallel view) 4 | #define speed 10. 5 | #define wave_thing 6 | //#define city 7 | 8 | //you can add any sound texture in iChannel0 to turn it into a cool audio visualizer 9 | // (it looks better with lower speeds though) 10 | //you should commment out or remove the following line to enable it (it's disabled mainly for performance reasons): 11 | #define disable_sound_texture_sampling 12 | 13 | #ifndef disable_sound_texture_sampling 14 | #undef speed 15 | // lower value of speed when using as audio visualizer 16 | #define speed 5. 17 | #endif 18 | 19 | //self-explainatory 20 | #define audio_vibration_amplitude .125 21 | 22 | float jTime; 23 | 24 | 25 | #ifdef disable_sound_texture_sampling 26 | #define textureMirror(a, b) vec4(0) 27 | #else 28 | vec4 textureMirror(sampler2D tex, vec2 c){ 29 | vec2 cf = fract(c); 30 | return texture(tex,mix(cf,1.-cf,mod(floor(c),2.))); 31 | } 32 | #endif 33 | 34 | 35 | float amp(vec2 p){ 36 | return smoothstep(1.,8.,abs(p.x)); 37 | } 38 | 39 | float pow512(float a){ 40 | a*=a;//^2 41 | a*=a;//^4 42 | a*=a;//^8 43 | a*=a;//^16 44 | a*=a;//^32 45 | a*=a;//^64 46 | a*=a;//^128 47 | a*=a;//^256 48 | return a*a; 49 | } 50 | float pow1d5(float a){ 51 | return a*sqrt(a); 52 | } 53 | float hash21(vec2 co){ 54 | return fract(sin(dot(co.xy,vec2(1.9898,7.233)))*45758.5433); 55 | } 56 | float hash(vec2 uv){ 57 | float a = amp(uv); 58 | #ifdef wave_thing 59 | float w = a>0.?(1.-.4*pow512(.51+.49*sin((.02*(uv.y+.5*uv.x)-jTime)*2.))):0.; 60 | #else 61 | float w=1.; 62 | #endif 63 | return (a>0.? 64 | a*pow1d5( 65 | //texture(iChannel0,uv/iChannelResolution[0].xy).r 66 | hash21(uv) 67 | )*w 68 | :0.)-(textureMirror(iChannel0,vec2((uv.x*29.+uv.y)*.03125,1.)).x)*audio_vibration_amplitude; 69 | } 70 | 71 | float edgeMin(float dx,vec2 da, vec2 db,vec2 uv){ 72 | uv.x+=5.; 73 | vec3 c = fract((round(vec3(uv,uv.x+uv.y)))*(vec3(0,1,2)+0.61803398875)); 74 | float a1 = textureMirror(iChannel0,vec2(c.y,0.)).x>.6?.15:1.; 75 | float a2 = textureMirror(iChannel0,vec2(c.x,0.)).x>.6?.15:1.; 76 | float a3 = textureMirror(iChannel0,vec2(c.z,0.)).x>.6?.15:1.; 77 | 78 | return min(min((1.-dx)*db.y*a3,da.x*a2),da.y*a1); 79 | } 80 | 81 | vec2 trinoise(vec2 uv){ 82 | const float sq = sqrt(3./2.); 83 | uv.x *= sq; 84 | uv.y -= .5*uv.x; 85 | vec2 d = fract(uv); 86 | uv -= d; 87 | 88 | bool c = dot(d,vec2(1))>1.; 89 | 90 | vec2 dd = 1.-d; 91 | vec2 da = c?dd:d,db = c?d:dd; 92 | 93 | float nn = hash(uv+float(c)); 94 | float n2 = hash(uv+vec2(1,0)); 95 | float n3 = hash(uv+vec2(0,1)); 96 | 97 | 98 | float nmid = mix(n2,n3,d.y); 99 | float ns = mix(nn,c?n2:n3,da.y); 100 | float dx = da.x/db.y; 101 | return vec2(mix(ns,nmid,dx),edgeMin(dx,da, db,uv+d)); 102 | } 103 | 104 | 105 | vec2 map(vec3 p){ 106 | vec2 n = trinoise(p.xz); 107 | return vec2(p.y-2.*n.x,n.y); 108 | } 109 | 110 | vec3 grad(vec3 p){ 111 | const vec2 e = vec2(.005,0); 112 | float a =map(p).x; 113 | return vec3(map(p+e.xyy).x-a 114 | ,map(p+e.yxy).x-a 115 | ,map(p+e.yyx).x-a)/e.x; 116 | } 117 | 118 | vec2 intersect(vec3 ro,vec3 rd){ 119 | float d =0.,h=0.; 120 | for(int i = 0;i<500;i++){ //look nice with 50 iterations 121 | vec3 p = ro+d*rd; 122 | vec2 s = map(p); 123 | h = s.x; 124 | d+= h*.5; 125 | if(abs(h)<.003*d) 126 | return vec2(d,s.y); 127 | if(d>150.|| p.y>2.) break; 128 | } 129 | 130 | return vec2(-1); 131 | } 132 | 133 | 134 | void addsun(vec3 rd,vec3 ld,inout vec3 col){ 135 | 136 | float sun = smoothstep(.21,.2,distance(rd,ld)); 137 | 138 | if(sun>0.){ 139 | float yd = (rd.y-ld.y); 140 | 141 | float a =sin(3.1*exp(-(yd)*14.)); 142 | 143 | sun*=smoothstep(-.8,0.,a); 144 | 145 | col = mix(col,vec3(1.,.8,.4)*.75,sun); 146 | } 147 | } 148 | 149 | 150 | float starnoise(vec3 rd){ 151 | float c = 0.; 152 | vec3 p = normalize(rd)*300.; 153 | for (float i=0.;i<4.;i++) 154 | { 155 | vec3 q = fract(p)-.5; 156 | vec3 id = floor(p); 157 | float c2 = smoothstep(.5,0.,length(q)); 158 | c2 *= step(hash21(id.xz/id.y),.06-i*i*0.005); 159 | c += c2; 160 | p = p*.6+.5*p*mat3(3./5.,0,4./5.,0,1,0,-4./5.,0,3./5.); 161 | } 162 | c*=c; 163 | float g = dot(sin(rd*10.512),cos(rd.yzx*10.512)); 164 | c*=smoothstep(-3.14,-.9,g)*.5+.5*smoothstep(-.3,1.,g); 165 | return c*c; 166 | } 167 | 168 | vec3 gsky(vec3 rd,vec3 ld,bool mask){ 169 | float haze = exp2(-5.*(abs(rd.y)-.2*dot(rd,ld))); 170 | 171 | 172 | //float st = mask?pow512(texture(iChannel0,(rd.xy+vec2(300.1,100)*rd.z)*10.).r)*(1.-min(haze,1.)):0.; 173 | //float st = mask?pow512(hash21((rd.xy+vec2(300.1,100)*rd.z)*10.))*(1.-min(haze,1.)):0.; 174 | float st = mask?(starnoise(rd))*(1.-min(haze,1.)):0.; 175 | vec3 back = vec3(.4,.1,.7)*(1.-.5*textureMirror(iChannel0,vec2(.5+.05*rd.x/rd.y,0.)).x 176 | *exp2(-.1*abs(length(rd.xz)/rd.y)) 177 | *max(sign(rd.y),0.)); 178 | #ifdef city 179 | float x = round(rd.x*30.); 180 | float h = hash21(vec2(x-166.)); 181 | bool building = (h*h*.125*exp2(-x*x*x*x*.0025)>rd.y); 182 | if(mask && building) 183 | back*=0.,haze=.8, mask=mask && !building; 184 | #endif 185 | vec3 col=clamp(mix(back,vec3(.7,.1,.4),haze)+st,0.,1.); 186 | if(mask)addsun(rd,ld,col); 187 | return col; 188 | } 189 | 190 | 191 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 192 | { 193 | fragColor=vec4(0); 194 | #ifdef AA 195 | for(float x = 0.;x<1.;x+=1./float(AA)){ 196 | for(float y = 0.;y<1.;y+=1./float(AA)){ 197 | #else 198 | const float AA=1.,x=0.,y=0.; 199 | #endif 200 | vec2 uv = (2.*(fragCoord+vec2(x,y))-iResolution.xy)/iResolution.y; 201 | 202 | const float shutter_speed = .25; // for motion blur 203 | //float dt = fract(texture(iChannel0,float(AA)*(fragCoord+vec2(x,y))/iChannelResolution[0].xy).r+iTime)*shutter_speed; 204 | float dt = fract(hash21(float(AA)*(fragCoord+vec2(x,y)))+iTime)*shutter_speed; 205 | jTime = mod(iTime-dt*iTimeDelta,4000.); 206 | vec3 ro = vec3(0.,1,(-20000.+jTime*speed)); 207 | 208 | #ifdef stereo 209 | ro+=stereo*vec3(.2*(float(uv.x>0.)-.5),0.,0.); 210 | const float de = .9; 211 | uv.x=uv.x+.5*(uv.x>0.?-de:de); 212 | uv*=2.; 213 | #endif 214 | 215 | vec3 rd = normalize(vec3(uv,4./3.));//vec3(uv,sqrt(1.-dot(uv,uv))); 216 | 217 | vec2 i = intersect(ro,rd); 218 | float d = i.x; 219 | 220 | vec3 ld = normalize(vec3(0,.125+.05*sin(.1*jTime),1)); 221 | 222 | vec3 fog = d>0.?exp2(-d*vec3(.14,.1,.28)):vec3(0.); 223 | vec3 sky = gsky(rd,ld,d<0.); 224 | 225 | vec3 p = ro+d*rd; 226 | vec3 n = normalize(grad(p)); 227 | 228 | float diff = dot(n,ld)+.1*n.y; 229 | vec3 col = vec3(.1,.11,.18)*diff; 230 | 231 | vec3 rfd = reflect(rd,n); 232 | vec3 rfcol = gsky(rfd,ld,true); 233 | 234 | col = mix(col,rfcol,.05+.95*pow(max(1.+dot(rd,n),0.),5.)); 235 | #ifdef VAPORWAVE 236 | col = mix(col,vec3(.4,.5,1.),smoothstep(.05,.0,i.y)); 237 | col = mix(sky,col,fog); 238 | col = sqrt(col); 239 | #else 240 | col = mix(col,vec3(.8,.1,.92),smoothstep(.05,.0,i.y)); 241 | col = mix(sky,col,fog); 242 | //no gamma for that old cg look 243 | #endif 244 | if(d<0.) 245 | d=1e6; 246 | d=min(d,10.); 247 | fragColor += vec4(clamp(col,0.,1.),d<0.?0.:.1+exp2(-d)); 248 | #ifdef AA 249 | } 250 | } 251 | fragColor/=float(AA*AA); 252 | #endif 253 | } 254 | 255 | /** SHADERDATA 256 | { 257 | "title": "another synthwave sunset thing", 258 | "description": "I was thinking of a way to make pseudo tesselation noise and i made this to illustrate it, i might not be the first one to come up with this solution.", 259 | "model": "car" 260 | } 261 | */ 262 | --------------------------------------------------------------------------------