diff --git a/3rdparty/libossia b/3rdparty/libossia index 879e5c3666..09d8744c7c 160000 --- a/3rdparty/libossia +++ b/3rdparty/libossia @@ -1 +1 @@ -Subproject commit 879e5c36660039015ae0c6ae223b7fe9f88dbda8 +Subproject commit 09d8744c7c2a2cd2c12b60baa7fc3a2cae0f0668 diff --git a/cmake/ScoreConfiguration.cmake b/cmake/ScoreConfiguration.cmake index 2ca900f383..38b44558cc 100644 --- a/cmake/ScoreConfiguration.cmake +++ b/cmake/ScoreConfiguration.cmake @@ -304,3 +304,56 @@ install( DESTINATION include/score COMPONENT Devel OPTIONAL) + +# Win32 codepage stuff +if(WIN32) + file(CONFIGURE + OUTPUT + "${PROJECT_BINARY_DIR}/score.exe.manifest" + CONTENT + [=[ + + + true/PM + PerMonitorV2,PerMonitor + true + UTF-8 + + + + + + + + + + + + + + + + + + ]=] + ) + + if(MINGW) + enable_language(RC) + # 1 = CREATEPROCESS_MANIFEST_RESOURCE_ID, 24 = RT_MANIFEST (no includes needed) + file(WRITE "${PROJECT_BINARY_DIR}/score.exe.rc" + "1 24 \"${PROJECT_BINARY_DIR}/score.exe.manifest\"\n") + endif() +endif() + +function(target_enable_utf8 tgt) + if(NOT SCORE_MSSTORE_DEPLOYMENT) # c.f. ScoreDeploymentWindowsStore + if(MSVC) + target_link_options(${tgt} PRIVATE + /MANIFEST:EMBED + "/MANIFESTINPUT:${CMAKE_BINARY_DIR}/score.exe.manifest") + elseif(MINGW) + target_sources(${tgt} PRIVATE "${CMAKE_BINARY_DIR}/score.exe.rc") + endif() + endif() +endfunction() \ No newline at end of file diff --git a/cmake/ScoreDeploymentWindowsStore.cmake b/cmake/ScoreDeploymentWindowsStore.cmake index c85d7d414e..393a68ec43 100644 --- a/cmake/ScoreDeploymentWindowsStore.cmake +++ b/cmake/ScoreDeploymentWindowsStore.cmake @@ -2,36 +2,6 @@ if(NOT SCORE_MSSTORE_DEPLOYMENT) return() endif() -file(CONFIGURE - OUTPUT - "${PROJECT_BINARY_DIR}/score.exe.manifest" - CONTENT - [=[ - - - true/PM - PerMonitorV2,PerMonitor - true - - - - - - - - - - - - - - - - - - ]=] -) - install( FILES "${PROJECT_BINARY_DIR}/score.exe.manifest" diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index bde903cb1b..b53230695d 100755 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -165,6 +165,7 @@ if(APPLE) endif() if(WIN32) + target_enable_utf8(${APPNAME}) target_link_libraries(${APPNAME} PRIVATE ntdll RuntimeObject) endif() diff --git a/src/lib/score/tools/RecursiveWatch.cpp b/src/lib/score/tools/RecursiveWatch.cpp index 905791e67e..1acdc35aef 100644 --- a/src/lib/score/tools/RecursiveWatch.cpp +++ b/src/lib/score/tools/RecursiveWatch.cpp @@ -71,8 +71,11 @@ void for_all_files(std::string_view root, std::function { if(auto res = algorithm::contents(pp.value())) { + try + { for(auto& p : res.value()) { + try { switch(p.second.st_type) { #if !defined(_WIN32) @@ -87,15 +90,46 @@ void for_all_files(std::string_view root, std::function #if !defined(_WIN32) f(r.native()); #else - f(r.generic_string()); + try { + f(r.generic_string()); + } + catch(...) + { + // Very inefficient but r.generic_string() sometimes throws + // filesystem error: in __wide_to_char: Illegal byte sequence + // on windows + auto s = QString::fromStdWString(r.native()).toStdString(); + std::replace(s.begin(), s.end(), '\\', '/'); + f(s); + } #endif break; } default: break; } + } + catch(const std::exception& e) + { + } + catch(...) + { + } + } + } + catch(const std::exception& e) + { + } + catch(...) + { } } + else + { + } + } + catch(const std::exception& e) + { } catch(...) { @@ -318,4 +352,11 @@ void RecursiveWatch::scanAsync(QObject* context) send_to_main_thread(); }); } + +void RecursiveWatch::reset() +{ + m_root.clear(); + m_watched.clear(); + m_asyncWatched.clear(); +} } diff --git a/src/lib/score/tools/RecursiveWatch.hpp b/src/lib/score/tools/RecursiveWatch.hpp index ce977feb1f..abd34696ba 100644 --- a/src/lib/score/tools/RecursiveWatch.hpp +++ b/src/lib/score/tools/RecursiveWatch.hpp @@ -69,12 +69,7 @@ class SCORE_LIB_BASE_EXPORT RecursiveWatch /// on the GUI thread via \a context's event loop. void scanAsync(QObject* context); - void reset() - { - m_root.clear(); - m_watched.clear(); - m_asyncWatched.clear(); - } + void reset(); private: std::string m_root; diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp index 5fe6d31a59..12360c6d98 100644 --- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp +++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp @@ -94,7 +94,7 @@ void ProcessModel::setInitialScript(const QString& script) fx.reset(ysfx_new(config.get()), ysfx_u_deleter{}); if(!ysfx_load_file(fx.get(), path_to_jsfx.c_str(), 0)) { - qDebug() << "ysfx: could not load " << script; + qDebug() << "ysfx: setInitialScript: could not load " << script; return; } @@ -108,7 +108,11 @@ void ProcessModel::setInitialScript(const QString& script) uint32_t compile_opts = 0; if(!ysfx_compile(fx.get(), compile_opts)) + { + qDebug() << "ysfx: setInitialScript: could not compile " << script; return; + } + ysfx_init(fx.get()); ysfx_process_double(fx.get(), 0, 0, 0, 0, 0); @@ -123,6 +127,7 @@ void ProcessModel::setInitialScript(const QString& script) } programChanged(); + flagsChanged(); // For UI } void ProcessModel::recreatePorts() @@ -313,7 +318,7 @@ Process::ScriptChangeResult ProcessModel::reload() QFile tempFile{tempFilePath}; if(!tempFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qDebug() << "ysfx: could not write temp file" << tempFilePath; + qDebug() << "ysfx: reload: could not write temp file" << tempFilePath; return res; } tempFile.write(m_text.toUtf8()); @@ -324,7 +329,10 @@ Process::ScriptChangeResult ProcessModel::reload() ysfx_register_builtin_audio_formats(config.get()); // Set import and data roots from the original file - ysfx_guess_file_roots(config.get(), m_jsfx_path.toStdString().c_str()); + const auto& c = config.get(); + ysfx_guess_file_roots(c, m_jsfx_path.toStdString().c_str()); + if(auto import = ysfx_get_import_root(c); !import || strlen(import) == 0) + ysfx_set_import_root(c, QFileInfo{m_jsfx_path}.dir().canonicalPath().toStdString().c_str()); // Create a new fx instance auto new_fx = std::shared_ptr(ysfx_new(config.get()), ysfx_u_deleter{}); @@ -332,14 +340,14 @@ Process::ScriptChangeResult ProcessModel::reload() auto tempPath = tempFilePath.toStdString(); if(!ysfx_load_file(new_fx.get(), tempPath.c_str(), 0)) { - qDebug() << "ysfx: could not load temp file" << tempFilePath; + qDebug() << "ysfx: reload: could not load temp file" << tempFilePath; return res; } uint32_t compile_opts = 0; if(!ysfx_compile(new_fx.get(), compile_opts)) { - qDebug() << "ysfx: compilation failed"; + qDebug() << "ysfx: reload: compilation failed"; return res; } @@ -363,7 +371,13 @@ Process::ScriptChangeResult ProcessModel::reload() recreatePorts(); + if(auto bank = ysfx_get_bank_path(this->fx.get())) + { + m_bank = ysfx_load_bank(bank); + } + programChanged(); + flagsChanged(); // For UI return res; } diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp index e6be61601b..a2a7c95396 100644 --- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp +++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp @@ -81,6 +81,9 @@ class SCORE_PLUGIN_YSFX_EXPORT ProcessModel final : public Process::ProcessModel QString m_jsfx_path; QString m_text; + // If we reload something, to preserve state when the object doesn't create + QByteArray m_saved_state; + std::bitset m_sliderBeingChanged{}; }; diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp index b59f978e06..4ae3bc91d3 100644 --- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp +++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp @@ -74,7 +74,11 @@ static void loadYSFXState(ysfx_t& fx, const QByteArray& state) template <> void DataStreamReader::read(const YSFX::ProcessModel& proc) { - m_stream << proc.m_jsfx_path << proc.m_text << readYSFXState(*proc.fx.get()); + m_stream << proc.m_jsfx_path << proc.m_text; + if(proc.fx) + m_stream << readYSFXState(*proc.fx.get()); + else + m_stream << proc.m_saved_state; readPorts(*this, proc.m_inlets, proc.m_outlets); @@ -94,19 +98,21 @@ void DataStreamWriter::write(YSFX::ProcessModel& proc) { proc.m_jsfx_path = script; proc.m_text = text; + proc.reload(); // If we also have a file path, we may need to reload from it // But for now, we prefer the stored text (live-coded version) - if(proc.fx) - loadYSFXState(*proc.fx.get(), dat); } else { proc.setInitialScript(script); - if(proc.fx) - loadYSFXState(*proc.fx.get(), dat); } + if(proc.fx) + loadYSFXState(*proc.fx.get(), dat); + else + proc.m_saved_state = dat; + writePorts( *this, components.interfaces(), proc.m_inlets, proc.m_outlets, &proc); @@ -147,6 +153,8 @@ void JSONWriter::write(YSFX::ProcessModel& proc) auto dat = QByteArray::fromBase64(obj["Chunk"].toByteArray()); if(proc.fx) loadYSFXState(*proc.fx.get(), dat); + else + proc.m_saved_state = dat; writePorts( *this, components.interfaces(), proc.m_inlets,