1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-27 03:40:24 +00:00

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.
This commit is contained in:
elsid 2021-05-25 01:49:27 +02:00
parent f7a6be053d
commit 5103120eef
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
6 changed files with 71 additions and 12 deletions

View file

@ -218,6 +218,14 @@ namespace
if (Settings::Manager::getInt("async num threads", "Physics") == 0) if (Settings::Manager::getInt("async num threads", "Physics") == 0)
profiler.removeUserStatsLine(" -Async"); 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() void OMW::Engine::executeLocalScripts()
@ -676,7 +684,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mWorkQueue, mWorkQueue,
new SceneUtil::WriteScreenshotToFileOperation( new SceneUtil::WriteScreenshotToFileOperation(
mCfgMgr.getScreenshotPath().string(), mCfgMgr.getScreenshotPath().string(),
Settings::Manager::getString("screenshot format", "General") Settings::Manager::getString("screenshot format", "General"),
ScheduleNonDialogMessageBox {}
) )
); );

View file

@ -228,6 +228,8 @@ namespace MWBase
virtual void exitCurrentGuiMode() = 0; virtual void exitCurrentGuiMode() = 0;
virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 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 staticMessageBox(const std::string& message) = 0;
virtual void removeStaticMessageBox() = 0; virtual void removeStaticMessageBox() = 0;
virtual void interactiveMessageBox (const std::string& message, virtual void interactiveMessageBox (const std::string& message,

View file

@ -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) void WindowManager::staticMessageBox(const std::string& message)
{ {
mMessageBoxManager->createMessageBox(message, true); mMessageBoxManager->createMessageBox(message, true);
@ -803,6 +808,8 @@ namespace MWGui
void WindowManager::update (float frameDuration) void WindowManager::update (float frameDuration)
{ {
handleScheduledMessageBoxes();
bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!= bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame; MWBase::StateManager::State_NoGame;
@ -2204,4 +2211,12 @@ namespace MWGui
{ {
return mVersionDescription; return mVersionDescription;
} }
void WindowManager::handleScheduledMessageBoxes()
{
const auto scheduledMessageBoxes = mScheduledMessageBoxes.lock();
for (const ScheduledMessageBox& v : *scheduledMessageBoxes)
messageBox(v.mMessage, v.mShowInDialogueMode);
scheduledMessageBoxes->clear();
}
} }

View file

@ -8,6 +8,7 @@
**/ **/
#include <stack> #include <stack>
#include <vector>
#include <osg/ref_ptr> #include <osg/ref_ptr>
@ -16,6 +17,7 @@
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/misc/guarded.hpp>
#include "mapwindow.hpp" #include "mapwindow.hpp"
#include "statswatcher.hpp" #include "statswatcher.hpp"
@ -264,6 +266,7 @@ namespace MWGui
void exitCurrentGuiMode() override; void exitCurrentGuiMode() override;
void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) 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 staticMessageBox(const std::string& message) override;
void removeStaticMessageBox() override; void removeStaticMessageBox() override;
void interactiveMessageBox (const std::string& message, void interactiveMessageBox (const std::string& message,
@ -524,6 +527,17 @@ namespace MWGui
float mScalingFactor; 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<std::vector<ScheduledMessageBox>> 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. * 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: * Supported syntax:
@ -555,6 +569,8 @@ namespace MWGui
void updatePinnedWindows(); void updatePinnedWindows();
void enableScene(bool enable); void enableScene(bool enable);
void handleScheduledMessageBoxes();
}; };
} }

View file

@ -51,59 +51,74 @@ namespace
namespace SceneUtil namespace SceneUtil
{ {
void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, std::string writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat,
const osg::Image& image) const osg::Image& image)
{ {
// Count screenshots. // Count screenshots.
int shotCount = 0; int shotCount = 0;
// Find the first unused filename with a do-while // Find the first unused filename with a do-while
std::ostringstream stream; std::ostringstream stream;
std::string lastFileName;
std::string lastFilePath;
do do
{ {
// Reset the stream // Reset the stream
stream.str(""); stream.str("");
stream.clear(); 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; 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); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(screenshotFormat);
if (!readerwriter) if (!readerwriter)
{ {
Log(Debug::Error) << "Error: Can't write screenshot, no '" << screenshotFormat << "' readerwriter found"; Log(Debug::Error) << "Error: Can't write screenshot, no '" << screenshotFormat << "' readerwriter found";
return; return std::string();
} }
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);
if (!result.success()) if (!result.success())
{ {
Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status();
return std::string();
} }
return lastFileName;
} }
WriteScreenshotToFileOperation::WriteScreenshotToFileOperation(const std::string& screenshotPath, WriteScreenshotToFileOperation::WriteScreenshotToFileOperation(const std::string& screenshotPath,
const std::string& screenshotFormat) const std::string& screenshotFormat,
std::function<void (std::string)> callback)
: mScreenshotPath(screenshotPath) : mScreenshotPath(screenshotPath)
, mScreenshotFormat(screenshotFormat) , mScreenshotFormat(screenshotFormat)
, mCallback(callback)
{ {
} }
void WriteScreenshotToFileOperation::operator()(const osg::Image& image, const unsigned int /*context_id*/) void WriteScreenshotToFileOperation::operator()(const osg::Image& image, const unsigned int /*context_id*/)
{ {
std::string fileName;
try try
{ {
writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image); fileName = writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
Log(Debug::Error) << "Failed to write screenshot to file with path=\"" << mScreenshotPath Log(Debug::Error) << "Failed to write screenshot to file with path=\"" << mScreenshotPath
<< "\", format=\"" << mScreenshotFormat << "\": " << e.what(); << "\", format=\"" << mScreenshotFormat << "\": " << e.what();
} }
if (fileName.empty())
mCallback("Failed to save screenshot");
else
mCallback(fileName + " has been saved");
} }
AsyncScreenCaptureOperation::AsyncScreenCaptureOperation(osg::ref_ptr<WorkQueue> queue, AsyncScreenCaptureOperation::AsyncScreenCaptureOperation(osg::ref_ptr<WorkQueue> queue,

View file

@ -15,19 +15,21 @@ namespace SceneUtil
{ {
class WorkQueue; class WorkQueue;
void writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat, std::string writeScreenshotToFile(const std::string& screenshotPath, const std::string& screenshotFormat,
const osg::Image& image); const osg::Image& image);
class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation
{ {
public: public:
WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat); WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat,
std::function<void (std::string)> callback);
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: private:
const std::string mScreenshotPath; const std::string mScreenshotPath;
const std::string mScreenshotFormat; const std::string mScreenshotFormat;
const std::function<void (std::string)> mCallback;
}; };
class AsyncScreenCaptureOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation class AsyncScreenCaptureOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation