diff --git a/nx/include/switch/services/applet.h b/nx/include/switch/services/applet.h index 7daad349..0ff1b3b5 100644 --- a/nx/include/switch/services/applet.h +++ b/nx/include/switch/services/applet.h @@ -106,6 +106,13 @@ typedef enum { AppletThemeColorType_Unknown3 = 3, } AppletThemeColorType; +/// Permission values for \ref appletSetScreenShotPermission. +typedef enum { + AppletScreenShotPermission_Inherit = 0, ///< Inherit from parent applet. + AppletScreenShotPermission_Enable = 1, ///< Enable. + AppletScreenShotPermission_Disable = 2, ///< Disable. +} AppletScreenShotPermission; + /// applet hook function. typedef void (*AppletHookFn)(AppletHookType hook, void* param); @@ -138,10 +145,19 @@ typedef struct { /// Attributes for launching applications for Quest. typedef struct { - u32 unk_x0; - u32 unk_x4; + u32 unk_x0; ///< See AppletApplicationAttribute::unk_x0. + u32 unk_x4; ///< See AppletApplicationAttribute::unk_x4. + float volume; ///< [7.0.0+] See AppletApplicationAttribute::volume. } AppletApplicationAttributeForQuest; +/// ApplicationAttribute +typedef struct { + u32 unk_x0; ///< Default is 0 for non-Quest. Only used when non-zero: unknown value in seconds. + u32 unk_x4; ///< Default is 0 for non-Quest. Only used when non-zero: unknown value in seconds. + float volume; ///< Audio volume. Must be in the range of 0.0f-1.0f. The default is 1.0f. + u8 unused[0x14]; ///< Unused. Default is 0. +} AppletApplicationAttribute; + /// Initialize applet, called automatically during app startup. Result appletInitialize(void); @@ -180,6 +196,7 @@ Result appletRequestLaunchApplication(u64 titleID, AppletStorage* s); /** * @brief Requests to launch the specified application, for kiosk systems. * @note Only available with AppletType_*Application on 3.0.0+. + * @note Identical to \ref appletRequestLaunchApplication, except this allows the user to specify the attribute fields instead of the defaults being used. * @param[in] titleID Application titleID * @param s Optional AppletStorage object, can be NULL. This is automatically closed. When NULL on pre-4.0.0, this will internally create a tmp storage with size 0 for use with the cmd. This is the storage available to the launched application via \ref appletPopLaunchParameter with ::AppletLaunchParameterKind_Application. * @param[in] attr Kiosk application attributes. @@ -207,7 +224,7 @@ Result appletSetGamePlayRecordingState(bool state); /// Initializes video recording. This allocates a 0x6000000-byte buffer for the TransferMemory, cleanup is handled automatically during app exit in \ref appletExit. /// Only available with AppletType_Application on 3.0.0+, hence errors from this can be ignored. /// Video recording is only fully available system-side with 4.0.0+. -/// Only usable when running under a title which supports video recording. +/// Only usable when running under a title which supports video recording. Using this is only needed when the host title control.nacp has VideoCaptureMode set to Enabled, with Automatic appletInitializeGamePlayRecording is not needed. Result appletInitializeGamePlayRecording(void); /** @@ -241,6 +258,15 @@ Result appletQueryApplicationPlayStatistics(PdmApplicationPlayStatistics *stats, */ Result appletQueryApplicationPlayStatisticsByUid(u128 userID, PdmApplicationPlayStatistics *stats, const u64 *titleIDs, s32 count, s32 *total_out); +/** + * @brief Gets an Event which is signaled for GpuErrorDetected. + * @note Only available with AppletType_*Application on [8.0.0+]. + * @note The Event must be closed by the user once finished with it. + * @note Official sw waits on this Event from a seperate thread, triggering an abort when it's signaled. + * @param[out] event_out Output Event with autoclear=false. + */ +Result appletGetGpuErrorDetectedSystemEvent(Event *out_event); + /** * @brief Delay exiting until \ref appletUnlockExit is called, with a 15 second timeout once exit is requested. * @note When exit is requested \ref appletMainLoop will return false, hence any main-loop using appletMainLoop will exit. This allows the app to handle cleanup post-main-loop instead of being force-terminated. @@ -254,9 +280,9 @@ Result appletUnlockExit(void); /** * @brief Controls whether screenshot-capture is allowed. - * @param val 0 = disable, 1 = enable. + * @param permission \ref AppletScreenShotPermission */ -Result appletSetScreenShotPermission(s32 val); +Result appletSetScreenShotPermission(AppletScreenShotPermission permission); Result appletSetScreenShotImageOrientation(s32 val); diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index d270f4c7..4534cef4 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -1084,11 +1084,11 @@ static Result _appletCreateApplicationAndPushAndRequestToStartForQuest(u64 title return rc; } -static Result _appletCreateApplicationAndRequestToStart(u64 titleID) { //4.0.0+ +static Result _appletCreateApplicationAndRequestToStart(u64 titleID) { // [4.0.0+] return _appletCmdInU64(&g_appletIFunctions, titleID, 12); } -static Result _appletCreateApplicationAndRequestToStartForQuest(u64 titleID, const AppletApplicationAttributeForQuest *attr) { //4.0.0+ +static Result _appletCreateApplicationAndRequestToStartForQuest(u64 titleID, const AppletApplicationAttributeForQuest *attr) { // [4.0.0+] IpcCommand c; ipcInitialize(&c); @@ -1125,6 +1125,80 @@ static Result _appletCreateApplicationAndRequestToStartForQuest(u64 titleID, con return rc; } +static Result _appletCreateApplicationWithAttributeAndPushAndRequestToStartForQuest(u64 titleID, AppletStorage* s, const AppletApplicationAttribute *attr) { // [7.0.0+] + IpcCommand c; + ipcInitialize(&c); + + serviceSendObject(&s->s, &c); + + ipcAddSendBuffer(&c, attr, sizeof(*attr), BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u64 titleID; + } *raw; + + raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 14; + raw->titleID = titleID; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _appletCreateApplicationWithAttributeAndRequestToStartForQuest(u64 titleID, const AppletApplicationAttribute *attr) { // [7.0.0+] + IpcCommand c; + ipcInitialize(&c); + + ipcAddSendBuffer(&c, attr, sizeof(*attr), BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u64 titleID; + } *raw; + + raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 15; + raw->titleID = titleID; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + Result appletRequestLaunchApplication(u64 titleID, AppletStorage* s) { AppletStorage tmpstorage={0}; Result rc=0; @@ -1160,6 +1234,7 @@ Result appletRequestLaunchApplication(u64 titleID, AppletStorage* s) { Result appletRequestLaunchApplicationForQuest(u64 titleID, AppletStorage* s, const AppletApplicationAttributeForQuest *attr) { AppletStorage tmpstorage={0}; + AppletApplicationAttribute appattr={0}; Result rc=0; if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) @@ -1173,12 +1248,26 @@ Result appletRequestLaunchApplicationForQuest(u64 titleID, AppletStorage* s, con if (R_FAILED(rc)) return rc; } + if (hosversionAtLeast(7,0,0)) { + appattr.unk_x0 = attr->unk_x0; + appattr.unk_x4 = attr->unk_x4; + appattr.volume = attr->volume; + } + if (hosversionAtLeast(4,0,0) && s==NULL) { - rc = _appletCreateApplicationAndRequestToStartForQuest(titleID, attr); + if (hosversionAtLeast(7,0,0)) + rc = _appletCreateApplicationWithAttributeAndRequestToStartForQuest(titleID, &appattr); + else + rc = _appletCreateApplicationAndRequestToStartForQuest(titleID, attr); } else { if (hosversionBefore(3,0,0)) rc = MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); - if (R_SUCCEEDED(rc)) rc = _appletCreateApplicationAndPushAndRequestToStartForQuest(titleID, s, attr); + if (R_SUCCEEDED(rc)) { + if (hosversionAtLeast(7,0,0)) + rc = _appletCreateApplicationWithAttributeAndPushAndRequestToStartForQuest(titleID, s, &appattr); + else + rc = _appletCreateApplicationAndPushAndRequestToStartForQuest(titleID, s, attr); + } } if (s) appletStorageClose(s); @@ -1450,7 +1539,7 @@ Result appletQueryApplicationPlayStatistics(PdmApplicationPlayStatistics *stats, IpcCommand c; ipcInitialize(&c); - if (!serviceIsActive(&g_appletSrv) || !_appletIsRegularApplication()) + if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(5,0,0)) @@ -1494,7 +1583,7 @@ Result appletQueryApplicationPlayStatisticsByUid(u128 userID, PdmApplicationPlay IpcCommand c; ipcInitialize(&c); - if (!serviceIsActive(&g_appletSrv) || !_appletIsRegularApplication()) + if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(6,0,0)) @@ -1536,6 +1625,16 @@ Result appletQueryApplicationPlayStatisticsByUid(u128 userID, PdmApplicationPlay return rc; } +Result appletGetGpuErrorDetectedSystemEvent(Event *out_event) { + if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (hosversionBefore(8,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _appletGetEvent(&g_appletIFunctions, out_event, 130, false); +} + // IOverlayFunctions Result appletBeginToWatchShortHomeButtonMessage(void) { @@ -1775,21 +1874,21 @@ static Result _appletWaitLibraryAppletLaunchableEvent(void) { return rc; } -Result appletSetScreenShotPermission(s32 val) { +Result appletSetScreenShotPermission(AppletScreenShotPermission permission) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; - s32 val; + s32 permission; } *raw; raw = serviceIpcPrepareHeader(&g_appletISelfController, &c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 10; - raw->val = val; + raw->permission = permission; Result rc = serviceIpcDispatch(&g_appletISelfController);