🔧 chore: add README.md and config.schema.json
This commit is contained in:
@@ -1,3 +1,245 @@
|
||||
🚧 WIP
|
||||
## What this is
|
||||
|
||||
Check [legacy/widgets](https://github.com/Uyanide/Wallpaper_Carousel/tree/legacy/widgets) branch for the old version.
|
||||
It might not be that worthy to write a QtWidget application for such a small feature, but I kind of enjoy the pain... So here it is.
|
||||
|
||||
<img src="https://github.com/Uyanide/backgrounds/blob/master/screenshots/desktop-alt.jpg?raw=true"/>
|
||||
|
||||
## How to build
|
||||
|
||||
1. Make sure you have Qt6 libraries, CMake and a C++ compiler installed.
|
||||
|
||||
e.g. On Arch-based systems:
|
||||
|
||||
```bash
|
||||
sudo pacman -S --needed qt6-base cmake gcc
|
||||
```
|
||||
|
||||
2. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Uyanide/Wallpaper_Carousel.git && \
|
||||
cd Wallpaper_Carousel
|
||||
```
|
||||
|
||||
3. Build and install:
|
||||
|
||||
This is a standard CMake managed project, so the build process is pretty normal and straightforward. First, configure the project:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
```
|
||||
|
||||
Adjust install prefix to your needs. Start building:
|
||||
|
||||
```bash
|
||||
cmake --build build -- -j$(nproc)
|
||||
```
|
||||
|
||||
The binary will be located at `build/wallreel` and can be run directly for testing:
|
||||
|
||||
```bash
|
||||
build/wallreel
|
||||
```
|
||||
|
||||
Install to previously specified prefix:
|
||||
|
||||
```bash
|
||||
cmake --install build
|
||||
```
|
||||
|
||||
This step may require root permissions if the install prefix is set to a system directory like `/usr/local`.
|
||||
|
||||
## How to use
|
||||
|
||||
The config file should be placed in `~/.config/wallreel/config.json`. Refer to [Configuration Reference](#configuration-reference) and [config.schema.json](config.schema.json) for more details on the format and available options.
|
||||
|
||||
A minimum config file should at least contain the path(s) to wallpapers, e.g.
|
||||
|
||||
```json
|
||||
{
|
||||
"wallpaper": {
|
||||
"dirs": [{ "path": "/path/to/your/wallpapers" }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By default, the path of the selected wallpaper will be printed to stdout. If you want to apply the selected wallpaper automatically after selection, the `action.onSelected` entry should be set, e.g.
|
||||
|
||||
```json
|
||||
{
|
||||
"wallpaper": {
|
||||
"dirs": [{ "path": "/path/to/your/wallpapers" }]
|
||||
},
|
||||
"action": {
|
||||
"onSelected": "swww img {{ path }}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`action.onSelected` should be a shell command, where `{{ path }}` will be replaced with the path of the selected wallpaper.
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
Refer to [config.schema.json](config.schema.json) for a complete reference of the configuration file schema. Below is a summary of the available options.
|
||||
|
||||
The configuration file is divided into five main sections: `wallpaper`, `theme`, `action`, `style`, and `sort`.
|
||||
|
||||
### Wallpaper (`wallpaper`)
|
||||
|
||||
Defines where WallReel looks for images and what to exclude.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| :--------- | :--------------- | :------ | :----------------------------------------------------------------------------------------------------- |
|
||||
| `paths` | Array of Strings | `[]` | Exact paths to specific image files. |
|
||||
| `dirs` | Array of Objects | `[]` | Directories to search for images. Each object should have a `path` (string) and `recursive` (boolean). |
|
||||
| `excludes` | Array of Strings | `[]` | Exclude patterns using Regular Expressions. |
|
||||
|
||||
### Theme (`theme`)
|
||||
|
||||
Configures the color palettes.
|
||||
|
||||
By default, a **dominant color** will be extracted from each wallpaper. If a palette is **selected**, the color that matches the dominant color the best will be selected as the **primary color**. This might be convinient if you prefer to set your desktop theme to match the wallpaper using a predefined palette (e.g. Catppuccin, Tokyo Night) instead of generating a custom one (e.g. using matugen).
|
||||
|
||||
There are a few embeded palettes available in the application, including "Catppuccin Frappe", "Catppuccin Latte", "Catppuccin Macchiato", and "Catppuccin Mocha". You can also define your own palettes or override the embeded ones by providing a custom configuration.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| :--------------- | :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `defaultPalette` | String | `""` | Name of the default palette to use. |
|
||||
| `palettes` | Array of Objects | `[]` | List of defined palettes. Each contains a `name` (string) and an array of `colors` (each with a `name` and a hex `value` like `"#ff0000"`). |
|
||||
|
||||
### Action (`action`)
|
||||
|
||||
Configures system commands to execute on specific events mapping to your window manager or wallpaper utility (e.g., `swaybg`, `feh`).
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| :-------------------- | :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `previewDebounceTime` | Integer | `300` | Debounce time (ms) for triggering the preview action. |
|
||||
| `printSelected` | Boolean | `true` | Print selected wallpaper path to stdout on confirm. |
|
||||
| `printPreview` | Boolean | `false` | Print previewed wallpaper path to stdout on preview. |
|
||||
| `onSelected` | String | `""` | Command to execute when a wallpaper is confirmed. |
|
||||
| `onPreview` | String | `""` | Command to execute when a wallpaper is previewed. |
|
||||
| `saveState` | Array of Objects | `[]` | Commands to fetch system states before changing wallpapers. Each object defines: `key`, `default` (fallback value), `command` (stdout mapping), and `timeout` (ms). |
|
||||
| `onRestore` | String | `""` | Command to execute on restore. Extracted states from `saveState` can be injected using `{{ key }}`. |
|
||||
| `quitOnSelected` | Boolean | `false` | Quit the application after a selection is made. |
|
||||
| `restoreOnClose` | Boolean | `true` | Run `onRestore` command if the application is closed without making a final selection. |
|
||||
|
||||
Available placeholders for `onSelected`, `onPreview` commands:
|
||||
|
||||
| Placeholder | Description |
|
||||
| :------------------ | :----------------------------------------------------------------------------------------- |
|
||||
| `{{ path }}` | Full path of the selected or previewed wallpaper. |
|
||||
| `{{ name }}` | Filename of the selected or previewed wallpaper. |
|
||||
| `{{ size }}` | Size of the selected or previewed wallpaper in bytes. |
|
||||
| `{{ palette }}` | Name of the currently selected color palette. ("null" if none) |
|
||||
| `{{ colorName }}` | Name of the currently determined primary color. ("null" if none) |
|
||||
| `{{ colorHex }}` | Hex code (starting with "#") of the currently determined primary color. ("null" if none) |
|
||||
| `{{ domColorHex }}` | Hex code (starting with "#") of the dominant color in the selected or previewed wallpaper. |
|
||||
| `{{ key }}` | Value of the saved state with the specified key. |
|
||||
|
||||
### Style (`style`)
|
||||
|
||||
Controls the layout and dimensions of the application window and image items.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| :------------------ | :------ | :------ | :--------------------------------------- |
|
||||
| `image_width` | Integer | `320` | Width of each thumbnail. |
|
||||
| `image_height` | Integer | `180` | Height of each thumbnail. |
|
||||
| `image_focus_scale` | Number | `1.5` | Scale multiplier for focused thumbnails. |
|
||||
| `window_width` | Integer | `750` | Initial application window width. |
|
||||
| `window_height` | Integer | `500` | Initial application window height. |
|
||||
|
||||
### Sort (`sort`)
|
||||
|
||||
Initial sorting behavior for loaded images.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| :----------- | :------ | :------- | :------------------------------------------------------------------------------- |
|
||||
| `type` | String | `"date"` | Defines sorting criteria. Acceptable values: `"name"`, `"date"`, `"size"`. |
|
||||
| `descending` | Boolean | `true` | If true, sorts in descending order (e.g. newer dates first, larger files first). |
|
||||
|
||||
---
|
||||
|
||||
## Example `config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Uyanide/WallReel/refs/heads/master/config.schema.json",
|
||||
"wallpaper": {
|
||||
"paths": ["/home/user/Pictures/favorite.jpg"],
|
||||
"dirs": [
|
||||
{
|
||||
"path": "/home/user/Pictures/Wallpapers",
|
||||
"recursive": true
|
||||
}
|
||||
],
|
||||
"excludes": ["\\.gif$"]
|
||||
},
|
||||
"theme": {
|
||||
"defaultPalette": "Dark",
|
||||
"palettes": [
|
||||
{
|
||||
"name": "Dark",
|
||||
"colors": [
|
||||
{ "name": "blue", "value": "#89b4fa" },
|
||||
{ "name": "red", "value": "#f38ba8" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"action": {
|
||||
"previewDebounceTime": 500,
|
||||
"quitOnSelected": true,
|
||||
"onPreview": "swww img {{ path }}",
|
||||
"onSelected": "cp {{ path }} ~/.config/current_wallpaper.jpg",
|
||||
"saveState": [
|
||||
{
|
||||
"key": "current_wp",
|
||||
"default": "/home/user/Pictures/default.jpg",
|
||||
"command": "find ~/.config/wallpaper/current -type f | head -n 1",
|
||||
"timeout": 1000
|
||||
}
|
||||
],
|
||||
"onRestore": "swww img {{ current_wp }}"
|
||||
},
|
||||
"style": {
|
||||
"image_width": 640,
|
||||
"image_height": 400,
|
||||
"image_focus_scale": 1.2,
|
||||
"window_width": 1280,
|
||||
"window_height": 720
|
||||
},
|
||||
"sort": {
|
||||
"type": "date",
|
||||
"descending": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
```
|
||||
Usage: wallreel [options]
|
||||
|
||||
Options:
|
||||
-h, --help Displays help on commandline options.
|
||||
-v, --version Displays version information.
|
||||
-V, --verbose Set log level to DEBUG (default is INFO)
|
||||
-C, --clear-cache Clear the cache and exit
|
||||
-q, --quiet Suppress all log output
|
||||
-d, --append-dir <dir> Append an additional wallpaper search directory
|
||||
-c, --config-file <file> Specify a custom configuration file
|
||||
```
|
||||
|
||||
A few things to notice:
|
||||
|
||||
- It's generally not necessary to provide any CLI arguments, I would recommend using the config file to customize the behavior instead. However, it is still possible to control some essential options via CLI.
|
||||
|
||||
- All logs are directed to stderr by default. Only the full path of the selected or previewed wallpaper (if any) will be sent to stdout. This allows easy piping of the output to other programs.
|
||||
|
||||
- The `--append-dir` option can be used multiple times to add multiple directories.
|
||||
|
||||
- It is quite obvious that some options conflicts with each other (e.g. `--verbose` and `--quiet`). Case mutually exclusive options are provided together, the behavior is un.. just please, don't do that.
|
||||
|
||||
- Paths passed via CLI options are tested before any further operation is performed. That is to say, if an invalid path is provided, the program will exit with an error before any further action, and you won't even have a chance to see a window.
|
||||
|
||||
On the contrary, paths provided in the config file are only tested when they are actually used (e.g. when searching for wallpapers). And most errors will be ignored silently (possibly with a warning log).
|
||||
|
||||
@@ -41,16 +41,16 @@
|
||||
// action.restoreOnClose boolean true Whether to run the restore command after closing the application without confirming a wallpaper
|
||||
//
|
||||
// style.image_width number 320 Width of each image
|
||||
// style.image_height number 200 Height of each image
|
||||
// style.image_height number 180 Height of each image
|
||||
// style.image_focus_scale number 1.5 Scale of the focused image (relative to unfocused image)
|
||||
// style.window_width number 750 Initial window width
|
||||
// style.window_height number 500 Initial window height
|
||||
//
|
||||
// sort.type string "date" Sorting type: "name", "date", "size"
|
||||
// sort.descending boolean true Whether to reverse the sorting order
|
||||
// Normal order: name: lexicographical, e.g. "a.jpg" before "b.jpg"
|
||||
// date: older before newer
|
||||
// size: smaller before larger
|
||||
// sort.type string "date" Initial sorting type: "name", "date", "size"
|
||||
// sort.descending boolean true Initial sorting order
|
||||
// Ascending: name: lexicographical, e.g. "a.jpg" before "b.jpg"
|
||||
// date: older before newer
|
||||
// size: smaller before larger
|
||||
|
||||
namespace WallReel::Core::Config {
|
||||
|
||||
@@ -124,7 +124,7 @@ struct ActionConfigItems {
|
||||
};
|
||||
|
||||
QList<SaveStateItem> saveStateConfig;
|
||||
QHash<QString, QString> saveState;
|
||||
QHash<QString, QString> savedState;
|
||||
QString onSelected;
|
||||
QString onPreview;
|
||||
QString onRestore;
|
||||
@@ -138,7 +138,7 @@ struct ActionConfigItems {
|
||||
struct StyleConfigItems {
|
||||
double imageFocusScale = 1.5;
|
||||
int imageWidth = 320;
|
||||
int imageHeight = 200;
|
||||
int imageHeight = 180;
|
||||
int windowWidth = 750;
|
||||
int windowHeight = 500;
|
||||
};
|
||||
|
||||
@@ -213,7 +213,7 @@ void WallReel::Core::Config::Manager::_loadActionConfig(const QJsonObject& root)
|
||||
}
|
||||
if (!sItem.key.isEmpty()) {
|
||||
m_actionConfig.saveStateConfig.append(sItem);
|
||||
m_actionConfig.saveState.insert(sItem.key, sItem.defaultVal);
|
||||
m_actionConfig.savedState.insert(sItem.key, sItem.defaultVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,7 +476,7 @@ void WallReel::Core::Config::Manager::captureState() {
|
||||
|
||||
void WallReel::Core::Config::Manager::_onCaptureResult(const QString& key, const QString& value) {
|
||||
// This is all in main thread, so no lock needed
|
||||
m_actionConfig.saveState[key] = value;
|
||||
m_actionConfig.savedState[key] = value;
|
||||
m_pendingCaptures--;
|
||||
if (m_pendingCaptures == 0) {
|
||||
emit stateCaptured();
|
||||
|
||||
@@ -101,17 +101,18 @@ QHash<QString, QString> WallReel::Core::Service::WallpaperService::_generateVari
|
||||
if (hex.isEmpty()) {
|
||||
hex = "null";
|
||||
}
|
||||
return {
|
||||
QHash<QString, QString> ret{
|
||||
{"path", imageData.getFullPath()},
|
||||
{"name", imageData.getFileName()},
|
||||
{"size", QString::number(imageData.getSize())},
|
||||
{"width", QString::number(imageData.getTargetSize().width())},
|
||||
{"height", QString::number(imageData.getTargetSize().height())},
|
||||
{"palette", palette},
|
||||
{"colorName", color},
|
||||
{"colorHex", hex},
|
||||
{"domColorHex", imageData.getDominantColor().name()},
|
||||
};
|
||||
|
||||
ret.insert(m_actionConfig.savedState);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WallReel::Core::Service::WallpaperService::_doPreview(const Image::Data& imageData) {
|
||||
@@ -174,7 +175,7 @@ void WallReel::Core::Service::WallpaperService::_doRestore() {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString command = Utils::renderTemplate(m_actionConfig.onRestore, m_actionConfig.saveState);
|
||||
const QString command = Utils::renderTemplate(m_actionConfig.onRestore, m_actionConfig.savedState);
|
||||
if (command.isEmpty()) {
|
||||
WR_DEBUG("Restore command is empty after rendering. Skipping restore action.");
|
||||
emit restoreCompleted();
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "WallReel Configuration",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"wallpaper": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"description": "List of paths to images."
|
||||
},
|
||||
"dirs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Path to the directory."
|
||||
},
|
||||
"recursive": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to search the directory recursively."
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [],
|
||||
"description": "Directories to search for images."
|
||||
},
|
||||
"excludes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"description": "Exclude patterns (regex)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"defaultPalette": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Name of the default palette to use"
|
||||
},
|
||||
"palettes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Name of the palette"
|
||||
},
|
||||
"colors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Name of the color"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Color value in hex format, e.g. \"#ff0000\" for red"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [],
|
||||
"description": "List of colors in the palette"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"previewDebounceTime": {
|
||||
"type": "integer",
|
||||
"default": 300,
|
||||
"description": "Debounce time for preview action in milliseconds"
|
||||
},
|
||||
"printSelected": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to print the selected wallpaper path to stdout on confirm"
|
||||
},
|
||||
"printPreview": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to print the previewed wallpaper path to stdout on preview"
|
||||
},
|
||||
"onSelected": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Command to execute on confirmation"
|
||||
},
|
||||
"onPreview": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Command to execute on preview"
|
||||
},
|
||||
"saveState": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Key of value to save, used as {{ key }} in onRestore command"
|
||||
},
|
||||
"default": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Value to save, used when \"cmd\" is not set or command execution fails or output is empty"
|
||||
},
|
||||
"command": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Command that outputs(to stdout) the value to save when executed"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
"default": 3000,
|
||||
"description": "Timeout for executing command in milliseconds. 0 or negative means no timeout"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [],
|
||||
"description": "Useful for restore command"
|
||||
},
|
||||
"onRestore": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Command to execute on restore ({{ key }} -> value defined or obtained in saveState)"
|
||||
},
|
||||
"quitOnSelected": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to quit the application after confirming a wallpaper"
|
||||
},
|
||||
"restoreOnClose": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to run the restore command after closing the application without confirming a wallpaper"
|
||||
}
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image_width": {
|
||||
"type": "integer",
|
||||
"default": 320,
|
||||
"description": "Width of each image"
|
||||
},
|
||||
"image_height": {
|
||||
"type": "integer",
|
||||
"default": 180,
|
||||
"description": "Height of each image"
|
||||
},
|
||||
"image_focus_scale": {
|
||||
"type": "number",
|
||||
"default": 1.5,
|
||||
"description": "Scale of the focused image (relative to unfocused image)"
|
||||
},
|
||||
"window_width": {
|
||||
"type": "integer",
|
||||
"default": 750,
|
||||
"description": "Initial window width"
|
||||
},
|
||||
"window_height": {
|
||||
"type": "integer",
|
||||
"default": 500,
|
||||
"description": "Initial window height"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"date",
|
||||
"size"
|
||||
],
|
||||
"default": "date",
|
||||
"description": "Initial sorting type"
|
||||
},
|
||||
"descending": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Initial sorting order. Ascending: name: lexicographical, date: older before newer, size: smaller before larger"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user