Compare commits

...

6 Commits

Author SHA1 Message Date
Roman
f27c268a4f Updating readme 2024-01-04 13:05:19 +01:00
Roman
d6ce9b4e64 Adding docs to readme 2024-01-04 13:02:53 +01:00
Roman
672ad87be6 Fixing crash on non-rendering images
This fixes issue #24
2024-01-04 12:21:13 +01:00
Roman
5b1513b84d Updating icon 2024-01-04 10:59:20 +01:00
Roman
958c8cf61e Fixing random selection 2024-01-04 10:22:45 +01:00
Roman
858c0127f2 Implementing swww transitions and improving config 2024-01-04 10:17:42 +01:00
8 changed files with 98 additions and 123 deletions

View File

@ -1,6 +1,6 @@
# Waypaper
GUI wallpaper setter for Wayland and Xorg window managers. It works as a frontend for popular wallpaper backends like `swaybg`, `swww`, `wallutils` and `feh`. You can check a [demo on reddit](https://www.reddit.com/r/unixporn/comments/15lbhuc/hyprland_waypaper_gui_wallpaper_setter_for_wayland/).
GUI wallpaper setter for Wayland and Xorg window managers. It works as a frontend for popular wallpaper backends like `swaybg`, `swww`, `wallutils` and `feh`. You can check a [demo on reddit](https://www.reddit.com/r/unixporn/comments/15lbhuc/hyprland_waypaper_gui_wallpaper_setter_for_wayland/). See details in [the documentation](https://anufrievroman.gitbook.io/waypaper).
![screenshot](screenshot.jpg)
@ -50,51 +50,26 @@ The `waypaper` package is available thanks to Basil Keeler.
## Usage
`waypaper` command will run GUI application. Make sure to choose the backend that you installed.
To restore your wallpaper at launch, add `waypaper --restore` to your startup config. For example:
**In Hyprland**
`exec-once=waypaper --restore`
**In Sway or I3**
`exec waypaper --restore`
To see the list of hotkeys, press `?`.
`waypaper` command will run GUI application.
### Options
`--restore` - sets the last chosen wallpaper. Useful at launch of the window manager.
To restore your wallpaper at launch, add `waypaper --restore` to your startup config.
`--random` - sets a random wallpaper. Makes sense only together with `--restore` key.
See more [details on user arguments in the documentation](https://anufrievroman.gitbook.io/waypaper/usage).
`--backend XXX` - specifies which backend to use, which can be either `swaybg`, `swww`, `feh`, or `wallutils`. Useful if you use waypaper on both Wayland and Xorg on the same machine. By default, last used backend is used.
### Configuration
`--fill XXX` - specifies filling type, which can be eiher `fill`, `stretch`, `fit`, `center`, or `tile`.
See [configuration details in the documentation](https://anufrievroman.gitbook.io/waypaper/configuration).
If you wish to change language, change `laguage` variable in `.config/waypaper/config.ini` file. Supported options are `en`, `de`, `fr`, `ru`, `pl`, `zh`.
### Troubleshooting
## Troubleshooting
See typical problems [explained in the documentation](https://anufrievroman.gitbook.io/waypaper/troubleshooting)
- If wallpaper does not change, first, try to launch waypaper in the terminal and see the output. Also, try to change it via command line using chosen backend to make sure that backend by itself works correctly.
- If application does not run, make sure to install `gobject` library (it might be called `python-gobject` or `python3-gi` in your package manager). Although it is supposed to be installed automatically with the package.
- Please understand that not all backends work on all systems. `feh` is only for Xorg, while `swww` and `swaybg` are only for Wayland.
- If you use different WMs on the same system, specify the backend when you restore the wallpaper at launch. For example: `waypaper --restore --backend feh` or use `wallutils` which works on both Wayland and Xorg.
## Roadmap
- Possible support for other backends like mpvpaper, and maybe hyprpaper.
- Support for various `swww` options
- Display animated previews of gif wallpapers
## Contributions
## Contribution and support
Feel free to propose PR and suggest the improvements. I'll highly appreciate help with packaging for various distributions. If you wish to contribute with translation into your language, please see the `translations.py` file.
## Support
I am not a professional developer and work on open-source projects in my free time. If you'd like to support the development, consider donations via [buymeacoffee](https://www.buymeacoffee.com/angryprofessor) or cryptocurrencies:
- BTC `bc1qpkzmutdqfxkce34skt09vll97s5smpa0r2tyzj`

View File

@ -5,7 +5,7 @@
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg8"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
sodipodi:docname="waypaper.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@ -24,8 +24,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.0712122"
inkscape:cx="25.913658"
inkscape:cy="43.230368"
inkscape:cx="-39.545961"
inkscape:cy="5.8950501"
inkscape:document-units="px"
inkscape:current-layer="g1103"
showgrid="false"
@ -91,49 +91,7 @@
id="g1103"
transform="matrix(0.90909086,0,0,0.90909086,0.53257261,26.307443)">
<rect
style="fill:#46b1cc;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect1"
width="4.8213077"
height="2.0975866"
x="1.2960513"
y="286.95178" />
<rect
style="fill:#46cc67;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect2"
width="4.8213077"
height="2.0975866"
x="6.6809821"
y="286.95178" />
<rect
style="fill:#2ae8e7;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect3"
width="4.8213077"
height="2.0975866"
x="1.2960513"
y="289.52353" />
<rect
style="fill:#46ccbd;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect4"
width="4.8213077"
height="2.0975866"
x="6.6809821"
y="289.52353" />
<rect
style="fill:#46ccbc;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect5"
width="4.8213077"
height="2.0975866"
x="1.2960513"
y="292.09531" />
<rect
style="fill:#46cc9c;fill-opacity:1;stroke:none;stroke-width:0.358317;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect6"
width="4.8213077"
height="2.0975866"
x="6.6809821"
y="292.09531" />
<rect
style="fill:none;fill-opacity:1;stroke:#00c3c4;stroke-width:0.380703;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;paint-order:stroke fill markers"
style="fill:none;fill-opacity:1;stroke:#00c4c2;stroke-width:0.70082837;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;paint-order:stroke fill markers;stroke-dasharray:none"
id="rect7"
width="11.552007"
height="8.6614714"
@ -142,21 +100,31 @@
transform="scale(-1)"
ry="0.48288548" />
<path
style="fill:none;stroke:#00c3c4;stroke-width:0.376317;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 2.573189,295.69389 7.651963,0.008"
style="fill:none;stroke:#00c4c2;stroke-width:0.72556691;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 2.573189,295.80911 7.651963,0.008"
id="path7" />
<text
xml:space="preserve"
style="font-size:7.91179px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;stroke-width:0.133816"
x="1.7834336"
y="322.71399"
id="text7"
transform="scale(1.0998074,0.90925011)"><tspan
sodipodi:role="line"
id="tspan7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.91179px;font-family:'Poiret One';-inkscape-font-specification:'Poiret One';fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.133816;stroke-opacity:1"
x="1.7834336"
y="322.71399">W</tspan></text>
<path
style="fill:none;stroke:#708bff;stroke-width:0.407458;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 6.3991704,293.58816 v -2.55686 l 4.3897986,-0.83342 -0.0046,-1.55235 c -0.281972,0 -2.1881754,10e-6 -2.1881754,10e-6"
id="path1"
sodipodi:nodetypes="ccccc" />
<rect
style="fill:#13b9ff;fill-opacity:1;stroke:#13b9ff;stroke-width:0.272997;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect8"
width="7.526495"
height="2.0975866"
x="2.6359229"
y="287.59674"
ry="0.46842164" />
<rect
style="fill:#13b9ff;fill-opacity:1;stroke:#13b9ff;stroke-width:0.13737167;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect9"
width="0.58187282"
height="2.5517273"
x="-6.6901069"
y="291.49551"
transform="scale(-1,1)"
ry="0.19655633" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -49,7 +49,7 @@ def run():
for wallpaper, monitor in zip(cf.wallpaper, cf.monitors):
if args.random:
wallpaper = get_random_file(cf.image_folder, cf.include_subfolders)
wallpaper = get_random_file(cf.backend, cf.image_folder, cf.include_subfolders)
if wallpaper is None:
continue

View File

@ -30,10 +30,17 @@ def read_webp_image(image_path):
def cache_image(image_path, cache_dir):
"""Resize and cache images using gtk library"""
ext = os.path.splitext(image_path)[1].lower()
if ext == ".webp":
pixbuf = read_webp_image(str(image_path))
else:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
try:
if ext == ".webp":
pixbuf = read_webp_image(str(image_path))
else:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
# If image processing failed, create a black placeholder:
except GLib.Error:
pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 1280, 720)
pixbuf.fill(0x0)
aspect_ratio = pixbuf.get_width() / pixbuf.get_height()
scaled_width = 240
scaled_height = int(scaled_width / aspect_ratio)
@ -420,7 +427,7 @@ class App(Gtk.Window):
def set_random_wallpaper(self):
"""Choose a random image and set it as the wallpaper"""
self.cf.backend = self.backend_option_combo.get_active_text()
self.cf.selected_wallpaper = get_random_file(self.cf.image_folder, self.cf.include_subfolders)
self.cf.selected_wallpaper = get_random_file(self.cf.backend, self.cf.image_folder, self.cf.include_subfolders)
if self.cf.selected_wallpaper is None:
return
print(self.txt.msg_path, self.cf.selected_wallpaper)

View File

@ -46,8 +46,10 @@ def change_wallpaper(image_path, cf, monitor, txt):
command = ["swww", "img", image_path]
command.extend(["--resize", fill])
command.extend(["--fill-color", cf.color])
command.extend(["--transition-type", cf.swww_transition])
# command.extend(["--transition-step", str(30)])
command.extend(["--transition-type", cf.swww_transition_type])
command.extend(["--transition-step", str(cf.swww_transition_step)])
command.extend(["--transition-angle", str(cf.swww_transition_angle)])
command.extend(["--transition-duration", str(cf.swww_transition_duration)])
if monitor != "All":
command.extend(["--outputs", monitor])
subprocess.Popen(command)

View File

@ -29,10 +29,10 @@ def get_image_paths(backend, root_folder, include_subfolders=False, depth=None):
return image_paths
def get_random_file(folder, include_subfolders):
def get_random_file(backend, folder, include_subfolders):
"""Pick a random file from the folder"""
try:
image_paths = get_image_paths(folder, include_subfolders, depth=1)
image_paths = get_image_paths(backend, folder, include_subfolders, depth=1)
return random.choice(image_paths)
except:
return None

View File

@ -1,4 +1,4 @@
"""Module responsible for taking care of configuration file"""
"""Module responsible for reading and saving the configuration file"""
import configparser
import pathlib
@ -7,7 +7,7 @@ from sys import exit
from platformdirs import user_config_path, user_pictures_path, user_cache_path
from waypaper.aboutdata import AboutData
from waypaper.options import FILL_OPTIONS, SORT_OPTIONS, SWWW_TRANSITIONS, BACKEND_OPTIONS
from waypaper.options import FILL_OPTIONS, SORT_OPTIONS, SWWW_TRANSITION_TYPES, BACKEND_OPTIONS
from waypaper.common import check_installed_backends
@ -22,7 +22,10 @@ class Config:
self.sort_option = SORT_OPTIONS[0]
self.backend = self.installed_backends[0] if self.installed_backends else BACKEND_OPTIONS[0]
self.color = "#ffffff"
self.swww_transition = SWWW_TRANSITIONS[0]
self.swww_transition_type = SWWW_TRANSITION_TYPES[0]
self.swww_transition_step = 90
self.swww_transition_angle = 0
self.swww_transition_duration = 2
self.lang = "en"
self.monitors = [self.selected_monitor]
self.wallpaper = []
@ -38,36 +41,53 @@ class Config:
self.cache_dir.mkdir(parents=True,exist_ok=True)
self.read()
self.check_validity()
def read(self):
"""Load data from the config.ini or use default if it does not exists"""
config = configparser.ConfigParser()
config.read(self.config_file, 'utf-8')
# Read parameters:
self.image_folder = config.get("Settings", "folder", fallback=self.image_folder)
self.fill_option = config.get("Settings", "fill", fallback=self.fill_option)
if self.fill_option not in FILL_OPTIONS:
self.sort_option = FILL_OPTIONS[0]
self.sort_option = config.get("Settings", "sort", fallback=self.sort_option)
if self.sort_option not in SORT_OPTIONS:
self.sort_option = SORT_OPTIONS[0]
self.backend = config.get("Settings", "backend", fallback=self.backend)
self.color = config.get("Settings", "color", fallback=self.color)
self.post_command = config.get("Settings", "post_command", fallback=self.post_command)
self.swww_transition = config.get("Settings", "swww_transition", fallback=self.swww_transition)
if self.swww_transition not in SWWW_TRANSITIONS:
self.swww_transition = "any"
self.swww_transition_type = config.get("Settings", "swww_transition_type", fallback=self.swww_transition_type)
self.swww_transition_step = config.get("Settings", "swww_transition_step", fallback=self.swww_transition_step)
self.swww_transition_angle = config.get("Settings", "swww_transition_angle", fallback=self.swww_transition_angle)
self.swww_transition_duration = config.get("Settings", "swww_transition_duration", fallback=self.swww_transition_duration)
self.lang = config.get("Settings", "language", fallback=self.lang)
self.include_subfolders = config.getboolean("Settings", "subfolders", fallback=self.include_subfolders)
self.monitors_str = config.get("Settings", "monitors", fallback=self.selected_monitor, raw=True)
self.wallpaper_str = config.get("Settings", "wallpaper", fallback="", raw=True)
# Convert strings to lists:
if self.monitors_str is not None:
self.monitors = [str(monitor) for monitor in self.monitors_str.split(",")]
self.wallpaper_str = config.get("Settings", "wallpaper", fallback="", raw=True)
if self.wallpaper_str is not None:
self.wallpaper = [str(paper) for paper in self.wallpaper_str.split(",")]
def check_validity(self):
"""Check if the config parameters are valid and correct them if needed"""
if self.backend not in BACKEND_OPTIONS:
self.backend = self.installed_backends[0] if self.installed_backends else BACKEND_OPTIONS[0]
if self.sort_option not in SORT_OPTIONS:
self.sort_option = SORT_OPTIONS[0]
if self.fill_option not in FILL_OPTIONS:
self.fill_option = FILL_OPTIONS[0]
if self.swww_transition_type not in SWWW_TRANSITION_TYPES:
self.swww_transition_type = "any"
if 0 > int(self.swww_transition_angle) > 180:
self.swww_transition_angle = 0
if 0 > int(self.swww_transition_step) > 255:
self.swww_transition_step = 90
if 0 > int(self.swww_transition_duration):
self.swww_transition_duration = 2
def save(self):
"""Update the parameters and save them to the configuration file"""
@ -88,17 +108,20 @@ class Config:
config.read(self.config_file)
if not config.has_section("Settings"):
config.add_section("Settings")
config.set("Settings", "language", self.lang)
config.set("Settings", "folder", self.image_folder)
config.set("Settings", "wallpaper", ",".join(self.wallpaper))
config.set("Settings", "backend", self.backend)
config.set("Settings", "monitors", ",".join(self.monitors))
config.set("Settings", "fill", self.fill_option)
config.set("Settings", "sort", self.sort_option)
config.set("Settings", "backend", self.backend)
config.set("Settings", "color", self.color)
config.set("Settings", "swww_transition", self.swww_transition)
config.set("Settings", "language", self.lang)
config.set("Settings", "subfolders", str(self.include_subfolders))
config.set("Settings", "wallpaper", ",".join(self.wallpaper))
config.set("Settings", "monitors", ",".join(self.monitors))
config.set("Settings", "post_command", self.post_command)
config.set("Settings", "swww_transition_type", str(self.swww_transition_type))
config.set("Settings", "swww_transition_step", str(self.swww_transition_step))
config.set("Settings", "swww_transition_angle", str(self.swww_transition_angle))
config.set("Settings", "swww_transition_duration", str(self.swww_transition_duration))
with open(self.config_file, "w") as configfile:
config.write(configfile)

View File

@ -14,5 +14,5 @@ IMAGE_EXTENSIONS = {
BACKEND_OPTIONS[3]: ['.gif', '.jpg', '.jpeg', '.png'],
}
SWWW_TRANSITIONS = ["any", "none", "simple", "fade", "wipe", "left", "right", "top",
SWWW_TRANSITION_TYPES = ["any", "none", "simple", "fade", "wipe", "left", "right", "top",
"bottom", "wave", "grow", "center", "outer", "random"]