From 0831da5703ba4630b29bb0c7fdce4ca861c0892c Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 22 Jan 2023 16:38:23 +0100 Subject: [PATCH] Show progress of screenshots on screen --- Makefile | 2 +- src/function_patcher.cpp | 101 ++++++++++++++++++++++++++------------- src/main.cpp | 10 ++++ src/retain_vars.cpp | 8 +++- src/retain_vars.hpp | 9 +++- src/screenshot_utils.cpp | 30 ++++++++++++ src/thread.cpp | 22 +++++---- src/thread.h | 2 + 8 files changed, 140 insertions(+), 44 deletions(-) diff --git a/Makefile b/Makefile index 3fd4e7d..cf9f4fb 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g endif -LIBS := -lwups -lwut -lgd -lpng -ljpeg -lz -lmappedmemory +LIBS := -lwups -lwut -lgd -lpng -ljpeg -lz -lnotifications -lmappedmemory #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level diff --git a/src/function_patcher.cpp b/src/function_patcher.cpp index fffe1d6..bd8e6d5 100644 --- a/src/function_patcher.cpp +++ b/src/function_patcher.cpp @@ -13,12 +13,75 @@ extern "C" uint32_t VPADGetButtonProcMode(uint32_t); -DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus *buffer, uint32_t buffer_size, VPADReadError *error) { +void AlreadyInProgressCallback(NotificationModuleHandle handle, void *context) { + auto scanTarget = (GX2ScanTarget) (uint32_t) context; + if (scanTarget == GX2_SCAN_TARGET_TV) { + gInProgressNotificationDisplayedTV = false; + } else if (scanTarget == GX2_SCAN_TARGET_DRC) { + gInProgressNotificationDisplayedDRC = false; + } + OSMemoryBarrier(); +} +void NotAvailableCallback(NotificationModuleHandle handle, void *context) { + gNotAvailableNotificationDisplayed = false; + OSMemoryBarrier(); +} + +void RequestScreenshot() { + NotificationModuleStatus err; + if (!OSIsHomeButtonMenuEnabled()) { + if (!gNotAvailableNotificationDisplayed) { + if ((err = NotificationModule_AddErrorNotificationWithCallback("Screenshots not available at the moment.", + NotAvailableCallback, + nullptr)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to display \"Screenshots not available at the moment.\" notification"); + DEBUG_FUNCTION_LINE_ERR("Error: %s,", NotificationModule_GetStatusStr(err)); + return; + } + + gNotAvailableNotificationDisplayed = true; + } + } else { + if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_TV) { + if (gTakeScreenshotTV == SCREENSHOT_STATE_READY) { + DEBUG_FUNCTION_LINE("Requested screenshot for TV!"); + gTakeScreenshotTV = SCREENSHOT_STATE_REQUESTED; + } else if (!gInProgressNotificationDisplayedTV) { + if ((err = NotificationModule_AddErrorNotificationWithCallback("Screenshot of the TV already in progress.", + AlreadyInProgressCallback, + (void *) GX2_SCAN_TARGET_TV)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to display \"Screenshot of the TV already in progress.\" notification"); + DEBUG_FUNCTION_LINE_ERR("Error: %s,", NotificationModule_GetStatusStr(err)); + return; + } + gInProgressNotificationDisplayedTV = true; + } + } + if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_DRC) { + if (gTakeScreenshotDRC == SCREENSHOT_STATE_READY) { + DEBUG_FUNCTION_LINE("Requested screenshot for DRC!"); + gTakeScreenshotDRC = SCREENSHOT_STATE_REQUESTED; + } else if (!gInProgressNotificationDisplayedDRC) { + if ((err = NotificationModule_AddErrorNotificationWithCallback("Screenshot of the GamePad already in progress.", + AlreadyInProgressCallback, + (void *) GX2_SCAN_TARGET_DRC)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to display \"Screenshot of the GamePad already in progress.\" notification"); + DEBUG_FUNCTION_LINE_ERR("Error: %s,", NotificationModule_GetStatusStr(err)); + return; + } + gInProgressNotificationDisplayedDRC = true; + } + } + OSMemoryBarrier(); + } +} + +DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus *buffer, uint32_t buffer_size, VPADReadError *error) { VPADReadError real_error; int32_t result = real_VPADRead(chan, buffer, buffer_size, &real_error); - if (gEnabled && (gTakeScreenshotTV == SCREENSHOT_STATE_READY || gTakeScreenshotDRC == SCREENSHOT_STATE_READY)) { + if (gEnabled) { if (result > 0 && real_error == VPAD_READ_SUCCESS) { uint32_t end = 1; // Fix games like TP HD @@ -27,23 +90,7 @@ DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus *buffer, uint32_t buf } for (uint32_t i = 0; i < end; i++) { if (((buffer[i].trigger & 0x000FFFFF) == gButtonCombo)) { - if (!OSIsHomeButtonMenuEnabled()) { - DEBUG_FUNCTION_LINE("Screenshots are disabled"); - } else { - if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_TV) { - if (gTakeScreenshotTV == SCREENSHOT_STATE_READY) { - DEBUG_FUNCTION_LINE("Requested screenshot for TV!"); - gTakeScreenshotTV = SCREENSHOT_STATE_REQUESTED; - } - } - if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_DRC) { - if (gTakeScreenshotDRC == SCREENSHOT_STATE_READY) { - DEBUG_FUNCTION_LINE("Requested screenshot for DRC!"); - gTakeScreenshotDRC = SCREENSHOT_STATE_REQUESTED; - } - } - OSMemoryBarrier(); - } + RequestScreenshot(); break; } } @@ -60,7 +107,7 @@ static uint32_t sWPADLastButtonHold[4]; DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) { real_WPADRead(chan, data); - if (gEnabled && chan >= 0 && chan < 4 && OSIsHomeButtonMenuEnabled() && (gTakeScreenshotTV == SCREENSHOT_STATE_READY || gTakeScreenshotDRC == SCREENSHOT_STATE_READY)) { + if (gEnabled && chan >= 0 && chan < 4) { if (data[0].err == 0) { if (data[0].extensionType != 0xFF) { uint32_t curButtonHold = 0; @@ -87,19 +134,7 @@ DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) { } if (forceScreenshot || (buttonComboConverted != 0 && curButtonTrigger == buttonComboConverted)) { - if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_TV) { - if (gTakeScreenshotTV == SCREENSHOT_STATE_READY) { - DEBUG_FUNCTION_LINE("Requested screenshot for TV!"); - gTakeScreenshotTV = SCREENSHOT_STATE_REQUESTED; - } - } - if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_DRC) { - if (gTakeScreenshotDRC == SCREENSHOT_STATE_READY) { - DEBUG_FUNCTION_LINE("Requested screenshot for DRC!"); - gTakeScreenshotDRC = SCREENSHOT_STATE_REQUESTED; - } - } - OSMemoryBarrier(); + RequestScreenshot(); } sWPADLastButtonHold[chan] = curButtonHold; diff --git a/src/main.cpp b/src/main.cpp index 65d595f..f9067f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,11 @@ INITIALIZE_PLUGIN() { gButtonCombo = VPAD_BUTTON_TV; OSMemoryBarrier(); + NotificationModuleStatus res; + if ((res = NotificationModule_InitLibrary()) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("NotificationModule_InitLibrary failed: %s", NotificationModule_GetStatusStr(res)); + } + // Open storage to read values WUPSStorageError storageRes = WUPS_OpenStorage(); if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { @@ -117,6 +123,10 @@ INITIALIZE_PLUGIN() { } } +DEINITIALIZE_PLUGIN() { + NotificationModule_DeInitLibrary(); +} + void multipleValueItemCallback(ConfigItemMultipleValues *item, uint32_t newValue) { if (item && item->configId) { DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configId, newValue); diff --git a/src/retain_vars.cpp b/src/retain_vars.cpp index 72f8291..b16a5b7 100644 --- a/src/retain_vars.cpp +++ b/src/retain_vars.cpp @@ -13,4 +13,10 @@ std::string gShortNameEn; ScreenshotState gTakeScreenshotTV = SCREENSHOT_STATE_READY; ScreenshotState gTakeScreenshotDRC = SCREENSHOT_STATE_READY; -bool gReservedBitUsage = true; \ No newline at end of file +bool gReservedBitUsage = true; + +bool gInProgressNotificationDisplayedDRC = false; +bool gInProgressNotificationDisplayedTV = false; +bool gNotAvailableNotificationDisplayed = false; + +NMColor COLOR_RED = {237, 28, 36, 255}; \ No newline at end of file diff --git a/src/retain_vars.hpp b/src/retain_vars.hpp index 2ed50b3..15a8d1f 100644 --- a/src/retain_vars.hpp +++ b/src/retain_vars.hpp @@ -1,6 +1,7 @@ #pragma once #include "common.h" #include +#include #include extern bool gEnabled; @@ -15,4 +16,10 @@ extern std::string gShortNameEn; extern ScreenshotState gTakeScreenshotTV; extern ScreenshotState gTakeScreenshotDRC; -extern bool gReservedBitUsage; \ No newline at end of file +extern bool gReservedBitUsage; + +extern bool gInProgressNotificationDisplayedDRC; +extern bool gInProgressNotificationDisplayedTV; +extern bool gNotAvailableNotificationDisplayed; + +extern NMColor COLOR_RED; \ No newline at end of file diff --git a/src/screenshot_utils.cpp b/src/screenshot_utils.cpp index c231ebf..301e8c7 100644 --- a/src/screenshot_utils.cpp +++ b/src/screenshot_utils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include bool saveTextureAsPicture(const std::string &path, uint8_t *sourceBuffer, uint32_t width, uint32_t height, uint32_t pitch, GX2SurfaceFormat format, ImageOutputFormatEnum outputFormat, bool convertRGBtoSRGB, int quality) { @@ -176,11 +177,31 @@ static bool copyBuffer(GX2ColorBuffer *sourceBuffer, GX2ColorBuffer *targetBuffe } } +void ScreenshotSavedCallback(NotificationModuleHandle handle, void *context) { + auto scanTarget = (GX2ScanTarget) (uint32_t) context; + if (scanTarget == GX2_SCAN_TARGET_TV) { + gTakeScreenshotTV = SCREENSHOT_STATE_READY; + } else { + gTakeScreenshotDRC = SCREENSHOT_STATE_READY; + } + OSMemoryBarrier(); +} + bool takeScreenshot(GX2ColorBuffer *srcBuffer, GX2ScanTarget scanTarget, GX2SurfaceFormat outputBufferSurfaceFormat, ImageOutputFormatEnum outputFormat, int quality) { if (srcBuffer == nullptr) { DEBUG_FUNCTION_LINE_ERR("Source buffer was NULL"); return false; } + auto text = string_format("\ue01e Saving screenshot of the %s...", scanTarget == GX2_SCAN_TARGET_TV ? "TV" : "GamePad"); + NotificationModuleHandle screenshot; + NotificationModuleStatus err; + if ((err = NotificationModule_AddDynamicNotificationWithCallback(text.c_str(), + &screenshot, + ScreenshotSavedCallback, + (void *) scanTarget)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("NotificationModule_AddDynamicNotificationWithCallback failed: %s", NotificationModule_GetStatusStr(err)); + return false; + } GX2ColorBuffer colorBuffer; @@ -231,6 +252,7 @@ bool takeScreenshot(GX2ColorBuffer *srcBuffer, GX2ScanTarget scanTarget, GX2Surf goto error; } + param->notificationHandle = screenshot; param->sourceBuffer = (uint8_t *) colorBuffer.surface.image; param->width = width; param->height = height; @@ -257,5 +279,13 @@ error: colorBuffer.surface.image = nullptr; } + auto errorText = string_format("\ue01e Saving screenshot of the %s failed", scanTarget == GX2_SCAN_TARGET_TV ? "TV" : "GamePad"); + if ((err = NotificationModule_UpdateDynamicNotificationText(screenshot, errorText.c_str())) != NOTIFICATION_MODULE_RESULT_SUCCESS || + (err = NotificationModule_UpdateDynamicNotificationBackgroundColor(screenshot, COLOR_RED)) != NOTIFICATION_MODULE_RESULT_SUCCESS || + (err = NotificationModule_FinishDynamicNotificationWithShake(screenshot, 2.0f, 0.5f)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to update notification: %s", NotificationModule_GetStatusStr(err)); + } + + OSMemoryBarrier(); return false; } diff --git a/src/thread.cpp b/src/thread.cpp index a05a81c..6d3fc35 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -4,6 +4,7 @@ #include "screenshot_utils.h" #include "utils/StringTools.h" #include "utils/logger.h" +#include "utils/utils.h" #include #include #include @@ -84,20 +85,25 @@ static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) DEBUG_FUNCTION_LINE("Saving to %s", path.c_str()); auto res = saveTextureAsPicture(path, message->sourceBuffer, message->width, message->height, message->pitch, message->format, message->outputFormat, message->convertRGBtoSRGB, message->quality); if (res) { - DEBUG_FUNCTION_LINE("Saving screenshot was successful"); + NotificationModuleStatus err; + auto text = string_format("\ue01e Saving screenshot of the %s done!", message->scanTarget == GX2_SCAN_TARGET_TV ? "TV" : "GamePad"); + if ((err = NotificationModule_UpdateDynamicNotificationText(message->notificationHandle, text.c_str())) != NOTIFICATION_MODULE_RESULT_SUCCESS || + (err = NotificationModule_FinishDynamicNotification(message->notificationHandle, 2.0)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to update notification: %s", NotificationModule_GetStatusStr(err)); + } success = true; } } else { DEBUG_FUNCTION_LINE_ERR("Failed to get and create path"); } if (!success) { - DEBUG_FUNCTION_LINE("Saving screenshot failed"); - } - - if (message->scanTarget == GX2_SCAN_TARGET_TV) { - gTakeScreenshotTV = SCREENSHOT_STATE_READY; - } else if (message->scanTarget == GX2_SCAN_TARGET_DRC) { - gTakeScreenshotDRC = SCREENSHOT_STATE_READY; + NotificationModuleStatus err; + auto errorText = string_format("\ue01e Saving screenshot of the %s failed", message->scanTarget == GX2_SCAN_TARGET_TV ? "TV" : "GamePad"); + if ((err = NotificationModule_UpdateDynamicNotificationText(message->notificationHandle, errorText.c_str())) != NOTIFICATION_MODULE_RESULT_SUCCESS || + (err = NotificationModule_UpdateDynamicNotificationBackgroundColor(message->notificationHandle, COLOR_RED)) != NOTIFICATION_MODULE_RESULT_SUCCESS || + (err = NotificationModule_FinishDynamicNotificationWithShake(message->notificationHandle, 2.0f, 0.5f)) != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to update notification: %s", NotificationModule_GetStatusStr(err)); + } } // Free the colorbuffer copy. diff --git a/src/thread.h b/src/thread.h index bb06377..1379e67 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,5 +1,6 @@ #pragma once #include "common.h" +#include "notifications/notifications.h" #include #include #include @@ -16,6 +17,7 @@ struct FSIOThreadData { }; struct SaveScreenshotMessage { + NotificationModuleHandle notificationHandle; uint8_t *sourceBuffer; uint32_t width; uint32_t height;