diff --git a/CMakeLists.txt b/CMakeLists.txt index 87aea33..ee9af85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,15 @@ set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(JSONCPP_WITH_PKGCONFIG_SUPPORT OFF CACHE BOOL "" FORCE) set(JSONCPP_WITH_CMAKE_PACKAGE OFF CACHE BOOL "" FORCE) -option(ENABLE_DBUS "Enables DBus support" ON) -option(USE_PORTALS "Enables libportal" ON) +if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + option(USE_PORTALS "Enables libportal" ON) + option(ENABLE_DBUS "Enables DBus support" ON) +endif() option(BUILD_LIBFMT "Builds libfmt" OFF) +if(HAIKU) + set(USE_CELT OFF CACHE BOOL "" FORCE) + set(USE_SPEEX OFF CACHE BOOL "" FORCE) +endif() if (DEFINED EMSCRIPTEN) option(FOR_WASMER "Enables support for wasmer" OFF) set(BUILD_STATIC ON CACHE BOOL "" FORCE) @@ -245,7 +251,10 @@ macro(run_protoc) set(_run_protoc_incpath_expanded) foreach (INCPATH_IDX ${PROTOC_INCPATH}) list(APPEND _run_protoc_incpath_expanded -I${INCPATH_IDX}) - endforeach() + endforeach() + if (HAIKU) + list(APPEND _run_protoc_incpath_expanded -I/boot/system/develop/headers) + endif() set(_run_protoc_output ${_run_protoc_dst_base}.cc ${_run_protoc_dst_base}.h)# ${_run_protoc_dst_base}-c.c ${_run_protoc_dst_base}-c.h) unset(_run_protoc_dst_base) add_custom_command(OUTPUT ${_run_protoc_output} COMMAND ${_PROTOBUF_PROTOC} --cpp_out ${PROTOC_OUTDIR} ${_run_protoc_incpath_expanded} ${PROTOC_SOURCE})# --plugin=${CMAKE_BINARY_DIR}/subprojects/protobuf-c/build-cmake/protoc-gen-c --c_out ${PROTOC_OUTDIR}) @@ -275,17 +284,29 @@ function(grpc_proto) endfunction() prefix_all(LIBRARY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ + config.hpp + backend.hpp backend.cpp options.cpp + options.hpp playback.cpp + playback.h util.cpp + util.hpp log.cpp - dbus.cpp + log.hpp + dbus.cpp + dbus.hpp file_backend.cpp + file_backend.hpp playback_backend.cpp + playback_backend.hpp translation.cpp - playback_process.cpp + translation.hpp + playback_process.cpp + playback_process.hpp base85.cpp + base85.h ) run_protoc(OUTDIR ${CMAKE_BINARY_DIR}/google/protobuf SOURCE google/protobuf/any.proto OUTVAR _src) add_library(liblooper STATIC ${LIBRARY_SOURCES}) @@ -352,8 +373,24 @@ else() pkg_check_modules(jsoncpp IMPORTED_TARGET jsoncpp) endif() if (NOT BUILD_SDL) + if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") + pkg_check_modules(sdl2 sdl2) + list(REMOVE_ITEM SDL2main) + add_library(SDL2::SDL2 INTERFACE IMPORTED) + target_include_directories(SDL2::SDL2 INTERFACE /boot/system/develop/headers/SDL2 ${sdl2_INCLUDE_DIRS}) + target_link_libraries(SDL2::SDL2 INTERFACE ${sdl2_LIBRARIES}) + else() find_package(SDL2 REQUIRED) - endif() + endif() + endif() + if (NOT BUILD_SDL_IMAGE) + if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") + pkg_check_modules(sdl2_image SDL2_image) + add_library(SDL2_image::SDL2_image INTERFACE IMPORTED) + target_include_directories(SDL2_image::SDL2_image INTERFACE ${sdl2_image_INCLUDE_DIRS}) + target_link_libraries(SDL2_image::SDL2_image INTERFACE ${sdl2_image_LIBRARIES}) + endif() + endif() if (ENABLE_DBUS) find_package(sdbus-c++) if(NOT ${sdbus-c++_FOUND}) @@ -365,13 +402,17 @@ else() if (TARGET SDL2-static) set(SDL2_TARGET SDL2-static) endif() - target_link_libraries(liblooper PUBLIC ${SDL2_TARGET} SDL2main ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream libvgmstream_shared ${JSONCPP_TARGET}) + target_link_libraries(liblooper PUBLIC ${SDL2_TARGET} ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream libvgmstream_shared ${JSONCPP_TARGET}) endif() if(BUILD_PROTOBUF) add_subdirectory(subprojects/protobuf) else() + if (CMAKE_SYSTEM_NAME STREQUAL "Haiku") + find_package(Protobuf REQUIRED) + else() find_package(protobuf REQUIRED) find_package(absl CONFIG REQUIRED) + endif() endif() if (${ENABLE_DBUS}) @@ -394,7 +435,7 @@ macro(add_ui_backend) set(USE_PORTALS OFF) else() info("Enabling libportal support...") - target_compile_definitions(imgui_ui PRIVATE "PORTALS") + target_compile_definitions(${target} PRIVATE "PORTALS") endif() endif() endmacro() @@ -442,6 +483,9 @@ endmacro() set(ENABLED_UIS ) set(ENABLED_PLAYBACK_BACKENDS ) ui_backend_subdir(NAME "IMGUI" READABLE_NAME "Dear ImGui" SUBDIR backends/ui/imgui) +if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + ui_backend_subdir(NAME "HAIKU" READABLE_NAME "Haiku Native" SUBDIR backends/ui/haiku) +endif() if (NOT (DEFINED EMSCRIPTEN OR DEFINED ANDROID_NDK)) ui_backend_subdir(DEFAULT_OFF NAME "GTK" READABLE_NAME "GTK4" SUBDIR backends/ui/gtk) endif() @@ -449,7 +493,7 @@ playback_backend_subdir(NAME "VGMSTREAM" READABLE_NAME "VgmStream" SUBDIR backen playback_backend_subdir(NAME "SDL_MIXER_X" READABLE_NAME "SDL Mixer X" SUBDIR backends/playback/sdl_mixer_x) playback_backend_subdir(NAME "ZSM" READABLE_NAME "ZSM" SUBDIR backends/playback/zsm) execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen_ui_backend_inc.py ${CMAKE_CURRENT_BINARY_DIR} --ui ${ENABLED_UIS} --playback ${ENABLED_PLAYBACK_BACKENDS}) -prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ main.cpp daemon_backend.cpp proxy_backend.cpp) +prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ main.cpp daemon_backend.cpp daemon_backend.hpp proxy_backend.cpp proxy_backend.hpp) list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/backend_glue.cpp) if(DEFINED EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".html") @@ -465,8 +509,21 @@ endfunction() if (DEFINED ANDROID_NDK OR SHARED_LIB) add_library(${TARGET_NAME} SHARED ${SOURCES}) else() + if(HAIKU) + set(HAIKU_RES ${CMAKE_SOURCE_DIR}/haiku-res.rdef) + foreach (asset haiku-icon slider-mode text-mode media-playback-start media-playback-stop media-playback-pause view-refresh folder document-open configure application-exit help-contents help-about) + list(APPEND HAIKU_RES ${CMAKE_SOURCE_DIR}/assets/${asset}.rdef) + endforeach() + list(APPEND SOURCES ${CMAKE_BINARY_DIR}/haiku-res.rsrc) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMAND rc -o ${CMAKE_BINARY_DIR}/haiku-res.rsrc ${HAIKU_RES} DEPENDS ${HAIKU_RES} COMMENT Compiling resources) + endif() add_executable(${TARGET_NAME} ${SOURCES}) + if(HAIKU) + # add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND resattr -o ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc DEPENDS ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMENT Adding resources to target attributes) + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND xres -o ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc DEPENDS ${TARGET_NAME} ${CMAKE_BINARY_DIR}/haiku-res.rsrc COMMENT Adding resources to target) + endif() endif() + add_dependencies(${TARGET_NAME} looper_assets ${UI_BACKENDS}) if(DEFINED EMSCRIPTEN) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..b1b23e7 --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,328 @@ + + + + + + EnvironmentId + {423a81c8-7465-41cf-ba38-7cdac117b05f} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 1 + true + + + + true + + + true + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Default Config (CMake preset) + Default Config (CMake preset) + {28867bdf-d8be-4243-8aa3-4dcf402f55e4} + 0 + 0 + 0 + + RelWithDebInfo + 1 + false + + CMAKE_CXX_COMPILER_LAUNCHER=ccache + CMAKE_C_COMPILER_LAUNCHER=ccache + + -DBUILD_SOUNDTOUCH:BOOL=ON +-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=ON +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_C_FLAGS:STRING=-O2 -march=native -g +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DDISABLE_GTK_UI:BOOL=ON +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_CXX_FLAGS:STRING=-O2 -march=native -g +-DCMAKE_POLICY_DEFAULT_CMP0069:STRING=NEW + /boot/home/Desktop/looper/build/default + + + default + + all + + false + + CMAKE_CXX_COMPILER_LAUNCHER=ccache + CMAKE_C_COMPILER_LAUNCHER=ccache + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + looper + CMakeProjectManager.CMakeRunConfiguration.looper + looper + false + true + true + true + /boot/home/Desktop/looper/build/default + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop + Fast building config (CMake preset) + Fast building config (CMake preset) + {ed57fb1f-eec5-4800-be42-516744e4f7eb} + 0 + 0 + 0 + + RelWithDebInfo + 1 + false + + CMAKE_CXX_COMPILER_LAUNCHER=ccache + CMAKE_C_COMPILER_LAUNCHER=ccache + + -DBUILD_SOUNDTOUCH:BOOL=ON +-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_C_FLAGS:STRING=-O2 -march=native -g +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DDISABLE_GTK_UI:BOOL=ON +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_CXX_FLAGS:STRING=-O2 -march=native -g +-DCMAKE_POLICY_DEFAULT_CMP0069:STRING=NEW + /boot/home/Desktop/looper/build/Fast_building_config_CMake_preset-RelWithDebInfo + + + fast + + all + + false + + CMAKE_CXX_COMPILER_LAUNCHER=ccache + CMAKE_C_COMPILER_LAUNCHER=ccache + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/CMakePresets.json b/CMakePresets.json index acfc145..d8a1c7f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,11 +1,30 @@ { - "version": 10, + "version": 9, "cmakeMinimumRequired": { "major": 3, "minor": 23, "patch": 0 }, "configurePresets": [ + { + "name": "fastbuild", + "displayName": "Fast building config", + "description": "Build with optimization for speed of building", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_POLICY_DEFAULT_CMP0069": "NEW", + "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "OFF", + "CMAKE_C_FLAGS": "-O2 -march=native -g", + "CMAKE_CXX_FLAGS": "-O2 -march=native -g", + "BUILD_SOUNDTOUCH": "ON", + "DISABLE_GTK_UI": "ON", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + }, + "environment": { + "CMAKE_C_COMPILER_LAUNCHER": "ccache", + "CMAKE_CXX_COMPILER_LAUNCHER": "ccache" + } + }, { "name": "default", "displayName": "Default Config", @@ -31,6 +50,10 @@ { "name": "default", "configurePreset": "default" + }, + { + "name": "fast", + "configurePreset": "fastbuild" } ], "testPresets": [ @@ -41,4 +64,4 @@ "execution": {"noTestsAction": "error", "stopOnFailure": true} } ] - } \ No newline at end of file + } diff --git a/assets/application-exit b/assets/application-exit new file mode 100644 index 0000000..dd437a1 Binary files /dev/null and b/assets/application-exit differ diff --git a/assets/application-exit.rdef b/assets/application-exit.rdef new file mode 100644 index 0000000..03f4680 --- /dev/null +++ b/assets/application-exit.rdef @@ -0,0 +1,29 @@ + +resource(1011) #'VICN' array { + $"6E63696606050004006603BF5000020006020000003AC2F3BAC2F30000004E5C" + $"5B49D87B00FFFF80FFFFD5000554020016020000003C9BA8BC9BA80000004E35" + $"1649D81000FFFFD9080606FF07B5F7B6F1B5F7B6F1B5F7BC7CB5F7C791B5F7C2" + $"06BA34C791C2AFC791BE72C791C2AFC206C2AFB6F1C2AFBC7CBE72B6F1B5F7B6" + $"F1BA34B6F1B5F7B6F1B6F10609FFFF01BEC5BEC4BEC5BEC4BEC5BFBEBEC5C1B3" + $"BEC5C0B9C0BAC1B3C4A4C1B3C2AFC1B3C4A4C2AEC4A4C4A3C4A4C3A8C646C32B" + $"C989C03CC7E7C1B3C7E7BEC4C4A4BBD5C646BD4DC4A4BCCFC4A4BEC4C4A4BDCA" + $"C2AFBEC4BEC5BEC4C0BABEC4BEC5BEC4BEC40609FFFF01BDCBBDC9BDCBBDC9BD" + $"CBBEC4BDCBC0B9BDCBBFBE40C0B9C3AAC0B9C1B5C0B9C3AAC1B3C3AAC3A8C3AA" + $"C2AEC54BC231C88EBF41C6EDC0B9C6EDBDCAC3AABADBC54BBC52C3AABBD5C3AA" + $"BDC9C3AABCCFC1B5BDC9BDCBBDC940BDC9BDCBBDC9BDC90609FFFF01BDCBBDC9" + $"BDCBBDC9BDCBBEC4BDCBC0B9BDCBBFBE40C0B9C3AAC0B9C1B5C0B9C3AAC1B3C3" + $"AAC3A8C3AAC2AEC54BC231C88EBF41C6EDC0B9C6EDBDCAC3AABADBC54BBC52C3" + $"AABBD5C3AABDC9C3AABCCFC1B5BDC9BDCBBDC940BDC9BDCBBDC9BDC90608FF7F" + $"B6F2B7EBB6F2B7EBB6F2BD76B6F2C88CB6F2C301B8E7C8DFBCD0C986BADBC933" + $"BD24C95CBDCBC909BD77C933BDCBC32BBDCBB76EBDCBBD4CBD77B744BCD0B6F1" + $"BD24B71BBADBB744B6F2B7EBB8E7B798B6F2B7EBB7EB0608FF7FB5F7B6F1B5F7" + $"B6F1B5F7BC7CB5F7C791B5F7C206B7ECC7E5BBD6C88CB9E1C838BC29C862BCD0" + $"C80EBC7DC838BCD0C230BCD0B674BCD0BC52BC7DB64ABBD6B5F6BC29B620B9E1" + $"B64AB5F7B6F1B7ECB69DB5F7B6F1B6F10606FF07B5F7B6F1B5F7B6F1B5F7BC7C" + $"B5F7C791B5F7C206B7ECC7E5BBD6C88CB9E1C838BBD6C25ABBD6B5F6BBD6BC28" + $"B9E1B64AB5F7B6F1B7ECB69DB5F7B6F1B6F10004BADBBEC4BADBBEC4BADBBF17" + $"BADBBFBEBADBBF6BBA34BFBEB8E7BFBEB98EBFBEB8E7BF6BB8E7BEC4B8E7BF17" + $"B8E7BEC4080A000100000A0101011001178422040A0201021001178422040A03" + $"0103000A0101041001178422040A0401051001178422040A050106000A040107" + $"00" +}; diff --git a/assets/configure b/assets/configure new file mode 100644 index 0000000..9712810 Binary files /dev/null and b/assets/configure differ diff --git a/assets/configure.rdef b/assets/configure.rdef new file mode 100644 index 0000000..dacdfaa --- /dev/null +++ b/assets/configure.rdef @@ -0,0 +1,37 @@ + +resource(1006) #'VICN' array { + $"6E63696603011A171B660554020016020000003CC4CBBCC4CB0000004E30CB49" + $"FD6400FFFFE5060606FF07C034BCC6C034BCC6BE4DBCC6BCC6C034BCC6BE4DBC" + $"C6C219C034C3A3BE4DC3A3C219C3A3C3A3C034C3A3C219C3A3BE4DC034BCC6C2" + $"19BCC6C034BCC6BCC60216C984C031C984C031C984C12CC90CC319C95BC227C7" + $"E3C348C590C3A6C6BAC377C50EC470C3A6C590C464C513C376C6BAC317C90EC3" + $"47C7E4C229C95EC02EC983C12CC983BFA6C877BE94C65EBF1DC76ABDACC621BC" + $"2BC523BCDBC5B3BB00C552B8ABC5B0B9D5C581B816C4E5B756C311B7A6C402B8" + $"2BC23BB9D6C090B901C166B9C9BFA1BA44BDE6B9F1BEBAB9BBBCD8B8A8BABCB9" + $"31BBCAB93BB9F1BABDB8A9B9EFB93EBBCAB932BDE3BA44BCD6B9BBBEC2B9ECC0" + $"8FB9D8BFACB9CAC165B902C311B756C23BB82CC400B7A4C5B0B8AEC4E2B819C5" + $"81B9D7C52337C552BB014FBCE4C65CBE94C621BDB8C769BF1EC984C031C877BF" + $"A7C984C031C984C031C984C031C984C0310216C88ABF36C88ABF36C88AC031C8" + $"11C21EC860C12CC6E8C24DC496C2ABC5BFC27CC413C375C2ABC494C36AC418C2" + $"7BC5BEC21BC813C24BC6E9C12DC862BF34C888C031C888BEABC77BBD99C562BE" + $"22C66FBCB1C526BB30C427BBE1C4B7BA05C456B7AFC4B5B8DAC485B71CC3EAB6" + $"5BC215B6AAC306B730C140B8DBBF96B806C06BB8CEBEA6B949BCEAB8F6BDBFB8" + $"BFBBDCB7ADB9C1B836BACFB840B8F6B9C2B7AEB8F5B844BACFB837BCE8B949BB" + $"DCB8C0BDC8B8F0BF94B8DCBEB1B8CEC06AB807C215B65BC13FB731C305B6A9C4" + $"B5B7B3C3E7B71EC486B8DCC427BB30C457BA06C4BFBBEAC561BD99C526BCBEC6" + $"6FBE22C88ABF36C77CBEACC88ABF36C88ABF36C88ABF36C88ABF360606FF07BF" + $"3ABBCBBF3ABBCBBD53BBCBBBCBBF3ABBCBBD53BBCBC11EBF3AC2A8BD53C2A8C1" + $"1EC2A8C2A8BF3AC2A8C11EC2A8BD53BF3ABBCBC11EBBCBBF3ABBCBBBCB0606FF" + $"07BF3ABBCBBF3ABBCBBD53BBCBBBCBBF3ABBCBBD53BBCBC11EBF3AC2A8BD53C2" + $"A8C11EC2A8C2A8BF3AC2A8C11EC2A8BD53BF3ABBCBC11EBBCBBF3ABBCBBBCB02" + $"16C88ABF36C88ABF36C88AC031C811C21EC860C12CC6E8C24DC496C2ABC5BFC2" + $"7CC413C375C2ABC494C36AC418C27BC5BEC21BC813C24BC6E9C12DC862BF34C8" + $"88C031C888BEABC77BBD99C562BE22C66FBCB1C526BB30C427BBE1C4B7BA05C4" + $"56B7AFC4B5B8DAC485B71CC3EAB65BC215B6AAC306B730C140B8DBBF96B806C0" + $"6BB8CEBEA6B949BCEAB8F6BDBFB8BFBBDCB7ADB9C1B836BACFB840B8F6B9C2B7" + $"AEB8F5B844BACFB837BCE8B949BBDCB8C0BDC8B8F0BF94B8DCBEB1B8CEC06AB8" + $"07C215B65BC13FB731C305B6A9C4B5B7B3C3E7B71EC486B8DCC427BB30C457BA" + $"06C4BFBBEAC561BD99C526BCBEC66FBE22C88ABF36C77CBEACC88ABF36C88ABF" + $"36C88ABF36C88ABF36030A000200011001178400040A01020203100117840004" + $"0A0202040500" +}; diff --git a/assets/document-edit b/assets/document-edit new file mode 100644 index 0000000..cd4c795 Binary files /dev/null and b/assets/document-edit differ diff --git a/assets/document-open b/assets/document-open new file mode 100644 index 0000000..37fa293 Binary files /dev/null and b/assets/document-open differ diff --git a/assets/document-open.rdef b/assets/document-open.rdef new file mode 100644 index 0000000..efe92a3 --- /dev/null +++ b/assets/document-open.rdef @@ -0,0 +1,28 @@ + +resource(1007) #'VICN' array { + $"6E6369660604006603BF500003FFC8000300008002000602000000387F5CB87F" + $"5C0000004E7480490F8500FFFFFFFFD9D9FF020006020000003A8000BA800000" + $"00004E607A4A9FAE00FFEA80FFFFBB0009060AFF7F05B7EAC8B4B7EAC8B4BD76" + $"C8B4C88CC8B4C301C8B4C839C310C7922CC7E5BD6CC6442CC3A82CC4F62CC354" + $"B871C2AD31C301B91BBF6B31B8E531BC2731B891BEBFB7EAC8B4B83EC3B9B7EA" + $"C8B4C8B4C8B4C8B40608FF57B7E9C8B4B7E9C8B4B796C60DB6EFC0BDB743C365" + $"BD21C0BDC987C0BDC354C0BDC933C365C88CC8B4C8DFC60DC301C8B4B7E9C8B4" + $"BD75C8B4B7E9C8B4C8B4C8B4C8B40004B7EABFBEB7EABFBEB7EABE6BB7EABBC2" + $"B7EABD16BD76BBC2C88CBBC2C301BBC2C88CBD16C88CBFBEC88CBE6BC88CBFBE" + $"060AFF7F05B6F0C7B5B6F0C7B5BC7CC7B5C792C7B5C207C7B5C73EC211C697B6" + $"C7C6EABC6CC549B6C7C2ADB6C7C3FCB6C7C25BB772C1B3B8C5C207B81BBE70B8" + $"C5B7EAB8C5BB2EB8C5B798BDC0B6F0C7B5B744C2BAB6F0C7B5C7B5C7B5C7B506" + $"0AFF7F05B6F0C7B5B6F0C7B5BC7CC7B5C792C7B5C207C7B5C73EC211C697B6C7" + $"C6EABC6CC549B6C7C2ADB6C7C3FCB6C7C25BB772C1B3B8C5C207B81BBE70B8C5" + $"B7EAB8C5BB2EB8C5B798BDC0B6F0C7B5B744C2BAB6F0C7B5C7B5C7B5C7B50004" + $"B6F0BEBEB6F0BEBEB6F0BD6BB6F0BAC3B6F0BC17BC7CBAC3C792BAC3C207BAC3" + $"C792BC17C792BEBEC792BD6BC792BEBE0004B6F0BEBEB6F0BEBEB6F0BD6BB6F0" + $"BAC3B6F0BC17BC7CBAC3C792BAC3C207BAC3C792BC17C792BEBEC792BD6BC792" + $"BEBE0608FF57B6EFC7B5B6EFC7B5B69CC50DB5F4BFBDB648C266BC27BFBDC88C" + $"BFBDC25ABFBDC839C266C792C7B5C7E5C50DC206C7B5B6EFC7B5BC7AC7B5B6EF" + $"C7B5C7B5C7B5C7B50608FF57B6EFC7B5B6EFC7B5B69CC50DB5F4BFBDB648C266" + $"BC27BFBDC88CBFBDC25ABFBDC839C266C792C7B5C7E5C50DC206C7B5B6EFC7B5" + $"BC7AC7B5B6EFC7B5C7B5C7B5C7B5070A00030001021001178422040A01010310" + $"01178422040A020104000A0301051001178422040A040106000A010107100117" + $"8422040A05010800" +}; diff --git a/assets/edit-emblem b/assets/edit-emblem new file mode 100644 index 0000000..9b0b99e Binary files /dev/null and b/assets/edit-emblem differ diff --git a/assets/folder b/assets/folder new file mode 100644 index 0000000..feca7c2 Binary files /dev/null and b/assets/folder differ diff --git a/assets/folder.rdef b/assets/folder.rdef new file mode 100644 index 0000000..23c1890 --- /dev/null +++ b/assets/folder.rdef @@ -0,0 +1,22 @@ + +resource(1008) #'VICN' array { + $"6E636966070401640500020006022C3D6F3C691EBC691E2C3D6F4C9B14497B5C" + $"00FACE79FFBC4105020016022C66683CAA8FBCAA8F2C66684C8FE14A215C00FF" + $"FF8E020016023AE3D73C7999BC79993AE3D74C30CC445999009AFF5002000602" + $"3841473C9B33BC9B333841474C41704A2F3300FFE2ACFFF4980603A03D030706" + $"08FF7F4A5D4A5D4C5D505D4E5DC6A7CAC65459C730CA3DC83F595859C8C8595A" + $"575E535C55C9D7C6CA504FC7FCC6414EC7964A5D4CC9714A5D5D020C232F232F" + $"B585BC6E2D49B6DABFE1BC084F4A5BBFE15550C4435C2C56BE0658B783502A54" + $"B740C598B7404C2CC50FB78346B7403A2840B6B7BD18B6DA382DBCD3B783BB80" + $"B7E9302BBA6FB7A6B95CB878B956BAA6B959B98FB79FBA17232FB5E8B988232F" + $"232F232F232F0608FF7F3A283A2836332E49323EBC4BC5984A5AC004C7D950C4" + $"005C2C56BDE358B783502A54B740C598B7404C2CC50FB78346B7403A2840B6B7" + $"3A28280606FF07302B302B30353049303FBCD4C5754A59C047C796C4A9C50F51" + $"36C598C06A46BA4E302B3BB8D8302B2B0606FF07302B302B3BB8D8513646BA4E" + $"C598C06A4A59C4A9C50FC50FC4D854BB1DC664BFFB48B9DF302B3CB8A0302B2B" + $"0606FF07232F232FB585BC6E2D49B6DABFE1BC084F4A5BBFE155C334C6A7463E" + $"C2ABC2CEBD7D39232FB8D834232F2F0606FF07232F232FB8D834463EBD7D39C2" + $"ABC2CE4A5BC334C6A7C389C664C3233C49C2453CBC6D232FB92DBAB4232F2F07" + $"0A000100000A0101011001178400040A020102000A030103000A040104000A05" + $"0105000A06010600" +}; diff --git a/assets/haiku-icon b/assets/haiku-icon new file mode 100644 index 0000000..8e339fd Binary files /dev/null and b/assets/haiku-icon differ diff --git a/assets/haiku-icon.rdef b/assets/haiku-icon.rdef new file mode 100644 index 0000000..913d768 --- /dev/null +++ b/assets/haiku-icon.rdef @@ -0,0 +1,31 @@ + +resource vector_icon array { + $"6E63696605050002010602C07FEC347267B45AEAC067ED4C37354C23A69AFFAA" + $"00FFFFBD3E0201040200FF0000FFFFE0A502000602BA3F2DA6FE6428A632BBF9" + $"FA47F3344870859AFFAA00FFFFBD3E0200060239D421B85A383A14EA3B6357C4" + $"D66E43223D9AFFAA00FFFFBD3E0702044030C34530BC3A30304030BC3A30C345" + $"4050BC3A50C34550504050C34550BC3A02044030C34530BC3A30304030BC3A30" + $"C3454050BC3A50C34550504050C34550BC3A0803273327272D2E0A0327332727" + $"2D2E02043432363231322F3A2F362F3E334330433643383A383E38360405EE02" + $"2E4A354E304E3A4E3B4B414E3C4E464E464B0802244A2F4A1A0A0001021A4155" + $"55000000000000415555C55555C6AAAA11FF01178222040A0001020A41555500" + $"0000000000415555C55555C6AAAA00100A0001000241C31700000000000041C2" + $"DDC9862EC985BB0A0101010A415FFF000000000000416000C8BFFFC8C00011FF" + $"0A0101000A417FFF000000000000418000C8FFFFC9000040FF0A0101000A4100" + $"0000000000000040FFFFC80000C7FFFF00100A0301020A422AAA000000000000" + $"41E666C51555C5533300100A0301020A415555000000000000410000C4D555C5" + $"C00011200A000102124110AD0000000000004108E8C46A0EC66EF30117812204" + $"0A0301020A415555000000000000410000C4D555C5C00020FF0A020102023E5F" + $"240000000000003D90FD444807462CA70A000102123E5F240000000000003D90" + $"FD444807462CA701178322040A0401020AC02B2F0000000000004086114B3D6E" + $"C433AC20FF0A020202030ABB81600000000000003B5A894AA5DE46C48820FF0A" + $"0001021ABD16050000000000003C57EF4AC889465F3320FF01178422040A0001" + $"021AC02B2F0000000000004030BC4B3D6EC33CAD21FF01178122040A0001021A" + $"C02B2F0000000000004030BC4B3D6EC33CAD112001178222040A000104000A00" + $"0104202E200A0001051001178200040A00010612400000000000000000420000" + $"000000CA980001178100040A000106123FD5FB3C44A6BC44A63FD5FB47130DC2" + $"F85301178100040A000106123FD3BABC53AC3C53AC3FD3BAC6F38945CA1A0117" + $"8100040A00010612C000000000000000004200004B5000CA980001178100040A" + $"00010612BFD5FB3C44A63C44A63FD5FB4A8B3CC2F85301178100040A00010612" + $"BFD3BABC53ACBC53AC3FD3BA4C067145CA1A0117810004" +}; diff --git a/assets/help-about b/assets/help-about new file mode 100644 index 0000000..f4fc0ae Binary files /dev/null and b/assets/help-about differ diff --git a/assets/help-about.rdef b/assets/help-about.rdef new file mode 100644 index 0000000..b8851fc --- /dev/null +++ b/assets/help-about.rdef @@ -0,0 +1,37 @@ + +resource(1009) #'VICN' array { + $"6E636966040400660500020016020000003D0601BD06010000004E3AD149D6EE" + $"0080FF28020002033D7FD40000000000003D7FD449FFD3CB172600FF05052679" + $"FF060681FFFF040426160003C552C961C552C961C5A9C961C656C961C600C961" + $"C600C9B7C552CA65C5A9CA0EC552CA650003C144C961C144C961C19BC9B7C248" + $"CA65C1F1CA0EC248CA0EC248C961C248C9B7C248C9610003BE3ACA65BE3ACA65" + $"BDE3CA0EBD36C961BD8DC9B7BD36C9B7BD36CA65BD36CA0EBD36CA650003BA2B" + $"C961BA2BC961B9D4C9B7B928CA65B97ECA0EB97ECA65BA2BCA65B9D4CA65BA2B" + $"CA650606FF07C75AC656C75AC656C75AC0EEC75AB61CC75ABB85C703B61CC656" + $"B61CC6ADB61CC656BB85C656C656C656C0EEC6ADC656C75AC656C703C656C75A" + $"C656C6560002C248B61CC248B61CC248BB85C248C656C248C0EEC248C6560002" + $"CA65B61CCA65B61CCA65BCDFCA65CA65CA65C3A2CA65CA650606FF07BF3DC656" + $"BF3DC656BF3DC0EEBF3DB61CBF3DBB85BEE6B61CBE3AB61CBE90B61CBE3ABB85" + $"BE3AC656BE3AC0EEBE90C656BF3DC656BEE6C656BF3DC656C6560606FF07BB2E" + $"C656BB2EC656BB2EC0EEBB2EB61CBB2EBB85BA81B61CB928B61CB9D5B61CB928" + $"BB85B928C656B928C0EEB9D5C656BB2EC656BA81C656BB2EC656C6560002B61C" + $"B61CB61CB61CB61CBCDFB61CCA65B61CC3A2B61CCA650003C44FC85EC44FC85E" + $"C4A5C85EC552C85EC4FCC85EC4FCC8B4C44FC961C4A5C90AC44FC9610003C040" + $"C85EC040C85EC097C8B4C144C961C0EEC90AC144C90AC144C85EC144C8B4C144" + $"C85E0003BD36C961BD36C961BCDFC90ABC32C85EBC89C8B4BC32C8B4BC32C961" + $"BC32C90ABC32C9610003B928C85EB928C85EB8D1C8B4B824C961B87AC90AB87A" + $"C961B928C961B8D1C961B928C9610606FF07C656C552C656C552C656BFEAC656" + $"B518C656BA81C600B518C552B518C5A9B518C552BA81C552C552C552BFEAC5A9" + $"C552C656C552C600C552C656C552C5520002C144B518C144B518C144BA81C144" + $"C552C144BFEAC144C5520002C961B518C961B518C961BBDBC961C961C961C29E" + $"C961C9610606FF07BE3AC552BE3AC552BE3ABFEABE3AB518BE3ABA81BDE3B518" + $"BD36B518BD8DB518BD36BA81BD36C552BD36BFEABD8DC552BE3AC552BDE3C552" + $"BE3AC552C5520606FF07BA2BC552BA2BC552BA2BBFEABA2BB518BA2BBA81B97E" + $"B518B824B518B8D1B518B824BA81B824C552B824BFEAB8D1C552BA2BC552B97E" + $"C552BA2BC552C5520002B518B518B518B518B518BBDBB518C961B518C29EB518" + $"C9610606FF07BA2BC552BA2BC552BA2BBFEABA2BB518BA2BBA81B97EB518B824" + $"B518B8D1B518B824BA81B824C552B824BFEAB8D1C552BA2BC552B97EC552BA2B" + $"C552C5520002B518C040B518C040BC32BF93CA65BE3AC34BBEE7CA65BE3A040A" + $"000A000102030405060708091001178222040A010A0A0B0C0D0E0F1011121310" + $"01178222040A020114000A030115100117822204" +}; diff --git a/assets/help-contents b/assets/help-contents new file mode 100644 index 0000000..dd07699 Binary files /dev/null and b/assets/help-contents differ diff --git a/assets/help-contents.rdef b/assets/help-contents.rdef new file mode 100644 index 0000000..8ebe71b --- /dev/null +++ b/assets/help-contents.rdef @@ -0,0 +1,17 @@ + +resource(1010) #'VICN' array { + $"6E6369660504006603800000020006020000003CEA53BCEA530000004E2B0249" + $"D85700FFABABFFD900000554020016020000003CEA53BCEA530000004E2B0249" + $"D85700FFFFE50C0002C56BC56BC56BC56BC848C28DC56BBB0CC848BDE9C56BBB" + $"0C0002BB0CBB0CBB0CBB0CB82FBDE9BB0CC56BB82FC28DBB0CC56B0002BB0CC5" + $"6BBB0CC56BBDE9C848C56BC56BC28DC848C56BC56B0002C56BBB0CC56BBB0CC2" + $"8DB82FBB0CBB0CBDE9B82FBB0CBB0C0002BA11C470BA11C470BCEFC74EC470C4" + $"70C192C74EC470C4700002C470BA11C470BA11C192B734BA11BA11BCEFB734BA" + $"11BA110002BA11C470BA11C470BCEFC74EC470C470C192C74EC470C4700002C4" + $"70BA11C470BA11C192B734BA11BA11BCEFB734BA11BA110002BA11BA11BA11BA" + $"11B734BCEFBA11C470B734C192BA11C4700002C470C470C470C470C74EC192C4" + $"70BA11C74EBCEFC470BA110002BA11BA11BA11BA11B734BCEFBA11C470B734C1" + $"92BA11C4700002C470C470C470C470C74EC192C470BA11C74EBCEFC470BA1105" + $"0A0004000102031001178E00040A010204051001178E00040A02020607100117" + $"8900040A030208091001178E00040A04020A0B100117890004" +}; diff --git a/assets/licenses/vgmstream.txt b/assets/licenses/vgmstream.txt new file mode 100644 index 0000000..493da2a --- /dev/null +++ b/assets/licenses/vgmstream.txt @@ -0,0 +1,22 @@ +Copyright (c) 2008-2019 Adam Gashlin, Fastelbja, Ronny Elfert, bnnm, + Christopher Snowhill, NicknineTheEagle, bxaimc, + Thealexbarney, CyberBotX, et al + +Portions Copyright (c) 2004-2008, Marko Kreen +Portions Copyright 2001-2007 jagarl / Kazunori Ueno +Portions Copyright (c) 1998, Justin Frankel/Nullsoft Inc. +Portions Copyright (C) 2006 Nullsoft, Inc. +Portions Copyright (c) 2005-2007 Paul Hsieh +Portions Public Domain originating with Sun Microsystems + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/assets/licenses/x16emu.txt b/assets/licenses/x16emu.txt new file mode 100644 index 0000000..28b02f5 --- /dev/null +++ b/assets/licenses/x16emu.txt @@ -0,0 +1,9 @@ +Copyright (c) 2019 Michael Steil + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/assets/media-playback-pause.rdef b/assets/media-playback-pause.rdef new file mode 100644 index 0000000..c4596c7 --- /dev/null +++ b/assets/media-playback-pause.rdef @@ -0,0 +1,19 @@ + +resource(1003) #'VICN' array { + $"6E6369660404006603005900020006020000003CEA27BCCA720000004E05DE49" + $"D8680080FF80FF00B200020006020000003CEA27BCCA720000004E68F249D868" + $"0080FF80FF00B200060606FF07BE27B6F2BE27B6F2BC5CB6F2B8C5B6F2BA90B6" + $"F2B8C5BD23B8C5C985B8C5C354BA90C985BE27C985BC5CC985BE27C354BE27B6" + $"F2BE27BD23BE27B6F2B6F20606FF07BD41B5F8BD41B5F8BB76B5F8B7DFB5F8B9" + $"AAB5F8B7DFBC29B7DFC88CB7DFC25AB9AAC88CBD41C88CBB76C88CBD41C25ABD" + $"41B5F8BD41BC29BD41B5F8B5F80606FF07BD41B5F8BD41B5F8BB76B5F8B7DFB5" + $"F8B9AAB5F8B7DFBC29B7DFC88CB7DFC25AB9AAC88CBD41C88CBB76C88CBD41C2" + $"5ABD41B5F8BD41BC29BD41B5F8B5F80606FF07C804B6F2C804B6F2C639B6F2C2" + $"A2B6F2C46EB6F2C2A2BD23C2A2C985C2A2C354C46EC985C804C985C639C985C8" + $"04C354C804B6F2C804BD23C804B6F2B6F20606FF07C71EB5F8C71EB5F84EB5F8" + $"45B5F8C389B5F845BC2945C88C45C25AC389C88CC71EC88C4EC88CC71EC25AC7" + $"1EB5F8C71EBC29C71EB5F8B5F80606FF07C71EB5F8C71EB5F84EB5F845B5F8C3" + $"89B5F845BC2945C88C45C25AC389C88CC71EC88C4EC88CC71EC25AC71EB5F8C7" + $"1EBC29C71EB5F8B5F8060A0001001001178422040A0101011001178422040A02" + $"0102000A0001031001178422040A0101041001178422040A03010500" +}; diff --git a/assets/media-playback-start.rdef b/assets/media-playback-start.rdef new file mode 100644 index 0000000..ca0493e --- /dev/null +++ b/assets/media-playback-start.rdef @@ -0,0 +1,11 @@ + +resource(1002) #'VICN' array { + $"6E6369660304006603005900020006020000003CEA3DBCEA3D0000004E387A49" + $"D7FF0080FF80FF00B200030605FF01C91AC03BC91AC03BC376BD222DB6EFBDD2" + $"BA082DBD212DC9862DC353BDD2C66DC91AC03BC376C354C91AC03BC03B0605FF" + $"01C81CBF40C81CBF40C278BC27B72FB5F5BCD3B90EB72FBC27B72FC88BB72FC2" + $"59BCD3C572C81CBF40C278C259C81CBF40BF400605FF01C81CBF40C81CBF40C2" + $"78BC27B72FB5F5BCD3B90EB72FBC27B72FC88BB72FC259BCD3C572C81CBF40C2" + $"78C259C81CBF40BF40030A0001001001178422040A0101011001178422040A02" + $"010200" +}; diff --git a/assets/media-playback-stop.rdef b/assets/media-playback-stop.rdef new file mode 100644 index 0000000..99b6efc --- /dev/null +++ b/assets/media-playback-stop.rdef @@ -0,0 +1,12 @@ + +resource(1004) #'VICN' array { + $"6E636966030400660500020016020000003CEA53BCEA530000004E2AF949D85B" + $"0080FF28030606FF07C986C986C986C986C986C354C986B6F2C986BD23C354B6" + $"F2B6F2B6F2BD23B6F2B6F2BD23B6F2C986B6F2C354BD23C986C986C986C354C9" + $"86C986C986C9860606FF07C88CC88CC88CC88CC88CC25AC88CB5F7C88CBC29C2" + $"5AB5F7B5F7B5F7BC29B5F7B5F7BC29B5F7C88CB5F7C25ABC29C88CC88CC88CC2" + $"5AC88CC88CC88CC88C0606FF07C88CC88CC88CC88CC88CC25AC88CB5F7C88CBC" + $"29C25AB5F7B5F7B5F7BC29B5F7B5F7BC29B5F7C88CB5F7C25ABC29C88CC88CC8" + $"8CC25AC88CC88CC88CC88C030A0001001001178422040A010101100117842204" + $"0A02010200" +}; diff --git a/assets/slider-mode b/assets/slider-mode new file mode 100644 index 0000000..6522132 Binary files /dev/null and b/assets/slider-mode differ diff --git a/assets/slider-mode.rdef b/assets/slider-mode.rdef new file mode 100644 index 0000000..a8d66b6 --- /dev/null +++ b/assets/slider-mode.rdef @@ -0,0 +1,39 @@ + +resource(1001) #'VICN' array { + $"6E6369660D0501040066030059000200060200000038C5B0B8AED50000004EAB" + $"3E4B4EB70080FF80FF00B200057B0200160400000038C5B0BF55DA0000004BAC" + $"F8483D6F00FFB7DABADCFFA70200160400000038C5B0BF55DA0000004BACF848" + $"3D6F00FFB7DABADCFFFF0200160400000038C5B0BF55DA0000004BACF8483D6F" + $"00FFB7DABADCFFA7040082033C23000200060238466638370AB8370A3846664C" + $"5ECCC753D700FFB629FFD85A0502000602386EB837F999B7F999386EB84A1570" + $"4958F500FFEBCEFFD3B283054D0F0605FF01BB73C6FABB73C6FABE56C6FAC41E" + $"C6FAC13AC6FAC2ACC874BFC8CB66C13AC9EDBE56C9EDBB73C6FABCE5C874BB73" + $"C6FAC6FA0605FF01BA5DC5E0BA5DC5E0BD41C5E0C308C5E0C025C5E0C197C759" + $"BEB3CA4BC025C8D2BD41C8D2BA5DC5E0BBCFC759BA5DC5E0C5E00605FF01BA5D" + $"C5E0BA5DC5E0BD41C5E0C308C5E0C025C5E0C197C759BEB3CA4BC025C8D2BD41" + $"C8D2BA5DC5E0BBCFC759BA5DC5E0C5E00606B20C2739585B355B385B30582F27" + $"2434243024380A04382E383A483A482E0A04382D383B483B482D0A04382D383B" + $"483B482D0802403140370606FF07C67EB79BC67EB79BC253BBC7B9FDC41E3CBF" + $"F3BB61C582BE2AC849BCC5C6E6C254C41ECAAABBC8C67FBFF3C946BA63C67EB7" + $"9BC7E2B8FFC67EB79BB79B0606FF07B9FDC41EB9FDC41EB986C50CB89AC6E6B9" + $"10C5F9B987C7D3BB62C9ABBA74C8BFBC4FC935BE2AC849BD3CC8BFBCC5C6E6B9" + $"FDC41EBB61C582B9FDC41EC41E0605FF01B89AC6E6B89AC6E6B823C84AB735CB" + $"10B7ACC9ADB899CA99BB62C9ABB9FDCA22BA74C8BFB89AC6E6B987C7D3B89AC6" + $"E6C6E60607FF1FC54CB604C54CB604C121BA2FB8CCC286BCF7BE5BB7DEC4D7B6" + $"04C978B6F1C728B855C88BBCF8C6B1BAA6C79EC122C286C978BA30C54DBE5BC8" + $"14B8CCC54CB604C6B0B768C54CB604B6040606FF07C54CB604C54CB604C121BA" + $"2FB8CCC286BCF7BE5BBA30C3EABCF8C6B1BB94C54EC122C286C978BA30C54DBE" + $"5BC814B8CCC54CB604C6B0B768C54CB604B6040606FF07B8CCC286B8CCC286B8" + $"55C374B768C54EB7DEC461B855C63BBA30C813B942C727BB1DC79DBCF8C6B1BC" + $"0AC727BB94C54EB8CCC286BA30C3EAB8CCC286C2860605FF01B768C54EB768C5" + $"4EB6F1C6B2B604C978B67AC815B768C901BA30C813B8CCC88AB942C727B768C5" + $"4EB855C63BB768C54EC54E0F0A0102040330212F01178522040A040103201E2C" + $"0A000103301E2C01178200040A000103381E2C001001178300040A050104201D" + $"2C0A000104381D2C21FF01178222040A000105381D2C102001178222040A0001" + $"06381D2C001001178310040A010107301D2C01178200040A0803090A08023CE9" + $"E90000000000003CE9E94A5C404A666C0A09010B123CE9E90000000000003CE9" + $"E94A5C404A666C01178C02040A09010B1A3CE9E90000000000003CE9E94A5C40" + $"4A666C00100117A002040A0A010C023CE9E90000000000003CE9E94A5C404A66" + $"6C0A0B010D023CE9E90000000000003CE9E94A5C404A666C0A0C010E023CE9E9" + $"0000000000003CE9E94A5C404A666C" +}; diff --git a/assets/text-frame b/assets/text-frame new file mode 100644 index 0000000..248ee84 Binary files /dev/null and b/assets/text-frame differ diff --git a/assets/text-mode b/assets/text-mode new file mode 100644 index 0000000..fd391ee Binary files /dev/null and b/assets/text-mode differ diff --git a/assets/text-mode.rdef b/assets/text-mode.rdef new file mode 100644 index 0000000..8cd0821 --- /dev/null +++ b/assets/text-mode.rdef @@ -0,0 +1,37 @@ + +resource(1000) #'VICN' array { + $"6E6369660B0400660500030059000200060200000038C5B0B8AED50000004EAB" + $"3E4B4EB70080FF80FF00B200050102001602B030EF3E6074BF5FC0B11C4B4BB3" + $"38432C6969D6FFF8040082033C23000200060238466638370AB8370A3846664C" + $"5ECCC753D700FFB629FFD85A0502000602386EB837F999B7F999386EB84A1570" + $"4958F500FFEBCEFFD3B283054D0A06043F254925BE4DBCAF495B49C2D0495BBE" + $"4D5B235BB93AC2D023250606FF07B67AC211B67AC211B67AC274B67AC33AB67A" + $"C2D7B6DDC33AB7A2C33AB740C33AB7A2C2D7B7A2C211B7A2C274B740C211B67A" + $"C211B6DDC211B67AC211C2110608FF7FC4F7B7A2C4F7B7A2C690B7A2C7DDBB1D" + $"C7DDB931C7DDBC45C7DDBE97C7DDBD6EC7DDC082C4F7C211C690C211C35DC211" + $"C211BE97C211C082C211BD6EC211BB1DC211BC45C211B931C4F7B7A2C35DB7A2" + $"C4F7B7A2B7A20606FF07C67EB79BC67EB79BC253BBC7B9FDC41E3CBFF3BB61C5" + $"82BE2AC849BCC5C6E6C254C41ECAAABBC8C67FBFF3C946BA63C67EB79BC7E2B8" + $"FFC67EB79BB79B0606FF07B9FDC41EB9FDC41EB986C50CB89AC6E6B910C5F9B9" + $"87C7D3BB62C9ABBA74C8BFBC4FC935BE2AC849BD3CC8BFBCC5C6E6B9FDC41EBB" + $"61C582B9FDC41EC41E0605FF01B89AC6E6B89AC6E6B823C84AB735CB10B7ACC9" + $"ADB899CA99BB62C9ABB9FDCA22BA74C8BFB89AC6E6B987C7D3B89AC6E6C6E606" + $"07FF1FC54CB604C54CB604C121BA2FB8CCC286BCF7BE5BB7DEC4D7B604C978B6" + $"F1C728B855C88BBCF8C6B1BAA6C79EC122C286C978BA30C54DBE5BC814B8CCC5" + $"4CB604C6B0B768C54CB604B6040606FF07C54CB604C54CB604C121BA2FB8CCC2" + $"86BCF7BE5BBA30C3EABCF8C6B1BB94C54EC122C286C978BA30C54DBE5BC814B8" + $"CCC54CB604C6B0B768C54CB604B6040606FF07B8CCC286B8CCC286B855C374B7" + $"68C54EB7DEC461B855C63BBA30C813B942C727BB1DC79DBCF8C6B1BC0AC727BB" + $"94C54EB8CCC286BA30C3EAB8CCC286C2860605FF01B768C54EB768C54EB6F1C6" + $"B2B604C978B67AC815B768C901BA30C813B8CCC88AB942C727B768C54EB855C6" + $"3BB768C54EC54E0F0A0601001240000000000000000040000041FFFF46800001" + $"178523040A0501000240000000000000000040000090987A45FFFF0A01010038" + $"2028001001178422040A01010038202810FF01178222040A04010128202511FF" + $"0A040102380B2511FF01178310040A04010238202511FF01178300040A040102" + $"380725001001178610040A040102381E25001001178700040A0603040503023C" + $"E9E90000000000003CE9E94A5C404A666C0A070106123CE9E90000000000003C" + $"E9E94A5C404A666C01178C02040A0701061A3CE9E90000000000003CE9E94A5C" + $"404A666C00100117A002040A080107023CE9E90000000000003CE9E94A5C404A" + $"666C0A090108023CE9E90000000000003CE9E94A5C404A666C0A0A0109023CE9" + $"E90000000000003CE9E94A5C404A666C" +}; diff --git a/assets/update_assets.py b/assets/update_assets.py index 16cfabb..2dc223a 100755 --- a/assets/update_assets.py +++ b/assets/update_assets.py @@ -88,6 +88,8 @@ add_license("licenses/lgpl-2.0.txt", "lgpl_2_0") add_license("licenses/ForkAwesome.txt", "forkawesome") add_license("licenses/cli11.txt", "cli11") add_license("licenses/TomlPlusPlus.txt", "tomlplusplus") +add_license("licenses/x16emu.txt", "x16emu") +add_license("licenses/vgmstream.txt", "vgmstream") add_license("../backends/ui/imgui/IconFontCppHeaders/licence.txt", "icnfntcpphdrs") add_css("gtk-frontend.css", "gtk_frontend_css") add_dbus('app.dbus.xml', 'dbus_stub') diff --git a/assets/view-refresh.rdef b/assets/view-refresh.rdef new file mode 100644 index 0000000..07977c8 --- /dev/null +++ b/assets/view-refresh.rdef @@ -0,0 +1,24 @@ + +resource(1005) #'VICN' array { + $"6E6369660404006603004080020006020000003A74EDBA74FF0000004E60F448" + $"285300ABD5FFFF006CD902000602000000BA74ED3A74FF000000CCD5964AC45B" + $"00006CD9FFAAD4FF060608FF7FC986B6F2C986B6F2C986B98EC986BEC5C986BC" + $"29C6EABEC5C1B3BEC5C44FBEC5C284BDF4C425BC53C354BD23C1B3B8E6B6F2BA" + $"DBBB58B76FBA5EB6F2C714B963C1E3B52CC7E5B893C986B6F2C8B5B7C2C986B6" + $"F2B6F20608FF7FC88CB5F7C88CB5F7C88CB893C88CBDCAC88CBB2FC5F0BDCAC0" + $"B9BDCAC355BDCAC18ABCFAC32BBB58C25ABC29C0B9B7ECB5F7B9E0BA5EB675B9" + $"64B5F7C61AB869C0E9B433C6EBB798C88CB5F7C7BBB6C8C88CB5F7B5F70608FF" + $"7FC88CB5F7C88CB5F7C88CB893C88CBDCAC88CBB2FC5F0BDCAC0B9BDCAC355BD" + $"CAC18ABCFAC32BBB58C25ABC29C0B9B7ECB5F7B9E0BA5EB675B964B5F7C61AB8" + $"69C0E9B433C6EBB798C88CB5F7C7BBB6C8C88CB5F7B5F70608FF7FB6F2C986B6" + $"F2C986B6F2C6EBB6F2C1B3B6F2C44FB98DC1B3BEC5C1B3BC29C1B3BDF4C284BC" + $"53C426BD23C355BEC5C791C987C59DC520C909C61AC986B964C715BE95CB4BB8" + $"93C7E5B6F2C986B7C2C8B6B6F2C986C9860608FF7FB5F8C88CB5F8C88CB5F8C5" + $"F0B5F8C0B9B5F8C355B893C0B9BDCAC0B9BB2FC0B9BCFAC18ABB59C32BBC29C2" + $"5ABDCAC698C88CC4A3C426C80FC520C88CB869C61BBD9BCA52B799C6EBB5F8C8" + $"8CB6C8C7BBB5F8C88CC88C0608FF7FB5F8C88CB5F8C88CB5F8C5F0B5F8C0B9B5" + $"F8C355B893C0B9BDCAC0B9BB2FC0B9BCFAC18ABB59C32BBC29C25ABDCAC698C8" + $"8CC4A3C426C80FC520C88CB869C61BBD9BCA52B799C6EBB5F8C88CB6C8C7BBB5" + $"F8C88CC88C060A0001001001178422040A0101011001178422040A020102000A" + $"0001031001178422040A0101041001178422040A03010500" +}; diff --git a/backend.hpp b/backend.hpp index b992d68..0b4f7f9 100644 --- a/backend.hpp +++ b/backend.hpp @@ -6,6 +6,12 @@ #include #include "playback.h" #include "dbus.hpp" +#define BACKEND_TYPE(type) \ + public: \ + inline int64_t default_priority() { \ + return (int64_t)UIBackend::PriorityType::type; \ + } \ + private: class UIBackend { protected: std::vector args; @@ -19,6 +25,13 @@ class UIBackend { bool pitch_set = false; bool multi_instance = false; bool daemon_found = false; + enum class PriorityType : int64_t { + Default = 0, + Native = 100, + MetaToolkit = 50, + NonNative = 0, + Fallback = -100 + }; public: DBusAPI *dbus_api; inline virtual bool allow_multi_instance() { @@ -31,6 +44,9 @@ class UIBackend { inline virtual void add_licenses() { // Don't add any here, but leave this specified. That way, licenses specific to UI frontends are only added per UI frontend. } + inline int64_t default_priority() { + return (int64_t)PriorityType::Default; + } void init_libportal(); void init_playback(); void setup_playback_args(); @@ -108,4 +124,4 @@ class UIBackend { static UIBackend *running_ui_backend; virtual ~UIBackend(); }; -void init_backends(); \ No newline at end of file +void init_backends(); diff --git a/backends/playback/sdl_mixer_x/backend.json b/backends/playback/sdl_mixer_x/backend.json index 682ea27..ec8952e 100644 --- a/backends/playback/sdl_mixer_x/backend.json +++ b/backends/playback/sdl_mixer_x/backend.json @@ -1,4 +1,5 @@ { "class_name": "SDLMixerXBackend", - "include_path": "sdl_mixer_x.hpp" + "include_path": "sdl_mixer_x.hpp", + "mime_types": ["audio/x-wav", "audio/x-flac", "audio/mid", "audio/midi", "audio/ogg", "audio.x-midi", "audio/wav"] } \ No newline at end of file diff --git a/backends/playback/sdl_mixer_x/cmake_install.cmake b/backends/playback/sdl_mixer_x/cmake_install.cmake new file mode 100644 index 0000000..e3344c0 --- /dev/null +++ b/backends/playback/sdl_mixer_x/cmake_install.cmake @@ -0,0 +1,39 @@ +# Install script for directory: /boot/home/Desktop/looper/backends/playback/sdl_mixer_x + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/boot/system") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/system/bin/objdump") +endif() + diff --git a/backends/playback/sdl_mixer_x/sdl_mixer_x.cpp b/backends/playback/sdl_mixer_x/sdl_mixer_x.cpp index 192f874..59a6f7f 100644 --- a/backends/playback/sdl_mixer_x/sdl_mixer_x.cpp +++ b/backends/playback/sdl_mixer_x/sdl_mixer_x.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include std::optional SDLMixerXBackend::get_max_samples() { return 4096; } @@ -144,3 +146,9 @@ void SDLMixerXBackend::cleanup() { open = false; Mix_CloseAudio(); } +void SDLMixerXBackend::add_licenses() { + auto &license_data = get_license_data(); + auto smx = LicenseData("SDL Mixer X", "Zlib"); + LOAD_LICENSE(smx, sdl_mixer_x); + license_data.insert(smx); +} diff --git a/backends/playback/sdl_mixer_x/sdl_mixer_x.hpp b/backends/playback/sdl_mixer_x/sdl_mixer_x.hpp index 404297a..fc42678 100644 --- a/backends/playback/sdl_mixer_x/sdl_mixer_x.hpp +++ b/backends/playback/sdl_mixer_x/sdl_mixer_x.hpp @@ -21,6 +21,7 @@ class SDLMixerXBackend : public PlaybackBackend { void switch_stream(int idx) override; void cleanup() override; size_t render(void *buf, size_t maxlen) override; + void add_licenses() override; SDLMixerXBackend(); ~SDLMixerXBackend() override; }; diff --git a/backends/playback/vgmstream/cmake_install.cmake b/backends/playback/vgmstream/cmake_install.cmake new file mode 100644 index 0000000..ef78d67 --- /dev/null +++ b/backends/playback/vgmstream/cmake_install.cmake @@ -0,0 +1,39 @@ +# Install script for directory: /boot/home/Desktop/looper/backends/playback/vgmstream + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/boot/system") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/system/bin/objdump") +endif() + diff --git a/backends/playback/vgmstream/vgmstream.cpp b/backends/playback/vgmstream/vgmstream.cpp index 393ffa1..ee17eda 100644 --- a/backends/playback/vgmstream/vgmstream.cpp +++ b/backends/playback/vgmstream/vgmstream.cpp @@ -8,6 +8,8 @@ extern "C" { #include "file_backend.hpp" #include #include +#include +#include void VgmStreamBackend::load(const char *filename) { spec.format = AUDIO_S16SYS; file = open_file(filename); @@ -134,4 +136,10 @@ double VgmStreamBackend::get_position() { } int VgmStreamBackend::get_stream_idx() { return vf->stream_index; -} \ No newline at end of file +} +void VgmStreamBackend::add_licenses() { + auto &license_data = get_license_data(); + auto vgmstream = LicenseData("VGMSTREAM", "ISC"); + LOAD_LICENSE(vgmstream, vgmstream); + license_data.insert(vgmstream); +} diff --git a/backends/playback/vgmstream/vgmstream.hpp b/backends/playback/vgmstream/vgmstream.hpp index 559455e..1a2e475 100644 --- a/backends/playback/vgmstream/vgmstream.hpp +++ b/backends/playback/vgmstream/vgmstream.hpp @@ -24,5 +24,6 @@ class VgmStreamBackend : public PlaybackBackend { int get_stream_idx() override; size_t render(void *buf, size_t maxlen) override; double get_position() override; + void add_licenses() override; inline ~VgmStreamBackend() override { } }; diff --git a/backends/playback/zsm/backend.json b/backends/playback/zsm/backend.json index 0cba29f..26e7825 100644 --- a/backends/playback/zsm/backend.json +++ b/backends/playback/zsm/backend.json @@ -1,4 +1,7 @@ { "class_name": "ZsmBackend", - "include_path": "zsm_backend.hpp" + "include_path": "zsm_backend.hpp", + "mime_types": [ + "audio/x-zsound" + ] } diff --git a/backends/playback/zsm/cmake_install.cmake b/backends/playback/zsm/cmake_install.cmake new file mode 100644 index 0000000..e0608bc --- /dev/null +++ b/backends/playback/zsm/cmake_install.cmake @@ -0,0 +1,39 @@ +# Install script for directory: /boot/home/Desktop/looper/backends/playback/zsm + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/boot/system") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/system/bin/objdump") +endif() + diff --git a/backends/playback/zsm/zsm_backend.cpp b/backends/playback/zsm/zsm_backend.cpp index f3c30d4..5f52415 100644 --- a/backends/playback/zsm/zsm_backend.cpp +++ b/backends/playback/zsm/zsm_backend.cpp @@ -14,6 +14,8 @@ extern "C" { #include #include #include +#include +#include #define HZ (AUDIO_SAMPLERATE) #define BUFFERS 32 #define _PROPERTY(name, type, default_value) \ @@ -205,11 +207,18 @@ void ZsmBackend::tick(bool step) { } break; case FmWrite: { for (uint8_t i = 0; i < cmd.fm_write.len; i++) { - DEBUG.writefln2("YM Pair index: {0}", ym_pairs.size()); - reg_pair pair; - memcpy(&pair, cmd.fm_write.regs + i, sizeof(reg_pair)); - ym_pairs.push(pair); - DEBUG.writefln2("Writing {1} to FM reg {0} later.", pair.reg, pair.val); + auto &pair = cmd.fm_write.regs[i]; + YM_write_reg(pair.reg, pair.val); + while (YM_read_status()) { + size_t clocksToAddForYm = (size_t)std::ceil(((double)YM_FREQ)/((double)PSG_FREQ)); + ticks_remaining -= clocksToAddForYm; + if (ticks_remaining < 0) { + delayTicks -= 1; + nextCpuClocks += ClocksPerTick; + ticks_remaining += ClocksPerTick; + } + audio_step(clocksToAddForYm); + } } } break; case Delay: { @@ -258,17 +267,6 @@ void ZsmBackend::tick(bool step) { } while (ym_pairs.size() > 0) { reg_pair pair = ym_pairs.pop(); - YM_write_reg(pair.reg, pair.val); - while (YM_read_status()) { - size_t clocksToAddForYm = (size_t)std::ceil(((double)YM_FREQ)/((double)PSG_FREQ)); - ticks_remaining -= clocksToAddForYm; - if (ticks_remaining < 0) { - delayTicks -= 1; - nextCpuClocks += ClocksPerTick; - ticks_remaining += ClocksPerTick; - } - audio_step(clocksToAddForYm); - } } size_t nextCpuClocksInt = std::floor(nextCpuClocks); size_t prevCpuClocksInt = std::floor(prevCpuClocks); @@ -491,3 +489,9 @@ void ZsmBackend::audio_step(size_t samples) { } audio_buf.push(out_ptr, samples); } +void ZsmBackend::add_licenses() { + auto &license_data = get_license_data(); + auto x16emu = LicenseData("x16emu", "bsd-2-clause"); + LOAD_LICENSE(x16emu, x16emu); + license_data.insert(x16emu); +} diff --git a/backends/playback/zsm/zsm_backend.hpp b/backends/playback/zsm/zsm_backend.hpp index e5879a4..a7cf566 100644 --- a/backends/playback/zsm/zsm_backend.hpp +++ b/backends/playback/zsm/zsm_backend.hpp @@ -134,6 +134,7 @@ class ZsmBackend : public PlaybackBackend { inline std::string get_name() override { return "ZSM player"; } + void add_licenses() override; std::vector get_property_list() override; void seek(double position) override; void load(const char *filename) override; diff --git a/backends/ui/haiku/CMakeLists.txt b/backends/ui/haiku/CMakeLists.txt new file mode 100644 index 0000000..40002c6 --- /dev/null +++ b/backends/ui/haiku/CMakeLists.txt @@ -0,0 +1,11 @@ +set(BACKEND_HAIKU_SRC_BASE main.cpp main_window.cpp prefs.cpp slider.cpp slider.h main.h main_window.h aboutwindow.h aboutwindow.cpp prefs.h) +set(BACKEND_HAIKU_SRC ) +foreach(SRC IN ITEMS ${BACKEND_HAIKU_SRC_BASE}) + set(BACKEND_HAIKU_SRC ${BACKEND_HAIKU_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/${SRC}) +endforeach() +set(BACKEND_HAIKU_INC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) +add_ui_backend(haiku_ui ${BACKEND_HAIKU_SRC}) + +target_link_libraries(haiku_ui PRIVATE be tracker SDL2::SDL2 fmt::fmt liblooper) +#target_link_libraries(haiku_ui PRIVATE PkgConfig::GTK4 PkgConfig::gtkmm4) +#target_include_directories(haiku_ui PRIVATE ${BACKEND_HAIKU_INC}) diff --git a/backends/ui/haiku/aboutwindow.cpp b/backends/ui/haiku/aboutwindow.cpp new file mode 100644 index 0000000..a751d82 --- /dev/null +++ b/backends/ui/haiku/aboutwindow.cpp @@ -0,0 +1,74 @@ +#include "aboutwindow.h" +#include +#include +#include +#include +#include +#include +#include "main_window.h" +#define CMD_LOAD_LICENSE 0x40 +LicenseItem::LicenseItem(const LicenseData license) : BStringItem("") { + SetText(fmt::format("{} ({})", license.Project, license.Spdx).c_str()); + this->license_text = license.LicenseContents; +} + +AboutWindow::AboutWindow() : BWindow(BRect(100, 100, 600, 400), "About Looper", B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS) { + BGroupLayout *root_layout = new BGroupLayout(B_VERTICAL); + float minW, minH, maxW, maxH; + GetSizeLimits(&minW, &maxW, &minH, &maxH); + SetLayout(root_layout); + root_layout->SetSpacing(0.0); + BFont *title_font = new BFont(); + title_font->SetSize(title_font->Size() * 4.0); + BStringView *looper_title = new BStringView("about:title", "Looper"); + looper_title->SetAlignment(B_ALIGN_HORIZONTAL_CENTER); + looper_title->SetFont(title_font); + float title_height, version_height; + looper_title->GetPreferredSize(NULL, &title_height); + looper_title->SetExplicitMaxSize(BSize(maxW, title_height)); + looper_title->ResizeToPreferred(); + BStringView *version_text = new BStringView("about:version_text", fmt::format("Version {}", TAG).c_str()); + version_text->SetAlignment(B_ALIGN_HORIZONTAL_CENTER); + version_text->GetPreferredSize(NULL, &version_height); + version_text->SetExplicitMaxSize(BSize(maxW, version_height)); + version_text->ResizeToPreferred(); + BLayoutItem *title_item = root_layout->AddView(looper_title); + BLayoutItem *version_item = root_layout->AddView(version_text); + title_item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); + version_item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); + BSplitView *view = new BSplitView(); + view->SetExplicitMaxSize(BSize(maxW, maxH)); + root_layout->AddView(view); + license_list = new BListView(); + license_list->SetTarget(this); + for (auto &license : get_license_data()) { + license_list->AddItem(new LicenseItem(license)); + } + license_list->SetSelectionMessage(new BMessage(CMD_LOAD_LICENSE)); + BScrollView *license_list_scroller = new BScrollView("about:license-list-scroller", license_list, B_FOLLOW_ALL_SIDES, B_SUPPORTS_LAYOUT|B_FRAME_EVENTS, false, true); + license_text = new BTextView("about:license-text"); + license_text->MakeEditable(false); + BScrollView *license_text_scroller = new BScrollView("about:license-text-scroller", license_text, B_FOLLOW_ALL_SIDES, B_SUPPORTS_LAYOUT|B_FRAME_EVENTS, false, true); + view->AddChild(license_list_scroller); + view->AddChild(license_text_scroller); + license_list->Select(0); + InvalidateLayout(true); + UpdateIfNeeded(); +} +void AboutWindow::Show() { + BWindow::Show(); +} +bool AboutWindow::QuitRequested() { + Hide(); + return quitting; +} +void AboutWindow::MessageReceived(BMessage *msg) { + if (msg->IsSystem()) return; + if (msg->what == CMD_LOAD_LICENSE) { + auto selection = license_list->ItemAt(msg->GetInt32("index", 0)); + std::string str = ((LicenseItem*)selection)->license_text; + + license_text->SetText(str.c_str()); + + } +} diff --git a/backends/ui/haiku/aboutwindow.h b/backends/ui/haiku/aboutwindow.h new file mode 100644 index 0000000..b1d1944 --- /dev/null +++ b/backends/ui/haiku/aboutwindow.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +class LicenseItem : public BStringItem { + public: + std::string license_text; + LicenseItem(const LicenseData license); +}; +class AboutWindow : public BWindow +{ + BListView *license_list; + BTextView *license_text; + public: + bool QuitRequested() override; + void Show() override; + void MessageReceived(BMessage *msg) override; + AboutWindow(); +}; diff --git a/backends/ui/haiku/cmake_install.cmake b/backends/ui/haiku/cmake_install.cmake new file mode 100644 index 0000000..e4637b6 --- /dev/null +++ b/backends/ui/haiku/cmake_install.cmake @@ -0,0 +1,39 @@ +# Install script for directory: /boot/home/Desktop/looper/backends/ui/haiku + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/boot/system") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/system/bin/objdump") +endif() + diff --git a/backends/ui/haiku/icons.h b/backends/ui/haiku/icons.h new file mode 100644 index 0000000..aab6ef9 --- /dev/null +++ b/backends/ui/haiku/icons.h @@ -0,0 +1,15 @@ +#pragma once +enum LooperIcon { + ICON_EDIT_TEXT = 1000, + ICON_EDIT_SLIDER = 1001, + ICON_PLAY = 1002, + ICON_PAUSE = 1003, + ICON_STOP = 1004, + ICON_REFRESH = 1005, + ICON_CONFIGURE = 1006, + ICON_OPEN = 1007, + ICON_FOLDER = 1008, + ICON_ABOUT = 1009, + ICON_HELP = 1010, + ICON_QUIT = 1011 +}; diff --git a/backends/ui/haiku/main.cpp b/backends/ui/haiku/main.cpp new file mode 100644 index 0000000..4a3aa4d --- /dev/null +++ b/backends/ui/haiku/main.cpp @@ -0,0 +1,38 @@ +#include "main.h" +#include "main_window.h" +#include +using namespace std::chrono_literals; +bool quitting = false; +std::string HaikuUIBackend::get_id() { + return "haiku"; +} +std::string HaikuUIBackend::get_name() { + return "Haiku Native"; +} +void HaikuUIBackend::QuitHandler() { + LooperWindow *app = (LooperWindow*)main_loop; + app->Hide(); +} +static bool about_window_shown = false; +static bool prefs_window_shown = false; +int HaikuUIBackend::run(std::vector realArgs, int argc, char **argv) { + int ret = UIBackend::run(realArgs, argc, argv); + if (ret != 0) return ret; + LooperWindow *app = new LooperWindow(playback); + main_loop = (void*)app; + app->Show(); + while (!app->IsHidden()) { + for (auto *subwindow : Subwindow::windows) { + if (subwindow->Showing.exchange(false)) { + subwindow->window->Show(); + subwindow->ShownEver.store(true); + } + } + std::this_thread::sleep_for(100ms); + } + quitting = true; + for (auto *subwindow : Subwindow::windows) { + if (!subwindow->ShownEver) subwindow->window->Run(); + } + return 0; +} diff --git a/backends/ui/haiku/main.h b/backends/ui/haiku/main.h new file mode 100644 index 0000000..80c3839 --- /dev/null +++ b/backends/ui/haiku/main.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include +class HaikuUIBackend : public UIBackend { + BACKEND_TYPE(Native); +public: + std::string get_id() override; + std::string get_name() override; + void QuitHandler() override; + void *main_loop; + int run(std::vector realArgs, int argc, char **argv) override; + HaikuUIBackend() = default; +}; diff --git a/backends/ui/haiku/main_window.cpp b/backends/ui/haiku/main_window.cpp new file mode 100644 index 0000000..75eb599 --- /dev/null +++ b/backends/ui/haiku/main_window.cpp @@ -0,0 +1,385 @@ +#include "main_window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "icons.h" +#include "utils.h" +#include "aboutwindow.h" +#define CMD_MOUSE_DOWN_KEY "catmeow:pressed" +#define CMD_PAUSE_RESUME 0x10 +#define CMD_RESTART 0x11 +#define CMD_STOP 0x12 +#define CMD_OPEN 0x13 +#define CMD_ABOUT 0x14 +#define CMD_PREFS 0x15 +#define CMD_SEEK 0x20 +#define CMD_VOLUME 0x21 +#define CMD_SPEED 0x22 +#define CMD_PITCH 0x23 +#define CMD_TEMPO 0x24 +#define CMD_UPDATE 0x80 +#define SLIDER_SCALE 1000 +#define D(x) ((double)(x)) +std::vector Subwindow::windows; +BMessage *make_slider_msg(uint32_t what, bool down) { + BMessage *msg = new BMessage(what); + msg->SetBool(CMD_MOUSE_DOWN_KEY, down); + return msg; +} +bool is_slider_down_msg(BMessage *msg) { + return msg->HasBool(CMD_MOUSE_DOWN_KEY) && msg->GetBool(CMD_MOUSE_DOWN_KEY); +} +void LooperWindow::UpdateViewFlags(BView *view) { + if (view == NULL) return; + view->SetFlags(view->Flags()|B_SUPPORTS_LAYOUT|B_FRAME_EVENTS); + view->SetResizingMode(B_FOLLOW_ALL_SIDES); +} +void LooperWindow::UpdateViewFlags(BLayout *layout) { + BView *owner = layout->Owner(); + if (owner == NULL) { + owner = layout->View(); + if (owner == NULL) { + return; + } + } + UpdateViewFlags(owner); +} +LooperWindow::LooperWindow(Playback *playback) : BWindow(BRect(100, 100, 500, 100), "Looper", B_TITLED_WINDOW, 0) { + pause_bitmap = load_icon(ICON_PAUSE); + resume_bitmap = load_icon(ICON_PLAY); + stop_bitmap = load_icon(ICON_STOP); + refresh_bitmap = load_icon(ICON_REFRESH); + ref_handler = new LooperRefHandler(this); + this->playback = playback; + float minW, minH, maxW, maxH; + GetSizeLimits(&minW, &maxW, &minH, &maxH); + minW = 600; + minH = 200; + SetSizeLimits(minW, maxW, minH, maxH); + ResizeTo(minW, minH); + DisableUpdates(); + auto about_window = new AboutWindow(); + layout = new BGroupLayout(B_VERTICAL); + SetLayout(layout); + layout->SetSpacing(0.0); + layout->SetInsets(0, 0, 0, 0); + BMenuBar *menu_bar = new BMenuBar("main_menu"); + file_menu = new BMenu("File"); + open_item = new BMenuItem("Open...", new BMessage(CMD_OPEN)); + prefs_item = new BMenuItem("Preferences...", new BMessage(CMD_PREFS)); + quit_item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)); + help_menu = new BMenu("Help"); + about_item = new BMenuItem("About...", new BMessage(CMD_ABOUT)); + file_menu->AddItem(open_item); + file_menu->AddItem(prefs_item); + file_menu->AddItem(quit_item); + help_menu->AddItem(about_item); + menu_bar->AddItem(file_menu); + menu_bar->AddItem(help_menu); + layout->AddView(menu_bar); + BView *spacer = new BView("spacer", B_SUPPORTS_LAYOUT|B_FRAME_EVENTS); + spacer->SetExplicitMinSize(BSize(0, 0)); + layout->AddView(spacer); + BGroupView *top_row = new BGroupView(B_HORIZONTAL); + BGroupView *bottom_row = new BGroupView(B_HORIZONTAL); + BLayoutItem *top_item = layout->AddView(top_row); + BLayoutItem *bottom_item = layout->AddView(bottom_row); + pause_resume_btn = new BButton(NULL, new BMessage(CMD_PAUSE_RESUME)); + pause_resume_btn->SetTarget(this); + top_row->AddChild(pause_resume_btn); + restart_btn = new BButton(NULL, new BMessage(CMD_RESTART)); + restart_btn->SetTarget(this); + top_row->AddChild(restart_btn); + slider = new BSlider("seek_slider", "Seek", make_slider_msg(CMD_SEEK, false), 0, INT32_MAX, B_HORIZONTAL); + slider->SetModificationMessage(make_slider_msg(CMD_SEEK, true)); + slider->SetTarget(this); + top_row->AddChild(slider); + stop_btn = new BButton(NULL, new BMessage(CMD_STOP)); + stop_btn->SetTarget(this); + top_row->AddChild(stop_btn); + volume_slider = new LooperSlider("volume", "Volume", new BMessage(CMD_VOLUME), 0, 0, 100, 1, false); + volume_slider->SetLimitLabels("Muted", "Full Volume"); + volume_slider->SetTarget(this); + top_row->AddChild(volume_slider); + speed_slider = new LooperSlider("speed", "Speed", new BMessage(CMD_SPEED), 0, 0.25, 4.0, 0.01, true); + speed_slider->SetLimitLabels("0.25x", "4.00x"); + speed_slider->SetTarget(this); + bottom_row->AddChild(speed_slider); + tempo_slider = new LooperSlider("tempo", "Tempo", new BMessage(CMD_TEMPO), 0, 0.25, 4.0, 0.01, true); + tempo_slider->SetLimitLabels("0.25x", "4.00x"); + tempo_slider->SetTarget(this); + bottom_row->AddChild(tempo_slider); + pitch_slider = new LooperSlider("pitch", "Pitch", new BMessage(CMD_PITCH), 0, 0.25, 4.0, 0.01, true); + pitch_slider->SetLimitLabels("0.25x", "4.00x"); + pitch_slider->SetTarget(this); + bottom_row->AddChild(pitch_slider); + file_panel = new BFilePanel(B_OPEN_PANEL, new BMessenger(this)); + auto prefs_window = new PrefsWindow(this); + prefs_subwindow = Subwindow::Add(prefs_window); + about_subwindow = Subwindow::Add(about_window); + EnableUpdates(); + InvalidateLayout(true); + BRect top_rect = top_item->Frame(); + top_item->SetExplicitMinSize(top_rect.Size()); + BRect bottom_rect = bottom_item->Frame(); + bottom_item->SetExplicitMinSize(bottom_rect.Size()); + Pulse(); + this->update_thread = new std::thread(std::mem_fn(&LooperWindow::ThreadFunc), this); + UpdateIfNeeded(); + StartWatching(prefs_window, CMD_UPDATE_LABEL_SETTING); + auto *msg = new BMessage(CMD_UPDATE_LABEL_SETTING); + MessageReceived(msg); + delete msg; +} +LooperWindow::~LooperWindow() { + delete ref_handler; + delete pause_bitmap; + delete resume_bitmap; + delete stop_bitmap; + delete refresh_bitmap; + playback->Stop(); + done = true; + update_thread->join(); + delete update_thread; +} +void LooperWindow::MessageReceived(BMessage *msg) { + SDL_Event ev; + while (SDL_PollEvent(&ev)) { + if (ev.type == SDL_DROPFILE) { + playback->Start(ev.drop.file); + } else if (ev.type == SDL_QUIT) { + Hide(); + return; + } + } + if (msg->what == B_REFS_RECEIVED) { + entry_ref ref; + if (msg->FindRef("refs", &ref) != B_OK) { // Don't crash when failing to get ref - This is probably an error in the system or another application + return; + } + BEntry entry(&ref, true); + BPath path; + if (entry.GetPath(&path) != B_OK) { // Don't know how this could cause an error, but don't crash if it does. + return; + } + playback->Start(path.Path()); + return; + } + if (msg->IsSystem()) { + switch (msg->what) { + default: { + msg->PrintToStream(); + } break; + } + return; + } + switch (msg->what) { + case CMD_PAUSE_RESUME: { + playback->Pause(); + } break; + case CMD_RESTART: { + playback->Seek(0.0); + } break; + case CMD_VOLUME: { + playback->SetVolume(msg->GetDouble("be:value", 100)); + volume_clicked = is_slider_down_msg(msg); + } break; + case CMD_SPEED: { + playback->SetSpeed(msg->GetDouble("be:value", 1.0)); + speed_clicked = is_slider_down_msg(msg); + } break; + case CMD_TEMPO: { + playback->SetTempo(msg->GetDouble("be:value", 1.0)); + tempo_clicked = is_slider_down_msg(msg); + } break; + case CMD_PITCH: { + playback->SetPitch(msg->GetDouble("be:value", 1.0)); + pitch_clicked = is_slider_down_msg(msg); + } break; + case CMD_SEEK: { + playback->Seek((((double)msg->GetInt32("be:value", 0)) / INT32_MAX) * playback->GetLength()); + seek_clicked = is_slider_down_msg(msg); + } break; + case CMD_UPDATE: { + Pulse(); + } break; + case CMD_STOP: { + playback->Stop(); + } break; + case CMD_OPEN: { + file_panel->Show(); + } break; + case CMD_ABOUT: { + about_subwindow->Show(); + } break; + case CMD_PREFS: { + prefs_subwindow->Show(); + } break; + case CMD_UPDATE_LABEL_SETTING: { + volume_slider->MessageReceived(msg); + speed_slider->MessageReceived(msg); + tempo_slider->MessageReceived(msg); + pitch_slider->MessageReceived(msg); + if (show_labels) { + stop_btn->SetLabel("Stop"); + restart_btn->SetLabel("Restart"); + } else { + stop_btn->SetLabel(""); + restart_btn->SetLabel(""); + } + if (show_icons) { + stop_btn->SetIcon(stop_bitmap); + restart_btn->SetIcon(refresh_bitmap); + } else { + stop_btn->SetIcon(get_empty_icon()); + restart_btn->SetIcon(get_empty_icon()); + } + } break; + case 'DATA': { + status_t err = B_OK; + entry_ref ref; + if ((err = msg->FindRef("refs", &ref)) == B_OK) { + BEntry entry(&ref); + BPath path; + if ((err = entry.GetPath(&path)) == B_OK) { + playback->Start(path.Path()); + } else { + WARNING.writefln("entry.GetPath(&path) == %d", (int32)(err)); + msg->PrintToStream(); + } + } else { + WARNING.writefln("msg->FindRef(\"refs\", &ref) == %d", (int32)(err)); + msg->PrintToStream(); + } + } break; + default: { + if (msg->HasPointer("catmeow:target")) { + BHandler *ptr = (BHandler*)msg->GetPointer("catmeow:target"); + ptr->MessageReceived(msg); + } else { + WARNING.writeln("Unrecognized message type"); + msg->PrintToStream(); + } + } break; + }; +} +LooperLogScaler::LooperLogScaler(double min, double max) { + update_min_max(min, max); +} +void LooperLogScaler::update_min_max(double min, double max) { + x0 = min; + x1 = (max - min) / (exp(1.0) - 1.0); + la = min; + lb = (max - min); +// x0 = scale_log(min); +// x1 = scale_log(max); +} +double LooperLogScaler::scale_log(double value) { + return (std::log(((value - x0) / x1) + 1.0) * lb) + la; +} +double LooperLogScaler::unscale_log(double value) { + return ((std::exp((value - la) / lb) - 1.0) * x1) + x0; +} +void LooperWindow::Pulse() { + auto len = playback->GetLength(); + auto pos = playback->GetPosition(); + auto pos_milliseconds = pos * 1000.0; + int32_t pos_int32 = (int32_t)((((int64_t)pos_milliseconds) * INT32_MAX) / 1000 / len); + if (!seek_clicked) slider->SetValue(pos_int32); + auto component_count = TimeToComponentCount(len); + bool enable_ui = !playback->IsStopped(); + if (enable_ui) { + slider->SetLabel(fmt::format("Position: {}", TimeToString(pos, component_count)).c_str()); + slider->SetLimitLabels(TimeToString(0, component_count).c_str(), TimeToString(len, component_count).c_str()); + if (show_icons) pause_resume_btn->SetIcon(playback->IsPaused() ? resume_bitmap : pause_bitmap); + if (show_labels) pause_resume_btn->SetLabel(playback->IsPaused() ? "Resume" : "Pause"); + } else { + slider->SetLabel("Position"); + slider->SetLimitLabels("N/A", "N/A"); + if (show_icons) pause_resume_btn->SetIcon(pause_bitmap); + if (show_labels) pause_resume_btn->SetLabel("Pause"); + } + if (!show_icons) pause_resume_btn->SetIcon(get_empty_icon()); + if (!show_labels) pause_resume_btn->SetLabel(""); + slider->SetEnabled(enable_ui); + pause_resume_btn->SetEnabled(enable_ui); + auto volume = playback->GetVolume(); + auto pitch = playback->GetPitch(); + auto speed = playback->GetSpeed(); + auto tempo = playback->GetTempo(); + if (!volume_clicked) volume_slider->SetValue(volume); + volume_slider->SetLabel(fmt::format("Volume: {}%", (uint8_t)volume).c_str()); + if (!pitch_clicked) pitch_slider->SetValueDouble(pitch); + pitch_slider->SetLabel(fmt::format("Pitch {:.02f}x", pitch).c_str()); + if (!speed_clicked) speed_slider->SetValueDouble(speed); + speed_slider->SetLabel(fmt::format("Speed: {:.02f}x", speed).c_str()); + if (!tempo_clicked) tempo_slider->SetValueDouble(tempo); + tempo_slider->SetLabel(fmt::format("Tempo: {:.02f}x", tempo).c_str()); + UpdateIfNeeded(); +} +void LooperWindow::FrameResized(float newWidth, float newHeight) { + InvalidateLayout(true); + Layout(true); + layout->SetExplicitSize(BSize(newWidth, newHeight)); +} +void LooperWindow::ThreadFunc() { + while (true) { + if (done) return; + if (Lock()) { + if (!IsHidden()) { + Unlock(); + break; + } + Unlock(); + } else { + break; + } + } + while (true) { + if (done) return; + if (Lock()) { + if (IsHidden()) { + Unlock(); + break; + } + PostMessage(CMD_UPDATE); + Unlock(); + } else { + break; + } + std::this_thread::sleep_for(1s / 60.0); + } +} +LooperRefHandler::LooperRefHandler(LooperWindow *win) { + this->win = win; + be_app->Lock(); + this->next_handler = be_app->PreferredHandler(); + be_app->AddHandler(this); + be_app->SetPreferredHandler(this); + if (next_handler != NULL) { + SetNextHandler(next_handler); + } + AddFilter(new BMessageFilter(B_REFS_RECEIVED)); + be_app->Unlock(); +} +void LooperRefHandler::MessageReceived(BMessage *msg) { + if (msg->what == B_REFS_RECEIVED) { + win->PostMessage(msg); + } +} diff --git a/backends/ui/haiku/main_window.h b/backends/ui/haiku/main_window.h new file mode 100644 index 0000000..29eebdb --- /dev/null +++ b/backends/ui/haiku/main_window.h @@ -0,0 +1,102 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aboutwindow.h" +#include "slider.h" +#include "prefs.h" +#include +#define CMD_UPDATE_LABEL_SETTING 0x1000 +class LooperLogScaler { + double la; + double lb; + public: + double x0; + double x1; + void update_min_max(double min, double max); + LooperLogScaler(double min, double max); + double scale_log(double value); + double unscale_log(double value); +}; +extern bool show_labels; +extern bool show_icons; +extern bool quitting; +class Subwindow { + public: + BWindow *window; + static std::vector windows; + std::atomic_bool Showing = false; + std::atomic_bool ShownEver = false; + private: + inline Subwindow(BWindow *window) + : window(window) + { } + public: + inline static Subwindow *Add(BWindow *window) { + auto output = new Subwindow(window); + windows.push_back(output); + return output; + } + inline void Show() { + Showing.store(true); + } +}; +class LooperRefHandler; +class LooperWindow : public BWindow { + BSlider *slider; + BBitmap *pause_bitmap; + BBitmap *resume_bitmap; + BBitmap *refresh_bitmap; + BBitmap *stop_bitmap; + BMenu *file_menu; + BMenuItem *open_item; + BMenuItem *prefs_item; + BMenuItem *quit_item; + BMenu *help_menu; + BMenuItem *about_item; + BGroupLayout *layout; + Playback *playback; + BButton *restart_btn; + BButton *stop_btn; + BButton *pause_resume_btn; + bool volume_clicked = false; + bool speed_clicked = false; + bool pitch_clicked = false; + bool tempo_clicked = false; + bool seek_clicked = false; + void UpdateViewFlags(BView *view); + void UpdateViewFlags(BLayout *layout); + LooperSlider *volume_slider; + LooperSlider *speed_slider; + BFilePanel *file_panel; + LooperSlider *pitch_slider; + LooperSlider *tempo_slider; + LooperRefHandler *ref_handler; + std::thread *update_thread = nullptr; + bool done = false; + void Pulse(); + void ThreadFunc(); + const char *file_to_play = nullptr; +public: + Subwindow *about_subwindow; + Subwindow *prefs_subwindow; + void FrameResized(float newWidth, float newHeight) override; + void MessageReceived(BMessage *msg) override; + LooperWindow(Playback *playback); + ~LooperWindow(); +}; +class LooperRefHandler : public BHandler { + LooperWindow *win; + BHandler *next_handler; + public: + void MessageReceived(BMessage *msg) override; + LooperRefHandler(LooperWindow *win); +}; diff --git a/backends/ui/haiku/prefs.cpp b/backends/ui/haiku/prefs.cpp new file mode 100644 index 0000000..198b6ca --- /dev/null +++ b/backends/ui/haiku/prefs.cpp @@ -0,0 +1,144 @@ +#include "prefs.h" +#include +#include +#include +#include +#include +#include +#include +#include "main_window.h" +using namespace Looper::Options; +#define CMD_UPDATE_LABEL_SETTING 0x1000 +#define CMD_FRONTEND 0x1001 +#define CMD_SET_SETTING 0x1002 +#define CMD_REVERT 0x1003 +#define CMD_APPLY 0x1004 +bool show_icons, show_labels; + +PrefsWindow::PrefsWindow(BLooper *next_handler) : BWindow(BRect(100, 100, 0, 0), "Preferences", B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS) { + this->next_handler = next_handler; + auto *root_layout = new BGroupLayout(B_VERTICAL); + SetLayout(root_layout); + root_layout->SetSpacing(0.0); + restart_warning = new BStringView("prefs:restart_needed", "A restart is needed to apply some changes."); + root_layout->AddView(restart_warning); + restart_warning->Hide(); + frontend_popup = new BOptionPopUp("prefs:frontend", "Frontend", new BMessage(CMD_FRONTEND)); + auto frontend = get_option("ui.frontend", "haiku"); + for (auto &kv : UIBackend::backends) { + UIBackend *backend = kv.second; + const char *name = backend->get_name().c_str(); + frontend_popup->AddOption(name, (int32)backend_ids.size()); + std::string id = backend->get_id(); + if (id == frontend) cur_option = (int32)backend_ids.size(); + backend_ids.push_back(id); + } + BLayoutItem *frontend_item = root_layout->AddView(frontend_popup); + BAlignment align; + align.SetVertical(B_ALIGN_TOP); + frontend_item->SetExplicitAlignment(align); + BBox *box = new BBox("prefs:label_settings_box"); + box->SetLabel("Labels and Icons"); + auto *label_settings_group = new BGroupView(B_VERTICAL); + BGroupLayout *label_settings_layout = label_settings_group->GroupLayout(); + BMessage *labels_only_msg = new BMessage(CMD_SET_SETTING); + labels_only_msg->AddString("pref_path", "ui.haiku.label_setting"); + labels_only_msg->AddString("pref_value", "labels"); + labels_only = new BRadioButton("prefs:labels_only", "Labels Only", labels_only_msg); + BMessage *icons_only_msg = new BMessage(CMD_SET_SETTING); + icons_only_msg->AddString("pref_path", "ui.haiku.label_setting"); + icons_only_msg->AddString("pref_value", "icons"); + icons_only = new BRadioButton("prefs:icons_only", "Icons Only", icons_only_msg); + BMessage *both_labels_icons_msg = new BMessage(CMD_SET_SETTING); + both_labels_icons_msg->AddString("pref_path", "ui.haiku.label_setting"); + both_labels_icons_msg->AddString("pref_value", "both"); + both_labels_icons = new BRadioButton("prefs:both_labels_and_icons", "Both", both_labels_icons_msg); + label_settings_layout->AddView(labels_only); + label_settings_layout->AddView(icons_only); + label_settings_layout->AddView(both_labels_icons); + box->AddChild(label_settings_group); + root_layout->AddView(box, 3.0); + BGroupView *btn_view = new BGroupView(B_HORIZONTAL); + BGroupLayout *btn_box = btn_view->GroupLayout(); + revert_btn = new BButton("prefs:revert", "Revert", new BMessage(CMD_REVERT)); + apply_btn = new BButton("prefs:apply", "Apply", new BMessage(CMD_APPLY)); + root_layout->AddView(btn_view); + btn_box->AddView(revert_btn); + btn_box->AddView(apply_btn); + { + auto *msg = new BMessage(CMD_REVERT); + MessageReceived(msg); + delete msg; + } +} + +bool PrefsWindow::QuitRequested() { + Hide(); + return quitting; +} +void PrefsWindow::set_options_changed(bool changed) { + revert_btn->SetEnabled(changed); + apply_btn->SetEnabled(changed); +} +void PrefsWindow::update_label_setting() { + + std::map label_settings_map({{"labels", labels_only}, {"icons", icons_only}, {"both", both_labels_icons}}); + auto cur_radio_btn = labels_only; + if (!label_settings_map.contains(new_label_setting)) { + new_label_setting = "icons"; + } + cur_radio_btn = label_settings_map[new_label_setting]; + show_icons = new_label_setting != "labels"; + show_labels = new_label_setting != "icons"; + cur_radio_btn->SetValue(B_CONTROL_ON); +} +void PrefsWindow::MessageReceived(BMessage *msg) { + if (msg->IsSystem()) return; + switch (msg->what) { + case CMD_APPLY: { + set_options_changed(false); + set_option("ui.frontend", new_frontend); + if (new_frontend != "haiku") restart_warning->Show(); + else restart_warning->Hide(); + set_option("ui.haiku.label_setting", new_label_setting); + update_label_setting(); + next_handler->PostMessage(CMD_UPDATE_LABEL_SETTING); + } break; + case CMD_REVERT: { + set_options_changed(false); + new_label_setting = get_option("ui.haiku.label_setting", "icons"); + new_frontend = get_option("ui.frontend", "haiki"); + if (new_frontend != "haiku") restart_warning->Show(); + else restart_warning->Hide(); + for (size_t i = 0; i < backend_ids.size(); i++) { + int32 backend_id = (int32)i; + if (backend_ids[i] == new_frontend) { + frontend_popup->SetValue(backend_id); + break; + } + } + update_label_setting(); + next_handler->PostMessage(CMD_UPDATE_LABEL_SETTING); + } break; + case CMD_FRONTEND: { + auto option = msg->GetInt32("be:value", cur_option); + if (backend_ids.size() < option && option >= 0) { + new_frontend = backend_ids[option]; + } + set_options_changed(true); + } break; + case CMD_SET_SETTING: { + const char *setting; + if (msg->FindString("pref_path", &setting) == B_OK) { + const char *setting_value; + int32 setting_value_int32; + if (msg->FindString("pref_value", &setting_value) == B_OK) { + if (std::string(setting) == "ui.haiku.label_setting") { + new_label_setting = setting_value; + set_options_changed(true); + } + } + } + } break; + } +} diff --git a/backends/ui/haiku/prefs.h b/backends/ui/haiku/prefs.h new file mode 100644 index 0000000..29de9de --- /dev/null +++ b/backends/ui/haiku/prefs.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class PrefsWindow : public BWindow { + std::vector backend_ids; + int32 cur_option = 0; + BLooper *next_handler; + BStringView *restart_warning; + std::string new_label_setting; + std::string new_frontend; + BOptionPopUp *frontend_popup; + BCheckBox *menu_icons; + BRadioButton *labels_only; + BRadioButton *icons_only; + BRadioButton *both_labels_icons; + BButton *revert_btn; + BButton *apply_btn; + void update_label_setting(); + void set_options_changed(bool changed); + public: + bool QuitRequested() override; + void MessageReceived(BMessage *msg) override; + PrefsWindow(BLooper *next_handler); +}; diff --git a/backends/ui/haiku/slider.cpp b/backends/ui/haiku/slider.cpp new file mode 100644 index 0000000..be8bf47 --- /dev/null +++ b/backends/ui/haiku/slider.cpp @@ -0,0 +1,273 @@ +#include "slider.h" +#include "main_window.h" +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include +#include "icons.h" +#define CMD_SLIDER_MOVED 0x90 +#define CMD_TEXT_CHANGED 0x91 +#define CMD_SLIDER_CHANGED 0x92 +#define CMD_CHANGE_MODE 0x93 +using namespace BPrivate; +void LooperSlider::UpdateLogScaler() { + scaler->update_min_max(min, max); +} +void LooperSlider::UpdateSlider(bool update_value) { + if (recreate_slider) { + if (slider != NULL) { + if (!text_edit_mode) text_layout->RemoveView(slider); + delete slider; + } + int32 min, max; + if (logarithmic) { + scaler->update_min_max(this->min, this->max); + } + min = std::floor(this->min / tick); + max = std::ceil(this->max / tick); + slider = new BSlider(NULL, NULL, make_self_msg(CMD_SLIDER_CHANGED), min, max, B_HORIZONTAL); + slider->SetModificationMessage(make_self_msg(CMD_SLIDER_MOVED)); + BSize slider_min = slider->MinSize(); + BSize text_min = text->MinSize(); + BSize min_combined = BSize(std::max(slider_min.Width(), text_min.Width()), std::max(slider_min.Height(), text_min.Height())); + slider->SetExplicitMinSize(min_combined); + text->SetExplicitMinSize(min_combined); + slider->SetTarget(this); + if (!text_edit_mode) { + auto item = text_layout->AddView(1, slider); + item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); + } + } + if (update_value || recreate_slider) slider->SetValue((logarithmic ? scaler->scale_log(this->value) : this->value) / tick); +// slider->SetLimitLabels(min_label == NULL ? "" : min_label, max_label == NULL ? "" : max_label); + min_label_view->SetText(min_label == NULL ? "" : min_label); + max_label_view->SetText(max_label == NULL ? "" : max_label); + if (min_label != NULL || max_label != NULL) { + limits_view->Show(); + } else { + limits_view->Hide(); + } + recreate_slider = false; +} +void LooperSlider::set_min(double min) { + this->min = min; + recreate_slider = true; +} +void LooperSlider::set_max(double max) { + this->max = max; + recreate_slider = true; +} +void LooperSlider::SetMinLabel(const char *label) { + if (min_label != NULL) free((void*)min_label); + min_label = label == NULL ? NULL : strdup(label); + UpdateSlider(); +} +void LooperSlider::SetMaxLabel(const char *label) { + if (max_label != NULL) free((void*)max_label); + max_label = label == NULL ? NULL : strdup(label); + UpdateSlider(); +} +void LooperSlider::set_tick(double value) { + this->tick = value; + recreate_slider = true; +} +const char *LooperSlider::MinLabel() { + return min_label; +} +const char *LooperSlider::MaxLabel() { + return max_label; +} +void LooperSlider::SetLimitLabels(const char *min, const char *max) { + SetMinLabel(min); + SetMaxLabel(max); +} +LooperSlider::~LooperSlider() { + if (min_label != NULL) free((void*)min_label); + if (max_label != NULL) free((void*)max_label); + delete slider; + delete btn; + delete text; + delete scaler; + delete text_mode_bitmap; + delete slider_mode_bitmap; +} +void LooperSlider::SetMin(double min) { + set_min(min); + UpdateSlider(); +} +void LooperSlider::SetMax(double max) { + set_max(max); + UpdateSlider(); +} +double LooperSlider::Min() { + return min; +} +double LooperSlider::Max() { + return max; +} +void LooperSlider::SetLimits(double min, double max) { + set_min(min); + set_max(max); + UpdateSlider(); +} +void LooperSlider::SetTick(double value) { + set_tick(value); + UpdateSlider(); +} +double LooperSlider::Tick() { + return tick; +} +void LooperSlider::SetLogarithmic(bool logarithmic) { + this->logarithmic = logarithmic; + recreate_slider = true; + UpdateSlider(true); +} +bool LooperSlider::IsLogarithmic() { + return this->logarithmic; +} +void LooperSlider::MessageReceived(BMessage *msg) { + switch (msg->what) { + case CMD_SLIDER_MOVED: + case CMD_SLIDER_CHANGED: { + this->pressed = msg->what == CMD_SLIDER_MOVED; + if (logarithmic) { + SetValueDouble(scaler->unscale_log(((double)msg->GetInt32("be:value", 0) * tick))); + } else { + SetValueDouble(((double)msg->GetInt32("be:value", 0)) * tick); + } + SendChangeMsg(); + } break; + case CMD_TEXT_CHANGED: { + bool text_valid = true; + double new_value; + try { + new_value = std::stod(text->Text()); + } catch(std::out_of_range) { + text_valid = false; + } catch (std::invalid_argument) { + text_valid = false; + } + if (text_valid) { + SetValueDouble(new_value); + SendChangeMsg(); + } + } break; + case CMD_CHANGE_MODE: { + text_edit_mode = !text_edit_mode; + { + auto update_label_msg = new BMessage(CMD_UPDATE_LABEL_SETTING); + MessageReceived(update_label_msg); + delete update_label_msg; + } + if (text_edit_mode) { + pressed = true; + text->SetText(fmt::to_string(ValueDouble()).c_str()); + text_layout->RemoveView(slider); + auto item = text_layout->AddView(1, text); + item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); + } else { + pressed = false; + text_layout->RemoveView(text); + auto item = text_layout->AddView(1, slider); + item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); + } + } break; + case CMD_UPDATE_LABEL_SETTING: { + const char *precision_text[2] = {"Imprecise", "Precise"}; + BBitmap *precision_bitmap[2] = {LooperSlider::slider_mode_bitmap, LooperSlider::text_mode_bitmap}; + BBitmap *bitmap = precision_bitmap[text_edit_mode ? 0 : 1]; + if (show_labels || bitmap == NULL) { + btn->SetLabel(precision_text[text_edit_mode ? 0 : 1]); + } else { + btn->SetLabel(""); + } + if (show_icons && bitmap != NULL) { + btn->SetIcon(bitmap); + } else { + btn->SetIcon(get_empty_icon()); + } + } break; + } +} +void LooperSlider::SendChangeMsg() { + BMessage *msg = change_msg; + msg->SetDouble("be:value", this->value); + msg->SetBool("catmeow:pressed", pressed); + Invoke(msg); +} +void LooperSlider::SetValue(int32 value) { + SetValueDouble(value * tick); +} +void LooperSlider::SetValueDouble(double value) { + this->value = value; + BControl::SetValue(std::round(value / tick)); + UpdateSlider(true); +} +double LooperSlider::ValueDouble() { + return this->value; +} +void LooperSlider::SetLabel(const char *label) { + this->label = label; + text_label->SetText(label); +} +void LooperSlider::set_logarithmic(bool logarithmic) { + this->logarithmic = logarithmic; + recreate_slider = true; +} +void LooperSlider::SetValueChangedMsg(BMessage *msg) { + this->change_msg = msg; +} +LooperSlider::LooperSlider(const char *name, const char *label, BMessage *msg, uint32_t flags, double min, double max, double tick, bool logarithmic) : BControl(name, label, msg, flags) { + scaler = new LooperLogScaler(min, max); + text_mode_bitmap = load_icon(ICON_EDIT_TEXT); + slider_mode_bitmap = load_icon(ICON_EDIT_SLIDER); + + auto *group_layout = new BGroupLayout(B_HORIZONTAL); + SetLayout(group_layout); + group_layout->SetSpacing(0.0); + text_layout_view = new BGroupView(B_VERTICAL); + text_layout = text_layout_view->GroupLayout(); + text_layout->SetInsets(0, 0, 0, 0); + text_layout->SetSpacing(0.0); + text_label = new BStringView(NULL, ""); + text = new BTextControl(NULL, "", make_self_msg(CMD_TEXT_CHANGED)); + text->ResizeToPreferred(); + text->SetModificationMessage(make_self_msg(CMD_TEXT_CHANGED)); + text_layout->AddView(text_label); + limits_view = new BGroupView(B_HORIZONTAL); + auto limits_layout = limits_view->GroupLayout(); + min_label_view = new BStringView(NULL, ""); + max_label_view = new BStringView(NULL, ""); + text_layout->AddView(limits_view); + auto min_label_item = limits_layout->AddView(min_label_view); + min_label_item->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); + auto max_label_item = limits_layout->AddView(max_label_view); + max_label_item->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP)); + limits_view->Hide(); + text->SetTarget(this); + btn = new BButton("", make_self_msg(CMD_CHANGE_MODE)); + btn->SetTarget(this); + group_layout->AddView(0, text_layout_view); + group_layout->AddView(1, btn); + SetValueChangedMsg(msg); + set_min(min); + set_max(max); + set_tick(tick); + set_logarithmic(logarithmic); + UpdateSlider(true); + SetValueDouble(min); + SetLabel(label); + InvalidateLayout(true); +} +void LooperSlider::AttachedToWindow() { + { + BMessage *msg = make_self_msg(CMD_CHANGE_MODE); + MessageReceived(msg); + MessageReceived(msg); + delete msg; + } +} diff --git a/backends/ui/haiku/slider.h b/backends/ui/haiku/slider.h new file mode 100644 index 0000000..286ddf0 --- /dev/null +++ b/backends/ui/haiku/slider.h @@ -0,0 +1,75 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class LooperLogScaler; +class LooperSlider : public BControl { + inline BMessage *make_self_msg(int32 what) { + auto output = new BMessage(what); + output->AddPointer("catmeow:target", this); + return output; + } + BSlider *slider = NULL; + BButton *btn; + BStringView *text_label; + BStringView *min_label_view; + BStringView *max_label_view; + BGroupView *limits_view; + bool limits_visible = false; + BTextControl *text; + BBitmap *text_mode_bitmap = nullptr; + BBitmap *slider_mode_bitmap = nullptr; + double value; + BGroupView *text_layout_view; + bool pressed = false; + BGroupLayout *text_layout; + LooperLogScaler *scaler; + void UpdateLogScaler(); + bool recreate_slider = false; + void UpdateSlider(bool update_value = false); + double min; + double max; + double tick; + bool text_edit_mode = false; + bool logarithmic; + const char *label = NULL; + const char *min_label = NULL; + const char *max_label = NULL; + BMessage *change_msg = NULL; + void set_tick(double value); + void set_min(double min); + void set_max(double max); + void set_logarithmic(bool logarithmic); + void SendChangeMsg(); + public: + void SetMin(double min); + double Min(); + void SetMax(double max); + double Max(); + void SetLimits(double min, double max); + void SetMinLabel(const char *label); + void SetMaxLabel(const char *label); + const char *MinLabel(); + const char *MaxLabel(); + void SetLimitLabels(const char *min, const char *max); + void SetTick(double value); + void SetValue(int32 value) override; + double Tick(); + double ValueDouble(); + void SetValueDouble(double value); + void SetLogarithmic(bool logarithmic); + bool IsLogarithmic(); + void SetLabel(const char *label) override; + void SetValueChangedMsg(BMessage *msg); + void MessageReceived(BMessage *msg) override; + void AttachedToWindow() override; + LooperSlider(const char *name, const char *label, BMessage *msg, uint32_t flags, double min, double max, double tick = 0.0001, bool logarithmic = false); + ~LooperSlider(); +}; diff --git a/backends/ui/haiku/ui.json b/backends/ui/haiku/ui.json new file mode 100644 index 0000000..382825a --- /dev/null +++ b/backends/ui/haiku/ui.json @@ -0,0 +1,4 @@ +{ + "class_name": "HaikuUIBackend", + "include_path": "main.h" +} diff --git a/backends/ui/haiku/utils.h b/backends/ui/haiku/utils.h new file mode 100644 index 0000000..56977d3 --- /dev/null +++ b/backends/ui/haiku/utils.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +inline BBitmap *load_icon(int32 id, BRect rect = BRect(0, 0, 16, 16)) { + const void *data = NULL; + size_t size = 0; + data = BApplication::AppResources()->LoadResource(B_VECTOR_ICON_TYPE, id, &size); + if (data != NULL) { + BBitmap *icon = new BBitmap(rect, B_RGBA32); + if (icon->InitCheck() == B_OK && BIconUtils::GetVectorIcon((const uint8 *)data, size, icon) == B_OK) { + return icon; + } else { + delete icon; + } + } + return nullptr; +} +inline BBitmap *make_empty_icon() { + return new BBitmap(BRect(0, 0, 0, 0), B_RGBA32); +} +inline BBitmap *get_empty_icon() { + static BBitmap *empty_icon = nullptr; + if (empty_icon == nullptr) { + empty_icon = make_empty_icon(); + } + return empty_icon; +} diff --git a/backends/ui/imgui/CMakeLists.txt b/backends/ui/imgui/CMakeLists.txt index e966351..549fbe6 100644 --- a/backends/ui/imgui/CMakeLists.txt +++ b/backends/ui/imgui/CMakeLists.txt @@ -9,7 +9,7 @@ option(USE_GLES "Enable using OpenGL ES" ${GLES_NORMALLY_REQUIRED_FOR_ARCHITECTU option(GLES_VERSION "Version of OpenGL ES" 3) set(IMGUI_SRC imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp imgui.cpp misc/cpp/imgui_stdlib.cpp) set(IMGUI_BACKEND_SRC imgui_impl_sdlrenderer2.cpp imgui_impl_sdl2.cpp) -set(BACKEND_IMGUI_SRC_BASE main.cpp file_browser.cpp main.cpp RendererBackend.cpp theme.cpp) +set(BACKEND_IMGUI_SRC_BASE main.cpp file_browser.cpp main.cpp RendererBackend.cpp theme.cpp base85.h file_browser.h main.h RendererBackend.h ui_backend.hpp theme.h) foreach(SRC IN ITEMS ${IMGUI_BACKEND_SRC}) list(APPEND IMGUI_SRC backends/${SRC}) endforeach() @@ -54,6 +54,8 @@ if(DEFINED EMSCRIPTEN) target_compile_options(imgui_ui PRIVATE "-sUSE_SDL_IMAGE=2") target_link_options(imgui_ui PUBLIC "-sMAX_WEBGL_VERSION=2" "-sMIN_WEBGL_VERSION=2" "-sFULL_ES3") target_link_libraries(imgui_ui PRIVATE ${LIBINTL_LIBRARY}) +elseif(HAIKU) + target_link_libraries(imgui_ui PRIVATE OpenGL::${GLTarget} SDL2::SDL2 SDL2_image::SDL2_image ${LIBINTL_LIBRARY}) else() find_package(SDL2 REQUIRED) find_package(SDL2_image REQUIRED) diff --git a/backends/ui/imgui/ui_backend.hpp b/backends/ui/imgui/ui_backend.hpp index 72e73a6..48e8f33 100644 --- a/backends/ui/imgui/ui_backend.hpp +++ b/backends/ui/imgui/ui_backend.hpp @@ -3,6 +3,7 @@ #include #include class ImGuiUIBackend : public UIBackend { + BACKEND_TYPE(Fallback); public: std::string get_id() override; std::string get_name() override; @@ -11,4 +12,4 @@ class ImGuiUIBackend : public UIBackend { int run(std::vector realArgs, int argc, char **argv) override; void add_licenses() override; ImGuiUIBackend() = default; -}; \ No newline at end of file +}; diff --git a/haiku-res.rdef b/haiku-res.rdef new file mode 100644 index 0000000..5d01cec --- /dev/null +++ b/haiku-res.rdef @@ -0,0 +1,13 @@ +resource app_signature "application/x-vnd.com.complecwaft.Looper"; +resource app_flags B_EXCLUSIVE_LAUNCH; +resource file_types message +{ + "types" = "audio", + "types" = "audio/x-zsound", + "types" = "audio/midi", + "types" = "audio/mid", + "types" = "audio/wav", + "types" = "audio/x-wav", + "types" = "audio/x-flac", + "types" = "audio/ogg" +}; diff --git a/log.cpp b/log.cpp index d8073eb..4aa39b4 100644 --- a/log.cpp +++ b/log.cpp @@ -5,6 +5,8 @@ #ifdef __ANDROID__ #include #endif +#include +using namespace std::filesystem; namespace Looper::Log { const std::locale LogStream::c_locale = std::locale("C"); std::set LogStream::global_outputs; @@ -16,8 +18,11 @@ namespace Looper::Log { #endif std::set LogStream::get_used_outputs() { std::set used_outputs; - for (auto my_output : outputs) { - used_outputs.insert(my_output); + + if (my_log_level >= log_level) { + for (auto my_output : outputs) { + used_outputs.insert(my_output); + } } for (auto global_output : global_outputs) { used_outputs.insert(global_output); @@ -44,15 +49,13 @@ namespace Looper::Log { std::set used_outputs = get_used_outputs(); for (auto &file : used_outputs) { fwrite(&chr, 1, 1, file); + fflush(file); } } } void LogStream::writec(const char chr) { log_mutex_guard guard(log_mutex); bool is_newline = (chr == '\n' || chr == '\r'); - if (my_log_level < log_level) { - return; - } writeprefix(); _writec(chr); if (is_newline) { @@ -164,6 +167,13 @@ namespace Looper::Log { nested(nested) { + } + LogStream &LogStream::substream(std::string name) { + if (!this->substreams.contains(name)) { + this->substreams[name] = new LogStream({name}, {this}, my_log_level); + } + return *this->substreams[name]; + } LogStream::LogStream(std::initializer_list names, std::initializer_list streams, int log_level) : LogStream(names, log_level, true, nullptr) @@ -227,6 +237,7 @@ namespace Looper::Log { std::string name = get_log_name_by_idx(id); #ifdef __ANDROID__ std::map stream_files = { + {-2, ANDROID_LOG_DEBUG}, {-1, ANDROID_LOG_DEBUG}, {0, ANDROID_LOG_INFO}, {1, ANDROID_LOG_WARN}, @@ -234,6 +245,7 @@ namespace Looper::Log { }; #else std::map stream_files = { + {-2, stderr}, {-1, stderr}, {0, stdout}, {1, stderr}, @@ -253,8 +265,18 @@ namespace Looper::Log { }; return stream_names[idx]; } + static FILE *log_file; + void deinit_logging() { + fclose(log_file); + } void init_logging() { + path prefs_path(get_prefs_path()); + path log_dir = prefs_path / path("looper") / path("logs"); + create_directories(log_dir); + log_file = fopen((log_dir / path("looper.log")).c_str(), "a"); + LogStream::global_outputs.insert(log_file); init_logging_custom(_init_logging_normal); + atexit(deinit_logging); } LogStream &get_log_stream_by_level(int level) { return *log_streams[level].top(); diff --git a/log.hpp b/log.hpp index 77f09ad..8fb41c5 100644 --- a/log.hpp +++ b/log.hpp @@ -31,7 +31,7 @@ namespace Looper::Log { #endif std::string line; LogStream(std::initializer_list names, int log_level, bool nested, void* discriminator); - + std::map substreams; public: virtual void _writec(const char chr); typedef std::lock_guard log_mutex_guard; @@ -69,6 +69,7 @@ namespace Looper::Log { inline void writefln2(fmt::format_string fmt, T&&... args) { return vwritefln2(fmt::string_view(fmt), fmt::make_format_args(args...)); } + LogStream &substream(std::string name); LogStream(std::initializer_list names, std::initializer_list streams, int log_level = 0); #ifdef __ANDROID__ @@ -85,6 +86,7 @@ namespace Looper::Log { void init_logging_custom(log_stream_creation_function_t fn); LogStream &get_log_stream_by_level(int level); #define LOG(level) (Looper::Log::get_log_stream_by_level(level)) + #define TRACE LOG(-2) #define DEBUG LOG(-1) #define INFO LOG(0) #define WARNING LOG(1) diff --git a/main.cpp b/main.cpp index 395d726..3ab740a 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,9 @@ #ifdef __EMSCRIPTEN__ #include #endif +#ifdef __HAIKU__ +#include +#endif #include "web_functions.hpp" using namespace Looper; using namespace Looper::Options; @@ -109,7 +112,6 @@ extern "C" int looper_run_as_executable(std::vector args) { auto looper_gpl = LicenseData("Looper (GPL)", "GPL-3.0-or-later"); auto looper = LicenseData("Looper", "MIT OR GPL-3.0-or-later"); auto tomlpp = LicenseData("TOML++", "MIT"); - auto smx = LicenseData("SDL Mixer X", "Zlib"); auto cli11 = LicenseData("CLI11", "BSD-3-Clause"); auto jsoncpp = LicenseData("JsonCpp", "MIT"); auto soundtouch = LicenseData("SoundTouch", "LGPL-2.1-only"); @@ -117,7 +119,6 @@ extern "C" int looper_run_as_executable(std::vector args) { LOAD_LICENSE(looper_gpl, looper_gpl); LOAD_LICENSE(looper, looper); LOAD_LICENSE(tomlpp, tomlplusplus); - LOAD_LICENSE(smx, sdl_mixer_x); LOAD_LICENSE(cli11, cli11); LOAD_LICENSE(jsoncpp, jsoncpp); LOAD_LICENSE(soundtouch, lgpl_2_1); @@ -125,7 +126,6 @@ extern "C" int looper_run_as_executable(std::vector args) { license_data.insert(looper_gpl); license_data.insert(looper_mit); license_data.insert(tomlpp); - license_data.insert(smx); license_data.insert(cli11); license_data.insert(jsoncpp); license_data.insert(soundtouch); @@ -160,6 +160,9 @@ extern "C" int looper_run_as_executable(std::vector args) { for (auto kv : UIBackend::backends) { kv.second->add_licenses(); } + for (auto kv : PlaybackBackendHelper()) { + kv.second->add_licenses(); + } DEBUG.writeln("Loaded frontends: "); for (auto kv : UIBackend::backends) { DEBUG.writefln(" - '%s'", kv.first.c_str()); @@ -239,6 +242,8 @@ int main(int argc, char **argv) { executable_path = malloc(size); size = GetModuleFileNameA(NULL, executable_path, size); realloc(executable_path, size); +#elif defined(__HAIKU__) + executable_path = strdup(fs::canonical(argv[0]).c_str()); #else executable_path = strdup(fs::canonical("/proc/self/exe").c_str()); #endif diff --git a/playback.cpp b/playback.cpp index 266bc27..cc696d1 100644 --- a/playback.cpp +++ b/playback.cpp @@ -342,11 +342,7 @@ void PlaybackInstance::LoopFunction() { std::string file = current_file.value(); if (current_stream >= streams.size() || current_stream < 0 || streams[current_stream].name == "" || streams[current_stream].length <= 0) { - if (stream != nullptr) { - current_stream = stream->stream_index; - } else { - current_stream = 0; - } + current_stream = 0; } else { LockAudioDevice(); if (process && process->process_running()) process->set_stream_idx(current_stream); @@ -395,16 +391,16 @@ void PlaybackInstance::DeinitLoopFunction() { playback_ready.store(false); // ==== Unload(); -#ifdef USE_SDL - SDL_CloseAudioDevice(device); -#elif defined(USE_OBOE) +#ifdef USE_OBOE if (ostream && ostream->getState() != oboe::StreamState::Closed) { ostream->stop(); ostream->close(); } ostream.reset(); +#elif defined(USE_SDL) + SDL_CloseAudioDevice(device); + SDL_QuitSubSystem(SDL_INIT_AUDIO); #endif - SDL_QuitSubSystem(SDL_INIT_AUDIO); delete st; st = nullptr; if (buf) free(buf); diff --git a/playback.h b/playback.h index 737d9de..d2fed32 100644 --- a/playback.h +++ b/playback.h @@ -1,8 +1,4 @@ #pragma once -#include -extern "C" { -#include -} #ifdef __ANDROID__ #include #endif @@ -284,8 +280,6 @@ private: static void SDLCallback(void *userdata, Uint8 *stream, int len); void Load(const char *file, int idx); void Unload(); - VGMSTREAM *stream; - Mix_Music *music; std::vector streams; std::mutex stream_list_mutex; double real_volume = 1.0; diff --git a/playback_backend.hpp b/playback_backend.hpp index adaa8be..05c6723 100644 --- a/playback_backend.hpp +++ b/playback_backend.hpp @@ -59,6 +59,7 @@ class PlaybackBackend { using const_iterator = map::const_iterator; using reverse_iterator = map::reverse_iterator; using const_reverse_iterator = map::const_reverse_iterator; + inline virtual void add_licenses() { } inline virtual std::string get_id() {return "";} inline virtual std::string get_name() {return "";} inline virtual void seek(double position) { } diff --git a/playback_process.cpp b/playback_process.cpp index a4e06b1..5a5e3b1 100644 --- a/playback_process.cpp +++ b/playback_process.cpp @@ -453,6 +453,7 @@ PlaybackProcess::PlaybackProcess(std::string filename, int idx) { bool PlaybackProcess::process_running() { if (is_playback_process) return true; if (other_process != nullptr) return true; + if (!multi_process) return other_process != nullptr; return kill(pid, 0) == 0; } RPCResponse PlaybackProcess::handle_command(RPCCall &call) { @@ -510,6 +511,7 @@ void PlaybackProcess::run_playback_process() { uint64_t len; blocking_read(recv_fd, &len, sizeof(len)); std::string in_bytes = std::string(' ', len, std::allocator()); + in_bytes.resize(len); blocking_read(recv_fd, in_bytes.data(), len); RPCCall call; if (!call.ParseFromString(in_bytes)) { @@ -599,6 +601,7 @@ std::string PlaybackProcess::get_file_name() { } RPCResponse PlaybackProcess::SendCommand(RPCCall *call) { if (multi_process) { + std::lock_guard guard(ipc_mutex); uint64_t len; std::string str = call->SerializeAsString(); len = str.length(); @@ -683,7 +686,7 @@ AudioSpec *PlaybackProcess::get_audio_spec() { return get_property_value(PropertyId::SpecProperty); } PlaybackProcess::~PlaybackProcess() { - if (init_failed || other_process == nullptr) { + if (init_failed || !process_running()) { done = true; return; } @@ -692,6 +695,7 @@ PlaybackProcess::~PlaybackProcess() { call.mutable_quit()->CopyFrom(quit_cmd); SendCommand(&call); done = true; + if (multi_process) kill(pid, SIGHUP); } void PlaybackProcess::threadfunc() { while (!process_running()) { diff --git a/playback_process.hpp b/playback_process.hpp index 380de52..bb5bbc9 100644 --- a/playback_process.hpp +++ b/playback_process.hpp @@ -46,6 +46,7 @@ class PlaybackProcess { int pid; bool init_failed = false; std::mutex start_mutex; + std::mutex ipc_mutex; std::condition_variable started; bool is_playback_process = false; std::atomic_bool done; diff --git a/subprojects/fmt b/subprojects/fmt index 0379bf3..720da57 160000 --- a/subprojects/fmt +++ b/subprojects/fmt @@ -1 +1 @@ -Subproject commit 0379bf3a5d52d8542aec1874677c9df5ff9ba5f9 +Subproject commit 720da57baba83b3b1829e20133575e57aa1a8a4f diff --git a/subprojects/googletest b/subprojects/googletest index 6dae7eb..d144031 160000 --- a/subprojects/googletest +++ b/subprojects/googletest @@ -1 +1 @@ -Subproject commit 6dae7eb4a5c3a169f3e298392bff4680224aa94a +Subproject commit d144031940543e15423a25ae5a8a74141044862f diff --git a/util.cpp b/util.cpp index 8050f40..4489dab 100644 --- a/util.cpp +++ b/util.cpp @@ -1,4 +1,5 @@ #include "util.hpp" +#include "file_backend.hpp" #ifdef __ANDROID__ #include #endif @@ -8,6 +9,36 @@ #include "log.hpp" #include #include +std::string _hexdump_line(uint8_t *ptr, size_t i, size_t len) { + std::string output = fmt::format("${:08x}: ", i); + for (size_t j = 0; j < len; j++) { + output += fmt::format("{:02x} ", ptr[j]); + } + size_t remainder = 8 - len; + for (size_t j = 0; j < remainder; j++) { + output += " "; + } + for (size_t j = 0; j < len; j++) { + if (ptr[j] >= ' ' && ptr[j] < 0x7F) { + output += *((char*)(ptr + j)); + } else { + output += "."; + } + } + output += "\n"; + return output; +} +std::string hexdump(void *ptr, size_t len) { + uint8_t *ptr8 = (uint8_t*)ptr; + size_t len_div_8 = len / 8; + size_t remainder = len % 8; + std::string output; + for (size_t i = 0; i < len_div_8; i++) { + output += _hexdump_line(ptr8 + (i * 8), i * 8, 8); + } + output += _hexdump_line(ptr8 + (len_div_8 * 8), len_div_8 * 8, remainder); + return output; +} std::string PadZeros(std::string input, size_t required_length) { return std::string(required_length - std::min(required_length, input.length()), '0') + input; } @@ -117,7 +148,7 @@ void blocking_write(int fd, const void *buf, const size_t len) { while (i < len) { tmp = write(fd, ((uint8_t*)buf) + i, len - i); if (tmp < 0) { - throw std::exception(); + throw CustomException("Failed to write to fd!"); } i += tmp; } @@ -128,8 +159,8 @@ void blocking_read(int fd, void *buf, const size_t len) { size_t i = 0; while (i < len) { tmp = read(fd, ((uint8_t*)buf) + i, len - i); - if (tmp < 0) { - throw std::exception(); + if (tmp < 0) { + throw CustomException("Failed to read from fd!"); } i += tmp; } diff --git a/util.hpp b/util.hpp index 52ab854..e8fc9d2 100644 --- a/util.hpp +++ b/util.hpp @@ -23,6 +23,10 @@ std::string PadZeros(std::string input, size_t required_length); uint8_t TimeToComponentCount(double time_code); std::string TimeToString(double time_code, uint8_t min_components = 1); std::string get_prefs_path(); +std::string hexdump(void *ptr, size_t len); +inline std::string hexdump(std::string data) { + return hexdump(data.data(), data.length()); +} inline const char *vcformat(const char *format, va_list args) { va_list args_copy; va_copy(args_copy, args);