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