Show progress of screenshots on screen

This commit is contained in:
Maschell 2023-01-22 16:38:23 +01:00
parent da7565dbdd
commit 0831da5703
8 changed files with 140 additions and 44 deletions

View File

@ -49,7 +49,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif 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 # list of directories containing libraries, this must be the top level

View File

@ -13,12 +13,75 @@
extern "C" uint32_t VPADGetButtonProcMode(uint32_t); 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; VPADReadError real_error;
int32_t result = real_VPADRead(chan, buffer, buffer_size, &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) { if (result > 0 && real_error == VPAD_READ_SUCCESS) {
uint32_t end = 1; uint32_t end = 1;
// Fix games like TP HD // 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++) { for (uint32_t i = 0; i < end; i++) {
if (((buffer[i].trigger & 0x000FFFFF) == gButtonCombo)) { if (((buffer[i].trigger & 0x000FFFFF) == gButtonCombo)) {
if (!OSIsHomeButtonMenuEnabled()) { RequestScreenshot();
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();
}
break; break;
} }
} }
@ -60,7 +107,7 @@ static uint32_t sWPADLastButtonHold[4];
DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) { DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) {
real_WPADRead(chan, 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].err == 0) {
if (data[0].extensionType != 0xFF) { if (data[0].extensionType != 0xFF) {
uint32_t curButtonHold = 0; uint32_t curButtonHold = 0;
@ -87,19 +134,7 @@ DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) {
} }
if (forceScreenshot || (buttonComboConverted != 0 && curButtonTrigger == buttonComboConverted)) { if (forceScreenshot || (buttonComboConverted != 0 && curButtonTrigger == buttonComboConverted)) {
if (gImageSource == IMAGE_SOURCE_TV_AND_DRC || gImageSource == IMAGE_SOURCE_TV) { RequestScreenshot();
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();
} }
sWPADLastButtonHold[chan] = curButtonHold; sWPADLastButtonHold[chan] = curButtonHold;

View File

@ -7,6 +7,7 @@
#include <coreinit/title.h> #include <coreinit/title.h>
#include <malloc.h> #include <malloc.h>
#include <nn/acp.h> #include <nn/acp.h>
#include <notifications/notifications.h>
#include <string> #include <string>
#include <wups.h> #include <wups.h>
#include <wups/config/WUPSConfigItemBoolean.h> #include <wups/config/WUPSConfigItemBoolean.h>
@ -38,6 +39,11 @@ INITIALIZE_PLUGIN() {
gButtonCombo = VPAD_BUTTON_TV; gButtonCombo = VPAD_BUTTON_TV;
OSMemoryBarrier(); 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 // Open storage to read values
WUPSStorageError storageRes = WUPS_OpenStorage(); WUPSStorageError storageRes = WUPS_OpenStorage();
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
@ -117,6 +123,10 @@ INITIALIZE_PLUGIN() {
} }
} }
DEINITIALIZE_PLUGIN() {
NotificationModule_DeInitLibrary();
}
void multipleValueItemCallback(ConfigItemMultipleValues *item, uint32_t newValue) { void multipleValueItemCallback(ConfigItemMultipleValues *item, uint32_t newValue) {
if (item && item->configId) { if (item && item->configId) {
DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configId, newValue); DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configId, newValue);

View File

@ -14,3 +14,9 @@ ScreenshotState gTakeScreenshotTV = SCREENSHOT_STATE_READY;
ScreenshotState gTakeScreenshotDRC = SCREENSHOT_STATE_READY; ScreenshotState gTakeScreenshotDRC = SCREENSHOT_STATE_READY;
bool gReservedBitUsage = true; bool gReservedBitUsage = true;
bool gInProgressNotificationDisplayedDRC = false;
bool gInProgressNotificationDisplayedTV = false;
bool gNotAvailableNotificationDisplayed = false;
NMColor COLOR_RED = {237, 28, 36, 255};

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include <gx2/surface.h> #include <gx2/surface.h>
#include <notifications/notifications.h>
#include <string> #include <string>
extern bool gEnabled; extern bool gEnabled;
@ -16,3 +17,9 @@ extern ScreenshotState gTakeScreenshotTV;
extern ScreenshotState gTakeScreenshotDRC; extern ScreenshotState gTakeScreenshotDRC;
extern bool gReservedBitUsage; extern bool gReservedBitUsage;
extern bool gInProgressNotificationDisplayedDRC;
extern bool gInProgressNotificationDisplayedTV;
extern bool gNotAvailableNotificationDisplayed;
extern NMColor COLOR_RED;

View File

@ -9,6 +9,7 @@
#include <gx2/event.h> #include <gx2/event.h>
#include <gx2/mem.h> #include <gx2/mem.h>
#include <memory/mappedmemory.h> #include <memory/mappedmemory.h>
#include <notifications/notifications.h>
#include <valarray> #include <valarray>
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) { 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) { bool takeScreenshot(GX2ColorBuffer *srcBuffer, GX2ScanTarget scanTarget, GX2SurfaceFormat outputBufferSurfaceFormat, ImageOutputFormatEnum outputFormat, int quality) {
if (srcBuffer == nullptr) { if (srcBuffer == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Source buffer was NULL"); DEBUG_FUNCTION_LINE_ERR("Source buffer was NULL");
return false; 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; GX2ColorBuffer colorBuffer;
@ -231,6 +252,7 @@ bool takeScreenshot(GX2ColorBuffer *srcBuffer, GX2ScanTarget scanTarget, GX2Surf
goto error; goto error;
} }
param->notificationHandle = screenshot;
param->sourceBuffer = (uint8_t *) colorBuffer.surface.image; param->sourceBuffer = (uint8_t *) colorBuffer.surface.image;
param->width = width; param->width = width;
param->height = height; param->height = height;
@ -257,5 +279,13 @@ error:
colorBuffer.surface.image = nullptr; 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; return false;
} }

View File

@ -4,6 +4,7 @@
#include "screenshot_utils.h" #include "screenshot_utils.h"
#include "utils/StringTools.h" #include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/cache.h> #include <coreinit/cache.h>
#include <coreinit/title.h> #include <coreinit/title.h>
#include <dirent.h> #include <dirent.h>
@ -84,20 +85,25 @@ static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv)
DEBUG_FUNCTION_LINE("Saving to %s", path.c_str()); 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); auto res = saveTextureAsPicture(path, message->sourceBuffer, message->width, message->height, message->pitch, message->format, message->outputFormat, message->convertRGBtoSRGB, message->quality);
if (res) { 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; success = true;
} }
} else { } else {
DEBUG_FUNCTION_LINE_ERR("Failed to get and create path"); DEBUG_FUNCTION_LINE_ERR("Failed to get and create path");
} }
if (!success) { if (!success) {
DEBUG_FUNCTION_LINE("Saving screenshot failed"); 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 ||
if (message->scanTarget == GX2_SCAN_TARGET_TV) { (err = NotificationModule_UpdateDynamicNotificationBackgroundColor(message->notificationHandle, COLOR_RED)) != NOTIFICATION_MODULE_RESULT_SUCCESS ||
gTakeScreenshotTV = SCREENSHOT_STATE_READY; (err = NotificationModule_FinishDynamicNotificationWithShake(message->notificationHandle, 2.0f, 0.5f)) != NOTIFICATION_MODULE_RESULT_SUCCESS) {
} else if (message->scanTarget == GX2_SCAN_TARGET_DRC) { DEBUG_FUNCTION_LINE_ERR("Failed to update notification: %s", NotificationModule_GetStatusStr(err));
gTakeScreenshotDRC = SCREENSHOT_STATE_READY; }
} }
// Free the colorbuffer copy. // Free the colorbuffer copy.

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include "notifications/notifications.h"
#include <coreinit/messagequeue.h> #include <coreinit/messagequeue.h>
#include <coreinit/semaphore.h> #include <coreinit/semaphore.h>
#include <coreinit/thread.h> #include <coreinit/thread.h>
@ -16,6 +17,7 @@ struct FSIOThreadData {
}; };
struct SaveScreenshotMessage { struct SaveScreenshotMessage {
NotificationModuleHandle notificationHandle;
uint8_t *sourceBuffer; uint8_t *sourceBuffer;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;