1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

Implemented background tasks for Borealis.

* core: implemented SCOPED_TRY_LOCK macro. Specific functions are now using it instead of SCOPED_LOCK to avoid potentially locking the Borealis UI.

* UI: implemented background tasks for Borealis, which call functions that now use SCOPED_TRY_LOCK.
This commit is contained in:
Pablo Curiel 2021-06-09 00:48:17 -04:00
parent a16f62fe6d
commit e48aa2e937
16 changed files with 259 additions and 160 deletions

View file

@ -32,7 +32,10 @@ extern "C" {
#endif
/* Scoped lock macro. */
#define SCOPED_LOCK(mtx) for(UtilsScopedLock ANONYMOUS_VARIABLE(scoped_lock) __attribute__((__cleanup__(utilsUnlockScope))) = utilsLockScope(mtx); ANONYMOUS_VARIABLE(scoped_lock).cond; ANONYMOUS_VARIABLE(scoped_lock).cond = 0)
#define SCOPED_LOCK(mtx) for(UtilsScopedLock ANONYMOUS_VARIABLE(scoped_lock) CLEANUP(utilsUnlockScope) = utilsLockScope(mtx); ANONYMOUS_VARIABLE(scoped_lock).cond; ANONYMOUS_VARIABLE(scoped_lock).cond = 0)
/* Scoped try lock macro. */
#define SCOPED_TRY_LOCK(mtx) for(UtilsScopedLock ANONYMOUS_VARIABLE(scoped_lock) CLEANUP(utilsUnlockScope) = utilsTryLockScope(mtx); ANONYMOUS_VARIABLE(scoped_lock).cond; ANONYMOUS_VARIABLE(scoped_lock).cond = 0)
/// Used by scoped locks.
typedef struct {
@ -146,6 +149,13 @@ NX_INLINE UtilsScopedLock utilsLockScope(Mutex *mtx)
return scoped_lock;
}
NX_INLINE UtilsScopedLock utilsTryLockScope(Mutex *mtx)
{
UtilsScopedLock scoped_lock = { mtx, !mutexIsLockedByCurrentThread(mtx), 1 };
if (scoped_lock.lock) scoped_lock.cond = (int)mutexTryLock(scoped_lock.mtx);
return scoped_lock;
}
NX_INLINE void utilsUnlockScope(UtilsScopedLock *scoped_lock)
{
if (scoped_lock->lock) mutexUnlock(scoped_lock->mtx);

View file

@ -43,9 +43,6 @@ void usbExit(void);
void *usbAllocatePageAlignedBuffer(size_t size);
/// Used to check if the console has been connected to a USB host device and if a valid USB session has been established.
/// Bear in mind this call will block the calling thread if the console is connected to a USB host device but no USB session has been established.
/// If the console is disconnected during this block, the function will return false.
/// If the console isn't connected to a USB host device when this function is called, false will be returned right away.
bool usbIsReady(void);
/// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData().

View file

@ -50,6 +50,8 @@
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define ALWAYS_INLINE_LAMBDA __attribute__((always_inline))
#define CLEANUP(func) __attribute__((__cleanup__(func)))
#define NXDT_ASSERT(name, size) static_assert(sizeof(name) == (size), "Bad size for " #name "! Expected " #size ".")
/* Global constants used throughout the application. */

86
include/tasks.hpp Normal file
View file

@ -0,0 +1,86 @@
/*
* tasks.hpp
*
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __TASKS_HPP__
#define __TASKS_HPP__
#include <borealis.hpp>
#include "core/gamecard.h"
#include "core/title.h"
#include "core/ums.h"
#include "core/usb.h"
namespace nxdt::tasks
{
/* Custom event types used by the tasks defined below. */
typedef brls::Event<GameCardStatus> GameCardStatusEvent;
typedef brls::VoidEvent VoidEvent;
typedef brls::Event<bool> BooleanEvent;
/* Gamecard task. */
class GameCardTask: public brls::RepeatingTask
{
private:
GameCardStatus cur_gc_status = GameCardStatus_NotInserted;
GameCardStatus prev_gc_status = GameCardStatus_NotInserted;
GameCardStatusEvent *gc_status_event = nullptr;
public:
GameCardTask(GameCardStatusEvent *gc_status_event);
void run(retro_time_t current_time) override;
};
/* Gamecard title task. */
class GameCardTitleTask: public brls::RepeatingTask
{
private:
VoidEvent *gc_title_event = nullptr;
public:
GameCardTitleTask(VoidEvent *gc_title_event);
void run(retro_time_t current_time) override;
};
/* USB Mass Storage task. */
class UmsTask: public brls::RepeatingTask
{
private:
VoidEvent *ums_event = nullptr;
public:
UmsTask(VoidEvent *ums_event);
void run(retro_time_t current_time) override;
};
/* USB host device connection task. */
class UsbHostTask: public brls::RepeatingTask
{
private:
bool cur_usb_host_status = false;
bool prev_usb_host_status = false;
BooleanEvent *usb_host_event = nullptr;
public:
UsbHostTask(BooleanEvent *usb_host_event);
void run(retro_time_t current_time) override;
};
}
#endif /* __TASKS_HPP__ */

View file

@ -1,5 +1,4 @@
{
"name": "{0}",
"tabs": {
"first": "First tab",
"second": "Second tab",

View file

@ -1,15 +0,0 @@
{
"hints": {
"ok": "OK",
"back": "Retour",
"exit": "Quitter"
},
"crash_frame": {
"button": "OK"
},
"thumbnail_sidebar": {
"save": "Sauvegarder"
}
}

View file

@ -1,5 +0,0 @@
{
"first_button": "Premier bouton",
"second_button": "Deuxième bouton",
"third_button": "Troisième bouton"
}

View file

@ -1,18 +0,0 @@
{
"open": "Ouvrir l'installateur de démo",
"title": "Mon super installateur",
"stage1": {
"text": "Ici vous devriez normalement faire des choses utiles",
"button": "Aller à l'étape 2"
},
"stage2": {
"text": "Exemple de chargement"
},
"stage3": {
"button": "Terminer"
}
}

View file

@ -1,77 +0,0 @@
{
"name": "App de Démo Borealis",
"tabs": {
"first": "Premier onglet",
"second": "Deuxième onglet",
"third": "Troisième onglet",
"fourth": "Quatrième onglet",
"custom_navigation_tab": "Disposition personnalisée"
},
"pozznx": {
"open": "Ouvrir une boîte de dialogue",
"warning": "Avertissement: PozzNX va effacer toutes les données de votre Switch et la rendre inutilisable, voulez-vous continuer ?",
"running": "Exécution de PozzNX...",
"continue": "Continuer"
},
"notify": "Afficher une notification aléatoire",
"tv": {
"resolution": "Résolution du téléviseur",
"automatic": "Automatique"
},
"i18n": {
"title": "Langue : {0} ({1})",
"lang": "Français"
},
"jank": {
"jank": "Bâclement de l'interface utilisateur",
"native": "Natif",
"minimal": "Minimal",
"regular": "Normal",
"maximum": "Maximum",
"saxophone": "SX OS",
"vista": "Windows Vista",
"ios": "iOS 14"
},
"divide": {
"title": "Diviser par 0",
"description": "Est-ce que la Switch peut le faire ?",
"crash": "Le logiciel a été arrêté à cause d'une erreur:\nSIGABRT (signal 6)"
},
"more": "Si vous désirez en savoir plus sur la console Nintendo Switch et ses fonctionnalités, visitez le site d'assistance Nintendo depuis un appareil connecté ou un ordinateur.",
"actions": {
"title": "Actions personnalisées",
"notify": "Afficher une notification",
"triggered": "Action personnalisée déclenchée"
},
"layers": {
"title": "Choisir une couche",
"layer1": "Couche 1",
"layer2": "Couche 2",
"item1": "Item 1",
"item2": "Item 2",
"item3": "Item 3"
},
"keyboard": {
"string": {
"title": "Ouvrir le clavier",
"default": "Texte par défaut",
"help": "Message d'aide"
},
"number": {
"title": "Ouvrir le clavier numérique",
"help": "Message d'aide"
}
}
}

View file

@ -1,13 +0,0 @@
{
"open": "Ouvrir une popup",
"red": "Rouge",
"green": "Vert",
"blue": "Bleu",
"title": "Titre de la popup",
"subtitle": {
"left": "Sous-titre gauche",
"right": "Sous-titre right"
}
}

View file

@ -307,7 +307,7 @@ u8 gamecardGetStatus(void)
{
u8 status = GameCardStatus_NotInserted;
SCOPED_LOCK(&g_gameCardMutex)
SCOPED_TRY_LOCK(&g_gameCardMutex)
{
if (g_gameCardInterfaceInit) status = g_gameCardStatus;
}

View file

@ -862,7 +862,7 @@ bool titleIsGameCardInfoUpdated(void)
{
bool ret = false;
SCOPED_LOCK(&g_titleMutex)
SCOPED_TRY_LOCK(&g_titleMutex)
{
/* Check if the gamecard thread detected a gamecard status change. */
ret = (g_titleInterfaceInit && g_titleGameCardInfoUpdated);

View file

@ -97,7 +97,7 @@ bool umsIsDeviceInfoUpdated(void)
{
bool ret = false;
SCOPED_LOCK(&g_umsMutex)
SCOPED_TRY_LOCK(&g_umsMutex)
{
if (!g_umsInterfaceInit || !g_umsDeviceInfoUpdated) break;
ret = true;

View file

@ -308,7 +308,7 @@ void *usbAllocatePageAlignedBuffer(size_t size)
bool usbIsReady(void)
{
bool ret = false;
SCOPED_LOCK(&g_usbInterfaceMutex) ret = (g_usbHostAvailable && g_usbSessionStarted);
SCOPED_TRY_LOCK(&g_usbInterfaceMutex) ret = (g_usbHostAvailable && g_usbSessionStarted);
return ret;
}

View file

@ -20,11 +20,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <nxdt_utils.h>
#include <scope_guard.hpp>
#include <borealis.hpp>
#include <string>
#include <nxdt_utils.h>
#include <tasks.hpp>
#include <scope_guard.hpp>
#include "custom_layout_tab.hpp"
#include "sample_installer_page.hpp"
#include "sample_loading_page.hpp"
@ -47,24 +48,54 @@ int main(int argc, char* argv[])
{
ON_SCOPE_EXIT { utilsCloseResources(); };
/* Initialize application resources. */
if (!utilsInitializeResources(argc, (const char**)argv)) return EXIT_FAILURE;
// Init the app
/* Set Borealis log level. */
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
/* Load Borealis translation files. */
i18n::loadTranslations();
if (!brls::Application::init("main/name"_i18n))
{
brls::Logger::error("Unable to init Borealis application");
return EXIT_FAILURE;
}
// Create a sample view
brls::TabFrame* rootFrame = new brls::TabFrame();
rootFrame->setTitle(i18n::getStr("main/name", APP_TITLE));
rootFrame->setIcon(BOREALIS_ASSET("icon/" APP_TITLE ".jpg" ));
/* Initialize Borealis. */
if (!brls::Application::init("main/name"_i18n)) return EXIT_FAILURE;
brls::List* testList = new brls::List();
/* Create root tab frame. */
brls::TabFrame *root_frame = new brls::TabFrame();
root_frame->setTitle(APP_TITLE);
root_frame->setIcon(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
root_frame->setFooterText("v" APP_VERSION);
/* Create and start gamecard task. */
nxdt::tasks::GameCardStatusEvent gc_status_event;
nxdt::tasks::GameCardTask *gc_task = new nxdt::tasks::GameCardTask(&gc_status_event);
gc_task->start();
/* Create and start gamecard title task. */
nxdt::tasks::VoidEvent gc_title_event;
nxdt::tasks::GameCardTitleTask *gc_title_task = new nxdt::tasks::GameCardTitleTask(&gc_title_event);
gc_title_task->start();
/* Create and start UMS task. */
nxdt::tasks::VoidEvent ums_event;
nxdt::tasks::UmsTask *ums_task = new nxdt::tasks::UmsTask(&ums_event);
ums_task->start();
/* Create and start USB host task. */
nxdt::tasks::BooleanEvent usb_host_event;
nxdt::tasks::UsbHostTask *usb_host_task = new nxdt::tasks::UsbHostTask(&usb_host_event);
usb_host_task->start();
brls::List *testList = new brls::List();
brls::ListItem* dialogItem = new brls::ListItem("main/pozznx/open"_i18n);
dialogItem->getClickEvent()->subscribe([](brls::View* view) {
@ -183,16 +214,16 @@ int main(int argc, char* argv[])
testList->addView(layerSelectItem);
rootFrame->addTab("main/tabs/first"_i18n, testList);
rootFrame->addTab("main/tabs/second"_i18n, testLayers);
rootFrame->addSeparator();
rootFrame->addTab("main/tabs/third"_i18n, new brls::Rectangle(nvgRGB(255, 0, 0)));
rootFrame->addTab("main/tabs/fourth"_i18n, new brls::Rectangle(nvgRGB(0, 255, 0)));
rootFrame->addSeparator();
rootFrame->addTab("main/tabs/custom_navigation_tab"_i18n, new CustomLayoutTab());
root_frame->addTab("main/tabs/first"_i18n, testList);
root_frame->addTab("main/tabs/second"_i18n, testLayers);
root_frame->addSeparator();
root_frame->addTab("main/tabs/third"_i18n, new brls::Rectangle(nvgRGB(255, 0, 0)));
root_frame->addTab("main/tabs/fourth"_i18n, new brls::Rectangle(nvgRGB(0, 255, 0)));
root_frame->addSeparator();
root_frame->addTab("main/tabs/custom_navigation_tab"_i18n, new CustomLayoutTab());
// Add the root view to the stack
brls::Application::pushView(rootFrame);
brls::Application::pushView(root_frame);
// Run the app
while (brls::Application::mainLoop());

102
source/tasks.cpp Normal file
View file

@ -0,0 +1,102 @@
/*
* tasks.cpp
*
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <nxdt_includes.h>
#include <tasks.hpp>
namespace nxdt::tasks
{
/* Gamecard task. */
GameCardTask::GameCardTask(GameCardStatusEvent *gc_status_event) : brls::RepeatingTask(100) /* 100 ms intervals. */
{
this->gc_status_event = gc_status_event;
}
void GameCardTask::run(retro_time_t current_time)
{
brls::RepeatingTask::run(current_time);
this->cur_gc_status = (GameCardStatus)gamecardGetStatus();
if (this->cur_gc_status != this->prev_gc_status)
{
this->gc_status_event->fire(this->cur_gc_status);
this->prev_gc_status = this->cur_gc_status;
brls::Logger::debug("Gamecard status change triggered: {}.", this->cur_gc_status);
}
}
/* Gamecard title task. */
GameCardTitleTask::GameCardTitleTask(VoidEvent *gc_title_event) : brls::RepeatingTask(100) /* 100 ms intervals. */
{
this->gc_title_event = gc_title_event;
}
void GameCardTitleTask::run(retro_time_t current_time)
{
brls::RepeatingTask::run(current_time);
if (titleIsGameCardInfoUpdated())
{
this->gc_title_event->fire();
brls::Logger::debug("Gamecard title info updated.");
}
}
/* USB Mass Storage task. */
UmsTask::UmsTask(VoidEvent *ums_event) : brls::RepeatingTask(100) /* 100 ms intervals. */
{
this->ums_event = ums_event;
}
void UmsTask::run(retro_time_t current_time)
{
brls::RepeatingTask::run(current_time);
if (umsIsDeviceInfoUpdated())
{
this->ums_event->fire();
brls::Logger::debug("UMS device info updated.");
}
}
/* USB host device connection task. */
UsbHostTask::UsbHostTask(BooleanEvent *usb_host_event) : brls::RepeatingTask(100) /* 100 ms intervals. */
{
this->usb_host_event = usb_host_event;
}
void UsbHostTask::run(retro_time_t current_time)
{
brls::RepeatingTask::run(current_time);
this->cur_usb_host_status = usbIsReady();
if (this->cur_usb_host_status != this->prev_usb_host_status)
{
this->usb_host_event->fire(this->cur_usb_host_status);
this->prev_usb_host_status = this->cur_usb_host_status;
brls::Logger::debug("USB host status change triggered: {}.", this->cur_usb_host_status);
}
}
}