Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ If you're one of those developers, feel free to open an issue to get your projec
| `--disable-parallax` | Disable parallax effect on backgrounds that support it |
| `--no-fullscreen-pause` | Prevent pausing while fullscreen apps are running |
| `--fullscreen-pause-only-active` | Wayland only: pause only when a fullscreen window is active |
| `--fullscreen-pause-per-monitor` | Wayland only: pause only the screens that have a fullscreen window, instead of all of them |
| `--fullscreen-pause-ignore-appid <val>` | Wayland only: ignore fullscreen windows whose app_id contains `<val>` (repeatable) |

---
Expand Down
7 changes: 7 additions & 0 deletions src/WallpaperEngine/Application/ApplicationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,13 @@ void ApplicationContext::loadSettingsFromArgv () {
this->settings.render.pauseOnFullscreenOnlyWhenActive = true;
});

performanceGroup.add_argument ("--fullscreen-pause-per-monitor")
.help ("Wayland only: pause only the screens that have a fullscreen window, instead of all of them")
.flag ()
.action ([this] (const std::string& value) -> void {
this->settings.render.pauseOnFullscreenPerMonitor = true;
});

performanceGroup.add_argument ("--fullscreen-pause-ignore-appid")
.help ("Wayland only: ignore fullscreen windows whose app_id contains this value (repeatable)")
.action ([this] (const std::string& value) -> void {
Expand Down
5 changes: 5 additions & 0 deletions src/WallpaperEngine/Application/ApplicationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class ApplicationContext {
* Useful for compositors with "virtual" fullscreen windows (e.g. scrollable tiling).
*/
bool pauseOnFullscreenOnlyWhenActive;
/**
* Wayland-only: pause only the outputs that have a fullscreen window on them, instead of every output.
*/
bool pauseOnFullscreenPerMonitor;
/**
* Wayland-only: list of app_id substrings to ignore for fullscreen pause.
* Example: "firefox" will match "org.mozilla.firefox".
Expand Down Expand Up @@ -202,6 +206,7 @@ class ApplicationContext {
.maximumFPS = 30,
.pauseOnFullscreen = true,
.pauseOnFullscreenOnlyWhenActive = false,
.pauseOnFullscreenPerMonitor = false,
.fullscreenPauseIgnoreAppIds = {},
.debug = {
.baseOnly = false,
Expand Down
76 changes: 73 additions & 3 deletions src/WallpaperEngine/Application/WallpaperApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,13 @@ void WallpaperApplication::updatePlaylists () {
const auto now = std::chrono::steady_clock::now ();

for (auto& [screen, playlist] : this->m_activePlaylists) {
const bool paused = this->m_pausedOutputs.contains (screen);

// screens paused by --fullscreen-pause-per-monitor hold their timer, like the global pause does
if (paused && !playlist.definition.settings.updateOnPause) {
playlist.nextSwitch += now - playlist.lastUpdate;
}

playlist.lastUpdate = now;

if (playlist.definition.settings.mode != "timer") {
Expand All @@ -496,6 +503,11 @@ void WallpaperApplication::updatePlaylists () {
}

this->advancePlaylist (screen, playlist, now);

// the new wallpaper starts unpaused
if (paused) {
this->m_renderContext->setPause (this->m_pausedOutputs);
}
}
}

Expand Down Expand Up @@ -858,10 +870,16 @@ void WallpaperApplication::render () {

if (this->m_isPaused) {
usleep (FULLSCREEN_CHECK_WAIT_TIME);
if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
// also refreshes the state isFullscreenOn reads
const bool anythingFullscreen = this->m_fullScreenDetector->anythingFullscreen ();
const bool stayPaused = this->pausesPerMonitor () ? this->updatePausedOutputs () : anythingFullscreen;

if (stayPaused && this->m_context.state.general.keepRunning) {
return;
}
m_renderContext->setPause (false);
// resume everything, except outputs that still have a fullscreen window (only with
// --fullscreen-pause-per-monitor)
m_renderContext->setPause (this->m_pausedOutputs);

// account for paused duration in playlist timers
const auto pausedNow = std::chrono::steady_clock::now ();
Expand Down Expand Up @@ -928,7 +946,11 @@ void WallpaperApplication::render () {
}
#endif /* DEMOMODE */
// check for fullscreen windows and wait until there's none fullscreen
if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
// also refreshes the state isFullscreenOn reads
const bool anythingFullscreen = this->m_fullScreenDetector->anythingFullscreen ();
const bool pauseEverything = this->pausesPerMonitor () ? this->updatePausedOutputs () : anythingFullscreen;

if (pauseEverything && this->m_context.state.general.keepRunning) {
this->m_isPaused = true;
this->m_pauseStart = std::chrono::steady_clock::now ();

Expand Down Expand Up @@ -969,7 +991,55 @@ void WallpaperApplication::show () {
cleanup ();
}

bool WallpaperApplication::pausesPerMonitor () const {
return this->m_context.settings.render.pauseOnFullscreenPerMonitor
&& this->m_fullScreenDetector->canTellOutputsApart ();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

bool WallpaperApplication::updatePausedOutputs () {
const auto& viewports = this->m_renderContext->getOutput ().getViewports ();
// forget outputs that were disconnected while paused
const auto disconnected = [&viewports] (const std::string& name) { return !viewports.contains (name); };
bool changed = std::erase_if (this->m_pausedOutputs, disconnected) > 0;
std::erase_if (this->m_skippedOutputs, disconnected);

std::vector<Render::Drivers::Output::OutputViewport*> resumed;

for (const auto& [name, viewport] : viewports) {
const bool fullscreen = this->m_fullScreenDetector->isFullscreenOn (name);
const bool paused = this->m_pausedOutputs.contains (name);

if (fullscreen && !paused) {
changed = true;
this->m_pausedOutputs.insert (name);
} else if (!fullscreen && paused) {
changed = true;
this->m_pausedOutputs.erase (name);

// outputs that stopped drawing won't get another frame callback until something is drawn
if (this->m_skippedOutputs.erase (name) > 0) {
resumed.push_back (viewport);
}
}
}

if (changed) {
this->m_renderContext->setPause (this->m_pausedOutputs);
}

for (const auto viewport : resumed) {
this->update (viewport);
}

return !viewports.empty () && this->m_pausedOutputs.size () == viewports.size ();
}

void WallpaperApplication::update (Render::Drivers::Output::OutputViewport* viewport) {
if (this->m_pausedOutputs.contains (viewport->name)) {
this->m_skippedOutputs.insert (viewport->name);
return;
}

// render the scene
m_renderContext->render (viewport);
}
Expand Down
14 changes: 14 additions & 0 deletions src/WallpaperEngine/Application/WallpaperApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ class WallpaperApplication {
std::vector<std::size_t> buildPlaylistOrder (const ApplicationContext::PlaylistDefinition& definition);
void ensureBrowserForProject (const Project& project);
bool makeAnyViewportCurrent () const;
/**
* Pauses the outputs that have a fullscreen window on them and resumes the rest
*
* @return If every output is paused
*/
bool updatePausedOutputs ();
/**
* @return If --fullscreen-pause-per-monitor is on and the fullscreen detector can tell outputs apart
*/
[[nodiscard]] bool pausesPerMonitor () const;

/** The application context that contains the current app settings */
ApplicationContext& m_context;
Expand All @@ -178,6 +188,10 @@ class WallpaperApplication {
std::unique_ptr<WallpaperEngine::Media::MediaSource> m_mediaSource = nullptr;
std::mt19937 m_playlistRng { std::random_device {}() };
bool m_isPaused = false;
/** Outputs paused by --fullscreen-pause-per-monitor */
std::set<std::string> m_pausedOutputs {};
/** Paused outputs that skipped a frame, so they need a render to start drawing again */
std::set<std::string> m_skippedOutputs {};
bool m_screenShotTaken = false;
uint32_t m_nextFrameScreenshot = 0;
std::chrono::steady_clock::time_point m_pauseStart {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ Application::ApplicationContext& FullScreenDetector::getApplicationContext () co

bool FullScreenDetector::anythingFullscreen () const { return false; }

bool FullScreenDetector::canTellOutputsApart () const { return false; }

bool FullScreenDetector::isFullscreenOn (const std::string& output) const { return this->anythingFullscreen (); }

void FullScreenDetector::reset () { }
14 changes: 14 additions & 0 deletions src/WallpaperEngine/Render/Drivers/Detectors/FullScreenDetector.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "WallpaperEngine/Application/ApplicationContext.h"

#include <string>

namespace WallpaperEngine::Render::Drivers::Detectors {
class FullScreenDetector {
public:
Expand All @@ -12,6 +14,18 @@ class FullScreenDetector {
* @return If anything is fullscreen
*/
[[nodiscard]] virtual bool anythingFullscreen () const;
/**
* @return If isFullscreenOn can tell outputs apart, otherwise it only repeats anythingFullscreen
*/
[[nodiscard]] virtual bool canTellOutputsApart () const;
/**
* Checks a single output. Only meaningful right after anythingFullscreen, which refreshes the state.
* Detectors that cannot tell outputs apart report the global state.
*
* @param output The output's name (e.g. DP-1)
* @return If anything is fullscreen on the given output
*/
[[nodiscard]] virtual bool isFullscreenOn (const std::string& output) const;
/**
* Restarts the fullscreen detector, specially useful if there's any resources tied to the output driver
*/
Expand Down
Loading