From 8d2d24ce65761d0a8e3de309c51f2f08a5df90d8 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 24 May 2021 18:46:45 +0200 Subject: [PATCH 1/7] Store screen capture operation as osg::ref_ptr --- apps/openmw/engine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 1aef62df53..49ae92abc1 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -62,7 +62,7 @@ namespace OMW boost::filesystem::path mResDir; osg::ref_ptr mViewer; osg::ref_ptr mScreenCaptureHandler; - osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation; + osg::ref_ptr mScreenCaptureOperation; std::string mCellName; std::vector mContentFiles; std::vector mGroundcoverFiles; From 33aa4d08224e1ddf6b9a7c02f5a643545ae38e75 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 24 May 2021 18:47:23 +0200 Subject: [PATCH 2/7] Move WriteScreenshotToFileOperation to components --- apps/openmw/engine.cpp | 52 ++---------------- components/CMakeLists.txt | 1 + components/sceneutil/screencapture.cpp | 73 ++++++++++++++++++++++++++ components/sceneutil/screencapture.hpp | 31 +++++++++++ 4 files changed, 108 insertions(+), 49 deletions(-) create mode 100644 components/sceneutil/screencapture.cpp create mode 100644 components/sceneutil/screencapture.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a06faa4d71..d03891fdc6 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -40,6 +40,8 @@ #include +#include + #include "mwinput/inputmanagerimp.hpp" #include "mwgui/windowmanagerimp.hpp" @@ -781,54 +783,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) } } -class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation -{ -public: - WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat) - : mScreenshotPath(screenshotPath) - , mScreenshotFormat(screenshotFormat) - { - } - - void operator()(const osg::Image& image, const unsigned int context_id) override - { - // Count screenshots. - int shotCount = 0; - - // Find the first unused filename with a do-while - std::ostringstream stream; - do - { - // Reset the stream - stream.str(""); - stream.clear(); - - stream << mScreenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << mScreenshotFormat; - - } while (boost::filesystem::exists(stream.str())); - - boost::filesystem::ofstream outStream; - outStream.open(boost::filesystem::path(stream.str()), std::ios::binary); - - osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat); - if (!readerwriter) - { - Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found"; - return; - } - - osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); - if (!result.success()) - { - Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); - } - } - -private: - std::string mScreenshotPath; - std::string mScreenshotFormat; -}; - // Initialise and enter main loop. void OMW::Engine::go() { @@ -860,7 +814,7 @@ void OMW::Engine::go() mViewer->setUseConfigureAffinity(false); #endif - mScreenCaptureOperation = new WriteScreenshotToFileOperation( + mScreenCaptureOperation = new SceneUtil::WriteScreenshotToFileOperation( mCfgMgr.getScreenshotPath().string(), Settings::Manager::getString("screenshot format", "General")); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7860f492ce..43987d6c7b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -53,6 +53,7 @@ add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller + screencapture ) add_component_dir (nif diff --git a/components/sceneutil/screencapture.cpp b/components/sceneutil/screencapture.cpp new file mode 100644 index 0000000000..660f296b63 --- /dev/null +++ b/components/sceneutil/screencapture.cpp @@ -0,0 +1,73 @@ +#include "screencapture.hpp" + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace SceneUtil +{ + void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, + const osg::Image& image) + { + // Count screenshots. + int shotCount = 0; + + // Find the first unused filename with a do-while + std::ostringstream stream; + do + { + // Reset the stream + stream.str(""); + stream.clear(); + + stream << screenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << screenshotFormat; + + } while (boost::filesystem::exists(stream.str())); + + boost::filesystem::ofstream outStream; + outStream.open(boost::filesystem::path(stream.str()), std::ios::binary); + + osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(screenshotFormat); + if (!readerwriter) + { + Log(Debug::Error) << "Error: Can't write screenshot, no '" << screenshotFormat << "' readerwriter found"; + return; + } + + osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); + if (!result.success()) + { + Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); + } + } + + WriteScreenshotToFileOperation::WriteScreenshotToFileOperation(const std::string& screenshotPath, + const std::string& screenshotFormat) + : mScreenshotPath(screenshotPath) + , mScreenshotFormat(screenshotFormat) + { + } + + void WriteScreenshotToFileOperation::operator()(const osg::Image& image, const unsigned int /*context_id*/) + { + try + { + writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image); + } + catch (const std::exception& e) + { + Log(Debug::Error) << "Failed to write screenshot to file with path=\"" << mScreenshotPath + << "\", format=\"" << mScreenshotFormat << "\": " << e.what(); + } + } +} diff --git a/components/sceneutil/screencapture.hpp b/components/sceneutil/screencapture.hpp new file mode 100644 index 0000000000..37ff5e39cc --- /dev/null +++ b/components/sceneutil/screencapture.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_SCREENCAPTURE_H +#define OPENMW_COMPONENTS_SCENEUTIL_SCREENCAPTURE_H + +#include + +#include + +namespace osg +{ + class Image; +} + +namespace SceneUtil +{ + void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, + const osg::Image& image); + + class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation + { + public: + WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat); + + void operator()(const osg::Image& image, const unsigned int /*context_id*/) override; + + private: + const std::string mScreenshotPath; + const std::string mScreenshotFormat; + }; +} + +#endif From f8e02000ece0459f501cbebf5c335707058d50d3 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 24 May 2021 19:27:09 +0200 Subject: [PATCH 3/7] Write screenshots to file asynchronously --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 20 ++++++---- components/sceneutil/screencapture.cpp | 53 +++++++++++++++++++++++++- components/sceneutil/screencapture.hpp | 18 ++++++++- 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c3b4e3354..a676b70b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #6129: Player avatar not displayed correctly for large window sizes when GUI scaling active Bug #6131: Item selection in the avatar window not working correctly for large window sizes Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player + Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive Feature #2780: A way to see current OpenMW version in the console Feature #5489: MCP: Telekinesis fix for activators Feature #6017: Separate persistent and temporary cell references when saving diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d03891fdc6..76f02fb683 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -670,6 +670,18 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) throw std::runtime_error("Invalid setting: 'preload num threads' must be >0"); mWorkQueue = new SceneUtil::WorkQueue(numThreads); + mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation( + mWorkQueue, + new SceneUtil::WriteScreenshotToFileOperation( + mCfgMgr.getScreenshotPath().string(), + Settings::Manager::getString("screenshot format", "General") + ) + ); + + mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation); + + mViewer->addEventHandler(mScreenCaptureHandler); + // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so @@ -814,14 +826,6 @@ void OMW::Engine::go() mViewer->setUseConfigureAffinity(false); #endif - mScreenCaptureOperation = new SceneUtil::WriteScreenshotToFileOperation( - mCfgMgr.getScreenshotPath().string(), - Settings::Manager::getString("screenshot format", "General")); - - mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation); - - mViewer->addEventHandler(mScreenCaptureHandler); - mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video")); prepareEngine (settings); diff --git a/components/sceneutil/screencapture.cpp b/components/sceneutil/screencapture.cpp index 660f296b63..c010b390aa 100644 --- a/components/sceneutil/screencapture.cpp +++ b/components/sceneutil/screencapture.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -10,9 +11,43 @@ #include #include -#include -#include +#include #include +#include +#include + +namespace +{ + class ScreenCaptureWorkItem : public SceneUtil::WorkItem + { + public: + ScreenCaptureWorkItem(const osg::ref_ptr& impl, + const osg::Image& image, unsigned int contextId) + : mImpl(impl), + mImage(new osg::Image(image)), + mContextId(contextId) + { + assert(mImpl != nullptr); + } + + void doWork() override + { + try + { + (*mImpl)(*mImage, mContextId); + } + catch (const std::exception& e) + { + Log(Debug::Error) << "ScreenCaptureWorkItem exception: " << e.what(); + } + } + + private: + const osg::ref_ptr mImpl; + const osg::ref_ptr mImage; + const unsigned int mContextId; + }; +} namespace SceneUtil { @@ -70,4 +105,18 @@ namespace SceneUtil << "\", format=\"" << mScreenshotFormat << "\": " << e.what(); } } + + AsyncScreenCaptureOperation::AsyncScreenCaptureOperation(osg::ref_ptr queue, + osg::ref_ptr impl) + : mQueue(std::move(queue)), + mImpl(std::move(impl)) + { + assert(mQueue != nullptr); + assert(mImpl != nullptr); + } + + void AsyncScreenCaptureOperation::operator()(const osg::Image& image, const unsigned int context_id) + { + mQueue->addWorkItem(new ScreenCaptureWorkItem(mImpl, image, context_id)); + } } diff --git a/components/sceneutil/screencapture.hpp b/components/sceneutil/screencapture.hpp index 37ff5e39cc..8087095e4e 100644 --- a/components/sceneutil/screencapture.hpp +++ b/components/sceneutil/screencapture.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_SCREENCAPTURE_H #define OPENMW_COMPONENTS_SCENEUTIL_SCREENCAPTURE_H +#include #include #include @@ -12,6 +13,8 @@ namespace osg namespace SceneUtil { + class WorkQueue; + void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, const osg::Image& image); @@ -20,12 +23,25 @@ namespace SceneUtil public: WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat); - void operator()(const osg::Image& image, const unsigned int /*context_id*/) override; + void operator()(const osg::Image& image, const unsigned int context_id) override; private: const std::string mScreenshotPath; const std::string mScreenshotFormat; }; + + class AsyncScreenCaptureOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation + { + public: + AsyncScreenCaptureOperation(osg::ref_ptr queue, + osg::ref_ptr impl); + + void operator()(const osg::Image& image, const unsigned int context_id) override; + + private: + const osg::ref_ptr mQueue; + const osg::ref_ptr mImpl; + }; } #endif From f7a6be053d55af0776bee13c91faa201bce48327 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 25 May 2021 13:44:40 +0200 Subject: [PATCH 4/7] Stop engine work queue before destructing environment To avoid access to null and dangling pointers from active work items on quitting. --- apps/openmw/engine.cpp | 2 ++ components/sceneutil/workqueue.cpp | 17 ++++++++++++++--- components/sceneutil/workqueue.hpp | 6 +++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 76f02fb683..9651aaafbd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -407,6 +407,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { + mWorkQueue->stop(); + mEnvironment.cleanup(); delete mScriptContext; diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 3f8ed8aaf2..3c1df80ac4 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -33,14 +33,25 @@ bool WorkItem::isDone() const return mDone; } -WorkQueue::WorkQueue(int workerThreads) +WorkQueue::WorkQueue(std::size_t workerThreads) : mIsReleased(false) { - for (int i=0; i(*this)); + start(workerThreads); } WorkQueue::~WorkQueue() +{ + stop(); +} + +void WorkQueue::start(std::size_t workerThreads) +{ + while (mThreads.size() < workerThreads) + mThreads.emplace_back(std::make_unique(*this)); + mIsReleased = false; +} + +void WorkQueue::stop() { { std::unique_lock lock(mMutex); diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 5b51c59e59..6eb6a36a3e 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -44,9 +44,13 @@ namespace SceneUtil class WorkQueue : public osg::Referenced { public: - WorkQueue(int numWorkerThreads=1); + WorkQueue(std::size_t workerThreads); ~WorkQueue(); + void start(std::size_t workerThreads); + + void stop(); + /// Add a new work item to the back of the queue. /// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete. /// @param front If true, add item to the front of the queue. If false (default), add to the back. From 5103120eefac41e7b53ca8dfafd91b0e072235b6 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 25 May 2021 01:49:27 +0200 Subject: [PATCH 5/7] Notify about saved screenshot Show message about saved screenshot via schedule message box. Since screenshot saving happens not in the main thread calling messageBox directly is unsafe. WindowManager::scheduleMessageBox delays message box showing until next update in the main thread. --- apps/openmw/engine.cpp | 11 ++++++++- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 15 +++++++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 16 +++++++++++++ components/sceneutil/screencapture.cpp | 31 +++++++++++++++++++------- components/sceneutil/screencapture.hpp | 8 ++++--- 6 files changed, 71 insertions(+), 12 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 9651aaafbd..278c97f029 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -218,6 +218,14 @@ namespace if (Settings::Manager::getInt("async num threads", "Physics") == 0) profiler.removeUserStatsLine(" -Async"); } + + struct ScheduleNonDialogMessageBox + { + void operator()(std::string message) const + { + MWBase::Environment::get().getWindowManager()->scheduleMessageBox(std::move(message), MWGui::ShowInDialogueMode_Never); + } + }; } void OMW::Engine::executeLocalScripts() @@ -676,7 +684,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mWorkQueue, new SceneUtil::WriteScreenshotToFileOperation( mCfgMgr.getScreenshotPath().string(), - Settings::Manager::getString("screenshot format", "General") + Settings::Manager::getString("screenshot format", "General"), + ScheduleNonDialogMessageBox {} ) ); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 6e3c615de3..f256cc387e 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -228,6 +228,8 @@ namespace MWBase virtual void exitCurrentGuiMode() = 0; virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; + /// Puts message into a queue to show on the next update. Thread safe alternative for messageBox. + virtual void scheduleMessageBox(std::string message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; virtual void interactiveMessageBox (const std::string& message, diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1694206504..eebcf4b094 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -749,6 +749,11 @@ namespace MWGui } } + void WindowManager::scheduleMessageBox(std::string message, enum MWGui::ShowInDialogueMode showInDialogueMode) + { + mScheduledMessageBoxes.lock()->emplace_back(std::move(message), showInDialogueMode); + } + void WindowManager::staticMessageBox(const std::string& message) { mMessageBoxManager->createMessageBox(message, true); @@ -803,6 +808,8 @@ namespace MWGui void WindowManager::update (float frameDuration) { + handleScheduledMessageBoxes(); + bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame; @@ -2204,4 +2211,12 @@ namespace MWGui { return mVersionDescription; } + + void WindowManager::handleScheduledMessageBoxes() + { + const auto scheduledMessageBoxes = mScheduledMessageBoxes.lock(); + for (const ScheduledMessageBox& v : *scheduledMessageBoxes) + messageBox(v.mMessage, v.mShowInDialogueMode); + scheduledMessageBoxes->clear(); + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index ef411a39df..8c7e365ec7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -8,6 +8,7 @@ **/ #include +#include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include "mapwindow.hpp" #include "statswatcher.hpp" @@ -264,6 +266,7 @@ namespace MWGui void exitCurrentGuiMode() override; void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) override; + void scheduleMessageBox (std::string message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) override; void staticMessageBox(const std::string& message) override; void removeStaticMessageBox() override; void interactiveMessageBox (const std::string& message, @@ -524,6 +527,17 @@ namespace MWGui float mScalingFactor; + struct ScheduledMessageBox + { + std::string mMessage; + MWGui::ShowInDialogueMode mShowInDialogueMode; + + ScheduledMessageBox(std::string&& message, MWGui::ShowInDialogueMode showInDialogueMode) + : mMessage(std::move(message)), mShowInDialogueMode(showInDialogueMode) {} + }; + + Misc::ScopeGuarded> mScheduledMessageBoxes; + /** * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property. * Supported syntax: @@ -555,6 +569,8 @@ namespace MWGui void updatePinnedWindows(); void enableScene(bool enable); + + void handleScheduledMessageBoxes(); }; } diff --git a/components/sceneutil/screencapture.cpp b/components/sceneutil/screencapture.cpp index c010b390aa..47119f3644 100644 --- a/components/sceneutil/screencapture.cpp +++ b/components/sceneutil/screencapture.cpp @@ -51,59 +51,74 @@ namespace namespace SceneUtil { - void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, - const osg::Image& image) + std::string writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, + const osg::Image& image) { // Count screenshots. int shotCount = 0; // Find the first unused filename with a do-while std::ostringstream stream; + std::string lastFileName; + std::string lastFilePath; do { // Reset the stream stream.str(""); stream.clear(); - stream << screenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << screenshotFormat; + stream << "screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << screenshotFormat; - } while (boost::filesystem::exists(stream.str())); + lastFileName = stream.str(); + lastFilePath = screenshotPath + "/" + lastFileName; + + } while (boost::filesystem::exists(lastFilePath)); boost::filesystem::ofstream outStream; - outStream.open(boost::filesystem::path(stream.str()), std::ios::binary); + outStream.open(boost::filesystem::path(std::move(lastFilePath)), std::ios::binary); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(screenshotFormat); if (!readerwriter) { Log(Debug::Error) << "Error: Can't write screenshot, no '" << screenshotFormat << "' readerwriter found"; - return; + return std::string(); } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); if (!result.success()) { Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); + return std::string(); } + + return lastFileName; } WriteScreenshotToFileOperation::WriteScreenshotToFileOperation(const std::string& screenshotPath, - const std::string& screenshotFormat) + const std::string& screenshotFormat, + std::function callback) : mScreenshotPath(screenshotPath) , mScreenshotFormat(screenshotFormat) + , mCallback(callback) { } void WriteScreenshotToFileOperation::operator()(const osg::Image& image, const unsigned int /*context_id*/) { + std::string fileName; try { - writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image); + fileName = writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image); } catch (const std::exception& e) { Log(Debug::Error) << "Failed to write screenshot to file with path=\"" << mScreenshotPath << "\", format=\"" << mScreenshotFormat << "\": " << e.what(); } + if (fileName.empty()) + mCallback("Failed to save screenshot"); + else + mCallback(fileName + " has been saved"); } AsyncScreenCaptureOperation::AsyncScreenCaptureOperation(osg::ref_ptr queue, diff --git a/components/sceneutil/screencapture.hpp b/components/sceneutil/screencapture.hpp index 8087095e4e..6395d989d8 100644 --- a/components/sceneutil/screencapture.hpp +++ b/components/sceneutil/screencapture.hpp @@ -15,19 +15,21 @@ namespace SceneUtil { class WorkQueue; - void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, - const osg::Image& image); + std::string writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, + const osg::Image& image); class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation { public: - WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat); + WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat, + std::function callback); void operator()(const osg::Image& image, const unsigned int context_id) override; private: const std::string mScreenshotPath; const std::string mScreenshotFormat; + const std::function mCallback; }; class AsyncScreenCaptureOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation From 4259f7f230a502462a53fd88e0ef28ff7487ebf1 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 25 May 2021 13:58:16 +0200 Subject: [PATCH 6/7] Add setting to enable/disabled notification for saved screenshots --- apps/launcher/advancedpage.cpp | 4 ++++ apps/openmw/engine.cpp | 9 ++++++++- docs/source/reference/modding/settings/general.rst | 9 +++++++++ files/settings-default.cfg | 3 +++ files/ui/advancedpage.ui | 9 ++++++++- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index bddb70aa02..00021268e3 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -213,6 +213,8 @@ bool Launcher::AdvancedPage::loadSettings() if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) screenshotFormatComboBox->addItem(screenshotFormatString); screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); + + loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); } // Testing @@ -376,6 +378,8 @@ void Launcher::AdvancedPage::saveSettings() std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General")) Settings::Manager::setString("screenshot format", "General", screenshotFormatString); + + saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); } // Testing diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 278c97f029..93d3530c0a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -226,6 +226,11 @@ namespace MWBase::Environment::get().getWindowManager()->scheduleMessageBox(std::move(message), MWGui::ShowInDialogueMode_Never); } }; + + struct IgnoreString + { + void operator()(std::string) const {} + }; } void OMW::Engine::executeLocalScripts() @@ -685,7 +690,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) new SceneUtil::WriteScreenshotToFileOperation( mCfgMgr.getScreenshotPath().string(), Settings::Manager::getString("screenshot format", "General"), - ScheduleNonDialogMessageBox {} + Settings::Manager::getBool("notify on saved screenshot", "General") + ? std::function(ScheduleNonDialogMessageBox {}) + : std::function(IgnoreString {}) ) ); diff --git a/docs/source/reference/modding/settings/general.rst b/docs/source/reference/modding/settings/general.rst index c885f77aa1..f0ebe4f972 100644 --- a/docs/source/reference/modding/settings/general.rst +++ b/docs/source/reference/modding/settings/general.rst @@ -59,3 +59,12 @@ texture mipmap Set the texture mipmap type to control the method mipmaps are created. Mipmapping is a way of reducing the processing power needed during minification by pregenerating a series of smaller textures. + +notify on saved screenshot +-------------- + +:Type: boolean +:Range: True/False +:Default: False + +Show message box when screenshot is saved to a file. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 88a6b48126..522762555d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -386,6 +386,9 @@ texture min filter = linear # Texture mipmap type. (none, nearest, or linear). texture mipmap = nearest +# Show message box when screenshot is saved to a file. +notify on saved screenshot = false + [Shaders] # Force rendering with shaders. By default, only bump-mapped objects will use shaders. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8abe8f59a4..f72150df22 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -1121,7 +1121,7 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C - + Screenshot Format @@ -1149,6 +1149,13 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C + + + + Notify on saved screenshot + + + From d8cd5b361a88862a7f9a828af6fddc66db719561 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 25 May 2021 21:04:05 +0200 Subject: [PATCH 7/7] Hide message boxes when HUD is hidden --- apps/openmw/mwgui/messagebox.cpp | 15 ++++++++++++--- apps/openmw/mwgui/messagebox.hpp | 4 ++++ apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 5bd8ceb5ae..4907a247ff 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -101,6 +101,8 @@ namespace MWGui if(stat) mStaticMessageBox = box; + box->setVisible(mVisible); + mMessageBoxes.push_back(box); if(mMessageBoxes.size() > 3) { @@ -167,8 +169,12 @@ namespace MWGui return pressed; } - - + void MessageBoxManager::setVisible(bool value) + { + mVisible = value; + for (MessageBox* messageBox : mMessageBoxes) + messageBox->setVisible(value); + } MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) : Layout("openmw_messagebox.layout") @@ -201,7 +207,10 @@ namespace MWGui return mMainWidget->getHeight()+mNextBoxPadding; } - + void MessageBox::setVisible(bool value) + { + mMainWidget->setVisible(value); + } InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) : WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "openmw_interactive_messagebox_notransp.layout" : "openmw_interactive_messagebox.layout") diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index aeb1b83002..26d26bac56 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -47,12 +47,15 @@ namespace MWGui void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); } + void setVisible(bool value); + private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; MessageBox* mStaticMessageBox; float mMessageBoxSpeed; int mLastButtonPressed; + bool mVisible = true; }; class MessageBox : public Layout @@ -62,6 +65,7 @@ namespace MWGui void setMessage (const std::string& message); int getHeight (); void update (int height); + void setVisible(bool value); float mCurrentTime; float mMaxTime; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index eebcf4b094..544a0927e7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1499,6 +1499,7 @@ namespace MWGui { mHudEnabled = !mHudEnabled; updateVisible(); + mMessageBoxManager->setVisible(mHudEnabled); return mHudEnabled; }