Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
90 changes: 87 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,25 @@ 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 perMonitor = this->pausesPerMonitor ();

// the driver's events aren't dispatched while paused, so an output that was removed in the meantime
// would still be a viewport, and could get resumed and drawn to. Frame callbacks dispatched here don't
// draw anything, since every output is paused
if (perMonitor) {
m_videoDriver->dispatchPendingEvents ();
}

const bool stayPaused = perMonitor ? 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 +955,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 +1000,60 @@ void WallpaperApplication::show () {
cleanup ();
}

bool WallpaperApplication::pausesPerMonitor () const {
// window modes have a single "default" viewport, which isn't an output
return this->m_context.settings.render.pauseOnFullscreenPerMonitor
&& this->m_context.settings.render.mode == ApplicationContext::DESKTOP_BACKGROUND
&& this->m_fullScreenDetector->canTellOutputsApart ();
}

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); };
std::erase_if (this->m_pausedOutputs, disconnected);
std::erase_if (this->m_skippedOutputs, disconnected);
// a monitor coming or going changes which ones a spanned wallpaper is on
bool changed = viewports.size () != this->m_lastViewportCount;
this->m_lastViewportCount = viewports.size ();

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
17 changes: 17 additions & 0 deletions src/WallpaperEngine/Application/WallpaperApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ 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, the wallpaper is the desktop background 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 +189,12 @@ 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 {};
/** Outputs seen by the last updatePausedOutputs, to notice monitors being connected or disconnected */
std::size_t m_lastViewportCount = 0;
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
Loading