├── dmgBg.png ├── icon.ico ├── wallpapers └── default-fullhd │ ├── 1.bmp │ ├── 2.bmp │ ├── 3.bmp │ ├── 4.bmp │ └── wallpaper.cfg ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question---help.md └── workflows │ └── build.yml ├── wallpaper.h ├── debug.h ├── parser.h ├── window.h ├── platform_guard.h ├── lwp.template.plist ├── setupPlist.command ├── uninstall.bat ├── .gitignore ├── debug.c ├── resource.template.rc ├── setupDmg.applescript ├── defaultWin.cfg ├── defaultMac.cfg ├── default.cfg ├── LICENSE.txt ├── main.h ├── wallpaper.c ├── install.bat ├── CMakeLists.txt ├── main.c ├── window.c ├── parser.c └── README.md /dmgBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/dmgBg.png -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/icon.ico -------------------------------------------------------------------------------- /wallpapers/default-fullhd/1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/wallpapers/default-fullhd/1.bmp -------------------------------------------------------------------------------- /wallpapers/default-fullhd/2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/wallpapers/default-fullhd/2.bmp -------------------------------------------------------------------------------- /wallpapers/default-fullhd/3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/wallpapers/default-fullhd/3.bmp -------------------------------------------------------------------------------- /wallpapers/default-fullhd/4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankozik/parallax-wallpaper/HEAD/wallpapers/default-fullhd/4.bmp -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: Bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /wallpaper.h: -------------------------------------------------------------------------------- 1 | #ifndef WALLPAPER_H 2 | #define WALLPAPER_H 3 | 4 | #include "main.h" 5 | 6 | int loadMonitors(App *app, Config *cfg); 7 | void freeMonitor(Monitor *monitor); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: Enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #define LOG_ERROR 0 5 | #define LOG_INFO 1 6 | #define LOG_WARNING 2 7 | 8 | void lwpLog(int type, const char *str, ...); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question---help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question / Help 3 | about: Ask a question about this project 4 | title: '' 5 | labels: Help Wanted / Question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "main.h" 5 | 6 | int parseConfig(App *app, Config *cfg); 7 | int parseWallpaperConfig(Wallpaper *wallpaper, const char *path); 8 | void freeConfig(Config *cfg); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /window.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_H 2 | #define WINDOW_H 3 | 4 | #include "main.h" 5 | #include "parser.h" 6 | 7 | void initWindow(App *app, Config *cfg); 8 | 9 | #ifdef __WIN32 10 | void initTrayIcon(); 11 | int updateTrayIcon(); 12 | void removeTrayIcon(); 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /platform_guard.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_GUARD_H 2 | #define PLATFORM_GUARD_H 3 | 4 | #ifndef __WIN32 5 | #ifndef __DARWIN 6 | #ifndef __LINUX 7 | 8 | #error "Unsupported platform." 9 | 10 | #endif // __LINUX 11 | #endif // __DARWIN 12 | #endif // __WIN32 13 | 14 | #endif // PLATFORM_GUARD_H 15 | -------------------------------------------------------------------------------- /wallpapers/default-fullhd/wallpaper.cfg: -------------------------------------------------------------------------------- 1 | # Layers count 2 | count=4 3 | 4 | # Base movement sensitivity 5 | # Sensitivity for each layer is [ movement_x * (i-1) ], where i is the layer index. 6 | movement_x=0.05 7 | movement_y=0.05 8 | 9 | # You can override base sensitivity for a specific layer like this: 10 | # movement2_x = 0.1 11 | 12 | # Set this to 1, if Your wallpaper is repeatable 13 | repeat_x=0 14 | repeat_y=0 15 | -------------------------------------------------------------------------------- /lwp.template.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | jszczerbinsky.lwp 7 | 8 | LaunchOnlyOnce 9 | 10 | 11 | RunAtLoad 12 | 13 | 14 | UserName 15 | @user@ 16 | 17 | ProgramArguments 18 | 19 | @binpath@ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /setupPlist.command: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | if [[ ! -a ~/Library/LaunchAgents/lwp.plist ]]; then 4 | mkdir -p ~/Library/LaunchAgents 5 | cp /Applications/Layered_WallPaper/lwp.template.plist ~/Library/LaunchAgents/lwp.plist 6 | sed -i "" "s/@user@/$(whoami)/g" ~/Library/LaunchAgents/lwp.plist 7 | sed -i "" "s^@binpath@^/Applications/Layered_WallPaper/lwp.app/Contents/MacOS/lwp^g" ~/Library/LaunchAgents/lwp.plist 8 | launchctl load ~/Library/LaunchAgents/lwp.plist 9 | 10 | echo Layered WallPaper has been added to autostart 11 | else 12 | launchctl unload ~/Library/LaunchAgents/lwp.plist 13 | rm ~/Library/LaunchAgents/lwp.plist 14 | 15 | echo Layered WallPaper has been removed from autostart 16 | fi 17 | -------------------------------------------------------------------------------- /uninstall.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Uninstall Layered WallPaper 3 | 4 | echo Are You sure, You want to remove Layered WallPaper from Your computer? 5 | set /p val="(y/n) > " 6 | if not %val%==y ( 7 | exit 8 | ) 9 | 10 | echo Killing all running instances... 11 | taskkill /F /IM lwp.exe 12 | echo. 13 | 14 | echo Restarting explorer... 15 | taskkill /im explorer.exe /f 16 | start explorer.exe 17 | echo. 18 | 19 | echo Removing Layered WallPaper's registry keys.. 20 | reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /f > nul 21 | reg delete "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v "lwp" /f > nul 22 | echo. 23 | 24 | echo Removing Layered WallPaper's files... 25 | rmdir /S /Q "%~dp0" 26 | exit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | resource.rc 2 | .DS_Store 3 | 4 | data/ 5 | build/ 6 | *.res 7 | 8 | # Prerequisites 9 | *.d 10 | 11 | # Object files 12 | *.o 13 | *.ko 14 | *.obj 15 | *.elf 16 | 17 | 18 | # Linker output 19 | *.ilk 20 | *.map 21 | *.exp 22 | 23 | # Precompiled Headers 24 | *.gch 25 | *.pch 26 | 27 | # Libraries 28 | *.lib 29 | *.a 30 | *.la 31 | *.lo 32 | 33 | # Shared objects (inc. Windows DLLs) 34 | *.dll 35 | *.so 36 | *.so.* 37 | *.dylib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | *.i*86 44 | *.x86_64 45 | *.hex 46 | 47 | # Debug files 48 | *.dSYM/ 49 | *.su 50 | *.idb 51 | *.pdb 52 | 53 | # Kernel Module Compile Results 54 | *.mod* 55 | *.cmd 56 | .tmp_versions/ 57 | modules.order 58 | Module.symvers 59 | Mkfile.old 60 | dkms.conf 61 | 62 | # Unwanted build artifacts 63 | lwp.plist 64 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | 3 | #include 4 | #include 5 | 6 | void lwpLog(int type, const char *str, ...) 7 | { 8 | char *typePrefixCodes; 9 | char *typePrefix; 10 | 11 | switch (type) 12 | { 13 | case LOG_ERROR: 14 | typePrefixCodes = "\x1b[31m\x1b[1m"; 15 | typePrefix = "ERROR"; 16 | break; 17 | case LOG_INFO: 18 | typePrefixCodes = "\x1b[34m\x1b[1m"; 19 | typePrefix = "INFO"; 20 | break; 21 | case LOG_WARNING: 22 | typePrefixCodes = "\x1b[33m\x1b[1m"; 23 | typePrefix = "WARNING"; 24 | break; 25 | } 26 | 27 | printf("%s%s: \x1b[0m", typePrefixCodes, typePrefix); 28 | 29 | va_list args; 30 | va_start(args, str); 31 | vprintf(str, args); 32 | va_end(args); 33 | 34 | putchar('\n'); 35 | fflush(stdout); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /resource.template.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define PROGRAM_VER "@PROGRAM_VERSION@" 5 | 6 | id ICON "icon.ico" 7 | 8 | VS_VERSION_INFO VERSIONINFO 9 | FILEOS VOS_NT 10 | FILETYPE VFT_APP 11 | BEGIN 12 | BLOCK "StringFileInfo" 13 | BEGIN 14 | BLOCK "040904b0" 15 | BEGIN 16 | VALUE "Comments", "Layered WallPaper" 17 | VALUE "CompanyName", "Jakub Szczerbiński" 18 | VALUE "FileDescription", "Layered WallPaper" 19 | VALUE "FileVersion", PROGRAM_VER 20 | VALUE "InternalName", "Layered WallPaper" 21 | VALUE "LegalCopyright", "(c) 2022 Jakub Szczerbiński" 22 | VALUE "OriginalFilename", "lwp.exe" 23 | VALUE "ProductName", "Layered WallPaper" 24 | VALUE "ProductVersion", PROGRAM_VER 25 | END 26 | END 27 | BLOCK "VarFileInfo" 28 | BEGIN 29 | VALUE "Translation", 0x0409,1200 30 | END 31 | END -------------------------------------------------------------------------------- /setupDmg.applescript: -------------------------------------------------------------------------------- 1 | on run argv 2 | tell application "Finder" 3 | tell disk (item 1 of argv) 4 | open 5 | set current view of container window to icon view 6 | set toolbar visible of container window to false 7 | set statusbar visible of container window to false 8 | set the bounds of container window to {400, 100, 1000, 530} 9 | set theViewOptions to the icon view options of container window 10 | set arrangement of theViewOptions to not arranged 11 | set icon size of theViewOptions to 72 12 | set background picture of theViewOptions to file ".background:background.png" 13 | set position of item "Applications" of container window to {450,120} 14 | set position of item "Layered_WallPaper" of container window to {130,120} 15 | set position of item "Toggle_Autorun.command" of container window to {260,330} 16 | update without registering applications 17 | delay 5 18 | close 19 | end tell 20 | end tell 21 | end run 22 | -------------------------------------------------------------------------------- /defaultWin.cfg: -------------------------------------------------------------------------------- 1 | # ██╗ ██╗ ██╗██████╗ 2 | # ██║ ██║ ██║██╔══██╗ 3 | # ██║ ██║ █╗ ██║██████╔╝ 4 | # ██║ ██║███╗██║██╔═══╝ 5 | # ███████╗╚███╔███╔╝██║ 6 | # ╚══════╝ ╚══╝╚══╝ ╚═╝ 7 | 8 | # Smooth movement 9 | # Increasing this value will make the wallpaper layers move faster 10 | smooth=8.0 11 | 12 | # Monitors count 13 | monitors=1 14 | 15 | # Monitor position 16 | monitor1_x=0 17 | monitor1_y=0 18 | 19 | # Monitor resolution 20 | monitor1_w=1920 21 | monitor1_h=1080 22 | 23 | # Absolute path to the wallpaper directory 24 | # Leave the value as empty to use the default wallpaper 25 | monitor1_wallpaper= 26 | 27 | # Wallpaper size and position relative to Your monitor 28 | # Wallpaper resolution ratio should be the same as in original image 29 | monitor1_wallpaper_x=0 30 | monitor1_wallpaper_y=0 31 | monitor1_wallpaper_w=1920 32 | monitor1_wallpaper_h=1080 33 | 34 | # How many times per second should the wallpaper render 35 | # (imprecise, but accurate enough) 36 | target_fps=60 37 | -------------------------------------------------------------------------------- /defaultMac.cfg: -------------------------------------------------------------------------------- 1 | # ██╗ ██╗ ██╗██████╗ 2 | # ██║ ██║ ██║██╔══██╗ 3 | # ██║ ██║ █╗ ██║██████╔╝ 4 | # ██║ ██║███╗██║██╔═══╝ 5 | # ███████╗╚███╔███╔╝██║ 6 | # ╚══════╝ ╚══╝╚══╝ ╚═╝ 7 | 8 | # Smooth movement 9 | # Increasing this value will make the wallpaper layers move faster 10 | smooth=8.0 11 | 12 | # Monitors count 13 | monitors=1 14 | 15 | # Monitor position 16 | monitor1_x=0 17 | monitor1_y=0 18 | 19 | # Monitor resolution 20 | monitor1_w=1920 21 | monitor1_h=1080 22 | 23 | # Absolute path to the wallpaper directory 24 | # Leave the value as empty to use the default wallpaper 25 | monitor1_wallpaper=/Applications/Layered_WallPaper/wallpapers/default-fullhd 26 | 27 | # Wallpaper size and position relative to Your monitor 28 | # Wallpaper resolution ratio should be the same as in original image 29 | monitor1_wallpaper_x=0 30 | monitor1_wallpaper_y=0 31 | monitor1_wallpaper_w=1920 32 | monitor1_wallpaper_h=1080 33 | 34 | # How many times per second should the wallpaper render 35 | # (imprecise, but accurate enough) 36 | target_fps=60 37 | -------------------------------------------------------------------------------- /default.cfg: -------------------------------------------------------------------------------- 1 | # ██╗ ██╗ ██╗██████╗ 2 | # ██║ ██║ ██║██╔══██╗ 3 | # ██║ ██║ █╗ ██║██████╔╝ 4 | # ██║ ██║███╗██║██╔═══╝ 5 | # ███████╗╚███╔███╔╝██║ 6 | # ╚══════╝ ╚══╝╚══╝ ╚═╝ 7 | 8 | # If You are using a compositor, or lwp window deosn't show, 9 | # try setting this to 1 10 | reload_rootwindow=0 11 | 12 | # Smooth movement 13 | # Increasing this value will make the wallpaper layers move faster 14 | smooth=8.0 15 | 16 | # Monitors count 17 | monitors=1 18 | 19 | # Monitor position 20 | monitor1_x=0 21 | monitor1_y=0 22 | 23 | # Monitor resolution 24 | monitor1_w=1920 25 | monitor1_h=1080 26 | 27 | # Absolute path to the wallpaper directory 28 | monitor1_wallpaper=/usr/local/share/lwp/wallpapers/default-fullhd 29 | 30 | # Wallpaper size and position relative to Your monitor 31 | # Wallpaper resolution ratio should be the same as in original image 32 | monitor1_wallpaper_x=0 33 | monitor1_wallpaper_y=0 34 | monitor1_wallpaper_w=1920 35 | monitor1_wallpaper_h=1080 36 | 37 | # How many times per second should the wallpaper render 38 | # (imprecise, but accurate enough) 39 | target_fps=60 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jan Kozik 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 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #ifdef _MSC_VER 5 | #define PATH_MAX MAX_PATH 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #ifdef __WIN32 12 | #include 13 | #include 14 | #elif __DARWIN 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #elif __LINUX 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | typedef struct 30 | { 31 | float sensitivityX; 32 | float sensitivityY; 33 | SDL_Texture *tex; 34 | } Layer; 35 | 36 | typedef struct 37 | { 38 | char dirPath[PATH_MAX]; 39 | int repeatX, repeatY; 40 | int layersCount; 41 | Layer *layers; 42 | int originalW, originalH; 43 | SDL_Texture *tex; 44 | } Wallpaper; 45 | 46 | typedef struct 47 | { 48 | int x, y, w, h; 49 | int wallpaperX, wallpaperY, wallpaperW, wallpaperH; 50 | SDL_Texture *tex; 51 | Wallpaper wallpaper; 52 | } Monitor; 53 | typedef struct 54 | { 55 | int reloadRootWnd; 56 | int monitorsCount; 57 | float smooth; 58 | int targetFPS; 59 | Monitor *monitors; 60 | } Config; 61 | 62 | typedef struct 63 | { 64 | SDL_Window *window; 65 | SDL_Renderer *renderer; 66 | } App; 67 | 68 | #endif // MAIN_H 69 | -------------------------------------------------------------------------------- /wallpaper.c: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #include 3 | #else 4 | #include 5 | #endif 6 | 7 | #include "debug.h" 8 | #include "parser.h" 9 | 10 | static int loadMonitor(App *app, Monitor *monitor, Wallpaper *wallpaper, const char *dirPath) 11 | { 12 | char path[PATH_MAX]; 13 | 14 | snprintf(path, PATH_MAX, "%s/wallpaper.cfg", dirPath); 15 | 16 | if (!parseWallpaperConfig(wallpaper, path)) 17 | { 18 | return 0; 19 | } 20 | 21 | for (int i = 0; i < wallpaper->layersCount; i++) 22 | { 23 | snprintf(path, PATH_MAX, "%s/%d.bmp", dirPath, i + 1); 24 | 25 | SDL_Surface *surf = SDL_LoadBMP(path); 26 | if (!surf) 27 | { 28 | lwpLog(LOG_ERROR, "File %s not found", path); 29 | return 0; 30 | } 31 | 32 | if (i == 0) 33 | { 34 | wallpaper->originalW = surf->w; 35 | wallpaper->originalH = surf->h; 36 | } 37 | 38 | wallpaper->layers[i].tex = SDL_CreateTextureFromSurface(app->renderer, surf); 39 | 40 | SDL_FreeSurface(surf); 41 | } 42 | 43 | wallpaper->tex = 44 | SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 45 | monitor->wallpaperW, monitor->wallpaperH); 46 | 47 | monitor->tex = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_ARGB8888, 48 | SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h); 49 | 50 | return 1; 51 | } 52 | 53 | int loadMonitors(App *app, Config *cfg) 54 | { 55 | for (int m = 0; m < cfg->monitorsCount; m++) 56 | if (!loadMonitor(app, &cfg->monitors[m], &cfg->monitors[m].wallpaper, 57 | cfg->monitors[m].wallpaper.dirPath)) 58 | return 0; 59 | 60 | return 1; 61 | } 62 | 63 | void freeMonitor(Monitor *monitor) 64 | { 65 | SDL_DestroyTexture(monitor->tex); 66 | SDL_DestroyTexture(monitor->wallpaper.tex); 67 | 68 | for (int i = 0; i < monitor->wallpaper.layersCount; i++) 69 | { 70 | SDL_DestroyTexture(monitor->wallpaper.layers[i].tex); 71 | } 72 | free(monitor->wallpaper.layers); 73 | } 74 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build app 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main", "dev" ] 8 | 9 | jobs: 10 | linux: 11 | name: Build on Linux 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | - name: Install cmake 18 | uses: lukka/get-cmake@v3.26.0 19 | - name: Install dependencies 20 | run: sudo apt install libsdl2-dev 21 | - name: Build 22 | run: mkdir -p build && cd build && cmake ../ && cmake --build . && cpack 23 | 24 | windows: 25 | name: Build on Windows 26 | runs-on: windows-latest 27 | 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Install cmake 32 | uses: lukka/get-cmake@v3.26.0 33 | - name: Install MinGW 34 | uses: egor-tensin/setup-mingw@v2 35 | - name: Install MSVC 36 | uses: ilammy/msvc-dev-cmd@v1 37 | - name: Install wget 38 | run: choco install wget 39 | - name: Install dependencies` 40 | run: | 41 | wget -nv https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-mingw.zip 42 | 7z x -bd SDL2-devel-2.26.3-mingw.zip -omingw 43 | wget -nv https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-VC.zip 44 | 7z x -bd SDL2-devel-2.26.3-VC.zip -omsvc 45 | - name: Build with MSVC 46 | run: | 47 | mkdir buildMSVC 48 | cd buildMSVC 49 | cmake -G "Visual Studio 17" -DSDL2_DIR=${{ github.workspace }}\msvc\SDL2-2.26.3\cmake -DSDL2_RUNTIME_DIR=${{ github.workspace }}\msvc\SDL2-2.26.3\lib\x64 ..\ 50 | cmake --build . 51 | cd .. 52 | - name: Build with MinGW 53 | run: | 54 | mkdir buildMinGW 55 | cd buildMinGW 56 | cmake -G "MinGW Makefiles" -DSDL2_DIR=${{ github.workspace }}\mingw\SDL2-2.26.3\cmake -DSDL2_RUNTIME_DIR=${{ github.workspace }}\mingw\SDL2-2.26.3\x86_64-mingw32\bin -DCMAKE_BUILD_TYPE=Release ..\ 57 | cmake --build . 58 | 59 | 60 | macOS: 61 | name: Build on MacOS 62 | runs-on: macos-12 63 | 64 | steps: 65 | - name: Checkout 66 | uses: actions/checkout@v3 67 | - name: Install cmake 68 | uses: lukka/get-cmake@v3.26.0 69 | - name: Install dependencies` 70 | run: brew install sdl2 71 | - name: Build 72 | run: mkdir build && cd build && cmake ../ && cmake --build . && sudo cpack 73 | -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd %SystemRoot%\system32 3 | title Install LWP 4 | 5 | call :logo 6 | net session >nul 2>&1 7 | if not %ERRORLEVEL% equ 0 ( 8 | echo Run me as admin 9 | pause>nul 10 | exit 11 | ) 12 | 13 | call :logo 14 | echo. 15 | echo If You enjoy lwp, You can give it a start on github 16 | echo https://github.com/jszczerbinsky/lwp 17 | echo. 18 | echo Lwp will be installed on Your computer 19 | echo Click any key to display the license 20 | pause > nul 21 | 22 | cls 23 | echo ________________________________________________________________________________________________________________________ 24 | type "%~dp0\data\LICENSE.txt" 25 | echo ________________________________________________________________________________________________________________________ 26 | echo. 27 | echo By continuing the installation You accept the license conditions 28 | echo. 29 | set /p val="Continue? (y/n) > " 30 | if not %val%==y ( 31 | call :logo 32 | echo You have to accept the license to continue installation 33 | echo Installator will exit now 34 | echo. 35 | echo Press any key to continue... 36 | pause>nul 37 | exit 38 | ) 39 | call :logo 40 | echo Starting installation 41 | echo. 42 | path=%ProgramFiles% 43 | echo Copying files... 44 | robocopy "%~dp0\data" "%path%\lwp" /E /NFL /NDL /NJH /NJS /nc /ns /np 45 | echo Adding lwp to autorun... 46 | reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v "lwp" /t REG_EXPAND_SZ /d "%path%\lwp\lwp.exe" /f > nul 47 | echo Creating uninstaller... 48 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /f > nul 49 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "UninstallString" /t REG_SZ /d "cmd /c \"%path%\lwp\uninstall.bat\"" /f > nul 50 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "DisplayName" /t REG_SZ /d "Layered WallPaper" /f > nul 51 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "DisplayIcon" /t REG_SZ /d "%path%\lwp\lwp.exe" /f > nul 52 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "Publisher" /t REG_SZ /d "Jakub Szczerbinski" /f > nul 53 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "URLInfoAbout" /t REG_SZ /d "https://github.com/jszczerbinsky/lwp" /f > nul 54 | 55 | echo. 56 | echo Lwp has been successfuly installed on Your computer! 57 | start "" "%path%\lwp\lwp.exe" 58 | pause > nul 59 | exit 60 | 61 | :logo 62 | cls 63 | echo. 64 | echo. 65 | echo " __ _ ______ " 66 | echo " / /| | / / __ \ " 67 | echo " / / | | /| / / /_/ / " 68 | echo " / /__| |/ |/ / ____/ " 69 | echo " /_____/__/|__/_/ " 70 | echo. 71 | echo. 72 | exit /B 0 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(lwp 4 | LANGUAGES C) 5 | 6 | if(SDL2_RUNTIME_DIR) 7 | string(REPLACE "\\" "/" SDL2_RUNTIME_DIR ${SDL2_RUNTIME_DIR}) 8 | endif() 9 | 10 | # Detect the platform 11 | if (APPLE) 12 | set(_UNAME "DARWIN") 13 | elseif (WIN32) 14 | set(_UNAME "WIN32") 15 | else() 16 | set(_UNAME "LINUX") 17 | endif() 18 | 19 | # Detect version 20 | find_package(Git) 21 | if(Git_FOUND) 22 | execute_process( 23 | COMMAND ${GIT_EXECUTABLE} describe --tags --dirty --match "v*" 24 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 25 | OUTPUT_VARIABLE GIT_DESCRIBE_VERSION 26 | RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE 27 | OUTPUT_STRIP_TRAILING_WHITESPACE 28 | ) 29 | if(NOT GIT_DESCRIBE_ERROR_CODE) 30 | set(PROGRAM_VERSION ${GIT_DESCRIBE_VERSION}) 31 | endif() 32 | endif() 33 | if(NOT DEFINED PROGRAM_VERSION) 34 | set(PROGRAM_VERSION v0.0.0-unknown) 35 | message(WARNING "Can't determine Layered WallPaper version") 36 | endif() 37 | 38 | set(_SOURCE_FILES 39 | main.c 40 | debug.c 41 | parser.c 42 | wallpaper.c 43 | window.c 44 | ) 45 | 46 | # Windows resource file 47 | if(_UNAME STREQUAL "WIN32") 48 | if(MINGW) 49 | set(CMAKE_RC_COMPILER_INIT windres) 50 | ENABLE_LANGUAGE(RC) 51 | SET(CMAKE_RC_COMPILE_OBJECT 52 | " -O coff -i -o ") 53 | endif(MINGW) 54 | 55 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource.template.rc ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc 56 | @ONLY) 57 | list(APPEND _SOURCE_FILES "resource.rc") 58 | endif() 59 | 60 | # SDL2 dependency 61 | find_package(SDL2 REQUIRED CONFIG) 62 | set(_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) 63 | set(_LIBS ${SDL2_LIBRARIES}) 64 | 65 | if (_UNAME STREQUAL "DARWIN") 66 | # MacOSX framework dependencies 67 | list(APPEND _LIBS "-framework CoreGraphics" "-framework Foundation") 68 | endif() 69 | 70 | if (_UNAME STREQUAL "LINUX") 71 | # X11 dependency 72 | find_package(X11 REQUIRED) 73 | list(APPEND _INCLUDE_DIRS ${X11_INCLUDE_DIR}) 74 | list(APPEND _LIBS ${X11_LIBRARIES}) 75 | endif() 76 | 77 | option(LWP_INSTALL_LAUNCHD "Launch lwp on login (MacOSX only)" OFF) 78 | 79 | # Config file 80 | set(_DEFAULT_CONFIG_FILE default.cfg) 81 | if(_UNAME STREQUAL "WIN32") 82 | set(_DEFAULT_CONFIG_FILE defaultWin.cfg) 83 | endif() 84 | if (_UNAME STREQUAL "DARWIN") 85 | set(_DEFAULT_CONFIG_FILE defaultMac.cfg) 86 | endif() 87 | 88 | # Main executable 89 | if (_UNAME STREQUAL "DARWIN") 90 | add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) 91 | else() 92 | add_executable(lwp ${_SOURCE_FILES}) 93 | endif() 94 | 95 | # Windows specific properties for executable 96 | if(_UNAME STREQUAL "WIN32") 97 | set_property(TARGET lwp PROPERTY VS_DPI_AWARE "PerMonitor") 98 | endif() 99 | 100 | if(MSVC) 101 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") 102 | endif() 103 | 104 | target_compile_definitions(lwp PUBLIC __${_UNAME}) 105 | target_include_directories(lwp PUBLIC ${_INCLUDE_DIRS}) 106 | target_link_libraries(lwp PRIVATE ${_LIBS}) 107 | 108 | # Installation rules 109 | if(_UNAME STREQUAL "WIN32") 110 | install(TARGETS lwp 111 | DESTINATION .) 112 | install(DIRECTORY wallpapers 113 | DESTINATION .) 114 | install(FILES LICENSE.txt 115 | DESTINATION .) 116 | install(FILES ${_DEFAULT_CONFIG_FILE} 117 | DESTINATION .) 118 | install(FILES ${SDL2_RUNTIME_DIR}/SDL2.dll 119 | DESTINATION .) 120 | install(FILES ${SDL2_RUNTIME_DIR}/README-SDL.txt 121 | DESTINATION .) 122 | elseif(_UNAME STREQUAL "LINUX") 123 | install(TARGETS lwp 124 | DESTINATION usr/local/bin) 125 | install(DIRECTORY wallpapers 126 | DESTINATION usr/local/share/lwp) 127 | install(FILES LICENSE.txt 128 | DESTINATION usr/local/share/lwp) 129 | install(FILES ${_DEFAULT_CONFIG_FILE} 130 | TYPE SYSCONF 131 | RENAME lwp.cfg) 132 | else() 133 | install(TARGETS lwp 134 | DESTINATION Layered_WallPaper) 135 | install(DIRECTORY wallpapers 136 | DESTINATION Layered_WallPaper) 137 | install(FILES LICENSE.txt 138 | DESTINATION Layered_WallPaper) 139 | install(FILES ${_DEFAULT_CONFIG_FILE} 140 | DESTINATION Layered_WallPaper 141 | RENAME lwp.cfg) 142 | install(FILES lwp.template.plist 143 | DESTINATION Layered_WallPaper) 144 | install (FILES setupPlist.command 145 | DESTINATION Layered_WallPaper 146 | RENAME Toggle_Autorun.command 147 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) 148 | install (FILES setupPlist.command 149 | DESTINATION . 150 | RENAME Toggle_Autorun.command 151 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) 152 | 153 | endif() 154 | 155 | # Installer 156 | if (_UNAME STREQUAL "WIN32") 157 | set(CPACK_GENERATOR NSIS) 158 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "Layered WallPaper") 159 | elseif(_UNAME STREQUAL "LINUX") 160 | set(CPACK_GENERATOR TGZ) 161 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) 162 | elseif(_UNAME STREQUAL "DARWIN") 163 | set(CPACK_GENERATOR DragNDrop) 164 | set(CPACK_DMG_BACKGROUND_IMAGE ${CMAKE_CURRENT_SOURCE_DIR}/dmgBg.png) 165 | set(CPACK_DMG_DS_STORE_SETUP_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/setupDmg.applescript) 166 | endif() 167 | set(CPACK_NSIS_MUI_ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) 168 | set(CPACK_NSIS_MUI_UNIICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) 169 | set(CPACK_NSIS_INSTALLED_ICON_NAME ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) 170 | set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " 171 | WriteRegExpandStr HKLM 'Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run' 'lwp' '\$INSTDIR\\\\lwp.exe' 172 | Exec '\$INSTDIR\\\\lwp.exe' 173 | ") 174 | set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " 175 | DeleteRegValue HKLM 'Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run' 'lwp' 176 | ") 177 | set(CPACK_PACKAGE_NAME "Layered WallPaper") 178 | set(CPACK_PACKAGE_VENDOR "Jakub Szczerbinski") 179 | set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt) 180 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Multi-platform parallax wallpapers") 181 | set(CPACK_PACKAGE_VERSION ${PROGRAM_VERSION}) 182 | 183 | INCLUDE(CPack) 184 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | #include "debug.h" 4 | #include "parser.h" 5 | #include "platform_guard.h" 6 | #include "wallpaper.h" 7 | #include "window.h" 8 | 9 | static int lerp(int a, int b, float t) 10 | { 11 | if (t > 1) t = 1; 12 | return (int)((float)a + (float)t * ((float)b - (float)a)); 13 | } 14 | 15 | static int init(App *app, Config *cfg) 16 | { 17 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) 18 | { 19 | lwpLog(LOG_ERROR, "%s", SDL_GetError()); 20 | return 0; 21 | } 22 | 23 | initWindow(app, cfg); 24 | 25 | app->renderer = 26 | SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 27 | if (app->renderer == NULL) 28 | { 29 | lwpLog(LOG_ERROR, "%s", SDL_GetError()); 30 | return 0; 31 | } 32 | 33 | return 1; 34 | } 35 | 36 | #ifdef __WIN32 37 | static void initCmd() 38 | { 39 | // Create console 40 | AllocConsole(); 41 | AttachConsole(ATTACH_PARENT_PROCESS); 42 | freopen("CONOUT$", "w", stdout); 43 | HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 44 | DWORD dwMode = 0; 45 | GetConsoleMode(hOut, &dwMode); 46 | SetConsoleMode(hOut, dwMode | 0x0004); 47 | 48 | // Remove closing button (because closing it closes the entire app) 49 | HWND hwnd = GetConsoleWindow(); 50 | HMENU hMenu = GetSystemMenu(hwnd, FALSE); 51 | DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); 52 | 53 | // Set console title 54 | SetConsoleTitle("Layered WallPaper"); 55 | } 56 | BOOL monitorenumproc( 57 | HMONITOR monitor, 58 | HDC hdc, 59 | LPRECT rect, 60 | LPARAM param 61 | ) 62 | { 63 | 64 | int px = GetSystemMetrics(SM_XVIRTUALSCREEN); 65 | int py = GetSystemMetrics(SM_YVIRTUALSCREEN); 66 | 67 | MONITORINFO monitorInfo; 68 | monitorInfo.cbSize = sizeof(MONITORINFO); 69 | 70 | GetMonitorInfo(monitor, &monitorInfo); 71 | lwpLog(LOG_INFO, " Monitor: position %ldx%ld, size %ldx%ld %s", 72 | monitorInfo.rcMonitor.left - px, 73 | monitorInfo.rcMonitor.top - py, 74 | monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, 75 | monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, 76 | (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY ? "primary" : "" 77 | ); 78 | 79 | return TRUE; 80 | } 81 | static void scanMonitors() 82 | { 83 | lwpLog(LOG_INFO, "Scanning monitors..."); 84 | EnumDisplayMonitors(NULL, NULL, &monitorenumproc, NULL); 85 | } 86 | #endif 87 | 88 | int main(int argc, char *argv[]) 89 | { 90 | lwpLog(LOG_INFO, "Starting Layered WallPaper"); 91 | 92 | #ifdef __WIN32 93 | if (argc == 2 && strcmp(argv[1], "/console") == 0) initCmd(); 94 | initTrayIcon(); 95 | 96 | scanMonitors(); 97 | #endif 98 | 99 | App app; 100 | Config cfg; 101 | 102 | int canRender = 1; 103 | 104 | if (!parseConfig(&app, &cfg) || !init(&app, &cfg) || !loadMonitors(&app, &cfg)) canRender = 0; 105 | 106 | SDL_Event event; 107 | int quit = 0; 108 | 109 | int mx = 0; 110 | int my = 0; 111 | 112 | while (!quit) 113 | { 114 | if(canRender) 115 | { 116 | static int currentX = 0; 117 | static int currentY = 0; 118 | 119 | static int lastTicks = 0; 120 | 121 | int ticks = SDL_GetTicks(); 122 | float dT = (ticks - lastTicks) / 1000.0f; 123 | lastTicks = ticks; 124 | 125 | #ifdef __WIN32 126 | POINT mPos; 127 | GetCursorPos(&mPos); 128 | mx = mPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN); 129 | my = mPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN); 130 | #else 131 | SDL_GetGlobalMouseState(&mx, &my); 132 | #endif 133 | 134 | while (SDL_PollEvent(&event)) 135 | if (event.type == SDL_QUIT) quit = 1; 136 | 137 | currentX = lerp(currentX, mx, dT * cfg.smooth); 138 | currentY = lerp(currentY, my, dT * cfg.smooth); 139 | 140 | for (int m = 0; m < cfg.monitorsCount; m++) 141 | { 142 | int relativeCurrentX = currentX - cfg.monitors[m].x; 143 | int relativeCurrentY = currentY - cfg.monitors[m].y; 144 | 145 | if (relativeCurrentX < 0) relativeCurrentX = 0; 146 | if (relativeCurrentY < 0) relativeCurrentY = 0; 147 | if (relativeCurrentX > cfg.monitors[m].w) relativeCurrentX = cfg.monitors[m].w; 148 | if (relativeCurrentY > cfg.monitors[m].h) relativeCurrentY = cfg.monitors[m].h; 149 | 150 | SDL_SetRenderTarget(app.renderer, cfg.monitors[m].wallpaper.tex); 151 | SDL_RenderClear(app.renderer); 152 | 153 | for (int i = 0; i < cfg.monitors[m].wallpaper.layersCount; i++) 154 | { 155 | SDL_Rect src = { 156 | .x = 0, 157 | .y = 0, 158 | .w = cfg.monitors[m].wallpaper.originalW, 159 | .h = cfg.monitors[m].wallpaper.originalH, 160 | }; 161 | 162 | int x = -((relativeCurrentX - cfg.monitors[m].w / 2) * 163 | cfg.monitors[m].wallpaper.layers[i].sensitivityX); 164 | int y = -((relativeCurrentY - cfg.monitors[m].h / 2) * 165 | cfg.monitors[m].wallpaper.layers[i].sensitivityY); 166 | 167 | for (int k = -cfg.monitors[m].wallpaper.repeatY; k <= cfg.monitors[m].wallpaper.repeatY; 168 | k++) 169 | { 170 | for (int j = -cfg.monitors[m].wallpaper.repeatX; j <= cfg.monitors[m].wallpaper.repeatX; 171 | j++) 172 | { 173 | SDL_Rect dest = { 174 | .x = x + j * cfg.monitors[m].wallpaperW, 175 | .y = y + k * cfg.monitors[m].wallpaperH, 176 | .w = cfg.monitors[m].wallpaperW, 177 | .h = cfg.monitors[m].wallpaperH, 178 | }; 179 | 180 | SDL_RenderCopy(app.renderer, cfg.monitors[m].wallpaper.layers[i].tex, &src, &dest); 181 | } 182 | } 183 | } 184 | 185 | SDL_SetRenderTarget(app.renderer, cfg.monitors[m].tex); 186 | 187 | SDL_Rect src = { 188 | .x = 0, 189 | .y = 0, 190 | .w = cfg.monitors[m].wallpaperW, 191 | .h = cfg.monitors[m].wallpaperH, 192 | }; 193 | 194 | SDL_Rect dest = { 195 | .x = cfg.monitors[m].wallpaperX, 196 | .y = cfg.monitors[m].wallpaperY, 197 | .w = cfg.monitors[m].wallpaperW, 198 | .h = cfg.monitors[m].wallpaperH, 199 | }; 200 | 201 | SDL_RenderCopy(app.renderer, cfg.monitors[m].wallpaper.tex, &src, &dest); 202 | 203 | SDL_SetRenderTarget(app.renderer, NULL); 204 | 205 | SDL_Rect finalSrc = { 206 | .x = 0, 207 | .y = 0, 208 | .w = cfg.monitors[m].w, 209 | .h = cfg.monitors[m].h, 210 | }; 211 | 212 | SDL_Rect finalDest = { 213 | .x = cfg.monitors[m].x, 214 | .y = cfg.monitors[m].y, 215 | .w = cfg.monitors[m].w, 216 | .h = cfg.monitors[m].h, 217 | }; 218 | 219 | SDL_RenderCopy(app.renderer, cfg.monitors[m].tex, &finalSrc, &finalDest); 220 | } 221 | SDL_RenderPresent(app.renderer); 222 | } 223 | SDL_Delay(1000 / cfg.targetFPS); 224 | #ifdef __WIN32 225 | if(!updateTrayIcon()) quit = 1; 226 | #endif 227 | } 228 | 229 | #ifdef __WIN32 230 | removeTrayIcon(); 231 | #endif 232 | 233 | freeConfig(&cfg); 234 | 235 | SDL_DestroyRenderer(app.renderer); 236 | SDL_DestroyWindow(app.window); 237 | SDL_Quit(); 238 | 239 | return 0; 240 | } 241 | -------------------------------------------------------------------------------- /window.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "main.h" 3 | 4 | #ifdef __WIN32 5 | #include 6 | #include 7 | #ifdef _MSC_VER 8 | #include 9 | #else 10 | #include 11 | #endif // _MSC_VER 12 | #endif // __WIN32 13 | 14 | #ifdef __LINUX 15 | #include 16 | #include 17 | #include 18 | #endif //__LINUX 19 | 20 | #ifdef __WIN32 21 | 22 | static HWND iconWorkerw; 23 | static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) 24 | { 25 | char buff[10]; 26 | GetClassName(hWnd, buff, 10); 27 | 28 | if (strcmp(buff, "WorkerW") == 0) 29 | { 30 | HWND defView = FindWindowEx(hWnd, NULL, "SHELLDLL_DefView", NULL); 31 | if (defView) 32 | { 33 | iconWorkerw = hWnd; 34 | return FALSE; 35 | } 36 | } 37 | return TRUE; 38 | } 39 | 40 | #define WM_TRAY_ICON (WM_USER + 1) 41 | 42 | static NOTIFYICONDATA nid; 43 | static int quit = 0; 44 | 45 | void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } 46 | 47 | int updateTrayIcon() 48 | { 49 | MSG msg; 50 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 51 | { 52 | TranslateMessage(&msg); 53 | DispatchMessage(&msg); 54 | } 55 | 56 | return !quit; 57 | } 58 | 59 | static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 60 | { 61 | switch (uMsg) 62 | { 63 | case WM_TRAY_ICON: 64 | if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) 65 | { 66 | int res = MessageBox(NULL, "Do You want to run Layered WallPaper with debug console?", 67 | "Restart Layered WallPaper", MB_YESNOCANCEL | MB_ICONQUESTION); 68 | 69 | TCHAR processParam = NULL; 70 | 71 | if (res != IDCANCEL) 72 | { 73 | TCHAR fileName[MAX_PATH]; 74 | GetModuleFileName(NULL, fileName, MAX_PATH); 75 | 76 | TCHAR cmd[MAX_PATH + 10]; 77 | _tcscpy(cmd, fileName); 78 | 79 | if (res == IDYES) _tcscat(cmd, " /console"); 80 | 81 | STARTUPINFO si; 82 | memset(&si, 0, sizeof(STARTUPINFO)); 83 | 84 | PROCESS_INFORMATION pi; 85 | 86 | CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 87 | 88 | quit = 1; 89 | } 90 | } 91 | break; 92 | } 93 | } 94 | 95 | void initTrayIcon() 96 | { 97 | // Create an invisible window to process tray icon events 98 | 99 | HINSTANCE hInstance = GetModuleHandle(NULL); 100 | const wchar_t CLASS_NAME[] = L"Hidden Window"; 101 | WNDCLASS wc; 102 | memset(&wc, 0, sizeof(WNDCLASS)); 103 | wc.lpfnWndProc = wndProc; 104 | wc.hInstance = hInstance; 105 | wc.lpszClassName = CLASS_NAME; 106 | RegisterClass(&wc); 107 | HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 108 | CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); 109 | 110 | // Create tray icon 111 | 112 | nid.cbSize = sizeof(NOTIFYICONDATA); 113 | nid.hWnd = hWnd; 114 | nid.uCallbackMessage = WM_TRAY_ICON; 115 | nid.hIcon = LoadIcon(hInstance, "ID"); 116 | nid.uFlags = NIF_ICON | NIF_MESSAGE; 117 | 118 | Shell_NotifyIcon(NIM_ADD, &nid); 119 | } 120 | 121 | void initWindow(App *app, Config *cfg) 122 | { 123 | app->window = 124 | SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); 125 | if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); 126 | 127 | SDL_SysWMinfo sysWmInfo; 128 | SDL_VERSION(&sysWmInfo.version) 129 | SDL_GetWindowWMInfo(app->window, &sysWmInfo); 130 | HWND hWindow = sysWmInfo.info.win.window; 131 | 132 | HWND progman = FindWindow("Progman", NULL); 133 | iconWorkerw = progman; 134 | SendMessageTimeout(progman, 0x052C, NULL, NULL, SMTO_NORMAL, 1000, NULL); 135 | if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) EnumWindows(getIconWorkerw, NULL); 136 | 137 | HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); 138 | SetParent(hWindow, wallpaperWorkerw); 139 | SetWindowLongPtr(hWindow, GWL_EXSTYLE, 140 | WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE); 141 | SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); 142 | 143 | SetWindowPos(hWindow, NULL, 0, 0, 144 | GetSystemMetrics(SM_CXVIRTUALSCREEN), 145 | GetSystemMetrics(SM_CYVIRTUALSCREEN), 146 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); 147 | } 148 | 149 | #elif __DARWIN 150 | 151 | // Helper macros for objc runtime interaction 152 | #define OBJC_MSG_INT ((id(*)(id, SEL, int))objc_msgSend) 153 | #define OBJC_MSG_ID ((id(*)(id, SEL, id))objc_msgSend) 154 | #define OBJC_MSG_PTR ((id(*)(id, SEL, void *))objc_msgSend) 155 | #define OBJC_MSG_CLS ((id(*)(Class, SEL))objc_msgSend) 156 | #define OBJC_MSG_CLS_CHR ((id(*)(Class, SEL, char *))objc_msgSend) 157 | 158 | void initWindow(App *app, Config *cfg) 159 | { 160 | // Get main display size 161 | const CGDirectDisplayID displayID = CGMainDisplayID(); 162 | const size_t w = CGDisplayPixelsWide(displayID); 163 | const size_t h = CGDisplayPixelsHigh(displayID); 164 | const struct CGRect frameRect = {0, 0, w, h}; 165 | 166 | // Get shared NSApplication instance 167 | const id ns_app = OBJC_MSG_CLS(objc_getClass("NSApplication"), sel_getUid("sharedApplication")); 168 | OBJC_MSG_INT(ns_app, sel_getUid("setActivationPolicy:"), 169 | 0); // NSApplicationActivationPolicyRegular 170 | 171 | // Create NSWindow 172 | const id window = ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( 173 | OBJC_MSG_CLS(objc_getClass("NSWindow"), sel_getUid("alloc")), 174 | sel_getUid("initWithContentRect:styleMask:backing:defer:"), frameRect, 175 | 0, // NSWindowStyleMaskBorderless 176 | 2, // NSBackingStoreBuffered 177 | false); 178 | 179 | // Set window attributes 180 | OBJC_MSG_ID(window, sel_getUid("setTitle:"), 181 | OBJC_MSG_CLS_CHR(objc_getClass("NSString"), sel_getUid("stringWithUTF8String:"), 182 | "Parallax wallpaper")); 183 | OBJC_MSG_PTR(window, sel_getUid("makeKeyAndOrderFront:"), nil); 184 | OBJC_MSG_INT(window, sel_getUid("setLevel:"), kCGDesktopWindowLevel - 1); 185 | OBJC_MSG_INT(ns_app, sel_getUid("activateIgnoringOtherApps:"), true); 186 | 187 | // Create SDL window from NSWindow 188 | app->window = SDL_CreateWindowFrom((void *)window); 189 | if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); 190 | } 191 | 192 | #elif __LINUX 193 | 194 | void initWindow(App *app, Config *cfg) 195 | { 196 | if (cfg->reloadRootWnd) 197 | { 198 | Display *display = XOpenDisplay(NULL); 199 | XCloseDisplay(display); 200 | 201 | app->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, DisplayWidth(display, 0), 202 | DisplayHeight(display, 0), SDL_WINDOW_OPENGL); 203 | 204 | SDL_SysWMinfo wmInfo; 205 | SDL_GetVersion(&wmInfo.version); 206 | SDL_GetWindowWMInfo(app->window, &wmInfo); 207 | 208 | Window xWnd = wmInfo.info.x11.window; 209 | display = wmInfo.info.x11.display; 210 | 211 | Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); 212 | Atom atomDesktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); 213 | XChangeProperty(display, xWnd, atomType, XA_ATOM, 32, PropModeReplace, 214 | (const unsigned char *)&atomDesktop, 1); 215 | 216 | Window rootWindow = RootWindow(display, DefaultScreen(display)); 217 | 218 | XReparentWindow(display, xWnd, rootWindow, 0, 0); 219 | 220 | XSync(display, 0); 221 | } 222 | else 223 | { 224 | Display *display = XOpenDisplay(NULL); 225 | Window rootWindow = RootWindow(display, DefaultScreen(display)); 226 | app->window = SDL_CreateWindowFrom((void *)rootWindow); 227 | XCloseDisplay(display); 228 | } 229 | 230 | if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); 231 | } 232 | #endif 233 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "debug.h" 7 | #include "wallpaper.h" 8 | 9 | #define TYPE_FLOAT 0 10 | #define TYPE_INT 1 11 | #define TYPE_STR 2 12 | 13 | #ifdef __WIN32 14 | static void getline(char **buff, size_t *buffSize, FILE *f) 15 | { 16 | long int startPos = ftell(f); 17 | int length = 1; 18 | char c; 19 | do 20 | { 21 | c = fgetc(f); 22 | length++; 23 | } while (c != '\n' && !feof(f)); 24 | 25 | if (*buffSize < length) 26 | { 27 | *buff = realloc(*buff, length * sizeof(char)); 28 | *buffSize = length * sizeof(char); 29 | } 30 | 31 | fseek(f, startPos, SEEK_SET); 32 | 33 | fgets(*buff, length - 1, f); 34 | (*buff)[length - 1] = '\0'; 35 | fgetc(f); 36 | } 37 | #endif 38 | 39 | static FILE *openConfigFile() 40 | { 41 | FILE *f; 42 | char userConfigPath[PATH_MAX]; 43 | 44 | #ifdef __WIN32 45 | strcpy(userConfigPath, getenv("AppData")); 46 | strcat(userConfigPath, "\\lwp\\lwp.cfg"); 47 | #else 48 | char *xdgConfigHome = getenv("XDG_CONFIG_HOME"); 49 | if (xdgConfigHome) 50 | { 51 | strcpy(userConfigPath, xdgConfigHome); 52 | strcat(userConfigPath, "/lwp/lwp.cfg"); 53 | } 54 | else 55 | { 56 | struct passwd *pw = getpwuid(getuid()); 57 | strcpy(userConfigPath, pw->pw_dir); 58 | strcat(userConfigPath, "/.config/lwp/lwp.cfg"); 59 | } 60 | #endif 61 | 62 | f = fopen(userConfigPath, "r"); 63 | if (!f) 64 | { 65 | lwpLog(LOG_INFO, "User config file not found, opening default config instead"); 66 | char defaultConfigPath[PATH_MAX]; 67 | #ifdef __WIN32 68 | GetModuleFileNameA(NULL, defaultConfigPath, PATH_MAX); 69 | char *ptr = defaultConfigPath + strlen(defaultConfigPath) - 1; 70 | while (*ptr != '\\') ptr--; 71 | *ptr = '\0'; 72 | strcat(defaultConfigPath, "\\defaultWin.cfg"); 73 | 74 | f = fopen(defaultConfigPath, "r"); 75 | #elif __DARWIN 76 | strcpy(defaultConfigPath, "/Applications/Layered_WallPaper/lwp.cfg"); 77 | 78 | f = fopen(defaultConfigPath, "r"); 79 | #else 80 | char *sysConfDir = getenv("sysconfdir"); 81 | if (sysConfDir) 82 | { 83 | strcpy(defaultConfigPath, sysConfDir); 84 | strcat(defaultConfigPath, "/lwp.cfg"); 85 | } 86 | else 87 | strcpy(defaultConfigPath, "/etc/lwp.cfg"); 88 | 89 | f = fopen(defaultConfigPath, "r"); 90 | #endif 91 | if (!f) lwpLog(LOG_ERROR, "Default config file not found!"); 92 | } 93 | 94 | return f; 95 | } 96 | 97 | static int findLine(FILE *f, const char *name, int type, void *output) 98 | { 99 | char *buff = NULL; 100 | size_t buffSize = 0; 101 | 102 | int found = 0; 103 | 104 | fseek(f, 0, SEEK_SET); 105 | 106 | do 107 | { 108 | getline(&buff, &buffSize, f); 109 | 110 | if (buffSize > 0 && buff[0] != '#') 111 | { 112 | if (strncmp(name, buff, strlen(name)) == 0) 113 | { 114 | char *valuePtr = buff; 115 | while (*valuePtr != '=' && *valuePtr != '\0') 116 | { 117 | valuePtr++; 118 | } 119 | 120 | if (*valuePtr == '=') 121 | { 122 | valuePtr++; 123 | 124 | if (valuePtr[strlen(valuePtr) - 1] == '\n') valuePtr[strlen(valuePtr) - 1] = '\0'; 125 | 126 | found = 1; 127 | 128 | switch (type) 129 | { 130 | case TYPE_INT: 131 | *(int *)output = atoi(valuePtr); 132 | break; 133 | case TYPE_FLOAT: 134 | *(float *)output = atof(valuePtr); 135 | break; 136 | case TYPE_STR: 137 | strcpy((char *)output, valuePtr); 138 | break; 139 | } 140 | } 141 | } 142 | } 143 | } while (!found && buffSize > 0 && !feof(f)); 144 | 145 | free(buff); 146 | return found; 147 | } 148 | 149 | int parseConfig(App *app, Config *cfg) 150 | { 151 | lwpLog(LOG_INFO, "Loading config file"); 152 | 153 | FILE *f = openConfigFile(); 154 | 155 | if (!findLine(f, "monitors", TYPE_INT, &cfg->monitorsCount)) 156 | { 157 | lwpLog(LOG_ERROR, "Can't find line 'monitors' in config"); 158 | return 0; 159 | } 160 | lwpLog(LOG_INFO, " monitors: %d", cfg->monitorsCount); 161 | 162 | if (!findLine(f, "smooth", TYPE_FLOAT, &cfg->smooth)) 163 | { 164 | lwpLog(LOG_INFO, "Can't find line 'smooth' in config, setting to default value"); 165 | cfg->smooth = 8; 166 | } 167 | lwpLog(LOG_INFO, " smooth: %f", cfg->smooth); 168 | 169 | if (!findLine(f, "target_fps", TYPE_INT, &cfg->targetFPS)) 170 | { 171 | lwpLog(LOG_INFO, "Can't find line 'target_fps' in config, setting to default value"); 172 | cfg->targetFPS = 60; 173 | } 174 | lwpLog(LOG_INFO, " target_fps: %d", cfg->targetFPS); 175 | 176 | #ifdef __LINUX 177 | if (!findLine(f, "reload_rootwindow", TYPE_INT, &cfg->reloadRootWnd)) 178 | { 179 | lwpLog(LOG_ERROR, "Can't find line 'reload_rootwindow' in config"); 180 | return 0; 181 | } 182 | lwpLog(LOG_INFO, " reload_rootwindow: %d", cfg->reloadRootWnd); 183 | #endif 184 | 185 | cfg->monitors = malloc(cfg->monitorsCount * sizeof(Monitor)); 186 | 187 | for (int m = 0; m < cfg->monitorsCount; m++) 188 | { 189 | char wallpaperPath[PATH_MAX]; 190 | 191 | const int PARAMS_COUNT = 9; 192 | 193 | const char *paramStr[] = {"wallpaper", "x", "y", "w", "h", 194 | "wallpaper_x", "wallpaper_y", "wallpaper_w", "wallpaper_h"}; 195 | void *outputs[] = { 196 | wallpaperPath, 197 | &cfg->monitors[m].x, 198 | &cfg->monitors[m].y, 199 | &cfg->monitors[m].w, 200 | &cfg->monitors[m].h, 201 | &cfg->monitors[m].wallpaperX, 202 | &cfg->monitors[m].wallpaperY, 203 | &cfg->monitors[m].wallpaperW, 204 | &cfg->monitors[m].wallpaperH, 205 | }; 206 | 207 | char str[100]; 208 | 209 | for (int p = 0; p < PARAMS_COUNT; p++) 210 | { 211 | snprintf(str, 100, "monitor%d_%s", m + 1, paramStr[p]); 212 | if (!findLine(f, str, (p == 0 ? TYPE_STR : TYPE_INT), outputs[p])) 213 | { 214 | lwpLog(LOG_ERROR, "Can't find line '%s' in config", str); 215 | return 0; 216 | } 217 | if(p > 0) 218 | lwpLog(LOG_INFO, " %s: %d", str, *(int*)(outputs[p])); 219 | } 220 | 221 | #ifdef __WIN32 222 | if (strlen(wallpaperPath) < 2) 223 | { 224 | GetModuleFileNameA(NULL, wallpaperPath, PATH_MAX); 225 | char *ptr = wallpaperPath + strlen(wallpaperPath) - 1; 226 | while (*ptr != '\\') ptr--; 227 | *ptr = '\0'; 228 | strcat(wallpaperPath, "\\wallpapers\\default-fullhd"); 229 | } 230 | #endif 231 | 232 | strncpy(cfg->monitors[m].wallpaper.dirPath, wallpaperPath, PATH_MAX); 233 | lwpLog(LOG_INFO, " monitor%d_wallpaper: %s", m+1, wallpaperPath); 234 | } 235 | 236 | fclose(f); 237 | lwpLog(LOG_INFO, "Config file loaded"); 238 | return 1; 239 | } 240 | 241 | int parseWallpaperConfig(Wallpaper *wallpaper, const char *path) 242 | { 243 | lwpLog(LOG_INFO, "Loading wallpaper config"); 244 | FILE *f = fopen(path, "r"); 245 | if (!f) 246 | { 247 | lwpLog(LOG_ERROR, "Wallpaper config file (%s) doesn't exist", path); 248 | return 0; 249 | } 250 | 251 | float defaultMovementX, defaultMovementY; 252 | 253 | const int PARAMS_COUNT = 5; 254 | const char *paramStr[] = {"count", "movement_x", "movement_y", "repeat_x", "repeat_y"}; 255 | const int types[] = {TYPE_INT, TYPE_FLOAT, TYPE_FLOAT, TYPE_INT, TYPE_INT}; 256 | void *outputs[] = { 257 | &wallpaper->layersCount, &defaultMovementX, &defaultMovementY, 258 | &wallpaper->repeatX, &wallpaper->repeatY, 259 | }; 260 | 261 | for (int p = 0; p < PARAMS_COUNT; p++) 262 | { 263 | if (!findLine(f, paramStr[p], types[p], outputs[p])) 264 | { 265 | lwpLog(LOG_ERROR, "Can't find line '%s' in config", paramStr[p]); 266 | return 0; 267 | } 268 | 269 | if (types[p] == TYPE_FLOAT) 270 | lwpLog(LOG_INFO, " %s: %f", paramStr[p], *(float*)(outputs[p])); 271 | else 272 | lwpLog(LOG_INFO, " %s: %d", paramStr[p], *(int*)(outputs[p])); 273 | } 274 | wallpaper->layers = malloc(wallpaper->layersCount * sizeof(Layer)); 275 | 276 | lwpLog(LOG_INFO, " Each layer movements:"); 277 | 278 | char str[100]; 279 | for (int l = 0; l < wallpaper->layersCount; l++) 280 | { 281 | snprintf(str, 100, "movement%d_x", l + 1); 282 | if (!findLine(f, str, TYPE_FLOAT, &wallpaper->layers[l].sensitivityX)) 283 | wallpaper->layers[l].sensitivityX = defaultMovementX * l; 284 | lwpLog(LOG_INFO, " %s: %f", str, wallpaper->layers[l].sensitivityX); 285 | 286 | snprintf(str, 100, "movement%d_y", l + 1); 287 | if (!findLine(f, str, TYPE_FLOAT, &wallpaper->layers[l].sensitivityY)) 288 | wallpaper->layers[l].sensitivityY = defaultMovementY * l; 289 | lwpLog(LOG_INFO, " %s: %f", str, wallpaper->layers[l].sensitivityY); 290 | } 291 | 292 | fclose(f); 293 | 294 | lwpLog(LOG_INFO, "Wallpaper config file loaded"); 295 | return 1; 296 | } 297 | 298 | void freeConfig(Config *cfg) 299 | { 300 | for (int i = 0; i < cfg->monitorsCount; i++) freeMonitor(&cfg->monitors[i]); 301 | free(cfg->monitors); 302 | } 303 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Parallax WallPaper 4 | 5 | This allows you to create multi-layered parallax wallpapers.
6 | Each layer moves with Your mouse cursor, creating this beautiful effect. 7 | 8 | [Installation](#installation) • 9 | [Configuration](#configuration) • 10 | [Creating Wallpapers](#creating-wallpapers) 11 | 12 | https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b-b147-b345e8a8949a.mp4 13 | 14 |
15 | 16 | ## Installation 17 | 18 |
19 | Linux 20 | 21 | #### Installation steps 22 | 23 | - Install `SDL2` using Your package manager 24 | - If You are using `Wayland`, You also must install `XWayland` 25 | - Download `.tar.gz` package from [releases](https://github.com/jankozik/parallax-wallpaper/releases/latest) 26 | - Extract the content to `/`: 27 | ```shell 28 | sudo tar -o -xvf [archive name].tar.gz --directory / 29 | ``` 30 | - Test Layered WallPaper by running `lwp` 31 | - Setting `reload_rootwindow=1` in config file may be necessary on some distributions for Layered WallPaper to work properly (see [configuration](#configuration)) 32 | - To make Layered WallPaper run on startup, add `lwp &` command to Your desktop enviroment `.rc` file 33 | 34 | #### Build from source instead 35 | - Install `SDL2` using Your package manager. On some distributions `SDL2` doesn't contain development files, so it may be also necessary to install development version of `SDL2` 36 | - If You are using `Wayland`, You also must install `XWayland` 37 | - Install `CMake` 38 | - Clone the repository and prepare a `build` directory: 39 | 40 | ```shell 41 | git clone https://github.com/jankozik/parallax-wallpaper 42 | cd lwp 43 | mkdir build 44 | cd build 45 | ``` 46 | - Compile the project and generate a `.tar.gz` package 47 | ```shell 48 | cmake ../ 49 | cmake --build . 50 | cpack 51 | ``` 52 | - Extract `.tar.gz` package 53 | ```shell 54 | sudo tar -o -xvf [archive name].tar.gz --directory / 55 | ``` 56 | - Test Layered WallPaper by running `lwp` 57 | - Setting `reload_rootwindow=1` in config file may be necessary on some distributions for Layered WallPaper to work properly (see [configuration](#configuration)) 58 | - To make Layered WallPaper run on startup, add `lwp &` command to Your desktop enviroment `.rc` file 59 | 60 |
61 | 62 |
63 | macOS 64 | 65 | #### Installation steps 66 | - Download and run the installer from [releases](https://github.com/jankozik/parallax-wallpaper/releases/latest) 67 | - Drag and drop Layered_WallPaper into Applications 68 | - To make Layered WallPaper run on startup, run Toggle_Autorun.command 69 | - To stop running Layered WallPaper on startup, run it again 70 | 71 | #### Build from source instead 72 | - Install `SDL2` (homebrew: `brew install sdl2`) 73 | - To build this project, You need to install `cmake` (homebrew: `brew install cmake`) 74 | - Clone the repository: 75 | ```zsh 76 | git clone https://github.com/jankozik/parallax-wallpaper 77 | cd lwp 78 | 79 | ``` 80 | - Compile and generate installer 81 | ```zsh 82 | mkdir build 83 | cd build 84 | cmake ../ 85 | cmake --build . 86 | cpack -G DragNDrop 87 | ``` 88 | - DMG installer should appear, open it and drag Layered_WallPaper into Applications 89 | - To make Layered WallPaper run on startup, run Toggle_Autorun.command 90 | - To stop running Layered WallPaper on startup, run it again 91 | 92 |
93 | 94 |
95 | Windows 96 | 97 | #### Installation steps 98 | - Download and run the installer from [releases](https://github.com/jankozik/parallax-wallpaper/releases/latest) 99 | - Layered WallPaper should run immediately after the installation 100 | 101 | #### Build from source instead 102 | - Layered WallPaper is built using [cmake](https://cmake.org/), so You must install it. 103 | - This project supports `MinGW` and `MSVC` compilers. Using different one could lead to unpredicted behavior. If You want to use `MSVC`, it should be installed with Visual Studio. 104 | - Download `SDL2` and `SDL2-devel` package for Your compiler from [SDL2 releases](https://github.com/libsdl-org/SDL/releases/latest) and extract them somewhere. 105 | - You also must install [NSIS](https://nsis.sourceforge.io/Download). It's required to build the installer, which is needed to correctly set the registry keys, that will make Layered WallPaper run on OS startup etc. 106 | - Clone the repository and create `build` directory 107 | ```shell 108 | git clone https://github.com/jszczerbinsky/lwp 109 | cd lwp 110 | mkdir -p build 111 | cd build 112 | ``` 113 | - Type the following commands, replace square brackets elements with paths to extracted `SDL2` packages, that You've downloaded: 114 | 115 | 116 | For `MSVC`: 117 | ```shell 118 | cmake -G "Visual Studio 17" -DSDL2_DIR=[PATH TO SDL2-MSVC-DEVEL DIRECTORY]\cmake -DSDL2_RUNTIME_DIR=[PATH TO SDL2 RUNTIME DIRECTORY] ../ 119 | cmake --build . --config Release 120 | cpack 121 | ``` 122 | For `MinGW`: 123 | ```shell 124 | cmake -G "MinGW Makefiles" -DSDL2_DIR=[PATH TO SDL2-MINGW-DEVEL DIRECTORY]\cmake -DSDL2_RUNTIME_DIR=[PATH TO SDL2 RUNTIME DIRECTORY] -DCMAKE_BUILD_TYPE=Release ../ 125 | cmake --build . 126 | cpack 127 | ``` 128 | - The installer should appear in `build` directory, that You've created earlier. After completing the installation Layered WallPaper should run immediately. 129 | 130 |
131 | 132 | ## Configuration 133 | 134 | #### Create a configuration file 135 |
136 | Linux 137 | 138 | - Copy default config file to `.config/lwp/lwp.cfg`: 139 | ```shell 140 | mkdir ~/.config/lwp 141 | cp /etc/lwp.cfg ~/.config/lwp/lwp.cfg 142 | ``` 143 | 144 |
145 |
146 | macOS 147 | 148 | - Copy default config file to `~/.config/lwp/lwp.cfg`: 149 | ```zsh 150 | mkdir -p ~/.config/lwp 151 | cp /opt/lwp/lwp.cfg ~/.config/lwp/ 152 | ``` 153 |
154 |
155 | Windows 156 | 157 | - Press ⊞ Win + R 158 | - Type `%appdata%` and press `Ok` 159 | - Create new directory and name it `lwp` 160 | - Copy file `C:\Program Files\lwp\defaultWin.cfg` to directory created in the previous step and rename it to `lwp.cfg` 161 | - Open `lwp.cfg` in notepad 162 | 163 |
164 | 165 | #### Using config file 166 | 167 | - Do not put spaces between `=` and values 168 | - Do not leave trailing spaces 169 | - Comments start with `#` 170 | - Do not put strings in quotation marks 171 | 172 | #### Available options: 173 | 174 | | Type | Name | Description | 175 | | ------ | ------------ | ----------- | 176 | | int | reload_rootwindow | Set this to 1 if You are using a compositor (linux only) | 177 | | float | smooth | Smooth movement multipler | 178 | | int | monitors | Monitors count | 179 | | int | monitor[n]_x | Position of nth monitor in X axis | 180 | | int | monitor[n]_y | Position of nth monitor in Y axis | 181 | | int | monitor[n]_w | Width of nth monitor | 182 | | int | monitor[n]_h | Height of nth monitor | 183 | | string | monitor[n]_wallpaper | Absolute path to the wallpaper directory | 184 | | int | monitor[n]_wallpaper_x | Position of the wallpaper relative to the monitor | 185 | | int | monitor[n]_wallpaper_y | Position of the wallpaper relative to the monitor | 186 | | int | monitor[n]_wallpaper_w | Wallpaper resolution | 187 | | int | monitor[n]_wallpaper_h | Wallpaper resolution | 188 | | int | target_fps | How many times per second should the wallpaper render (imprecise, hence "target") | 189 | 190 | ## Creating Wallpapers 191 | 192 | Parallax wallpapers are not popular. Because of this if You want some cool parallax wallpaper, You have to either find a parallax game background on the internet and use it as a wallpaper or cut some real wallpaper into layers using Gimp or Photoshop. 193 | 194 | #### How to create a wallpaper for Layered WallPaper 195 | - Create a directory for Your wallpaper 196 | - Save each layer to this directory as `.bmp` file and name them `1.bmp`, `2.bmp` ... (`1.bmp` is bottom most layer) 197 | - Create a wallpaper config file and name it `wallpaper.cfg` (You can make a copy from `C:\Program Files\lwp\wallpapers\default-fullhd\wallpaper.cfg`) 198 | 199 | #### Example: 200 | 201 | File structure: 202 | ``` 203 | C: 204 | └ MyCoolWallpaperDirectory 205 | └ wallpaper.cfg 206 | └ 1.bmp 207 | └ 2.bmp 208 | └ 3.bmp 209 | ``` 210 | 211 | #### Config file: 212 | 213 | | Type | Name | Description | 214 | | ------ | ------------ | ----------- | 215 | | int | count | Wallpaper layers count | 216 | | float | movement_x | Mouse sensitivity in X axis | 217 | | float | movement_y | Mouse sensitivity in Y axis | 218 | | float | movement[n]_x | Mouse sensitivity in X axis for nth layer (optional)| 219 | | float | movement[n]_y | Mouse sensitivity in Y axis for nth layer (optional)| 220 | | int | repeat_x | Repeat the wallpaper in X axis | 221 | | int | repeat_y | Repeat the wallpaper in Y axis | 222 | --------------------------------------------------------------------------------