diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index 2b3beee..84114ac 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -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); diff --git a/include/core/usb.h b/include/core/usb.h index 8288ef8..16cefba 100644 --- a/include/core/usb.h +++ b/include/core/usb.h @@ -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(). diff --git a/include/defines.h b/include/defines.h index 6218d2d..def0f41 100644 --- a/include/defines.h +++ b/include/defines.h @@ -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. */ diff --git a/include/tasks.hpp b/include/tasks.hpp new file mode 100644 index 0000000..19be79d --- /dev/null +++ b/include/tasks.hpp @@ -0,0 +1,86 @@ +/* + * tasks.hpp + * + * Copyright (c) 2020-2021, DarkMatterCore . + * + * 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 . + */ + +#pragma once + +#ifndef __TASKS_HPP__ +#define __TASKS_HPP__ + +#include + +#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 GameCardStatusEvent; + typedef brls::VoidEvent VoidEvent; + typedef brls::Event 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__ */ diff --git a/romfs/i18n/en-US/main.json b/romfs/i18n/en-US/main.json index c398ccf..5eb224d 100644 --- a/romfs/i18n/en-US/main.json +++ b/romfs/i18n/en-US/main.json @@ -1,5 +1,4 @@ { - "name": "{0}", "tabs": { "first": "First tab", "second": "Second tab", diff --git a/romfs/i18n/fr/brls.json b/romfs/i18n/fr/brls.json deleted file mode 100644 index 9ed2173..0000000 --- a/romfs/i18n/fr/brls.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hints": { - "ok": "OK", - "back": "Retour", - "exit": "Quitter" - }, - - "crash_frame": { - "button": "OK" - }, - - "thumbnail_sidebar": { - "save": "Sauvegarder" - } -} diff --git a/romfs/i18n/fr/custom_layout.json b/romfs/i18n/fr/custom_layout.json deleted file mode 100644 index 9f29306..0000000 --- a/romfs/i18n/fr/custom_layout.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "first_button": "Premier bouton", - "second_button": "Deuxième bouton", - "third_button": "Troisième bouton" -} diff --git a/romfs/i18n/fr/installer.json b/romfs/i18n/fr/installer.json deleted file mode 100644 index 5ef768e..0000000 --- a/romfs/i18n/fr/installer.json +++ /dev/null @@ -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" - } -} diff --git a/romfs/i18n/fr/main.json b/romfs/i18n/fr/main.json deleted file mode 100644 index 160cb6f..0000000 --- a/romfs/i18n/fr/main.json +++ /dev/null @@ -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" - } - } -} - diff --git a/romfs/i18n/fr/popup.json b/romfs/i18n/fr/popup.json deleted file mode 100644 index f1afbab..0000000 --- a/romfs/i18n/fr/popup.json +++ /dev/null @@ -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" - } -} diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 4f9e760..97cd63a 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -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; } diff --git a/source/core/title.c b/source/core/title.c index dec3990..1fca06f 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -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); diff --git a/source/core/ums.c b/source/core/ums.c index 0891713..7ca15ba 100644 --- a/source/core/ums.c +++ b/source/core/ums.c @@ -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; diff --git a/source/core/usb.c b/source/core/usb.c index 548f9ee..db7acc7 100644 --- a/source/core/usb.c +++ b/source/core/usb.c @@ -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; } diff --git a/source/main.cpp b/source/main.cpp index 80cbebf..0971553 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -20,11 +20,12 @@ #include #include -#include -#include -#include #include +#include +#include +#include + #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; - } + + /* Initialize Borealis. */ + if (!brls::Application::init("main/name"_i18n)) return EXIT_FAILURE; + + /* 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(); - // 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" )); - brls::List* testList = new brls::List(); + + + + + + + + + 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()); diff --git a/source/tasks.cpp b/source/tasks.cpp new file mode 100644 index 0000000..b9cdd83 --- /dev/null +++ b/source/tasks.cpp @@ -0,0 +1,102 @@ +/* + * tasks.cpp + * + * Copyright (c) 2020-2021, DarkMatterCore . + * + * 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 . + */ + +#include +#include + +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); + } + } +}