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