diff --git a/CMakeLists.txt b/CMakeLists.txt index 644d7f4..71fe340 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.2.37 + VERSION 1.2.38 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) -set (DISPLAY_VERSION "v1.2.37beta4") +set (DISPLAY_VERSION "v1.2.38beta5") set (CMAKE_INSTALL_PREFIX "/usr/") diff --git a/README.md b/README.md index 81ffe65..24b232b 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ -Download: v1.2.37 Beta4     Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). +Download: v1.2.38 Beta5     Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).   -#### NEW version 1.2.37 Beta4 release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +#### NEW version 1.2.38 Beta5 release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.   diff --git a/docs/BuildPrerequisites.md b/docs/BuildPrerequisites.md index d8411f5..0bc656f 100644 --- a/docs/BuildPrerequisites.md +++ b/docs/BuildPrerequisites.md @@ -26,7 +26,7 @@ Run the following commands to install dependent libraries required by the PiPeda sudo apt install -y libasound2-dev jackd2 uuid-dev libxrandr-dev sudo apt install -y authbind sudo apt install -y libavahi-client-dev - sudo apt install -y libnm-dev + sudo apt install -y libnm-dev libicu-dev sudo apt install -y libsdbus-c++-dev libzip-dev diff --git a/docs/Installing.md b/docs/Installing.md index 7a0515d..7a381a3 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,17 +13,17 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (64-bit) v1.2.37 Beta4](https://github.com/rerdavies/pipedal/releases/download/) +- [Raspberry Pi OS bookworm (64-bit) v1.2.38 Beta5](https://github.com/rerdavies/pipedal/releases/download/) - [Ubuntu/Raspberry Pi OS bullseyeye (64-bit) v1.2.31](https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb) -Version 1.2.37 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31. +Version 1.2.38 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31. Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install pipedal_1.2.37_arm64.deb + sudo apt-get install pipedal_1.2.38_arm64.deb ``` Adjust accordingly if you have downloaded v1.1.31. diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index e0a64bc..de4668d 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,4 +1,9 @@ # Release Notes +## Pipedal 1.2.38 Beta5 + +Fixes: +- PiPedal won't run on non-en-US locales. + ## Pipedal 1.2.37 Beta4 You can now upload GuitarML Proteus models from the GuitarML project into TooB ML. diff --git a/docs/download.md b/docs/download.md index 2867647..a308f3d 100644 --- a/docs/download.md +++ b/docs/download.md @@ -4,17 +4,17 @@ Download the most recent Debian (.deb) package for your platform: -- Raspberry Pi OS Bookworm (64-bit) v1.2.37 Beta4 +- Raspberry Pi OS Bookworm (64-bit) v1.2.38 Beta5 - Ubuntu 21.04 or Raspberry Pi OS bullseyeyeye (64-bit) v1.1.31 -v1.2.37 Beta4 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. +v1.2.38 Beta5 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.2.37_arm64.deb + sudo apt-get install ./pipedal_1.2.38_arm64.deb ``` Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation. diff --git a/docs/index.md b/docs/index.md index 5903571..d17e9cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,13 +1,13 @@ -v1.2.37 +v1.2.38   To download PiPedal, click [here](download.md). -> NEW version 1.2.37 Beta4 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +> NEW version 1.2.38 Beta5 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6a23245..9aa027f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -133,6 +133,7 @@ endif() set (PIPEDAL_SOURCES WebServerConfig.cpp WebServerConfig.hpp + Locale.hpp Locale.cpp Finally.hpp ZipFile.cpp ZipFile.hpp TemporaryFile.cpp TemporaryFile.hpp @@ -204,7 +205,6 @@ set (PIPEDAL_SOURCES AdminClient.hpp AdminClient.cpp MidiBinding.hpp MidiBinding.cpp PiPedalMath.hpp - Locale.hpp Locale.cpp Lv2EventBufferWriter.hpp Lv2EventBufferWriter.cpp IpSubnet.hpp PiPedalAlsa.hpp PiPedalAlsa.cpp @@ -235,6 +235,9 @@ set (PIPEDAL_INCLUDES ) set(PIPEDAL_LIBS libpipedald zip + icui18n + icuuc + icudata pthread atomic stdc++fs asound avahi-common avahi-client systemd ${VST3_LIBRARIES} ${LILV_0_LIBRARIES} diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 13c1d7f..3e2efbe 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -607,7 +607,6 @@ void SetVarPermissions( if (fs::is_directory(path)) { fs::permissions(path, directoryPermissions, fs::perm_options::replace); for (const auto& entry : fs::recursive_directory_iterator(path)) { - cout << " " << entry.path() << endl; if (chown(entry.path().c_str(),uid,gid) != 0) { diff --git a/src/Locale.cpp b/src/Locale.cpp index aced0d2..6caec02 100644 --- a/src/Locale.cpp +++ b/src/Locale.cpp @@ -23,43 +23,134 @@ #include #include "Lv2Log.hpp" - +#include +#include +#include +#include +#include +#include "ss.hpp" +#include // Must be UNICODE. Should reflect system locale. (.e.g en-US.UTF8, de-de.UTF8) // This is correct for libstdc++; most probably not correct for Windows or CLANG. :-( using namespace pipedal; -static const char*getLocale(const char*localeEnvironmentVariable) + +std::string getCurrentLocale() { + std::string locale = setlocale(LC_ALL, nullptr); + if (locale.empty() || locale == "C") { + // If setlocale fails, try getting it from environment variables + const char* lang = getenv("LC_ALL"); + if (lang) { + locale = lang; + } else { + // If LANG is not set, fall back to a default + lang = getenv("LC_COLLATE"); + if (lang) + { + locale = lang; + } else{ + lang = getenv("LANG"); + if (lang) { + locale = lang; + } else { + locale = "en_US"; + } + } + } + } + // Extract just the language and country code + size_t dot_pos = locale.find('.'); + if (dot_pos != std::string::npos) { + locale = locale.substr(0, dot_pos); + } + return locale; +} + +Collator::~Collator() { + +} + +class LocaleImpl; + +class CollatorImpl : public Collator { +public: + CollatorImpl(std::shared_ptr LocaleImpl,icu::Locale &locale); + ~CollatorImpl(); + + virtual int Compare(const std::string &left, const std::string&right); +private: + icu::Collator* collator = nullptr; + std::shared_ptr localeImpl; +}; + +CollatorImpl::~CollatorImpl() { - const char*result = getenv(localeEnvironmentVariable); - if (result == nullptr) - { - result = getenv("LC_ALL"); - } + delete collator; + localeImpl = nullptr; +} +CollatorImpl::CollatorImpl(std::shared_ptr localeImpl, icu::Locale &locale) +:localeImpl(localeImpl) +{ + UErrorCode status = U_ZERO_ERROR; - if (result == nullptr) { - result = "en_US.UTF-8"; + this->collator = icu::Collator::createInstance(locale, status); + + if (U_FAILURE(status)) { + throw std::runtime_error(SS("Failed to create collator: " << u_errorName(status))); } - std::stringstream s; - s << "Locale: " << result; - Lv2Log::error(s.str()); - return result; +} + +int CollatorImpl::Compare(const std::string &left, const std::string&right) { + return collator->compare(left.c_str(),right.c_str()); +} +Locale::ptr g_instance; + +class LocaleImpl: public Locale, public std::enable_shared_from_this { +public: + LocaleImpl(); + virtual const std::string &CurrentLocale() const ; + virtual Collator::ptr GetCollator(); +private: + std::unique_ptr locale; + std::string currentLocale; +}; + +LocaleImpl::LocaleImpl() +{ + currentLocale = getCurrentLocale(); + locale = std::make_unique(currentLocale.c_str()); +} +const std::string &LocaleImpl::CurrentLocale() const +{ + return currentLocale; } -void Locale::setDefaultLocale() { - const char* locale = getLocale("LC_ALL"); - try { - setlocale(LC_ALL,locale); - } catch (const std::exception&) - { - std::stringstream s; - s << "Failed to set default locale (" << locale << "). Defaulting to en_US."; - Lv2Log::error(s.str()); - - std::setlocale(LC_ALL,"en_US.UTF-8"); - } +Collator::ptr LocaleImpl::GetCollator(){ + auto pThis = shared_from_this(); + return std::shared_ptr(new CollatorImpl(pThis,*locale)); } -const std::collate& Locale::collation() { return std::use_facet >(std::locale()); } + +static std::mutex createMutex; + +Locale::~Locale() +{ + +} +Locale::ptr Locale::g_instance; + +Locale::ptr Locale::GetInstance() +{ + std::lock_guard lock { createMutex}; + + if (g_instance) + { + return g_instance; + } + g_instance = std::make_shared(); + return g_instance; +} + diff --git a/src/Locale.hpp b/src/Locale.hpp index 5cce1f2..9d0ae1b 100644 --- a/src/Locale.hpp +++ b/src/Locale.hpp @@ -19,14 +19,34 @@ #pragma once -#include +#include +#include namespace pipedal { - - class Locale { + class Collator { public: - static void setDefaultLocale(); + using ptr = std::shared_ptr; + virtual int Compare(const std::string &left, const std::string&right) = 0; + virtual ~Collator(); + }; + class Locale { + protected: + Locale() { } + public: + virtual ~Locale(); + // no copy, no move + Locale(const Locale&) = delete; + Locale(Locale&&) = delete; + Locale&operator =(const Locale&) = delete; + Locale&operator =(Locale&&) = delete; + + using ptr = std::shared_ptr; - static const std::collate& collation(); + static ptr GetInstance(); + + virtual const std::string &CurrentLocale() const = 0; + virtual Collator::ptr GetCollator() = 0; + private: + static ptr g_instance; }; } diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 3cb47a2..b069caf 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -21,7 +21,7 @@ #include "PluginHost.hpp" #include #include -#include +#include "Locale.hpp" #include #include "Lv2Log.hpp" #include @@ -461,16 +461,16 @@ void PluginHost::Load(const char *lv2Path) Lv2Log::info("lilv: " + s); } - const auto &collation = std::use_facet>(std::locale()); - auto compare = [&collation]( + auto collator = Locale::GetInstance()->GetCollator(); + auto compare = [&collator]( const std::shared_ptr &left, const std::shared_ptr &right) { const char *pb1 = left->name().c_str(); const char *pb2 = right->name().c_str(); - return collation.compare( - pb1, pb1 + left->name().size(), - pb2, pb2 + right->name().size()) < 0; + return collator->Compare( + left->name(),right->name()) + < 0; }; std::sort(this->plugins_.begin(), this->plugins_.end(), compare); diff --git a/src/Storage.cpp b/src/Storage.cpp index 24079a6..d593afb 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -18,6 +18,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "pch.h" +#include "Locale.hpp" #include "Storage.hpp" #include "AudioConfig.hpp" #include "PiPedalException.hpp" @@ -40,7 +41,6 @@ const char *BANKS_FILENAME = "index.banks"; #define USER_SETTINGS_FILENAME "userSettings.json"; Storage::Storage() -: locale("en_US.UTF-8") { SetConfigRoot("~/var/Config"); SetDataRoot("~/var/PiPedal"); @@ -1525,9 +1525,10 @@ std::vector Storage::GetFileList(const UiFileProperty &fileProperty } // sort lexicographically + auto collator = Locale::GetInstance()->GetCollator(); - std::sort(result.begin(), result.end(), [this](const std::string&left,const std::string&right) { - return this->locale(left,right) < 0; + std::sort(result.begin(), result.end(), [&collator](const std::string&left,const std::string&right) { + return collator->Compare(left,right) < 0; }); return result; } @@ -1606,12 +1607,14 @@ std::vector Storage::GetFileList2(const std::string &relativePath,con // sort lexicographically - std::sort(result.begin(), result.end(),[this](const FileEntry&l, const FileEntry&r) { + auto collator = Locale::GetInstance()->GetCollator(); + + std::sort(result.begin(), result.end(),[&collator](const FileEntry&l, const FileEntry&r) { if (l.isDirectory_ != r.isDirectory_) { return l.isDirectory_ > r.isDirectory_; } - return this->locale(l.filename_,r.filename_); + return collator->Compare(l.filename_,r.filename_) < 0; }); return result; @@ -1813,10 +1816,11 @@ void Storage::FillSampleDirectoryTree(FilePropertyDirectoryTree*node, const std: node->children_.push_back(std::move(childTree)); } } + auto collator = Locale::GetInstance()->GetCollator(); std::sort(node->children_.begin(),node->children_.end(), - [this](const FilePropertyDirectoryTree::ptr&left,const FilePropertyDirectoryTree::ptr&right) + [&collator](const FilePropertyDirectoryTree::ptr&left,const FilePropertyDirectoryTree::ptr&right) { - return this->locale(left->directoryName_,right->directoryName_); + return collator->Compare(left->directoryName_,right->directoryName_) < 0; }); } FilePropertyDirectoryTree::ptr Storage::GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty) const diff --git a/src/Storage.hpp b/src/Storage.hpp index deaea9c..514686b 100644 --- a/src/Storage.hpp +++ b/src/Storage.hpp @@ -30,7 +30,6 @@ #include "WifiDirectConfigSettings.hpp" #include "FileEntry.hpp" #include -#include #include "FilePropertyDirectoryTree.hpp" @@ -67,7 +66,6 @@ struct PluginPresetValues { class Storage { private: - std::locale locale; std::filesystem::path dataRoot; std::filesystem::path configRoot; BankIndex bankIndex; diff --git a/src/main.cpp b/src/main.cpp index 8355963..7ea3fa7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,7 @@ #include "pch.h" #include "util.hpp" - +#include "Locale.hpp" #include "AudioConfig.hpp" #include "WebServer.hpp" #include @@ -230,6 +230,17 @@ int main(int argc, char *argv[]) try { + { + auto locale = Locale::GetInstance(); + Lv2Log::info(SS("Locale: " << locale->CurrentLocale())); + try { + auto collator = locale->GetCollator(); + } catch (std::exception&e) + { + Lv2Log::error(e.what()); + return EXIT_SUCCESS; //tell systemd not to auto-restart. + } + } PiPedalModel model; model.Init(configuration); diff --git a/todo.txt b/todo.txt index a0d6a32..096c487 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,4 @@ -- Zip file upload in a subdirectory. +- Bug: text Cursor hovering over the breadcrumb bar in file property dialog. - Turning off wifi-direct sould re-enable NetworkManager. - ToobML save/restore state. @@ -16,6 +16,5 @@ Pri Description ----------------- -9 Upload proteus patches. 8 Migrate to Vite toolchain. 5 Re-use plugin instances when rebuilding pedalboard.