From ff72b0e661332d51f2cca576d8f18d076bc1d5c3 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sat, 21 Jan 2023 21:33:00 +0100 Subject: [PATCH] First commit --- .clang-format | 67 +++ .github/workflows/pr.yml | 26 + .github/workflows/push_image.yml | 33 ++ .gitignore | 9 + Dockerfile | 9 + Dockerfile.buildlocal | 3 + LICENSE | 155 ++++++ Makefile | 158 ++++++ README.md | 28 ++ include/notifications/notification_defines.h | 50 ++ include/notifications/notifications.h | 390 +++++++++++++++ source/internal.h | 12 + source/logger.h | 23 + source/utils.cpp | 492 +++++++++++++++++++ 14 files changed, 1455 insertions(+) create mode 100644 .clang-format create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/push_image.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Dockerfile.buildlocal create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 include/notifications/notification_defines.h create mode 100644 include/notifications/notifications.h create mode 100644 source/internal.h create mode 100644 source/logger.h create mode 100644 source/utils.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56cc685 --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..061209a --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,26 @@ +name: CI-PR + +on: [pull_request] + +jobs: + clang-format: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include + build-lib: + runs-on: ubuntu-22.04 + needs: clang-format + steps: + - uses: actions/checkout@v3 + - name: build lib + run: | + docker build . -t tmp + docker build . -f Dockerfile.buildlocal -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: lib + path: "lib/*.a" \ No newline at end of file diff --git a/.github/workflows/push_image.yml b/.github/workflows/push_image.yml new file mode 100644 index 0000000..1cc416a --- /dev/null +++ b/.github/workflows/push_image.yml @@ -0,0 +1,33 @@ +name: Publish Docker Image +on: + push: + branches: + - main +jobs: + clang-format: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include + build: + runs-on: ubuntu-latest + needs: clang-format + steps: + - uses: actions/checkout@master + - name: Get release version + id: get_release_tag + run: | + echo RELEASE_VERSION=$(echo $(date '+%Y%m%d')) >> $GITHUB_ENV + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//" | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV + echo REPOSITORY_OWNER=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $1}' | sed 's/[^a-zA-Z0-9]//g' | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: ${{ env.REPOSITORY_OWNER }}/${{ env.REPOSITORY_NAME }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + snapshot: true + cache: true + tags: "latest, ${{ env.RELEASE_VERSION }}" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..163041f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/*.a +/build +*.bz2 +release/ +lib/ +CMakeLists.txt +.idea/ +cmake-build-debug/ +share/libfunctionpatcher.ld diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a58bd86 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM wiiuenv/devkitppc:20221228 + +WORKDIR tmp_build +COPY . . +RUN make clean && make && mkdir -p /artifacts/wums && cp -r lib /artifacts/wums && cp -r include /artifacts/wums +WORKDIR /artifacts + +FROM scratch +COPY --from=0 /artifacts /artifacts \ No newline at end of file diff --git a/Dockerfile.buildlocal b/Dockerfile.buildlocal new file mode 100644 index 0000000..4973d08 --- /dev/null +++ b/Dockerfile.buildlocal @@ -0,0 +1,3 @@ +FROM wiiuenv/devkitppc:20221228 + +WORKDIR project \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..57e7a53 --- /dev/null +++ b/LICENSE @@ -0,0 +1,155 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms +and conditions of version 3 of the GNU General Public License, supplemented +by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, “this License” refers to version 3 of the GNU Lesser General +Public License, and the “GNU GPL” refers to version 3 of the +GNU General Public License. + +“The Library” refers to a covered work governed by this License, other than +an Application or a Combined Work as defined below. + +An “Application” is any work that makes use of an interface provided by the +Library, but which is not otherwise based on the Library. Defining a subclass +of a class defined by the Library is deemed a mode of using an interface +provided by the Library. + +A “Combined Work” is a work produced by combining or linking an Application +with the Library. The particular version of the Library with which the +Combined Work was made is also called the “Linked Version”. + +The “Minimal Corresponding Source” for a Combined Work means the Corresponding +Source for the Combined Work, excluding any source code for portions of the +Combined Work that, considered in isolation, are based on the Application, +and not on the Linked Version. + +The “Corresponding Application Code” for a Combined Work means the object code +and/or source code for the Application, including any data and utility programs +needed for reproducing the Combined Work from the Application, but excluding +the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License without +being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a facility +refers to a function or data to be supplied by an Application that uses the +facility (other than as an argument passed when the facility is invoked), +then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the function or + data, the facility still operates, and performs whatever part of its + purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this + License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from a header +file that is part of the Library. You may convey such object code under terms +of your choice, provided that, if the incorporated material is not limited to +numerical parameters, data structure layouts and accessors, or small macros, +inline functions and templates (ten or fewer lines in length), +you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library + is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL + and this license document. + +4. Combined Works. + +You may convey a Combined Work under terms of your choice that, taken together, +effectively do not restrict modification of the portions of the Library +contained in the Combined Work and reverse engineering for debugging such +modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the + Library is used in it and that the Library and its use are covered + by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and + this license document. + + c) For a Combined Work that displays copyright notices during execution, + include the copyright notice for the Library among these notices, as well + as a reference directing the user to the copies of the GNU GPL + and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form suitable + for, and under terms that permit, the user to recombine or relink + the Application with a modified version of the Linked Version to + produce a modified Combined Work, in the manner specified by section 6 + of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time a + copy of the Library already present on the user's computer system, + and (b) will operate properly with a modified version of the Library + that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be + required to provide such information under section 6 of the GNU GPL, and + only to the extent that such information is necessary to install and + execute a modified version of the Combined Work produced by recombining + or relinking the Application with a modified version of the Linked Version. + (If you use option 4d0, the Installation Information must accompany the + Minimal Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in the + manner specified by section 6 of the GNU GPL for + conveying Corresponding Source.) + +5. Combined Libraries. + +You may place library facilities that are a work based on the Library side by +side in a single library together with other library facilities that are not +Applications and are not covered by this License, and convey such a combined +library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on + the Library, uncombined with any other library facilities, conveyed under + the terms of this License. + + b) Give prominent notice with the combined library that part of it is a + work based on the Library, and explaining where to find the accompanying + uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions of the +GNU Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you +received it specifies that a certain numbered version of the GNU Lesser +General Public License “or any later version” applies to it, you have the +option of following the terms and conditions either of that published version +or of any later version published by the Free Software Foundation. If the +Library as you received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser General +Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether +future versions of the GNU Lesser General Public License shall apply, that +proxy's public statement of acceptance of any version is permanent +authorization for you to choose that version for the Library. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..03ae464 --- /dev/null +++ b/Makefile @@ -0,0 +1,158 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wut/share/wut_rules + +export VER_MAJOR := 1 +export VER_MINOR := 0 +export VER_PATCH := 0 + +VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) + +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := source \ + include \ + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS := -Wall -Werror -save-temps \ + -ffunction-sections -fdata-sections \ + $(MACHDEP) \ + $(BUILD_CFLAGS) + +CFLAGS += $(INCLUDE) -D__WIIU__ + +CXXFLAGS := $(CFLAGS) -std=gnu++20 + +ASFLAGS := $(MACHDEP) + +LDFLAGS = $(ARCH) -Wl,--gc-sections + + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +.PHONY: all dist-bin dist-src dist install clean + +#--------------------------------------------------------------------------------- +all: lib/libnotifications.a + +dist-bin: all + @tar --exclude=*~ -cjf libnotifications-$(VERSION).tar.bz2 include lib + +dist-src: + @tar --exclude=*~ -cjf libnotifications-src-$(VERSION).tar.bz2 include source Makefile + +dist: dist-src dist-bin + +install: dist-bin + mkdir -p $(DESTDIR)$(DEVKITPRO)/wums + bzip2 -cd libnotifications-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(DEVKITPRO)/wums + +lib: + @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) + +release: + @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) + +lib/libnotifications.a :$(SOURCES) $(INCLUDES) | lib release + @$(shell [ ! -d lib ] && mkdir -p lib) + @$(shell [ ! -d release ] && mkdir -p release) + @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2 -s" \ + DEPSDIR=$(CURDIR)/release \ + --no-print-directory -C release \ + -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -rf release lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES_SRC) : $(HFILES) + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2dea32 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +[![Publish Docker Image](https://github.com/wiiu-env/libnotifications/actions/workflows/push_image.yml/badge.svg)](https://github.com/wiiu-env/libnotifications/actions/workflows/push_image.yml) + +# libnotifications +Requires the [NotificationModule](https://github.com/wiiu-env/NotificationModule) to be running via [WUMSLoader](https://github.com/wiiu-env/WUMSLoader). +Requires [wut](https://github.com/devkitPro/wut) for building. +Install via `make install`. + +## Usage +Make also sure to define +``` +WUMS_ROOT := $(DEVKITPRO)/wums +``` +and add `-lnotifications` to `LIBS` and `$(WUMS_ROOT)` to `LIBDIRS`. + +After that you can simply include `` to get access to the function patcher functions after calling `NotificationModule_InitLibrary()`. + +## Use this lib in Dockerfiles. +A prebuilt version of this lib can found on dockerhub. To use it for your projects, add this to your Dockerfile. +``` +[...] +COPY --from=wiiuenv/libnotifications:[tag] /artifacts $DEVKITPRO +[...] +``` +Replace [tag] with a tag you want to use, a list of tags can be found [here](https://hub.docker.com/r/wiiuenv/libnotifications/tags). +It's highly recommended to pin the version to the **latest date** instead of using `latest`. + +## Format the code via docker +`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include -i` diff --git a/include/notifications/notification_defines.h b/include/notifications/notification_defines.h new file mode 100644 index 0000000..916d464 --- /dev/null +++ b/include/notifications/notification_defines.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +typedef enum NotificationModuleStatus { + NOTIFICATION_MODULE_RESULT_SUCCESS = 0, + NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND = -0x1, + NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT = -0x2, + NOTIFICATION_MODULE_RESULT_UNSUPPORTED_VERSION = -0x3, + NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT = -0x4, + NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED = -0x5, + NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND = -0x06, + NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY = -0x10, + NOTIFICATION_MODULE_RESULT_UNSUPPORTED_TYPE = -0x11, + NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED = -0x12, + NOTIFICATION_MODULE_RESULT_INVALID_HANDLE = -0x13, + NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR = -0x1000, +} NotificationModuleStatus; + +typedef uint32_t NotificationModuleAPIVersion; +typedef uint32_t NotificationModuleHandle; + + +typedef void (*NotificationModuleNotificationFinishedCallback)(NotificationModuleHandle, void *); + +#define NOTIFICATION_MODULE_API_VERSION_ERROR 0xFFFFFFFF + +typedef struct _NMColor { + uint8_t r, g, b, a; +} NMColor; + +typedef enum NotificationModuleNotificationType { + NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO = 0, /* Static notification, fades out after fixed time. Can not be updated after creation. */ + NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR = 1, /* Static notification, fades out after fixed time, shakes for a fixed time. Can not be updated after creation. */ + NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC = 2, /* Dynamic notification, only fades out when told to fade out. Can be updated after creation. */ +} NotificationModuleNotificationType; + +typedef enum NotificationModuleStatusFinish { + NOTIFICATION_MODULE_STATUS_FINISH = 0, /* Fades out the Notification after `durationBeforeFadeOutInSeconds` seconds */ + NOTIFICATION_MODULE_STATUS_FINISH_WITH_SHAKE = 1, /* Fades out the Notification after `durationBeforeFadeOutInSeconds` seconds, shakes it for `shakeDurationInSeconds` seconds */ +} NotificationModuleStatusFinish; + +typedef enum NotificationModuleNotificationOption { + NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR, /* Background Color of the Notification. Type: NMColor */ + NOTIFICATION_MODULE_DEFAULT_OPTION_TEXT_COLOR, /* Text Color of the Notification. Type: NMColor */ + NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, /* Time in seconds before the Notification will fade out: Type: float. Example: 2.5f = 2.5 seconds*/ + NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION, /* Function that will be called when the Notification starts to fade out. Type: NotificationModuleNotificationFinishedCallback*/ + NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT, /* Context that will be passed to the NOTIFICATION_MODULE_DEFAULT_TYPE_FINISH_FUNCTION callback. Type: void* */ +} NotificationModuleNotificationOption; diff --git a/include/notifications/notifications.h b/include/notifications/notifications.h new file mode 100644 index 0000000..c4fa23d --- /dev/null +++ b/include/notifications/notifications.h @@ -0,0 +1,390 @@ +#pragma once + +#include "notification_defines.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Returns a NotificationModuleStatus as a string + * @param status + * @return String representation of a given status +**/ +const char *NotificationModule_GetStatusStr(NotificationModuleStatus status); + +/** + * This function has to be called before any other function of this lib (except NotificationModule_GetVersion) can be used. + * + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The library has been initialized successfully. Other functions can now be used.
+ * NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.
+ * NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_API_VERSION: The version of the loaded module is not compatible with this version of the lib. +**/ +NotificationModuleStatus NotificationModule_InitLibrary(); + +/** + * Deinitializes the NotificationModule lib + * @return NOTIFICATION_MODULE_RESULT_SUCCESS or NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR + */ +NotificationModuleStatus NotificationModule_DeInitLibrary(); + +/** + * Retrieves the API Version of the loaded NotificationModule.
+ *
+ * Requires NotificationModule API version 1 or higher + * @param outVersion pointer to the variable where the version will be stored. + * + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The API version has been store in the version ptr.
+ * NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.
+ * NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: Invalid version pointer.
+ * NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR: Retrieving the module version failed. +**/ +NotificationModuleStatus NotificationModule_GetVersion(NotificationModuleAPIVersion *outVersion); + +/** + * Checks if the Overlay for Notification is ready.
+ * Notifications can only be added if the overlay is ready.
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param outIsReady pointer to the variable where the result will be stored. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The result has been stored in the outIsReady pointer.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: Invalid version pointer.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ */ +NotificationModuleStatus NotificationModule_IsOverlayReady(bool *outIsReady); + +/** + * Can be used to override the default settings for a certain Notification Type.
+ * See the NotificationModuleNotificationType and NotificationModuleNotificationOption enums for more information.
+ *
+ * These default values will be use when calling
+ * - NotificationModule_AddInfoNotification/NotificationModule_AddInfoNotificationWithCallback (type = NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO)
+ * - NotificationModule_AddErrorNotification/NotificationModule_AddErrorNotificationWithCallback (type = NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR)
+ * - NotificationModule_AddDynamicNotification/NotificationModule_AddDynamicNotificationWithCallback (type = NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC)
+ *
+ * The "WithCallback" function will NOT take the default callback + context.
+ * + * @param type Type of Notification for which the default value will be set. + * @param optionType Defines which option will be set + * @param ... Expected to be a single value. Depends on the option type. See NotificationModuleNotificationOption enum for more information. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: The given notification or option type was invalid
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized
+ */ +NotificationModuleStatus NotificationModule_SetDefaultValue(NotificationModuleNotificationType type, + NotificationModuleNotificationOption optionType, + ...); + +/** + * Displays a Notification that fade outs after a given time.
+ * Notification will appear in the top left corner. It's possible to display multiple notifications at the same time.
+ * The newest notification will always be at the top, the oldest at the bottom. Notifications will fade out to the left side.
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the notification. + * @param durationBeforeFadeOutInSeconds Time in seconds before fading out + * @param textColor Text color of the Notification + * @param backgroundColor Background color of the Notification + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddInfoNotificationEx(const char *text, + float durationBeforeFadeOutInSeconds, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Displays a Notification that fade outs after a given time.
+ * Similar to NotificationModule_AddInfoNotificationEx, but uses default values for all parameters except "text".
+ * The default values are NotificationModule_AddInfoNotificationEx(text, 2.0f, {255, 255, 255, 255}, {100, 100, 100, 255}, nullptr, nullptr); but can be
+ * overridden via NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, xxx, yyy);
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the Notification + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddInfoNotification(const char *text); + +/** + * Similar to NotificationModule_AddInfoNotification, but uses default values for all parameters except "text", callback, callbackContext
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the Notification + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddInfoNotificationWithCallback(const char *text, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Displays a (error) Notification that shakes and fade outs after a given time.
+ * Notification will appear in the top left corner. It's possible to display multiple notifications at the same time.
+ * The newest notification will always be at the top, the oldest at the bottom. Notifications will fade out to the left side.
+ * The Notification will at first shake for "shakeDurationInSeconds" seconds, then fades out after "durationBeforeFadeOutInSeconds"
+ * have been passed (since displaying the Notification, this include the shake duration) + *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the notification. + * @param durationBeforeFadeOutInSeconds Time in seconds before fading out + * @param shakeDurationInSeconds Time in seconds the notification will "shake" + * @param textColor Text color of the Notification + * @param backgroundColor Background color of the Notification + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddErrorNotificationEx(const char *text, + float durationBeforeFadeOutInSeconds, + float shakeDurationInSeconds, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Displays a (error) Notification (default background color: red) that shakes and fade outs after a given time.
+ * Similar to NotificationModule_AddErrorNotificationEx, but uses default values for all parameters except "text".
+ * The default values are NotificationModule_AddErrorNotificationEx(text, 2.0f, 0.5f, {255, 255, 255, 255}, {237, 28, 36, 255}, nullptr, nullptr); but can be
+ * overridden via NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, xxx, yyy);
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the notification. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddErrorNotification(const char *text); + +/** + * Similar to NotificationModule_AddErrorNotification, but uses default values for all parameters except "text", callback, callbackContext
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param text Content of the notification. + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The notification will show up.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text was NULL.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddErrorNotificationWithCallback(const char *text, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Displays a Notification that can be updated and stays on the screen until `NotificationModule_FinishDynamicNotification*` has been called.
+ *
+ * This functions give you a NotificationHandle which is needed to finish or update this notification.
+ *
+ * Use the `NotificationModule_UpdateDynamicNotificationText*` functions to update the notification after creating it.
+ *
+ * The Notification will be deleted automatically when the running application changes. + * @param text Content of the notification + * @param outHandle Pointer where the resulting + * @param textColor Text color of the notification + * @param backgroundColor Background color of the notification + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text or outHandle was NULL
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_AddDynamicNotificationEx(const char *text, + NotificationModuleHandle *outHandle, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Displays a Notification that can be updated and stays on the screen until `NotificationModule_FinishDynamicNotification*` has been called.
+ * Similar to NotificationModule_AddDynamicNotificationEx, but uses default values for all parameters except "text".
+ * The default values are NotificationModule_AddDynamicNotificationEx(text, outHandle, {255, 255, 255, 255}, {100, 100, 100, 255}, nullptr, nullptr); but can be
+ * overridden via NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC, xxx, yyy);
+ *
+ * Requires NotificationModule API version 1 or higher
+ * @param text Content of the notification + * @param outHandle Pointer where the resulting + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text or outHandle was NULL
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+*/ +NotificationModuleStatus NotificationModule_AddDynamicNotification(const char *text, + NotificationModuleHandle *outHandle); + +/** + * Displays a Notification that can be updated and stays on the screen until `NotificationModule_FinishDynamicNotification*` has been called.
+ * Similar to NotificationModule_AddDynamicNotificationEx, but uses default values for all parameters except "text", callback and callbackContext.
+ * The default values are NotificationModule_AddDynamicNotificationEx(text, outHandle, {255, 255, 255, 255}, {100, 100, 100, 255}, callback, callbackContext); but can be
+ * overridden via NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC, xxx, yyy);
+ *
+ * Requires NotificationModule API version 1 or higher
+ * + * @param text Content of the notification + * @param outHandle Pointer where the resulting + * @param callback Function that will be called then the Notification fades out. + * @param callbackContext Context that will be passed to the callback. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The default value has been set.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: text or outHandle was NULL
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: The overlay is not ready. See NotificationModule_IsOverlayReady.
+ * NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: Allocation of the Notification has failed.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+*/ +NotificationModuleStatus NotificationModule_AddDynamicNotificationWithCallback(const char *text, + NotificationModuleHandle *outHandle, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext); + +/** + * Updates the text of a dynamic notification. + *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param handle Handle of the notification + * @param textColor New background color + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The text has been updated.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: handle was NULL.
+ * NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: handle was not found.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationText(NotificationModuleHandle handle, + const char *text); + +/** + * Updates the background color of a dynamic notification. + *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param handle Handle of the notification + * @param textColor New background color + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The background color has been updated.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: handle was NULL.
+ * NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: handle was not found.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationBackgroundColor(NotificationModuleHandle handle, + NMColor backgroundColor); + +/** + * Updates the text color of a dynamic notification.
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param handle Handle of the notification + * @param textColor New text color + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: The text color has been updated.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: handle was NULL.
+ * NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: handle was not found.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationTextColor(NotificationModuleHandle handle, + NMColor textColor); + +/** + * Fades out a existing dynamic notification `durationBeforeFadeOutInSeconds` seconds after calling this funcion.
+ * Calls the callback of the notification before fading out.
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param handle handle of the notification to fade out. + * @param durationBeforeFadeOutInSeconds duration before fading out in seconds. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: Finish the given notification.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: handle was NULL.
+ * NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: handle was not found.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_FinishDynamicNotification(NotificationModuleHandle handle, + float durationBeforeFadeOutInSeconds); + +/** + * Shakes an exiting notification for `shakeDuration` seconds and fades it out `durationBeforeFadeOutInSeconds` seconds after calling this function.
+ * Calls the callback of the notification before fading out.
+ *
+ * Requires NotificationModule API version 1 or higher
+ *
+ * @param handle handle of the notification to fade out. + * @param durationBeforeFadeOutInSeconds duration before fading out in seconds. + * @param shakeDuration shake duration in seconds. + * @return NOTIFICATION_MODULE_RESULT_SUCCESS: Finish the given notification with a shake.
+ * NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: The loaded module version doesn't not support this function.
+ * NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: handle was NULL.
+ * NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: handle was not found.
+ * NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: The library is not initialized.
+ */ +NotificationModuleStatus NotificationModule_FinishDynamicNotificationWithShake(NotificationModuleHandle handle, + float durationBeforeFadeOutInSeconds, + float shakeDuration); + +// Copy pasted from libcurl... +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ + +#define NotificationModule_SetDefaultValue(type, valueType, param) NotificationModule_SetDefaultValue(type, valueType, param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + + +#ifdef __cplusplus +} +#endif diff --git a/source/internal.h b/source/internal.h new file mode 100644 index 0000000..c9ff274 --- /dev/null +++ b/source/internal.h @@ -0,0 +1,12 @@ +#pragma once + +#include "notifications/notification_defines.h" + +struct NMDefaultValueStore { + float durationBeforeFadeOutInSeconds = 2.0f; + float shakeDurationOnErrorInSeconds = 0.5f; + NMColor backgroundColor = {100, 100, 100, 255}; + NMColor textColor = {255, 255, 255, 255}; + void (*finishFunc)(NotificationModuleHandle, void *context) = nullptr; + void *finishFuncContext = nullptr; +}; \ No newline at end of file diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..a784135 --- /dev/null +++ b/source/logger.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +#define __FILENAME__ ({ \ + const char *__filename = __FILE__; \ + const char *__pos = strrchr(__filename, '/'); \ + if (!__pos) __pos = strrchr(__filename, '\\'); \ + __pos ? __pos + 1 : __filename; \ +}) + +#define LOG_APP_TYPE "L" +#define LOG_APP_NAME "libfunctionpatcher" + +#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \ + do { \ + LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \ + } while (0) + +#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARNING## ", "\n", FMT, ##ARGS) diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..e0618f0 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,492 @@ +#include "internal.h" +#include "logger.h" +#include +#include +#include +#include +#include + +static OSDynLoad_Module sModuleHandle = nullptr; + +static NotificationModuleStatus (*sNMGetVersion)(NotificationModuleAPIVersion *) = nullptr; +static NotificationModuleStatus (*sNMIsOverlayReady)(bool *) = nullptr; +static NotificationModuleStatus (*sNMAddStaticNotification)(const char *, + NotificationModuleNotificationType, + float, + float, + NMColor, + NMColor, + void (*)(void *context), + void *) = nullptr; + +static NotificationModuleStatus (*sNMAddDynamicNotification)(const char *, + NMColor, + NMColor, + void (*)(void *context), + void *) = nullptr; + +static NotificationModuleStatus (*sNMUpdateDynamicNotificationText)(NotificationModuleHandle, + const char *) = nullptr; + +static NotificationModuleStatus (*sNMUpdateDynamicNotificationBackgroundColor)(NotificationModuleHandle, + NMColor) = nullptr; + +static NotificationModuleStatus (*sNMUpdateDynamicNotificationTextColor)(NotificationModuleHandle, + NMColor) = nullptr; + +static NotificationModuleStatus (*sNMFinishDynamicNotification)(NotificationModuleHandle handle, + NotificationModuleStatusFinish finishMode, + float durationBeforeFadeOutInSeconds, + float shakeDurationInSeconds) = nullptr; + +static bool sLibInitDone = false; +std::map sDefaultValues; + +static NotificationModuleAPIVersion sNotificationModuleVersion = NOTIFICATION_MODULE_API_VERSION_ERROR; + +const char *NotificationModule_GetStatusStr(NotificationModuleStatus status) { + switch (status) { + case NOTIFICATION_MODULE_RESULT_SUCCESS: + return "NOTIFICATION_MODULE_RESULT_SUCCESS"; + case NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND: + return "NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND"; + case NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT: + return "NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT"; + case NOTIFICATION_MODULE_RESULT_UNSUPPORTED_VERSION: + return "NOTIFICATION_MODULE_RESULT_UNSUPPORTED_VERSION"; + case NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR: + return "NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR"; + case NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT: + return "NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT"; + case NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED: + return "NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED"; + case NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND: + return "NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND"; + case NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY: + return "NOTIFICATION_MODULE_RESULT_OVERLAY_NOT_READY"; + case NOTIFICATION_MODULE_RESULT_UNSUPPORTED_TYPE: + return "NOTIFICATION_MODULE_RESULT_UNSUPPORTED_TYPE"; + case NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED: + return "NOTIFICATION_MODULE_RESULT_ALLOCATION_FAILED"; + case NOTIFICATION_MODULE_RESULT_INVALID_HANDLE: + return "NOTIFICATION_MODULE_RESULT_INVALID_HANDLE"; + } + return "NOTIFICATION_MODULE_RESULT_UNKNOWN_ERROR"; +} + +NotificationModuleStatus NotificationModule_InitLibrary() { + if (sLibInitDone) { + return NOTIFICATION_MODULE_RESULT_SUCCESS; + } + if (OSDynLoad_Acquire("homebrew_notifications", &sModuleHandle) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("OSDynLoad_Acquire failed."); + return NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMGetVersion", (void **) &sNMGetVersion) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMGetVersion failed."); + return NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT; + } + + auto res = NotificationModule_GetVersion(&sNotificationModuleVersion); + if (res != NOTIFICATION_MODULE_RESULT_SUCCESS) { + sNotificationModuleVersion = NOTIFICATION_MODULE_API_VERSION_ERROR; + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_VERSION; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMIsOverlayReady", (void **) &sNMIsOverlayReady) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMIsOverlayReady failed."); + sNMIsOverlayReady = nullptr; + } + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMAddStaticNotification", (void **) &sNMAddStaticNotification) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMAddStaticNotification failed."); + sNMAddStaticNotification = nullptr; + } + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMAddDynamicNotification", (void **) &sNMAddDynamicNotification) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMAddDynamicNotification failed."); + sNMAddStaticNotification = nullptr; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMUpdateDynamicNotificationText", (void **) &sNMUpdateDynamicNotificationText) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMUpdateDynamicNotificationText failed."); + sNMAddStaticNotification = nullptr; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMUpdateDynamicNotificationBackgroundColor", (void **) &sNMUpdateDynamicNotificationBackgroundColor) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMUpdateDynamicNotificationBackgroundColor failed."); + sNMAddStaticNotification = nullptr; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMUpdateDynamicNotificationTextColor", (void **) &sNMUpdateDynamicNotificationTextColor) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMUpdateDynamicNotificationTextColor failed."); + sNMAddStaticNotification = nullptr; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMFinishDynamicNotification", (void **) &sNMFinishDynamicNotification) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("FindExport NMFinishDynamicNotification failed."); + sNMAddStaticNotification = nullptr; + } + + sDefaultValues.clear(); + + sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO]; + sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR].backgroundColor = {237, 28, 36, 255}; + sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC]; + + sLibInitDone = true; + return NOTIFICATION_MODULE_RESULT_SUCCESS; +} + +NotificationModuleStatus NotificationModule_DeInitLibrary() { + if (sLibInitDone) { + sNMGetVersion = nullptr; + sNotificationModuleVersion = NOTIFICATION_MODULE_API_VERSION_ERROR; + OSDynLoad_Release(sModuleHandle); + sModuleHandle = nullptr; + sDefaultValues.clear(); + sLibInitDone = false; + } + return NOTIFICATION_MODULE_RESULT_SUCCESS; +} + +NotificationModuleStatus NotificationModule_GetVersion(NotificationModuleAPIVersion *outVersion) { + if (sNMGetVersion == nullptr) { + if (OSDynLoad_Acquire("homebrew_notifications", &sModuleHandle) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed."); + return NOTIFICATION_MODULE_RESULT_MODULE_NOT_FOUND; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "NMGetVersion", (void **) &sNMGetVersion) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_WARN("FindExport NMGetVersion failed."); + return NOTIFICATION_MODULE_RESULT_MODULE_MISSING_EXPORT; + } + } + + return reinterpret_cast(sNMGetVersion)(outVersion); +} + +NotificationModuleStatus NotificationModule_IsOverlayReady(bool *outIsReady) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMIsOverlayReady == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (outIsReady == nullptr) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMIsOverlayReady)(outIsReady); +} + +NotificationModuleStatus NotificationModule_AddDynamicNotificationExDeclare(const char *text, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext, + NotificationModuleHandle *outHandle); + +NotificationModuleStatus NotificationModule_AddDynamicNotificationEx(const char *text, + NotificationModuleHandle *outHandle, + NMColor textColor, + NMColor backgroundColor, + void (*finishFunc)(NotificationModuleHandle, void *context), + void *context) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMAddDynamicNotification == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (text == nullptr || outHandle == nullptr) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMAddDynamicNotification)(text, + textColor, + backgroundColor, + finishFunc, + context, + outHandle); +} + +NotificationModuleStatus NotificationModule_AddDynamicNotification(const char *text, NotificationModuleHandle *outHandle) { + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC]; + return NotificationModule_AddDynamicNotificationEx(text, + outHandle, + cur.textColor, + cur.backgroundColor, + cur.finishFunc, + cur.finishFuncContext); +} + +NotificationModuleStatus NotificationModule_AddDynamicNotificationWithCallback(const char *text, + NotificationModuleHandle *outHandle, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_DYNAMIC]; + return NotificationModule_AddDynamicNotificationEx(text, + outHandle, + cur.textColor, + cur.backgroundColor, + callback, + callbackContext); +} + +static NotificationModuleStatus NotificationModule_AddStaticNotification(const char *text, + NotificationModuleNotificationType type, + float durationBeforeFadeOutInSeconds, + float shakeDurationInSeconds, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMAddStaticNotification == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (text == nullptr) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMAddStaticNotification)(text, + type, + durationBeforeFadeOutInSeconds, + shakeDurationInSeconds, + textColor, + backgroundColor, + callback, + callbackContext); +} + +#undef NotificationModule_SetDefaultValue +NotificationModuleStatus NotificationModule_SetDefaultValue(NotificationModuleNotificationType type, + NotificationModuleNotificationOption valueType, + ...) { + if (sModuleHandle == nullptr) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + va_list va; + + if (!sDefaultValues.contains(type) && type == NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR) { + sDefaultValues[type].backgroundColor = {237, 28, 36, 255}; + } + + auto &cur = sDefaultValues[type]; + va_start(va, valueType); + auto res = NOTIFICATION_MODULE_RESULT_SUCCESS; + switch (valueType) { + case NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR: { + auto arg = va_arg(va, NMColor); + cur.backgroundColor = arg; + break; + } + case NOTIFICATION_MODULE_DEFAULT_OPTION_TEXT_COLOR: { + auto arg = va_arg(va, NMColor); + cur.textColor = arg; + break; + } + case NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT: { + auto arg = va_arg(va, double); + cur.durationBeforeFadeOutInSeconds = (float) arg; + break; + } + case NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION: { + auto arg = va_arg(va, void (*)(NotificationModuleHandle, void *)); + cur.finishFunc = arg; + break; + } + case NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT: { + auto arg = va_arg(va, void *); + cur.finishFuncContext = arg; + break; + } + default: + res = NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + break; + } + + va_end(va); + + return res; +} + +NotificationModuleStatus NotificationModule_AddInfoNotificationEx(const char *text, + float durationBeforeFadeOutInSeconds, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + return NotificationModule_AddStaticNotification(text, + NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, + durationBeforeFadeOutInSeconds, + 0.0f, + textColor, + backgroundColor, + callback, + callbackContext); +} + +NotificationModuleStatus NotificationModule_AddInfoNotification(const char *text) { + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO]; + return NotificationModule_AddInfoNotificationEx(text, + cur.durationBeforeFadeOutInSeconds, + cur.textColor, + cur.backgroundColor, + cur.finishFunc, + cur.finishFuncContext); +} +NotificationModuleStatus NotificationModule_AddInfoNotificationWithCallback(const char *text, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO]; + return NotificationModule_AddInfoNotificationEx(text, + cur.durationBeforeFadeOutInSeconds, + cur.textColor, + cur.backgroundColor, + callback, + callbackContext); +} + +NotificationModuleStatus NotificationModule_AddErrorNotificationEx(const char *text, + float durationBeforeFadeOutInSeconds, + float shakeDurationInSeconds, + NMColor textColor, + NMColor backgroundColor, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + return NotificationModule_AddStaticNotification(text, + NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, + durationBeforeFadeOutInSeconds, + shakeDurationInSeconds, + textColor, + backgroundColor, + callback, + callbackContext); +} + +NotificationModuleStatus NotificationModule_AddErrorNotification(const char *text) { + if (sModuleHandle == nullptr) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR]; + return NotificationModule_AddErrorNotificationEx(text, + cur.durationBeforeFadeOutInSeconds, + cur.shakeDurationOnErrorInSeconds, + cur.textColor, + cur.backgroundColor, + cur.finishFunc, + cur.finishFuncContext); +} + +NotificationModuleStatus NotificationModule_AddErrorNotificationWithCallback(const char *text, + NotificationModuleNotificationFinishedCallback callback, + void *callbackContext) { + if (sModuleHandle == nullptr) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + + auto &cur = sDefaultValues[NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR]; + return NotificationModule_AddErrorNotificationEx(text, + cur.durationBeforeFadeOutInSeconds, + cur.shakeDurationOnErrorInSeconds, + cur.textColor, + cur.backgroundColor, + callback, + callbackContext); +} + +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationText(NotificationModuleHandle handle, + const char *text) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMUpdateDynamicNotificationText == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (handle == 0 || text == nullptr) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMUpdateDynamicNotificationText)(handle, + text); +} + +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationBackgroundColor(NotificationModuleHandle handle, + NMColor backgroundColor) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMUpdateDynamicNotificationBackgroundColor == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (handle == 0) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMUpdateDynamicNotificationBackgroundColor)(handle, + backgroundColor); +} + +NotificationModuleStatus NotificationModule_UpdateDynamicNotificationTextColor(NotificationModuleHandle handle, + NMColor textColor) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMUpdateDynamicNotificationTextColor == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (handle == 0) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMUpdateDynamicNotificationTextColor)(handle, + textColor); +} + +static NotificationModuleStatus NotificationModule_FinishDynamicNotificationEx(NotificationModuleHandle handle, + NotificationModuleStatusFinish finishMode, + float durationBeforeFadeOutInSeconds, + float shakeDurationInSeconds) { + if (sNotificationModuleVersion == NOTIFICATION_MODULE_API_VERSION_ERROR) { + return NOTIFICATION_MODULE_RESULT_LIB_UNINITIALIZED; + } + if (sNMFinishDynamicNotification == nullptr || sNotificationModuleVersion < 1) { + return NOTIFICATION_MODULE_RESULT_UNSUPPORTED_COMMAND; + } + + if (handle == 0) { + return NOTIFICATION_MODULE_RESULT_INVALID_ARGUMENT; + } + + return reinterpret_cast(sNMFinishDynamicNotification)(handle, + finishMode, + durationBeforeFadeOutInSeconds, + shakeDurationInSeconds); +} + +NotificationModuleStatus NotificationModule_FinishDynamicNotification(NotificationModuleHandle handle, + float durationBeforeFadeOutInSeconds) { + return NotificationModule_FinishDynamicNotificationEx(handle, + NOTIFICATION_MODULE_STATUS_FINISH, + durationBeforeFadeOutInSeconds, + 0.0f); +} + +NotificationModuleStatus NotificationModule_FinishDynamicNotificationWithShake(NotificationModuleHandle handle, + float durationBeforeFadeOutInSeconds, + float shakeDuration) { + return NotificationModule_FinishDynamicNotificationEx(handle, + NOTIFICATION_MODULE_STATUS_FINISH_WITH_SHAKE, + durationBeforeFadeOutInSeconds, + shakeDuration); +} \ No newline at end of file