Skip to content

refactor(mpvpaper): Rewriting multiple features#13

Open
3ri4nG0ld wants to merge 4 commits into
noctalia-dev:mainfrom
3ri4nG0ld:main
Open

refactor(mpvpaper): Rewriting multiple features#13
3ri4nG0ld wants to merge 4 commits into
noctalia-dev:mainfrom
3ri4nG0ld:main

Conversation

@3ri4nG0ld

@3ri4nG0ld 3ri4nG0ld commented Jul 1, 2026

Copy link
Copy Markdown

Summary

I have taken the liberty of making some modifications to the mpvpaper plugin to improve some aspects that I consider useful and to fix minor issues.

The modifications include:

  • Removed the dependency on the external shell script supervisor.sh.
    In my opinion, the mpvpaper process can be managed without needing an additional background process keeping a FIFO open, and I believe it's a cleaner and more efficient solution.

  • Added the ability to run mpvpaper inside systemd transient scopes (systemd-run).
    This allows some advantages, such as being able to set limits to the resources used by the process or the ability to pause/resume a wallpaper in a simple way.

  • Implemented a function to automatically extract a static frame from the wallpaper using ffmpeg when stopping video playback (to allow pausing or stopping a wallpaper without using systemd).
    This is based on how the dms-mpvpaper repository works, and above all it is intended to fix some issues like the one mentioned in #8 or the issue I mentioned in my previous PR (#12), to set the color palette automatically.

  • Added external IPC commands to pause/resume playback via the cgroup freezer.
    Mainly to allow using commands such as noctalia msg plugin noctalia/mpvpaper:service all toggle to pause, resume or stop the wallpaper.

  • Applied some changes so that if the user selects "All Outputs", a separate process is not created for each of the wallpapers, and the command mpvpaper "*" video.mp4 is utilized, which should save resources.

  • Updated the panel interface to dynamically change the "Pause" button text to "Resume" when a video is paused, and to disable the button completely if no video is assigned.

Motivation

Many of these changes are suggestions based on how I previously used the mpvpaper wallpaper, which I have always preferred to "limit" in resources to avoid influencing the performance of my system while playing games.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Build / packaging

Related Issue

#8

Testing

Manual Coverage

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Tested with different bar positions and density settings
  • Tested at different interface scaling values
  • Tested with multiple monitors

Screenshots / Videos

Overview:

output.mp4

Pause/Resume button:
image

Checklist

  • This PR is ready for review, or it is marked as Draft.
  • I self-reviewed the changes.
  • I added or updated translations/en.json, or this PR adds no new user-facing strings.
  • I did not edit non-English translation files unless this PR is explicitly for translation tooling, an import/export sync, or a maintainer-requested locale change.

Additional Notes

This PR makes many changes, and makes my previous PR (#12) unnecessary.

I understand that many of these changes are not really necessary for the normal functioning of the plugin, but I believe they are useful features that would improve the user experience in some aspects.

Feel free to reject this PR if you do not think it fits what is expected or to modify it if you think it is necessary.
Any comments or suggestions will be welcome.

@ItsLemmy

ItsLemmy commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Hey, thanks for the PR. I did not have a chance to review the PR manually or test it, so here is an automated review for now.

  1. mpvpaper_service.luau:243 — Pause/resume is broken in the default (non-systemd) mode because killMpvpaper clears the paused flag that freezeMpvpaper just set.

run_as_systemd defaults to false, so this is the default path. freezeMpvpaper does:
isPaused[connector] = true -- line 298
...
killMpvpaper(connector, function() extractStaticWallpaper(...) end) -- line 305
but killMpvpaper runs synchronously first and executes isPaused[connector] = false (line 243). Net synchronous result: isPaused[connector] == false, which is then publish()ed. Consequence: after clicking Pause, the video stops and the static frame shows, but the state never records "paused" — the button stays "Pause", isPausedForSelection() returns false, and a second click calls freezeMpvpaper again (re-killing/re-extracting) instead of thawMpvpaper. Resume never works and the video can't be brought back in the default configuration. (The systemd branch is unaffected because it doesn't call killMpvpaper.) Fix: set isPaused[connector] = true in/after the callback, not before the kill.

  1. mpvpaper_service.luau:232 — pkill -f "mpvpaper .*" .. connector is an unanchored substring match, so stopping one output kills sibling outputs whose names share a prefix.

For connector = "DP-1", the pattern is mpvpaper .*DP-1, which also matches the command line of the DP-10 (or DP-11, …) instance (mpvpaper -p 'DP-10' … contains DP-1). Clearing/pausing DP-1 on a multi-monitor setup therefore also tears down DP-10. (For the wildcard case the pattern degrades to mpvpaper .**, an odd regex that happens to match all instances.) Consider matching the exact -p 'connector' token or using the systemd unit consistently.

  1. plugin.toml:772 — the nice setting is gated behind visible_when run_as_systemd, but the code applies nice regardless of systemd mode (mpvpaper_service.luau:270).

startMpvpaper builds nice -n N mpvpaper … before the optional systemd wrapping, so nice is fully functional without systemd — yet the slider only appears when "Run as systemd service" is enabled, leaving non-systemd users unable to configure a value that would otherwise work. Either drop the visible_when on nice or move the nice application under the systemd branch.

@3ri4nG0ld

Copy link
Copy Markdown
Author
  1. mpvpaper_service.luau:243 — Pause/resume is broken in the default (non-systemd) mode because killMpvpaper clears the paused flag that freezeMpvpaper just set.

run_as_systemd defaults to false, so this is the default path. freezeMpvpaper does:
isPaused[connector] = true -- line 298
...
killMpvpaper(connector, function() extractStaticWallpaper(...) end) -- line 305
but killMpvpaper runs synchronously first and executes isPaused[connector] = false (line 243). Net synchronous result: isPaused[connector] == false, which is then publish()ed. Consequence: after clicking Pause, the video stops and the static frame shows, but the state never records "paused" — the button stays "Pause", isPausedForSelection() returns false, and a second click calls freezeMpvpaper again (re-killing/re-extracting) instead of thawMpvpaper. Resume never works and the video can't be brought back in the default configuration. (The systemd branch is unaffected because it doesn't call killMpvpaper.) Fix: set isPaused[connector] = true in/after the callback, not before the kill.

I've modified the logic so that, instead of killing the process when it's paused (when systemd isn't used), it takes advantage of the SIGSTOP and SIGCONT signals.

This replicates the behavior of systemctl --user freeze/thaw, so in principle it should work.

…erns and refined UI state handling for wildcard assignments.
@3ri4nG0ld

Copy link
Copy Markdown
Author
  1. mpvpaper_service.luau:232 — pkill -f "mpvpaper .*" .. connector is an unanchored substring match, so stopping one output kills sibling outputs whose names share a prefix.

For connector = "DP-1", the pattern is mpvpaper .*DP-1, which also matches the command line of the DP-10 (or DP-11, …) instance (mpvpaper -p 'DP-10' … contains DP-1). Clearing/pausing DP-1 on a multi-monitor setup therefore also tears down DP-10. (For the wildcard case the pattern degrades to mpvpaper .**, an odd regex that happens to match all instances.) Consider matching the exact -p 'connector' token or using the systemd unit consistently.

I've made several changes regarding this point.

Previously, it was implemented in such a way that all processes could be terminated, rather than allowing control over each individual monitor.

I've now modified the behavior so that these actions can be performed and so that the UI can control which actions are allowed and which are not.

The behavior can be summarized roughly in this table:

UI Selector Previous System State User Action Final Effect
All Outputs Any state Pick Video Cancels previous wallpapers. Spawns 1 single global process (*).
🔒 Locks the monitor selector (greyed out).
All Outputs Global (*) or Individual Stop Button Terminates absolutely ALL active processes.
🔓 Unlocks the monitor selector.
All Outputs Global (*) or Individual Pause Button Sends SIGSTOP to ALL active processes.
All Outputs Global or Indiv. (At least 1 paused) Resume Button Sends SIGCONT to ALL processes (Resumes everything at once without flipping states).
Monitor X Global (*) running Pick Video ❌ Impossible. Selector is locked (greyed out).
Monitor X Global (*) running Pause / Stop Buttons ❌ Buttons disabled (greyed out) to prevent conflicts.
Monitor X No wallpaper or Individual running Pick Video Cancels X's previous wallpaper and spawns 1 individual process just for Monitor X.
Monitor X Individual running on X Stop Button Terminates ONLY Monitor X's process. Other monitors remain untouched.
Monitor X Individual running on X Pause Button Sends SIGSTOP ONLY to Monitor X. Other monitors remain untouched.
Monitor X Individual paused on X Resume Button Sends SIGCONT ONLY to Monitor X.

Video example:

output.mp4

Note

The monitor is the DP-1; the fact that it displays the video even when I select a video from the eDP-1 monitor appears to be a bug in mpvpaper, which I have already reported.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants