diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 2173eecd59..bf55108e06 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -1,9 +1,78 @@ +#include + #include "Common/System/Request.h" #include "Common/System/System.h" #include "Common/Log.h" #include "Common/File/Path.h" +#include "Common/TimeUtil.h" RequestManager g_requestManager; +OnScreenDisplay g_OSD; + +void OnScreenDisplay::Update() { + std::lock_guard guard(mutex_); + + double now = time_now_d(); + for (auto iter = entries_.begin(); iter != entries_.end(); ) { + if (now >= iter->endTime) { + iter = entries_.erase(iter); + } else { + iter++; + } + } +} + +std::vector OnScreenDisplay::Entries() { + std::lock_guard guard(mutex_); + return entries_; // makes a copy. +} + +void OnScreenDisplay::Show(OSDType type, const std::string &text, float duration_s, const char *id) { + // Automatic duration based on type. + if (duration_s <= 0.0f) { + switch (type) { + case OSDType::MESSAGE_ERROR: + case OSDType::MESSAGE_WARNING: + duration_s = 3.0f; + break; + case OSDType::MESSAGE_FILE_LINK: + duration_s = 5.0f; + break; + case OSDType::MESSAGE_SUCCESS: + duration_s = 2.0f; + break; + default: + duration_s = 1.5f; + break; + } + } + + double now = time_now_d(); + std::lock_guard guard(mutex_); + if (id) { + for (auto iter = entries_.begin(); iter != entries_.end(); ++iter) { + if (iter->id && !strcmp(iter->id, id)) { + Entry msg = *iter; + msg.endTime = now + duration_s; + msg.text = text; + entries_.erase(iter); + entries_.insert(entries_.begin(), msg); + return; + } + } + } + + Entry msg; + msg.text = text; + msg.endTime = now + duration_s; + msg.id = id; + entries_.insert(entries_.begin(), msg); +} + +void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float duration_s) { + // TODO: translate "on" and "off"? Or just get rid of this whole thing? + Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s); +} const char *RequestTypeAsString(SystemRequestType type) { switch (type) { diff --git a/Common/System/System.h b/Common/System/System.h index d00324d9b6..943145e28a 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -4,6 +4,7 @@ #include #include #include +#include // Platform integration @@ -212,7 +213,45 @@ void System_PostUIMessage(const std::string &message, const std::string ¶m); // Shows a visible message to the user. // The default implementation in NativeApp.cpp uses our "osm" system (on screen messaging). -void System_NotifyUserMessage(const std::string &message, float duration = 1.0f, uint32_t color = 0x00FFFFFF, const char *id = nullptr); +enum class OSDType { + MESSAGE_INFO, + MESSAGE_SUCCESS, + MESSAGE_WARNING, + MESSAGE_ERROR, + MESSAGE_ERROR_DUMP, // displays lots of text (after the first line), small size + MESSAGE_FILE_LINK, + // PROGRESS_BAR, + // PROGRESS_INDETERMINATE, +}; + +// Data holder for on-screen messages. +class OnScreenDisplay { +public: + // If you specify 0.0f as duration, a duration will be chosen automatically depending on type. + void Show(OSDType type, const std::string &message, float duration_s = 0.0f, const char *id = nullptr); + void ShowOnOff(const std::string &message, bool on, float duration_s = 0.0f); + + bool IsEmpty() const { return entries_.empty(); } // Shortcut to skip rendering. + + // Call this every frame, cleans up old entries. + void Update(); + + struct Entry { + OSDType type; + std::string text; + const char *id; + double endTime; + double duration; + float progress; + }; + std::vector Entries(); + +private: + std::vector entries_; + std::mutex mutex_; +}; + +extern OnScreenDisplay g_OSD; // For these functions, most platforms will use the implementation provided in UI/AudioCommon.cpp, // no need to implement separately. diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 664f3f9275..4e1b736991 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -53,6 +53,10 @@ void ScreenManager::update() { stack_.back().screen->update(); } + if (overlayScreen_) { + overlayScreen_->update(); + } + g_iconCache.FrameUpdate(); } @@ -174,6 +178,9 @@ void ScreenManager::render() { stack_.back().screen->render(); if (postRenderCb_) postRenderCb_(getUIContext(), postRenderUserdata_); + if (overlayScreen_) { + overlayScreen_->render(); + } backback.screen->postRender(); break; } @@ -183,6 +190,9 @@ void ScreenManager::render() { stack_.back().screen->render(); if (postRenderCb_) postRenderCb_(getUIContext(), postRenderUserdata_); + if (overlayScreen_) { + overlayScreen_->render(); + } stack_.back().screen->postRender(); break; } @@ -233,6 +243,8 @@ void ScreenManager::shutdown() { for (auto layer : nextStack_) delete layer.screen; nextStack_.clear(); + delete overlayScreen_; + overlayScreen_ = nullptr; } void ScreenManager::push(Screen *screen, int layerFlags) { @@ -325,3 +337,11 @@ void ScreenManager::processFinishDialog() { dialogFinished_ = nullptr; } } + +void ScreenManager::SetOverlayScreen(Screen *screen) { + if (overlayScreen_) { + delete overlayScreen_; + } + overlayScreen_ = screen; + overlayScreen_->setScreenManager(this); +} diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index fca213fdb8..7e9e4f8489 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -144,6 +144,9 @@ public: void getFocusPosition(float &x, float &y, float &z); + // Will delete any existing overlay screen. + void SetOverlayScreen(Screen *screen); + std::recursive_mutex inputLock_; private: @@ -157,8 +160,10 @@ private: PostRenderCallback postRenderCb_ = nullptr; void *postRenderUserdata_ = nullptr; - const Screen *dialogFinished_; - DialogResult dialogResult_; + const Screen *dialogFinished_ = nullptr; + DialogResult dialogResult_{}; + + Screen *overlayScreen_ = nullptr; struct Layer { Screen *screen; diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 48f2dceb7d..e3de622934 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -378,7 +378,7 @@ void CWCheatEngine::CreateCheatFile() { } if (!File::Exists(filename_)) { auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Unable to create cheat file, disk may be full")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Unable to create cheat file, disk may be full")); } } } diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index bc67dd807a..573f77c059 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -456,7 +456,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (!pspFileSystem.GetFileInfo(dirPath).exists) { if (!pspFileSystem.MkDir(dirPath)) { auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Unable to write savedata, disk may be full")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Unable to write savedata, disk may be full")); } } @@ -485,7 +485,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (EncryptData(decryptMode, cryptedData, &cryptedSize, &aligned_len, cryptedHash, (hasKey ? param->key : 0)) != 0) { auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Save encryption failed. This save won't work on real PSP"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Save encryption failed. This save won't work on real PSP"), 6.0f); ERROR_LOG(SCEUTILITY,"Save encryption failed. This save won't work on real PSP"); delete[] cryptedData; cryptedData = 0; @@ -775,8 +775,8 @@ u32 SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, con // Don't notify the user if we're not going to upgrade the save. if (!g_Config.bEncryptSave) { auto di = GetI18NCategory(I18NCat::DIALOG); - System_NotifyUserMessage(di->T("When you save, it will load on a PSP, but not an older PPSSPP"), 6.0f); - System_NotifyUserMessage(di->T("Old savedata detected"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, di->T("When you save, it will load on a PSP, but not an older PPSSPP"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, di->T("Old savedata detected"), 6.0f); } } else { if (decryptMode == 5 && prevCryptMode == 3) { @@ -787,8 +787,8 @@ u32 SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, con if (g_Config.bSavedataUpgrade) { decryptMode = prevCryptMode; auto di = GetI18NCategory(I18NCat::DIALOG); - System_NotifyUserMessage(di->T("When you save, it will not work on outdated PSP Firmware anymore"), 6.0f); - System_NotifyUserMessage(di->T("Old savedata detected"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, di->T("When you save, it will not work on outdated PSP Firmware anymore"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, di->T("Old savedata detected"), 6.0f); } } hasKey = decryptMode > 1; diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 71c2d2a11e..ed5c06e47d 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -73,7 +73,7 @@ u32 BlockDevice::CalculateCRC(volatile bool *cancel) { void BlockDevice::NotifyReadError() { auto err = GetI18NCategory(I18NCat::ERRORS); if (!reportedError_) { - System_NotifyUserMessage(err->T("Game disc read error - ISO corrupt"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Game disc read error - ISO corrupt"), 6.0f); reportedError_ = true; } } diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 4e700d4a3a..1007ce9b9c 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -192,7 +192,7 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File if (w32err == ERROR_DISK_FULL || w32err == ERROR_NOT_ENOUGH_QUOTA) { // This is returned when the disk is full. auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Disk full while writing data")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; } else if (!success) { error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; @@ -290,7 +290,7 @@ bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, File } else if (errno == ENOSPC) { // This is returned when the disk is full. auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Disk full while writing data")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; } else { error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; @@ -365,7 +365,7 @@ size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) if (diskFull) { ERROR_LOG(FILESYS, "Disk full"); auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("Disk full while writing data")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Disk full while writing data")); // We only return an error when the disk is actually full. // When writing this would cause the disk to be full, so it wasn't written, we return 0. if (MemoryStick_FreeSpace() == 0) { diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index fcf853ec0b..87c42f9936 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1385,7 +1385,7 @@ int friendFinder(){ g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE; if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) { ERROR_LOG(SCENET, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str()); - System_NotifyUserMessage(n->T("DNS Error Resolving ") + g_Config.proAdhocServer, 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, n->T("DNS Error Resolving ") + g_Config.proAdhocServer); } if (resolved) { for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) { @@ -1447,7 +1447,7 @@ int friendFinder(){ shutdown((int)metasocket, SD_BOTH); closesocket((int)metasocket); metasocket = (int)INVALID_SOCKET; - System_NotifyUserMessage(std::string(n->T("Disconnected from AdhocServer")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(error) + ")", 2.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Disconnected from AdhocServer")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(error) + ")"); // Mark all friends as timedout since we won't be able to detects disconnected friends anymore without being connected to Adhoc Server peerlock.lock(); timeoutFriendsRecursive(friends); @@ -2191,7 +2191,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ iResult = bind((int)metasocket, &g_localhostIP.addr, sizeof(g_localhostIP.addr)); if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", ip2str(g_localhostIP.in.sin_addr).c_str(), iResult); - System_NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str(), 2.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str()); } } @@ -2246,7 +2246,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ } if (!done) { ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); - System_NotifyUserMessage(std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")", 1.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")"); return iResult; } } @@ -2268,10 +2268,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ socklen_t addrLen = sizeof(LocalIP); memset(&LocalIP, 0, addrLen); getsockname((int)metasocket, &LocalIP, &addrLen); - System_NotifyUserMessage(n->T("Network Initialized"), 1.0); + g_OSD.Show(OSDType::MESSAGE_SUCCESS, n->T("Network Initialized"), 1.0); return 0; - } - else{ + } else { return SOCKET_ERROR; } } diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index e2cfa93c5d..4f78028560 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -1858,7 +1858,7 @@ int create_listen_socket(uint16_t port) else { ERROR_LOG(SCENET, "AdhocServer: Bind returned %i (Socket error %d)", bindresult, errno); auto n = GetI18NCategory(I18NCat::NETWORKING); - System_NotifyUserMessage(std::string(n->T("AdhocServer Failed to Bind Port")) + " " + std::to_string(port), 3.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("AdhocServer Failed to Bind Port")) + " " + std::to_string(port)); } // Close Socket diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 3ad5060da2..8c0adfd1c3 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -576,9 +576,9 @@ void __DisplayFlip(int cyclesLate) { #ifndef _DEBUG auto err = GetI18NCategory(I18NCat::ERRORS); if (g_Config.bSoftwareRendering) { - System_NotifyUserMessage(err->T("Running slow: Try turning off Software Rendering"), 6.0f, 0xFF30D0D0); + g_OSD.Show(OSDType::MESSAGE_INFO, err->T("Running slow: Try turning off Software Rendering")); } else { - System_NotifyUserMessage(err->T("Running slow: try frameskip, sound is choppy when slow"), 6.0f, 0xFF30D0D0); + g_OSD.Show(OSDType::MESSAGE_INFO, err->T("Running slow: try frameskip, sound is choppy when slow")); } #endif hasNotifiedSlow = true; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8abed50a50..2c83c14c12 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -277,7 +277,7 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { if (peer != NULL) truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data); WARN_LOG(SCENET, "GameMode: Unknown Source Port from [%s][%s:%u -> %u] (Result=%i, Size=%i)", name, mac2str(&sendermac).c_str(), senderport, ADHOC_GAMEMODE_PORT, ret, bufsz); - System_NotifyUserMessage(std::string(n->T("GM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(ADHOC_GAMEMODE_PORT) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 2.0, 0x0080ff); + g_OSD.Show(OSDType::MESSAGE_WARNING, std::string(n->T("GM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(ADHOC_GAMEMODE_PORT) + std::string(" (") + std::to_string(portOffset) + std::string(")")); peerlock.unlock(); } // Keeping track of the source port for further communication, in case it was re-mapped by router or ISP for some reason. @@ -1507,7 +1507,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); auto n = GetI18NCategory(I18NCat::NETWORKING); - System_NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset"))); return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); } @@ -3524,7 +3524,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, else { ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); auto n = GetI18NCategory(I18NCat::NETWORKING); - System_NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset"))); } // Close Socket @@ -4117,7 +4117,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int } else { auto n = GetI18NCategory(I18NCat::NETWORKING); - System_NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset"))); } if (iResult == SOCKET_ERROR) { @@ -7782,7 +7782,7 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi if (peer != NULL) truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data); WARN_LOG(SCENET, "InputLoop[%d]: Unknown Source Port from [%s][%s:%u -> %u] (Recved=%i, Length=%i)", matchingId, name, mac2str(&sendermac).c_str(), senderport, context->port, recvresult, rxbuflen); - System_NotifyUserMessage(std::string(n->T("AM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 2.0, 0x0080ff); + g_OSD.Show(OSDType::MESSAGE_WARNING, std::string(n->T("AM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")")); } // Keep tracks of re-mapped peer's ports for further communication. // Note: This will only works if this player were able to receives data on normal port from other players (ie. this player's port wasn't remapped) diff --git a/Core/Util/PortManager.cpp b/Core/Util/PortManager.cpp index 1a45900705..636cc28da6 100644 --- a/Core/Util/PortManager.cpp +++ b/Core/Util/PortManager.cpp @@ -190,7 +190,7 @@ bool PortManager::Initialize(const unsigned int timeout) { ERROR_LOG(SCENET, "PortManager - upnpDiscover failed (error: %i) or No UPnP device detected", error); if (g_Config.bEnableUPnP) { auto n = GetI18NCategory(I18NCat::NETWORKING); - System_NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_ERROR, n->T("Unable to find UPnP device")); } m_InitState = UPNP_INITSTATE_NONE; #endif // WITH_UPNP @@ -215,11 +215,12 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short { if (g_Config.bEnableUPnP) { WARN_LOG(SCENET, "PortManager::Add - the init was not done !"); - System_NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_INFO, n->T("UPnP need to be reinitialized")); } Terminate(); return false; } + snprintf(port_str, sizeof(port_str), "%d", port); snprintf(intport_str, sizeof(intport_str), "%d", intport); // Only add new port map if it's not previously created by PPSSPP for current IP @@ -244,7 +245,7 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short ERROR_LOG(SCENET, "PortManager - AddPortMapping failed (error: %i)", r); if (r == UPNPCOMMAND_HTTP_ERROR) { if (g_Config.bEnableUPnP) { - System_NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_INFO, n->T("UPnP need to be reinitialized")); } Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts return false; @@ -270,7 +271,7 @@ bool PortManager::Remove(const char* protocol, unsigned short port) { { if (g_Config.bEnableUPnP) { WARN_LOG(SCENET, "PortManager::Remove - the init was not done !"); - System_NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_INFO, n->T("UPnP need to be reinitialized")); } Terminate(); return false; @@ -282,7 +283,7 @@ bool PortManager::Remove(const char* protocol, unsigned short port) { ERROR_LOG(SCENET, "PortManager - DeletePortMapping failed (error: %i)", r); if (r == UPNPCOMMAND_HTTP_ERROR) { if (g_Config.bEnableUPnP) { - System_NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + g_OSD.Show(OSDType::MESSAGE_INFO, n->T("UPnP need to be reinitialized")); } Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts return false; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 7d48c9ac06..4bf462056e 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2714,7 +2714,7 @@ void FramebufferManagerCommon::ShowScreenResolution() { messageStream << gr->T("Window Size") << ": "; messageStream << PSP_CoreParameter().pixelWidth << "x" << PSP_CoreParameter().pixelHeight; - System_NotifyUserMessage(messageStream.str(), 2.0f, 0xFFFFFF, "resize"); + g_OSD.Show(OSDType::MESSAGE_INFO, messageStream.str(), 0.0f, "resize"); INFO_LOG(SYSTEM, "%s", messageStream.str().c_str()); } diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 33861b4c72..2c6a62a1f4 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -450,9 +450,9 @@ void PresentationCommon::ShowPostShaderError(const std::string &errorString) { } } if (!firstLine.empty()) { - System_NotifyUserMessage("Post-shader error: " + firstLine + "...:\n" + errorString, 10.0f, 0xFF3090FF); + g_OSD.Show(OSDType::MESSAGE_ERROR_DUMP, "Post-shader error: " + firstLine + "...:\n" + errorString, 10.0f); } else { - System_NotifyUserMessage("Post-shader error, see log for details", 10.0f, 0xFF3090FF); + g_OSD.Show(OSDType::MESSAGE_ERROR, "Post-shader error, see log for details", 10.0f); } } diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 116473e5ec..b3c7306b9a 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -310,7 +310,7 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri if (filenameWarning) { auto err = GetI18NCategory(I18NCat::ERRORS); - System_NotifyUserMessage(err->T("textures.ini filenames may not be cross-platform (banned characters)"), 6.0f); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("textures.ini filenames may not be cross-platform (banned characters)"), 6.0f); } if (ini.HasSection("hashranges")) { diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 1994384829..8d018a3150 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -88,7 +88,8 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // Disable hardware tessellation bacause DX9 is still unsupported. ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); + // TODO: Badly formulated + g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("Turn off Hardware Tessellation - unsupported")); } } diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 2bf18f2130..2c9edb7280 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -606,7 +606,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat ERROR_LOG(G3D, "Shader compilation failed, falling back to software transform"); } if (!g_Config.bHideSlowWarnings) { - System_NotifyUserMessage(gr->T("hardware transform error - falling back to software"), 2.5f, 0xFF3030FF); + g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("hardware transform error - falling back to software"), 2.5f); } delete vs; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 47ed2699c6..bba70b682c 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -122,7 +122,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) if (!drawEngine_.SupportsHWTessellation()) { ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); + g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("Turn off Hardware Tessellation - unsupported")); } } } diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 797a51b8aa..0ae124e1c0 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -803,7 +803,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess auto gr = GetI18NCategory(I18NCat::GRAPHICS); ERROR_LOG(G3D, "Vertex shader generation failed, falling back to software transform"); if (!g_Config.bHideSlowWarnings) { - System_NotifyUserMessage(gr->T("hardware transform error - falling back to software"), 2.5f, 0xFF3030FF); + g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("hardware transform error - falling back to software"), 2.5f); } delete vs; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index e83ff0a82a..f10c2f5ae1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -150,9 +150,9 @@ void TextureCacheGLES::StartFrame() { auto err = GetI18NCategory(I18NCat::ERRORS); if (standardScaleFactor_ > 1) { - System_NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); } else { - System_NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); } } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 3fa809c096..397afe8934 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -513,9 +513,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { auto err = GetI18NCategory(I18NCat::ERRORS); if (plan.scaleFactor > 1) { - System_NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); } else { - System_NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); } // Turn off texture replacement for this texture. diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 0d926f9531..741fe66888 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -122,7 +122,7 @@ static void __EmuScreenVblank() if (g_Config.bDumpFrames && !startDumping) { avi.Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); - osm.Show(sy->T("AVI Dump started."), 0.5f); + g_OSD.Show(OSDType::MESSAGE_INFO, sy->T("AVI Dump started."), 1.0f); startDumping = true; } if (g_Config.bDumpFrames && startDumping) @@ -132,7 +132,7 @@ static void __EmuScreenVblank() else if (!g_Config.bDumpFrames && startDumping) { avi.Stop(); - osm.Show(sy->T("AVI Dump stopped."), 1.0f); + g_OSD.Show(OSDType::MESSAGE_INFO, sy->T("AVI Dump stopped."), 1.0f); startDumping = false; } #endif @@ -333,17 +333,17 @@ void EmuScreen::bootGame(const Path &filename) { if (PSP_CoreParameter().compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_NotifyUserMessage(gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 15.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 10.0f); } if (PSP_CoreParameter().compat.flags().RequireBlockTransfer && g_Config.bSkipGPUReadbacks) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_NotifyUserMessage(gr->T("BlockTransferRequired", "Warning: This game requires Simulate Block Transfer Mode to be set to On."), 15.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("BlockTransferRequired", "Warning: This game requires Simulate Block Transfer Mode to be set to On."), 10.0f); } if (PSP_CoreParameter().compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_NotifyUserMessage(gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 15.0f); + g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f); } loadingViewColor_->Divert(0xFFFFFFFF, 0.75f); @@ -364,7 +364,7 @@ void EmuScreen::bootComplete() { #ifndef MOBILE_DEVICE if (g_Config.bFirstRun) { - osm.Show(sc->T("PressESC", "Press ESC to open the pause menu"), 3.0f); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("PressESC", "Press ESC to open the pause menu")); } #endif @@ -372,13 +372,13 @@ void EmuScreen::bootComplete() { if (GetGPUBackend() == GPUBackend::OPENGL) { const char *renderer = gl_extensions.model; if (strstr(renderer, "Chainfire3D") != 0) { - osm.Show(sc->T("Chainfire3DWarning", "WARNING: Chainfire3D detected, may cause problems"), 10.0f, 0xFF30a0FF, -1, true); + g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("Chainfire3DWarning", "WARNING: Chainfire3D detected, may cause problems"), 10.0f); } else if (strstr(renderer, "GLTools") != 0) { - osm.Show(sc->T("GLToolsWarning", "WARNING: GLTools detected, may cause problems"), 10.0f, 0xFF30a0FF, -1, true); + g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("GLToolsWarning", "WARNING: GLTools detected, may cause problems"), 10.0f); } if (g_Config.bGfxDebugOutput) { - osm.Show("WARNING: GfxDebugOutput is enabled via ppsspp.ini. Things may be slow.", 10.0f, 0xFF30a0FF, -1, true); + g_OSD.Show(OSDType::MESSAGE_WARNING, "WARNING: GfxDebugOutput is enabled via ppsspp.ini. Things may be slow.", 10.0f); } } #endif @@ -386,9 +386,9 @@ void EmuScreen::bootComplete() { if (Core_GetPowerSaving()) { auto sy = GetI18NCategory(I18NCat::SYSTEM); #ifdef __ANDROID__ - osm.Show(sy->T("WARNING: Android battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); + g_OSD.Show(OSDType::MESSAGE_WARNING, sy->T("WARNING: Android battery save mode is on"), 2.0f, "core_powerSaving"); #else - osm.Show(sy->T("WARNING: Battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); + g_OSD.Show(OSDType::MESSAGE_WARNING, sy->T("WARNING: Battery save mode is on"), 2.0f, "core_powerSaving"); #endif } @@ -410,7 +410,7 @@ EmuScreen::~EmuScreen() { if (g_Config.bDumpFrames && startDumping) { avi.Stop(); - osm.Show("AVI Dump stopped.", 1.0f); + g_OSD.Show(OSDType::MESSAGE_INFO, "AVI Dump stopped.", 2.0f); startDumping = false; } #endif @@ -437,7 +437,7 @@ void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { static void AfterSaveStateAction(SaveState::Status status, const std::string &message, void *) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { - osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); + g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } } @@ -514,9 +514,10 @@ void EmuScreen::sendMessage(const char *message, const char *value) { RecreateViews(); #if defined(USING_WIN_UI) - //temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable + // temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable if (g_Config.bBypassOSKWithKeyboard) { - osm.Show("Disable windows native keyboard options to use ctrl + c hotkey", 2.0f); + // TODO: Make translatable. + g_OSD.Show(OSDType::MESSAGE_INFO, "Disable \"Use system native keyboard\" to use ctrl + c hotkey", 2.0f); } else { UI::EventParams e{}; OnChatMenu.Trigger(e); @@ -580,13 +581,13 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { // Cycle through enabled speeds. if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; - osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("fixed", "Speed: alternate"), 1.0); } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; - osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0); } } break; @@ -595,12 +596,12 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { if (down) { if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; - osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("fixed", "Speed: alternate"), 1.0); } } else { if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) { PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0); } } break; @@ -608,12 +609,12 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { if (down) { if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; - osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); } } else { if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0); } } break; @@ -651,7 +652,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { case VIRTKEY_AXIS_SWAP: if (down) { controlMapper_.ToggleSwapAxes(); - osm.Show(mc->T("AxisSwap")); // best string we have. + g_OSD.Show(OSDType::MESSAGE_INFO, mc->T("AxisSwap")); // best string we have. } break; @@ -689,7 +690,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { if (SaveState::CanRewind()) { SaveState::Rewind(&AfterSaveStateAction); } else { - osm.Show(sc->T("norewind", "No rewind save states available"), 2.0); + g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0); } } break; @@ -727,10 +728,10 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { if (down) { g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures; if (g_Config.bSaveNewTextures) { - osm.Show(sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0); NativeMessageReceived("gpu_configChanged", ""); } else { - osm.Show(sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0); } } break; @@ -738,9 +739,9 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { if (down) { g_Config.bReplaceTextures = !g_Config.bReplaceTextures; if (g_Config.bReplaceTextures) - osm.Show(sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0); else - osm.Show(sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); + g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); NativeMessageReceived("gpu_configChanged", ""); } break; @@ -936,7 +937,6 @@ void EmuScreen::CreateViews() { saveStatePreview_->SetVisibility(V_GONE); saveStatePreview_->SetCanBeFocused(false); root_->Add(saveStatePreview_); - onScreenMessagesView_ = root_->Add(new OnScreenMessagesView(new AnchorLayoutParams((Size)bounds.w, (Size)bounds.h))); GameInfoBGView *loadingBG = root_->Add(new GameInfoBGView(gamePath_, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT))); TextView *loadingTextView = root_->Add(new TextView(sc->T(PSP_GetLoading()), new AnchorLayoutParams(bounds.centerX(), NONE, NONE, 40, true))); @@ -1042,7 +1042,6 @@ void EmuScreen::update() { using namespace UI; UIScreen::update(); - onScreenMessagesView_->SetVisibility(g_Config.bShowOnScreenMessages ? V_VISIBLE : V_GONE); resumeButton_->SetVisibility(coreState == CoreState::CORE_RUNTIME_ERROR && Memory::MemFault_MayBeResumable() ? V_VISIBLE : V_GONE); resetButton_->SetVisibility(coreState == CoreState::CORE_RUNTIME_ERROR ? V_VISIBLE : V_GONE); @@ -1556,7 +1555,7 @@ bool EmuScreen::hasVisibleUI() { // Regular but uncommon UI. if (saveStatePreview_->GetVisibility() != UI::V_GONE || loadingSpinner_->GetVisibility() == UI::V_VISIBLE) return true; - if (!osm.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowStatusFlags != 0) + if (!g_OSD.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowStatusFlags != 0) return true; if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat) return true; diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 7eda3988d4..6bfafe62e7 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -32,7 +32,6 @@ struct AxisInput; class AsyncImageFileView; -class OnScreenMessagesView; class ChatMenu; class EmuScreen : public UIScreen { @@ -113,7 +112,6 @@ private: ChatMenu *chatMenu_ = nullptr; UI::Button *cardboardDisableButton_ = nullptr; - OnScreenMessagesView *onScreenMessagesView_ = nullptr; ControlMapper controlMapper_; }; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 322c9c55d4..9b364c25f6 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -765,7 +765,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch if (!boot_filename.empty() && stateToLoad.Valid()) { SaveState::Load(stateToLoad, -1, [](SaveState::Status status, const std::string &message, void *) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { - osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); + g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, + message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } }); } @@ -786,6 +787,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT)); } + g_screenManager->SetOverlayScreen(new OSDOverlayScreen()); + // Easy testing // screenManager->push(new GPUDriverTestScreen()); @@ -1008,35 +1011,14 @@ void TakeScreenshot() { bool success = TakeGameScreenshot(filename, g_Config.bScreenshotsAsPNG ? ScreenshotFormat::PNG : ScreenshotFormat::JPG, SCREENSHOT_OUTPUT); if (success) { - osm.Show(filename.ToVisualString()); + g_OSD.Show(OSDType::MESSAGE_FILE_LINK, filename.ToString()); } else { auto err = GetI18NCategory(I18NCat::ERRORS); - osm.Show(err->T("Could not save screenshot file")); + g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Could not save screenshot file")); } } void RenderOverlays(UIContext *dc, void *userdata) { - // Thin bar at the top of the screen. - std::vector progress = g_DownloadManager.GetCurrentProgress(); - if (!progress.empty()) { - static const uint32_t colors[4] = { - 0xFFFFFFFF, - 0xFFCCCCCC, - 0xFFAAAAAA, - 0xFF777777, - }; - - dc->Begin(); - int h = 5; - for (size_t i = 0; i < progress.size(); i++) { - float barWidth = 10 + (dc->GetBounds().w - 10) * progress[i]; - Bounds bounds(0, h * i, barWidth, h); - UI::Drawable solid(colors[i & 3]); - dc->FillRect(solid, bounds); - } - dc->Flush(); - } - if (g_TakeScreenshot) { TakeScreenshot(); } @@ -1159,7 +1141,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { auto sy = GetI18NCategory(I18NCat::SYSTEM); std::string msg = StringFromFormat("%s: %d", sy->T("Savestate Slot"), SaveState::GetCurrentSlot() + 1); // Show for the same duration as the preview. - osm.Show(msg, 2.0f, 0xFFFFFF, -1, true, "savestate_slot"); + g_OSD.Show(OSDType::MESSAGE_INFO, msg, 2.0f, "savestate_slot"); } else if (msg == "gpu_displayResized") { if (gpu) { @@ -1181,9 +1163,9 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { if (value != "false") { auto sy = GetI18NCategory(I18NCat::SYSTEM); #if PPSSPP_PLATFORM(ANDROID) - osm.Show(sy->T("WARNING: Android battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); + g_OSD.Show(OSDType::MESSAGE_WARNING, sy->T("WARNING: Android battery save mode is on"), 2.0f, "core_powerSaving"); #else - osm.Show(sy->T("WARNING: Battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); + g_OSD.Show(OSDType::MESSAGE_WARNING, sy->T("WARNING: Battery save mode is on"), 2.0f, "core_powerSaving"); #endif } Core_SetPowerSaving(value != "false"); @@ -1232,6 +1214,8 @@ void NativeUpdate() { g_Discord.Update(); g_BackgroundAudio.Play(); + g_OSD.Update(); + UI::SetSoundEnabled(g_Config.bUISound); } @@ -1369,10 +1353,6 @@ void System_PostUIMessage(const std::string &message, const std::string &value) NativeMessageReceived(message.c_str(), value.c_str()); } -void System_NotifyUserMessage(const std::string &message, float duration_s, u32 color, const char *id) { - osm.Show(message, duration_s, color, -1, true, id); -} - void NativeResized() { // NativeResized can come from any thread so we just set a flag, then process it later. VERBOSE_LOG(G3D, "NativeResized - setting flag"); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 06d3d2f9f7..5522ea343e 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -1,5 +1,6 @@ #include #include + #include "UI/OnScreenDisplay.h" #include "Common/Data/Color/RGBAUtil.h" @@ -8,15 +9,28 @@ #include "Common/Render/DrawBuffer.h" #include "Common/UI/Context.h" +#include "Common/System/System.h" #include "Common/TimeUtil.h" +#include "Common/Net/HTTPClient.h" +#include "Core/Config.h" -OnScreenMessages osm; +static uint32_t GetOSDBackgroundColor(OSDType type) { + // Colors from Infima + switch (type) { + case OSDType::MESSAGE_ERROR: + case OSDType::MESSAGE_ERROR_DUMP: return 0xd53035; // danger-darker + case OSDType::MESSAGE_WARNING: return 0xd99e00; // warning-darker + case OSDType::MESSAGE_INFO: return 0x606770; // gray-700 + case OSDType::MESSAGE_SUCCESS: return 0x008b00; + default: return 0x606770; + } +} void OnScreenMessagesView::Draw(UIContext &dc) { - // First, clean out old messages. - osm.Lock(); - osm.Clean(); + if (!g_Config.bShowOnScreenMessages) { + return; + } // Get height float w, h; @@ -24,9 +38,9 @@ void OnScreenMessagesView::Draw(UIContext &dc) { float y = 10.0f; // Then draw them all. - const std::list &messages = osm.Messages(); + const std::vector entries = g_OSD.Entries(); double now = time_now_d(); - for (auto iter = messages.begin(); iter != messages.end(); ++iter) { + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { float alpha = (iter->endTime - now) * 4.0f; if (alpha > 1.0) alpha = 1.0f; if (alpha < 0.0) alpha = 0.0f; @@ -56,18 +70,37 @@ void OnScreenMessagesView::Draw(UIContext &dc) { dc.SetFontScale(scale, scale); } dc.SetFontStyle(dc.theme->uiFont); - dc.DrawTextShadow(iter->text.c_str(), x, y, colorAlpha(iter->color, alpha), align); + dc.DrawTextShadow(iter->text.c_str(), x, y, colorAlpha(0xFFFFFFFF, alpha), align); y += th * scale; } - osm.Unlock(); + // Thin bar at the top of the screen. + std::vector progress = g_DownloadManager.GetCurrentProgress(); + if (!progress.empty()) { + static const uint32_t colors[4] = { + 0xFFFFFFFF, + 0xFFCCCCCC, + 0xFFAAAAAA, + 0xFF777777, + }; + + dc.Begin(); + int h = 5; + for (size_t i = 0; i < progress.size(); i++) { + float barWidth = 10 + (dc.GetBounds().w - 10) * progress[i]; + Bounds bounds(0, h * i, barWidth, h); + UI::Drawable solid(colors[i & 3]); + dc.FillRect(solid, bounds); + } + dc.Flush(); + } } std::string OnScreenMessagesView::DescribeText() const { std::stringstream ss; - const auto &messages = osm.Messages(); - for (auto iter = messages.begin(); iter != messages.end(); ++iter) { - if (iter != messages.begin()) { + const auto &entries = g_OSD.Entries(); + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { + if (iter != entries.begin()) { ss << "\n"; } ss << iter->text; @@ -75,42 +108,7 @@ std::string OnScreenMessagesView::DescribeText() const { return ss.str(); } -void OnScreenMessages::Clean() { -restart: - double now = time_now_d(); - for (auto iter = messages_.begin(); iter != messages_.end(); iter++) { - if (iter->endTime < now) { - messages_.erase(iter); - goto restart; - } - } -} - -void OnScreenMessages::Show(const std::string &text, float duration_s, uint32_t color, int icon, bool checkUnique, const char *id) { - double now = time_now_d(); - std::lock_guard guard(mutex_); - if (checkUnique) { - for (auto iter = messages_.begin(); iter != messages_.end(); ++iter) { - if (iter->text == text || (id && iter->id && !strcmp(iter->id, id))) { - Message msg = *iter; - msg.endTime = now + duration_s; - msg.text = text; - msg.color = color; - messages_.erase(iter); - messages_.insert(messages_.begin(), msg); - return; - } - } - } - Message msg; - msg.text = text; - msg.color = color; - msg.endTime = now + duration_s; - msg.icon = icon; - msg.id = id; - messages_.insert(messages_.begin(), msg); -} - -void OnScreenMessages::ShowOnOff(const std::string &message, bool b, float duration_s, uint32_t color, int icon) { - Show(message + (b ? ": on" : ": off"), duration_s, color, icon); +void OSDOverlayScreen::CreateViews() { + root_ = new UI::AnchorLayout(); + root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f))); } diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 7a56c0b08e..3c8ec988b2 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -6,38 +6,12 @@ #include "Common/Math/geom2d.h" #include "Common/UI/View.h" +#include "Common/UI/UIScreen.h" +#include "Common/System/System.h" class DrawBuffer; -class OnScreenMessages { -public: - void Show(const std::string &message, float duration_s = 1.0f, uint32_t color = 0xFFFFFF, int icon = -1, bool checkUnique = true, const char *id = nullptr); - void ShowOnOff(const std::string &message, bool b, float duration_s = 1.0f, uint32_t color = 0xFFFFFF, int icon = -1); - bool IsEmpty() const { return messages_.empty(); } - - void Lock() { - mutex_.lock(); - } - void Unlock() { - mutex_.unlock(); - } - - void Clean(); - - struct Message { - int icon; - uint32_t color; - std::string text; - const char *id; - double endTime; - double duration; - }; - const std::list &Messages() { return messages_; } - -private: - std::list messages_; - std::mutex mutex_; -}; +// Infrastructure for rendering overlays. class OnScreenMessagesView : public UI::InertView { public: @@ -46,4 +20,8 @@ public: std::string DescribeText() const override; }; -extern OnScreenMessages osm; +class OSDOverlayScreen : public UIScreen { +public: + const char *tag() const override { return "OSDOverlayScreen"; } + void CreateViews() override; +}; diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index ac12066af0..c96942dc3a 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -54,7 +54,8 @@ static void AfterSaveStateAction(SaveState::Status status, const std::string &message, void *) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { - osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); + g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, + message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } } diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index a1bbe2dfcd..de44143050 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -422,7 +422,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { } draw_->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - System_NotifyUserMessage(details, 5.0, 0xFFFFFFFF, "error_callback"); + g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback"); }, nullptr); // These are auto-reset events. diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index c7ff2deab8..040f14fbbd 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -358,7 +358,7 @@ namespace MainWindow { static void SaveStateActionFinished(SaveState::Status status, const std::string &message, void *userdata) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { - osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); + g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0); } @@ -397,7 +397,7 @@ namespace MainWindow { else messageStream << g_Config.iFrameSkip; - osm.Show(messageStream.str()); + g_OSD.Show(OSDType::MESSAGE_INFO, messageStream.str()); } static void setFrameSkippingType(int fskipType = -1) { @@ -417,7 +417,7 @@ namespace MainWindow { else messageStream << gr->T("Percent of FPS"); - osm.Show(messageStream.str()); + g_OSD.Show(OSDType::MESSAGE_INFO, messageStream.str()); } static void RestartApp() { @@ -507,7 +507,7 @@ namespace MainWindow { case ID_EMULATION_CHEATS: g_Config.bEnableCheats = !g_Config.bEnableCheats; - osm.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats); + g_OSD.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats); break; case ID_EMULATION_CHAT: @@ -677,7 +677,7 @@ namespace MainWindow { case ID_OPTIONS_SKIP_BUFFER_EFFECTS: g_Config.bSkipBufferEffects = !g_Config.bSkipBufferEffects; NativeMessageReceived("gpu_renderResized", ""); - osm.ShowOnOff(gr->T("Skip Buffer Effects"), g_Config.bSkipBufferEffects); + g_OSD.ShowOnOff(gr->T("Skip Buffer Effects"), g_Config.bSkipBufferEffects); break; case ID_DEBUG_SHOWDEBUGSTATISTICS: @@ -688,7 +688,7 @@ namespace MainWindow { case ID_OPTIONS_HARDWARETRANSFORM: g_Config.bHardwareTransform = !g_Config.bHardwareTransform; NativeMessageReceived("gpu_configChanged", ""); - osm.ShowOnOff(gr->T("Hardware Transform"), g_Config.bHardwareTransform); + g_OSD.ShowOnOff(gr->T("Hardware Transform"), g_Config.bHardwareTransform); break; case ID_OPTIONS_DISPLAY_LAYOUT: diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9f135c72ec..9d675a7983 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -946,7 +946,7 @@ extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, } graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - System_NotifyUserMessage(details, 5.0, 0xFFFFFFFF, "error_callback"); + g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); }, nullptr); EmuThreadStart(); @@ -962,7 +962,7 @@ extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, } graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - System_NotifyUserMessage(details, 5.0, 0xFFFFFFFF, "error_callback"); + g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); }, nullptr); graphicsContext->ThreadStart(); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index ee19008202..a59ebafc45 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -118,7 +118,6 @@ bool System_GetPropertyBool(SystemProperty prop) { } void System_Notify(SystemNotification notification) {} void System_PostUIMessage(const std::string &message, const std::string ¶m) {} -void System_NotifyUserMessage(const std::string &message, float duration, u32 color, const char *id) {} bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int param3) { switch (type) { case SystemRequestType::SEND_DEBUG_OUTPUT: diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 5f125990af..c09b1dbf7a 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -205,7 +205,7 @@ extern float g_safeInsetBottom; graphicsContext = new IOSGraphicsContext(); graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { - System_NotifyUserMessage(details, 5.0, 0xFFFFFFFF, "error_callback"); + g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback"); }, nullptr); graphicsContext->ThreadStart(); diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index f712039d60..a114d5a8de 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1702,7 +1702,6 @@ void System_Notify(SystemNotification notification) { } bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int param3) { return false; } void System_PostUIMessage(const std::string &message, const std::string ¶m) {} -void System_NotifyUserMessage(const std::string &message, float duration, u32 color, const char *id) {} void NativeUpdate() {} void NativeRender(GraphicsContext *graphicsContext) {} void NativeResized() {} diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 7980486af1..ca588ea6c4 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -99,7 +99,6 @@ bool System_GetPropertyBool(SystemProperty prop) { } void System_Notify(SystemNotification notification) {} void System_PostUIMessage(const std::string &message, const std::string ¶m) {} -void System_NotifyUserMessage(const std::string &message, float duration, u32 color, const char *id) {} void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples) {}