Compare commits

...

15 Commits
v0.1.1 ... main

Author SHA1 Message Date
Maschell 36993e1630 make sure gOverlayFrame is valid 2024-05-06 17:35:14 +02:00
dependabot[bot] 5801f61ec7 Bump wiiu-env/devkitppc from 20240423 to 20240505
Bumps wiiu-env/devkitppc from 20240423 to 20240505.

---
updated-dependencies:
- dependency-name: wiiu-env/devkitppc
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 10:20:14 +02:00
Maschell a2cf5293d8 Add support for notification that can survive application changes 2024-04-26 13:57:56 +02:00
Maschell 42727408df Update github actions 2024-04-25 21:19:13 +02:00
Maschell 4963c1b304 Update Dockerfile 2024-04-25 21:19:13 +02:00
Maschell 846fa5ed6a Bump version 2024-04-25 21:19:13 +02:00
Maschell 6954c0dc64 Fix some warnings in SchriftGX2 2024-04-25 21:19:13 +02:00
Maschell 8fb509ad9c Update .gitignore to ignore all files with .zip extension 2024-04-25 21:19:13 +02:00
dependabot[bot] 4b0f42e5fb Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-25 21:19:13 +02:00
Maschell 920258c265 Make sure to stop Drawing on the screen before destorying all shaders and other data 2024-04-25 21:19:13 +02:00
Maschell 209f2c1805 Improve font system init logic 2024-04-25 21:19:13 +02:00
Maschell e19f9ba398 Implement WUMS_DEINITIALIZE 2024-04-25 21:19:13 +02:00
Maschell b1e06017ac Add missing OSFatals and make existing OSFatals more verbose 2024-04-25 21:19:13 +02:00
Maschell 1bf259db4b Save Notifications in a queue until the overlay has been initialized 2024-04-25 21:19:13 +02:00
Maschell d9da174f4b Create dependabot.yml 2023-07-23 10:21:40 +02:00
13 changed files with 161 additions and 61 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -9,7 +9,7 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src
@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
@ -48,7 +48,7 @@ jobs:
- name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
- name: Create Release
uses: "softprops/action-gh-release@v1"
uses: "softprops/action-gh-release@v2"
with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false

View File

@ -6,7 +6,7 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src
@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: build binary with logging
run: |
docker build . -t builder
@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ build/
cmake-build-debug/
CMakeLists.txt
*.wms
*.zip

View File

@ -1,8 +1,8 @@
FROM ghcr.io/wiiu-env/devkitppc:20230621
FROM ghcr.io/wiiu-env/devkitppc:20240505
COPY --from=ghcr.io/wiiu-env/libnotifications:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20230719 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -8,19 +8,29 @@
void ExportCleanUp() {
std::lock_guard<std::mutex> lock(gNotificationListMutex);
gNotificationList.clear();
std::lock_guard overlay_lock(gOverlayFrameMutex);
// Remove notification in queue that should not survive
std::vector<std::shared_ptr<Notification>> keepQueue;
for (const auto &notification : gOverlayQueueDuringStartup) {
if (notification->isKeepUntilShown()) {
keepQueue.push_back(notification);
} else {
}
}
gOverlayQueueDuringStartup.clear();
gOverlayQueueDuringStartup = keepQueue;
}
NotificationModuleStatus NMAddStaticNotification(const char *text,
NotificationModuleNotificationType type,
float durationBeforeFadeOutInSeconds,
float shakeDurationInSeconds,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context) {
if (!gOverlayFrame) {
return NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY;
}
NotificationModuleStatus NMAddStaticNotificationV2(const char *text,
NotificationModuleNotificationType type,
float durationBeforeFadeOutInSeconds,
float shakeDurationInSeconds,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context,
bool keepUntilShown) {
NotificationStatus status;
switch (type) {
@ -41,16 +51,37 @@ NotificationModuleStatus NMAddStaticNotification(const char *text,
(GX2Color){textColor.r, textColor.g, textColor.b, textColor.a},
(GX2Color){backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a},
finishFunc,
context);
context,
nullptr,
keepUntilShown);
if (!notification) {
return NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED;
}
gOverlayFrame->addNotification(notification);
{
std::lock_guard lock(gOverlayFrameMutex);
if (gOverlayFrame && gDrawReady) {
gOverlayFrame->addNotification(std::move(notification));
} else {
gOverlayQueueDuringStartup.push_back(std::move(notification));
}
}
return NOTIFICATION_MODULE_RESULT_SUCCESS;
}
NotificationModuleStatus NMAddStaticNotification(const char *text,
NotificationModuleNotificationType type,
float durationBeforeFadeOutInSeconds,
float shakeDurationInSeconds,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context) {
return NMAddStaticNotificationV2(text, type, durationBeforeFadeOutInSeconds, shakeDurationInSeconds, textColor, backgroundColor, finishFunc, context, false);
}
void NMNotificationRemovedFromOverlay(Notification *notification) {
if (notification) {
auto handle = notification->getHandle();
@ -60,19 +91,17 @@ void NMNotificationRemovedFromOverlay(Notification *notification) {
}
}
NotificationModuleStatus NMAddDynamicNotification(const char *text,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context,
NotificationModuleHandle *outHandle) {
NotificationModuleStatus NMAddDynamicNotificationV2(const char *text,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context,
bool keep_until_shown,
NotificationModuleHandle *outHandle) {
if (outHandle == nullptr) {
return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT;
}
*outHandle = 0;
if (!gOverlayFrame) {
return NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY;
}
auto notification = make_shared_nothrow<Notification>(
text,
@ -83,7 +112,8 @@ NotificationModuleStatus NMAddDynamicNotification(const char *text,
(GX2Color){backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a},
finishFunc,
context,
NMNotificationRemovedFromOverlay);
NMNotificationRemovedFromOverlay,
keep_until_shown);
if (!notification) {
return NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED;
}
@ -91,13 +121,29 @@ NotificationModuleStatus NMAddDynamicNotification(const char *text,
{
std::lock_guard<std::mutex> lock(gNotificationListMutex);
*outHandle = notification->getHandle();
gOverlayFrame->addNotification(notification);
gNotificationList.push_front(notification);
{
std::lock_guard overlay_lock(gOverlayFrameMutex);
if (gOverlayFrame) {
gOverlayFrame->addNotification(notification);
} else {
gOverlayQueueDuringStartup.push_back(notification);
}
}
gNotificationList.push_front(std::move(notification));
}
return NOTIFICATION_MODULE_RESULT_SUCCESS;
}
NotificationModuleStatus NMAddDynamicNotification(const char *text,
NMColor textColor,
NMColor backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *context),
void *context,
NotificationModuleHandle *outHandle) {
return NMAddDynamicNotificationV2(text, textColor, backgroundColor, finishFunc, context, false, outHandle);
}
NotificationModuleStatus NMUpdateDynamicNotificationText(NotificationModuleHandle handle,
const char *text) {
NotificationModuleStatus res = NOTIFICATION_MODULE_RESULT_INVALID_HANDLE;
@ -186,10 +232,12 @@ NotificationModuleStatus NMGetVersion(NotificationModuleAPIVersion *outVersion)
if (outVersion == nullptr) {
return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT;
}
*outVersion = 1;
*outVersion = 2;
return NOTIFICATION_MODULE_RESULT_SUCCESS;
}
WUMS_EXPORT_FUNCTION(NMAddDynamicNotificationV2);
WUMS_EXPORT_FUNCTION(NMAddStaticNotificationV2);
WUMS_EXPORT_FUNCTION(NMAddDynamicNotification);
WUMS_EXPORT_FUNCTION(NMAddStaticNotification);
WUMS_EXPORT_FUNCTION(NMUpdateDynamicNotificationText);

View File

@ -71,8 +71,21 @@ void drawScreenshotSavedTexture2(GX2ColorBuffer *colorBuffer, GX2ScanTarget scan
drawIntoColorBuffer(colorBuffer, gOverlayFrame, scan_target);
}
static void TryAddFromQueue() {
if (!gOverlayFrame) {
return;
}
std::lock_guard overlay_lock(gOverlayFrameMutex);
// Add notification that had been called before the overlay was ready
for (const auto &notification : gOverlayQueueDuringStartup) {
gOverlayFrame->addNotification(notification);
}
gOverlayQueueDuringStartup.clear();
}
DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target) {
gDrawReady = true;
TryAddFromQueue();
if (drawScreenshotSavedTexture(colorBuffer, scan_target)) {
// if it returns true we don't need to call GX2CopyColorBufferToScanBuffer
return;
@ -110,19 +123,24 @@ bool drawScreenshotSavedTexture(const GX2ColorBuffer *colorBuffer, GX2ScanTarget
DECL_FUNCTION(void, GX2Init, uint32_t attributes) {
real_GX2Init(attributes);
if (!gOverlayInitDone) {
std::lock_guard overlay_lock(gOverlayFrameMutex);
DEBUG_FUNCTION_LINE_VERBOSE("Init Overlay");
gOverlayFrame = new (std::nothrow) OverlayFrame(1280.0f, 720.0f);
if (!gOverlayFrame) {
OSFatal("Failed to alloc gOverlayFrame");
OSFatal("NotificationModule: Failed to alloc gOverlayFrame");
}
// Allocate shader.
ColorShader::instance();
Texture2DShader::instance();
if (ColorShader::instance() == nullptr) {
OSFatal("NotificationModule: Failed to alloc ColorShader");
}
if (Texture2DShader::instance() == nullptr) {
OSFatal("NotificationModule: Failed to alloc Texture2DShader");
}
// has been allocated in WUMS INIT
if (!gContextState) {
OSFatal("Failed to alloc gContextState");
OSFatal("NotificationModule: Failed to alloc gContextState");
}
real_GX2SetupContextStateEx(gContextState, GX2_TRUE);
DCInvalidateRange(gContextState, sizeof(GX2ContextState)); // Important!
@ -132,6 +150,7 @@ DECL_FUNCTION(void, GX2Init, uint32_t attributes) {
DECL_FUNCTION(void, GX2MarkScanBufferCopied, GX2ScanTarget scan_target) {
gDrawReady = true;
TryAddFromQueue();
if (scan_target == GX2_SCAN_TARGET_TV) {
drawScreenshotSavedTexture2(&lastTVColorBuffer, scan_target);
} else {

View File

@ -8,7 +8,8 @@ Notification::Notification(const std::string &overlayText,
GX2Color backgroundColor,
void (*finishFunc)(NotificationModuleHandle, void *),
void *context,
void (*removedFromOverlayCallback)(Notification *)) : GuiFrame(0, 0), mBackground(0, 0, backgroundColor) {
void (*removedFromOverlayCallback)(Notification *),
bool keepUntilShown) : GuiFrame(0, 0), mBackground(0, 0, backgroundColor) {
mFinishFunction = finishFunc;
mFinishFunctionContext = context;
mRemovedFromOverlayCallback = removedFromOverlayCallback;
@ -20,6 +21,7 @@ Notification::Notification(const std::string &overlayText,
mNotificationText.setPosition(0, 0);
mNotificationText.setFontSize(20);
mNotificationText.setAlignment(ALIGN_CENTERED);
mKeepUntilShown = keepUntilShown;
updateStatus(status);

View File

@ -35,7 +35,8 @@ public:
GX2Color backgroundColor = {100, 100, 100, 255},
void (*finishFunc)(NotificationModuleHandle, void *) = nullptr,
void *context = nullptr,
void (*removedFromOverlayCallback)(Notification *) = nullptr);
void (*removedFromOverlayCallback)(Notification *) = nullptr,
bool keepUntilShown = false);
~Notification() override;
@ -90,6 +91,10 @@ public:
mPositionSet = true;
}
[[nodiscard]] bool isKeepUntilShown() const {
return mKeepUntilShown;
}
private:
std::function<void(NotificationModuleHandle, void *)> mFinishFunction;
std::function<void(Notification *)> mRemovedFromOverlayCallback;
@ -106,6 +111,8 @@ private:
bool mTextDirty = false;
bool mPositionSet = false;
bool mKeepUntilShown = false;
NotificationStatus mStatus = NOTIFICATION_STATUS_INFO;
NotificationInternalStatus mInternalStatus = NOTIFICATION_STATUS_NOTHING;
};

View File

@ -217,6 +217,7 @@ ftgxCharData *SchriftGX2::cacheGlyphData(wchar_t charCode, int16_t pixelSize) {
textureHeight = mtx.minHeight;
SFT_Image img = {
.pixels = nullptr,
.width = textureWidth,
.height = textureHeight,
};
@ -307,7 +308,7 @@ bool SchriftGX2::loadGlyphData(SFT_Image *bmp, ftgxCharData *charData, ftGX2Data
auto *src = (uint8_t *) bmp->pixels;
auto *dst = (uint32_t *) charData->texture->surface.image;
uint32_t x, y;
int32_t x, y;
for (y = 0; y < bmp->height; y++) {
for (x = 0; x < bmp->width; x++) {
@ -391,6 +392,7 @@ int16_t SchriftGX2::getStyleOffsetHeight(int16_t format, uint16_t pixelSize) {
*/
uint16_t SchriftGX2::drawText(int16_t x, int16_t y, int16_t z, const wchar_t *text, int16_t pixelSize, const glm::vec4 &color, uint16_t textStyle, uint16_t textWidth, const float &textBlur, const float &colorBlurIntensity, const glm::vec4 &blurColor) {
(void) textWidth;
if (!text) {
return 0;
}

View File

@ -11,7 +11,7 @@
WUMS_MODULE_EXPORT_NAME("homebrew_notifications");
#define VERSION "v0.1.1"
#define VERSION "v0.1.2"
WUMS_DEPENDS_ON(homebrew_memorymapping);
WUMS_DEPENDS_ON(homebrew_functionpatcher);
@ -20,7 +20,7 @@ WUMS_INITIALIZE() {
initLogging();
if (FunctionPatcher_InitLibrary() != FUNCTION_PATCHER_RESULT_SUCCESS) {
OSFatal("homebrew_notifications: FunctionPatcher_InitLibrary failed");
OSFatal("NotificationModule: FunctionPatcher_InitLibrary failed");
}
DEBUG_FUNCTION_LINE("Patch NotificationModule functions");
for (uint32_t i = 0; i < function_replacements_size; i++) {
@ -38,22 +38,22 @@ WUMS_INITIALIZE() {
sizeof(GX2ContextState),
GX2_CONTEXT_STATE_ALIGNMENT);
if (gContextState == nullptr) {
OSFatal("Failed to allocate gContextState");
OSFatal("NotificationModule: Failed to allocate gContextState");
} else {
DEBUG_FUNCTION_LINE("Allocated %d bytes for gCont extState", sizeof(GX2ContextState));
DEBUG_FUNCTION_LINE_VERBOSE("Allocated %d bytes for gContextState", sizeof(GX2ContextState));
}
void *font = nullptr;
uint32_t size = 0;
OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size);
if (font && size) {
if (OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size) && font && size > 0) {
gFontSystem = new (std::nothrow) SchriftGX2((uint8_t *) font, (int32_t) size);
if (gFontSystem) {
GuiText::setPresetFont(gFontSystem);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to init font system");
}
}
if (gFontSystem != nullptr) {
GuiText::setPresetFont(gFontSystem);
} else {
OSFatal("NotificationModule: Failed to init font system");
}
OSMemoryBarrier();
deinitLogging();
}
@ -65,6 +65,7 @@ WUMS_APPLICATION_STARTS() {
}
WUMS_APPLICATION_ENDS() {
gDrawReady = false;
if (gOverlayFrame) {
gOverlayFrame->clearElements();
}
@ -76,3 +77,9 @@ WUMS_APPLICATION_ENDS() {
Texture2DShader::destroyInstance();
deinitLogging();
}
WUMS_DEINITIALIZE() {
delete gOverlayFrame;
delete gFontSystem;
MEMFreeToMappedMemory(gContextState);
}

View File

@ -1,10 +1,12 @@
#include "retain_vars.hpp"
GX2SurfaceFormat gTVSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
GX2SurfaceFormat gDRCSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
GX2ContextState *gContextState = nullptr;
GX2ContextState *gOriginalContextState = nullptr;
OverlayFrame *gOverlayFrame = nullptr;
SchriftGX2 *gFontSystem = nullptr;
bool gOverlayInitDone = false;
bool gDrawReady = false;
GX2SurfaceFormat gTVSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
GX2SurfaceFormat gDRCSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
GX2ContextState *gContextState = nullptr;
GX2ContextState *gOriginalContextState = nullptr;
std::mutex gOverlayFrameMutex = {};
std::vector<std::shared_ptr<Notification>> gOverlayQueueDuringStartup = {};
OverlayFrame *gOverlayFrame = nullptr;
SchriftGX2 *gFontSystem = nullptr;
bool gOverlayInitDone = false;
bool gDrawReady = false;

View File

@ -7,6 +7,8 @@ extern GX2SurfaceFormat gTVSurfaceFormat;
extern GX2SurfaceFormat gDRCSurfaceFormat;
extern GX2ContextState *gContextState;
extern GX2ContextState *gOriginalContextState;
extern std::mutex gOverlayFrameMutex;
extern std::vector<std::shared_ptr<Notification>> gOverlayQueueDuringStartup;
extern OverlayFrame *gOverlayFrame;
extern SchriftGX2 *gFontSystem;
extern bool gOverlayInitDone;