ref: 4df615146ef61a17f20afc27b6caf96a572e2dff
parent: 05785ffb3b0df250fb1fcc9b16a47555fc2abf75
parent: c57a06db152c56449df9723c5779617016a06382
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Mon Aug 25 09:25:57 EDT 2025
Merge pull request #198 from laamaa/release/2.0.0 2.0.0 release
--- a/.devcontainer.json
+++ /dev/null
@@ -1,5 +1,0 @@
-{- "build": {- "dockerfile": "Dockerfile"
- }
-}
\ No newline at end of file
--- a/.github/workflows/build-macos-arm.yml
+++ b/.github/workflows/build-macos-arm.yml
@@ -3,6 +3,8 @@
on:
push:
pull_request:
+ release:
+ types: [published]
workflow_dispatch:
jobs:
@@ -9,6 +11,9 @@
build-macos:
runs-on: macos-latest
+ name: m8c MacOS build (Apple Silicon)
+ env:
+ BUILD_DIR: build-arm64
steps:
- name: 'Environment info'
@@ -16,7 +21,7 @@
uname -m
- name: 'Install dependencies'
- run: brew install cmake pkg-config sdl2 libserialport
+ run: brew install cmake pkg-config sdl3 libserialport
- name: 'Checkout'
uses: actions/checkout@v4
@@ -23,17 +28,95 @@
- name: Set current date as env variable
run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
-
- - name: 'Build m8c'
+
+ - name: Create Custom Keychain
+ id: createCustomKeychain
+ if: github.event_name == 'release'
+ env:
+ KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}run: |
- mkdir -p build_arm64 && cd build_arm64 && cmake .. && cpack -V
+ security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
+ security list-keychains -s build.keychain
+ security default-keychain -s build.keychain
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
+ security set-keychain-settings build.keychain
- - name: 'Build package'
+ - name: Import Apple Developer Certificate
+ id: importAppleCertificate
+ if: github.event_name == 'release'
+ env:
+ CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}+ KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}run: |
- mv build_arm64/m8c-0.1.1-Darwin.dmg m8c-${{ env.NOW }}-macos-applesilicon.dmg- - name: 'Upload artifact'
+ echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > developer_cert.p12+ security import developer_cert.p12 -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
+ security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
+
+ - name: 'Configure m8c'
+ id: configureApplication
+ run: |
+ mkdir -p ${{ env.BUILD_DIR }}+ pushd ${{ env.BUILD_DIR }}+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCODESIGN_CERT_NAME="$CODESIGN_CERT_NAME"
+ popd
+ env:
+ CODESIGN_CERT_NAME: ${{ secrets.MACOS_CODE_SIGN_IDENTITY }}+
+ - name: 'Build and package m8c'
+ id: buildApplication
+ continue-on-error: true
+ run: |
+ pushd ${{ env.BUILD_DIR }}+ cpack -V .
+ popd
+ env:
+ CMAKE_INSTALL_PREFIX: build_output
+
+ - name: 'View debug log if compilation fails'
+ if: failure() && steps.buildApplication.outcome == 'failure'
+ run: cat /Users/runner/work/m8c/m8c/${{ env.BUILD_DIR }}/_CPack_Packages/Darwin/DragNDrop/PreinstallOutput.log+
+ - name: 'Notarize the App'
+ id: notarizeApp
+ if: github.event_name == 'release'
+ run: |
+ pushd ${{ env.BUILD_DIR }}+ APP_PATH=$(find package-output -maxdepth 1 -name "m8c*.dmg" | head -n 1)
+ xcrun notarytool submit \
+ --apple-id "$APPLE_ID" \
+ --team-id "$TEAM_ID" \
+ --password "$APPLE_PASSWORD" \
+ --wait \
+ $APP_PATH
+ xcrun stapler staple "$APP_PATH"
+ popd
+ env:
+ APPLE_ID: ${{ secrets.APPLE_ID }}+ APPLE_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}+ TEAM_ID: ${{ secrets.TEAM_ID }}+
+ - name: 'Verify Stapling'
+ id: verifyStapling
+ if: github.event_name == 'release'
+ run: |
+ APP_PATH=$(find "${{ env.BUILD_DIR }}/package-output" -maxdepth 1 -name "m8c*.dmg" | head -n 1)+ echo "Verifying stapling on $APP_PATH"
+ xcrun stapler validate "$APP_PATH"
+
+ - name: 'Delete Custom Keychain'
+ id: deleteCustomKeychain
+ if: github.event_name == 'release' && steps.createCustomKeychain.outcome == 'success'
+ run: |
+ security delete-keychain build.keychain
+
+ - name: 'Copy package'
+ run: |
+ APP_PATH=$(find "${{ env.BUILD_DIR }}/package-output" -maxdepth 1 -name "m8c*.dmg" | head -n 1)+ mv "$APP_PATH" m8c-${{ env.NOW }}-macos-applesilicon.dmg+
+ - name: 'Upload DMG package'
uses: actions/upload-artifact@v4
with:
name: m8c-${{ env.NOW }}-macos-applesiliconpath: |
- m8c-${{ env.NOW }}-macos-applesilicon.dmg+ m8c-${{ env.NOW }}-macos-applesilicon.dmg\ No newline at end of file
--- a/.github/workflows/build-macos-intel.yml
+++ b/.github/workflows/build-macos-intel.yml
@@ -1,8 +1,10 @@
-name: m8c macos intel build
+name: m8c macos intel build
on:
push:
pull_request:
+ release:
+ types: [published]
workflow_dispatch:
jobs:
@@ -9,31 +11,18 @@
build-macos:
runs-on: macos-13
+ name: m8c MacOS build (Intel)
env:
- SDL_VERSION: 2.32.0
- SDL_SHA256: f5c2b52498785858f3de1e2996eba3c1b805d08fe168a47ea527c7fc339072d0
-
+ BUILD_DIR: build-intel
+
steps:
+ - name: 'Environment info'
+ run: |
+ uname -m
+
- name: 'Install dependencies'
- run: brew install cmake pkg-config autoconf automake libtool
-
- - name: 'Cache x86_64 files'
- id: cache-x86_64-files
- uses: actions/cache@v4
- with:
- path: '/Users/runner/x86_64'
- key: mac-x86_64-files
+ run: brew install cmake pkg-config sdl3 libserialport
- - name: 'Download 10.7 SDK for x86_64'
- if: steps.cache-x86_64-files.outputs.cache-hit != 'true'
- run: |
- mkdir -p "$HOME/x86_64"
- pushd "$HOME/x86_64"
- mkdir SDKs
- cd SDKs
- curl -L "https://github.com/alexey-lysiuk/macos-sdk/releases/download/10.7/MacOSX10.7.tar.bz2" | tar -xvf -
- popd
-
- name: 'Checkout'
uses: actions/checkout@v4
@@ -40,57 +29,94 @@
- name: Set current date as env variable
run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- - name: 'Cache SDL2 files'
- id: cache-x86_64-sdl2-files
- uses: actions/cache@v4
- with:
- path: 'SDL2-2.30.4'
- key: mac-x86_64-sdl2-files
-
- - name: 'Download SDL2 sources'
- if: steps.cache-x86_64-sdl2-files.outputs.cache-hit != 'true'
+ - name: Create Custom Keychain
+ id: createCustomKeychain
+ if: github.event_name == 'release'
+ env:
+ KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}run: |
- (curl https://www.libsdl.org/release/SDL2-$SDL_VERSION.tar.gz || curl -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/SDL2-$SDL_VERSION.tar.gz) | tar xvf -
-
- - name: 'Build SDL2'
- if: steps.cache-x86_64-sdl2-files.outputs.cache-hit != 'true'
+ security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
+ security list-keychains -s build.keychain
+ security default-keychain -s build.keychain
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
+ security set-keychain-settings build.keychain
+
+ - name: Import Apple Developer Certificate
+ id: importAppleCertificate
+ if: github.event_name == 'release'
+ env:
+ CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}+ KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}run: |
- export MACOSX_DEPLOYMENT_TARGET="10.9"
- pushd SDL2-$SDL_VERSION
- mkdir build_x86_64
- cd build_x86_64
- ../configure CPPFLAGS="-arch x86_64 -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -isysroot $HOME/x86_64/SDKs/MacOSX10.7.sdk" CFLAGS="-arch x86_64" CXXFLAGS="-arch x86_64" LDFLAGS="-arch x86_64 -F$HOME/x86_64/SDKs/MacOSX10.7.sdk/System/Library/Frameworks -L/usr/lib -L/usr/lib/system -Wl,-syslibroot,$HOME/x86_64/SDKs/MacOSX10.7.sdk" --host=x86_64-apple-darwin13 --prefix="$HOME/x86_64prefix"
- make
+ echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > developer_cert.p12+ security import developer_cert.p12 -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
+ security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
+
+ - name: 'Configure m8c'
+ id: configureApplication
+ run: |
+ mkdir -p ${{ env.BUILD_DIR }}+ pushd ${{ env.BUILD_DIR }}+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCODESIGN_CERT_NAME="$CODESIGN_CERT_NAME"
popd
+ env:
+ CODESIGN_CERT_NAME: ${{ secrets.MACOS_CODE_SIGN_IDENTITY }}- - name: 'Install SDL2'
+ - name: 'Build and package m8c'
+ id: buildApplication
+ continue-on-error: true
run: |
- pushd SDL2-$SDL_VERSION/build_x86_64
- make install
+ pushd ${{ env.BUILD_DIR }}+ cpack -V .
popd
+ env:
+ CMAKE_INSTALL_PREFIX: build_output
- - name: 'Download libserialport sources'
+ - name: 'View debug log if compilation fails'
+ if: failure() && steps.buildApplication.outcome == 'failure'
+ run: cat /Users/runner/work/m8c/m8c/${{ env.BUILD_DIR }}/_CPack_Packages/Darwin/DragNDrop/PreinstallOutput.log+
+ - name: 'Notarize the App'
+ id: notarizeApp
+ if: github.event_name == 'release'
run: |
- curl -L -O https://github.com/sigrokproject/libserialport/archive/refs/heads/master.zip && unzip master.zip && rm master.zip
-
- - name: 'Build libserialport'
- run: |
- pushd libserialport-master
- mkdir autostuff
- autoreconf -I"$HOME/x86_64prefix/share/aclocal" -i
- export MACOSX_DEPLOYMENT_TARGET="10.7" && mkdir build_x86_64 && cd build_x86_64 && ../configure CFLAGS="-arch x86_64 -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -isysroot $HOME/x86_64/SDKs/MacOSX10.7.sdk" LDFLAGS="-arch x86_64 -F$HOME/x86_64/SDKs/MacOSX10.7.sdk/System/Library/Frameworks -L/usr/lib -L/usr/lib/system -Wl,-syslibroot,$HOME/x86_64/SDKs/MacOSX10.7.sdk" --host=x86_64-apple-darwin13 --prefix="$HOME/x86_64prefix" && make && make install
+ pushd ${{ env.BUILD_DIR }}+ APP_PATH=$(find package-output -maxdepth 1 -name "m8c*.dmg" | head -n 1)
+ xcrun notarytool submit \
+ --apple-id "$APPLE_ID" \
+ --team-id "$TEAM_ID" \
+ --password "$APPLE_PASSWORD" \
+ --wait \
+ $APP_PATH
+ xcrun stapler staple "$APP_PATH"
popd
+ env:
+ APPLE_ID: ${{ secrets.APPLE_ID }}+ APPLE_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}+ TEAM_ID: ${{ secrets.TEAM_ID }}- - name: 'Build m8c'
+ - name: 'Verify Stapling'
+ id: verifyStapling
+ if: github.event_name == 'release'
run: |
- export MACOSX_DEPLOYMENT_TARGET="10.7" && export PKG_CONFIG_PATH="$HOME/x86_64prefix/lib/pkgconfig" && export CFLAGS="-arch x86_64 -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -isysroot $HOME/x86_64/SDKs/MacOSX10.7.sdk" && mkdir build_x86_64 && cd build_x86_64 && cmake -DCMAKE_OSX_ARCHITECTURES="x86_64" .. && cpack -V
+ APP_PATH=$(find "${{ env.BUILD_DIR }}/package-output" -maxdepth 1 -name "m8c*.dmg" | head -n 1)+ echo "Verifying stapling on $APP_PATH"
+ xcrun stapler validate "$APP_PATH"
- - name: 'Build package'
+ - name: 'Delete Custom Keychain'
+ id: deleteCustomKeychain
+ if: github.event_name == 'release' && steps.createCustomKeychain.outcome == 'success'
run: |
- mv build_x86_64/m8c-0.1.1-Darwin.dmg m8c-${{ env.NOW }}-macos-intel.dmg- - name: 'Upload artifact'
+ security delete-keychain build.keychain
+
+ - name: 'Copy package'
+ run: |
+ APP_PATH=$(find "${{ env.BUILD_DIR }}/package-output" -maxdepth 1 -name "m8c*.dmg" | head -n 1)+ mv "$APP_PATH" m8c-${{ env.NOW }}-macos-intel.dmg+
+ - name: 'Upload DMG package'
uses: actions/upload-artifact@v4
with:
name: m8c-${{ env.NOW }}-macos-intelpath: |
- m8c-${{ env.NOW }}-macos-intel.dmg+ m8c-${{ env.NOW }}-macos-intel.dmg\ No newline at end of file
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -1,5 +1,9 @@
name: m8c linux x64 build
+env:
+ SDL_VERSION: 3.2.20
+ SDL_SHA256: "467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67 SDL3-3.2.20.tar.gz"
+
on:
push:
pull_request:
@@ -8,13 +12,14 @@
jobs:
build-linux:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
name: linux-x86_64
steps:
- name: 'Install dependencies'
run: |
sudo apt-get update
- sudo apt-get install --fix-missing build-essential libsdl2-dev libserialport-dev zip
+ sudo apt-get install --fix-missing build-essential libserialport-dev zip git make pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libpipewire-0.3-dev libwayland-dev libdecor-0-dev
+
- name: 'Checkout'
uses: actions/checkout@v4
@@ -21,16 +26,57 @@
- name: Set current date as env variable
run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- - name: 'Build package'
+ - name: 'Cache SDL3 files'
+ id: cache-x86_64-sdl3-files
+ uses: actions/cache@v4
+ with:
+ path: 'SDL3-3.2.20'
+ key: linux-x86_64-sdl3-files
+
+ - name: 'Download SDL3 sources'
+ if: steps.cache-x86_64-sdl3-files.outputs.cache-hit != 'true'
run: |
+ (curl -O https://www.libsdl.org/release/SDL3-$SDL_VERSION.tar.gz || curl -O -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/SDL3-$SDL_VERSION.tar.gz)
+ if ! echo $SDL_SHA256 | sha256sum -c --status -; then echo "SDL archive checksum failed"; exit 1; fi
+ tar zxvf SDL3-$SDL_VERSION.tar.gz
+
+ - name: 'Build SDL3'
+ if: steps.cache-x86_64-sdl3-files.outputs.cache-hit != 'true'
+ run: |
+ pushd SDL3-$SDL_VERSION
+ mkdir build_x86_64
+ cmake -S . -B build_x86_64 -DCMAKE_BUILD_TYPE=Release
+ cmake --build build_x86_64
+ popd
+
+ - name: 'Install SDL3'
+ run: |
+ pushd SDL3-$SDL_VERSION
+ sudo cmake --install build_x86_64
+ popd
+
+ - name: 'Build binary'
+ run: |
make
+
- name: 'Upload artifact'
uses: actions/upload-artifact@v4
with:
- name: m8c-${{ env.NOW }}-linux+ name: m8c-${{ env.NOW }}-linux-x86_64path: |
LICENSE
README.md
AUDIOGUIDE.md
m8c
- gamecontrollerdb.txt
\ No newline at end of file
+ gamecontrollerdb.txt
+
+ - name: 'Build AppImage'
+ run: |
+ bash package/appimage/package.sh
+
+ - name: 'Upload artifact'
+ uses: actions/upload-artifact@v4
+ with:
+ name: m8c-${{ env.NOW }}-linux-x86_64.AppImage+ path: |
+ m8c*.AppImage
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -20,6 +20,7 @@
shell: msys2 {0}env:
MINGW_ARCH: ${{ matrix.sys }}+ SDL_VERSION: 3.2.20
steps:
- name: 'git config'
@@ -36,9 +37,44 @@
with:
msystem: ${{ matrix.sys }}update: true
- install: mingw-w64-${{ matrix.env }}-toolchain make mingw-w64-${{ matrix.env }}-SDL2 zip dos2unix autoconf automake-wrapper libtool make unzip+ install: mingw-w64-${{ matrix.env }}-toolchain make mingw-w64-${{ matrix.env }}-cmake zip dos2unix autoconf automake-wrapper libtool make unzip+ - name: 'Cache SDL3 files'
+ id: cache-sdl3-files
+ uses: actions/cache@v4
+ with:
+ path: 'SDL3-3.2.20'
+ key: win-sdl3-files-${{ matrix.sys }}+
+ - name: 'Download SDL3 sources'
+ if: steps.cache-sdl3-files.outputs.cache-hit != 'true'
+ run: |
+ (curl https://www.libsdl.org/release/SDL3-$SDL_VERSION.tar.gz || curl -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/SDL3-$SDL_VERSION.tar.gz) | tar zxvf -
+
+ - name: 'Build and install SDL3'
+ if: steps.cache-sdl3-files.outputs.cache-hit != 'true'
+ run: |
+ pushd SDL3-$SDL_VERSION
+ mkdir build_${{ matrix.env }}+ cmake -S . -B build_${{ matrix.env }} -DCMAKE_TOOLCHAIN_FILE=build-scripts/cmake-toolchain-mingw64-${{ matrix.env }}.cmake+ cmake --build build_${{ matrix.env }} --parallel+ popd
+
+ - name: 'Install SDL3'
+ run: |
+ pushd SDL3-$SDL_VERSION
+ cmake --install build_${{ matrix.env }} --prefix C:/Libraries+ popd
+
+ - name: 'Cache libserialport files'
+ id: cache-libserialport-files
+ uses: actions/cache@v4
+ with:
+ path: 'libserialport-master'
+ key: win-libserialport-files-${{ matrix.sys }}+
- name: 'Build libserialport manually'
+ if: steps.cache-libserialport-files.outputs.cache-hit != 'true'
run: |
wget https://github.com/sigrokproject/libserialport/archive/refs/heads/master.zip
unzip master.zip
@@ -46,6 +82,11 @@
./autogen.sh
./configure
make
+ popd
+
+ - name: 'Install libserialport'
+ run: |
+ pushd libserialport-master
make install
popd
@@ -54,16 +95,17 @@
- name: 'Build package'
run: |
- make
+ cmake . -G Ninja -DCMAKE_PREFIX_PATH=C:/Libraries -DCMAKE_BUILD_TYPE=Release
+ cmake --build .
strip -g m8c.exe
if [ ${{ matrix.win }} == "win32" ]then
- cp /mingw32/bin/SDL2.dll .
+ cp C:/Libraries/bin/SDL3.dll .
cp /mingw32/bin/libgcc_s_dw2-1.dll .
cp /mingw32/bin/libserialport-0.dll .
cp /mingw32/bin/libwinpthread-1.dll .
else
- cp /mingw64/bin/SDL2.dll .
+ cp C:/Libraries/bin/SDL3.dll .
cp /mingw64/bin/libserialport-0.dll .
fi
unix2dos README.md LICENSE AUDIOGUIDE.md
@@ -74,7 +116,7 @@
name: m8c-${{ env.NOW }}-${{ matrix.win }}path: |
m8c.exe
- SDL2.dll
+ SDL3.dll
libserialport-0.dll
libgcc_s_dw2-1.dll
libwinpthread-1.dll
@@ -89,7 +131,7 @@
name: m8c-${{ env.NOW }}-${{ matrix.win }}path: |
m8c.exe
- SDL2.dll
+ SDL3.dll
libserialport-0.dll
gamecontrollerdb.txt
LICENSE
--- a/Android.mk
+++ b/Android.mk
@@ -10,7 +10,7 @@
LOCAL_CFLAGS += -DUSE_LIBUSB
-LOCAL_SHARED_LIBRARIES := usb-1.0 SDL2
+LOCAL_SHARED_LIBRARIES := usb-1.0 SDL3
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,66 +1,111 @@
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.20)
-project(m8c LANGUAGES C)
+project(m8c LANGUAGES C VERSION 2.0.0)
-set(CMAKE_C_FLAGS "-O2 -Wall -Wextra")
-
set(APP_NAME m8c)
-find_package(PkgConfig REQUIRED)
+option(USE_LIBSERIALPORT "Use libserialport as a backend" OFF)
+option(USE_LIBUSB "Use libusb as a backend" OFF)
+option(USE_RTMIDI "Use RtMidi as a backend" OFF)
-pkg_check_modules(SDL2 REQUIRED sdl2)
-if (USE_LIBUSB)
- pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
- link_directories(${SDL2_LIBRARY_DIRS} ${LIBUSB_LIBRARY_DIRS})- add_compile_definitions(USE_LIBUSB)
-else ()
- pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
- link_directories(${SDL2_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})-endif (USE_LIBUSB)
+# Enable USE_LIBSERIALPORT by default if neither USE_LIBUSB nor USE_RTMIDI are defined
+if (NOT USE_LIBUSB AND NOT USE_RTMIDI)
+ message(STATUS "Neither USE_LIBUSB nor USE_RTMIDI are enabled. Enabling USE_LIBSERIALPORT by default.")
+ set(USE_LIBSERIALPORT ON)
+endif ()
-file(GLOB m8c_SRC "src/*.h" "src/*.c")
+file(GLOB m8c_SRC "src/*.h" "src/*.c" "src/backends/*.h" "src/backends/*.c" "src/fonts/*.h")
+
set(MACOS_CONTENTS "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/m8c.app/Contents")+set(MACOS_ENTITLEMENTS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/Entitlements.plist") set(APP_ICON ${MACOS_CONTENTS}/Resources/m8c.icns) set_source_files_properties(${APP_ICON} PROPERTIESMACOSX_PACKAGE_LOCATION "Resources")
-add_executable(${APP_NAME} WIN32 MACOSX_BUNDLE ${APP_ICON} ${m8c_SRC})+if (APPLE)
+ add_executable(${APP_NAME} MACOSX_BUNDLE ${APP_ICON} ${m8c_SRC})+elseif (WIN32)
+ add_executable(${APP_NAME} WIN32 ${m8c_SRC})+else ()
+ add_executable(${APP_NAME} ${m8c_SRC})+endif ()
+find_package(PkgConfig REQUIRED)
+find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
+
+pkg_check_modules(SDL3 REQUIRED sdl3)
+
+target_link_options(${APP_NAME} PRIVATE ${SDL3_LDFLAGS})+target_include_directories(${APP_NAME} PRIVATE ${SDL3_INCLUDE_DIRS})+target_compile_options(${APP_NAME} PRIVATE ${SDL3_CFLAGS_OTHER})+
+if (USE_LIBSERIALPORT)
+ pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
+ target_link_options(${APP_NAME} PRIVATE ${LIBSERIALPORT_LDFLAGS})+ target_include_directories(${APP_NAME} PRIVATE ${LIBSERIALPORT_INCLUDE_DIRS})+ target_compile_options(${APP_NAME} PRIVATE ${LIBSERIALPORT_CFLAGS_OTHER})+ target_compile_definitions(${APP_NAME} PRIVATE USE_LIBSERIALPORT)+endif ()
+
if (USE_LIBUSB)
- target_link_libraries(${APP_NAME} ${SDL2_LIBRARIES} ${LIBUSB_LIBRARIES})- target_include_directories(${APP_NAME} PUBLIC ${SDL2_INCLUDE_DIRS} ${LIBUSB_INCLUDE_DIRS})- target_compile_options(${APP_NAME} PUBLIC ${SDL2_CFLAGS_OTHER} ${LIBUSB_CFLAGS_OTHER})-else ()
- target_link_libraries(${APP_NAME} ${SDL2_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})- target_include_directories(${APP_NAME} PUBLIC ${SDL2_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})- target_compile_options(${APP_NAME} PUBLIC ${SDL2_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})+ pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
+ target_link_options(${APP_NAME} PRIVATE ${LIBUSB_LDFLAGS})+ target_include_directories(${APP_NAME} PRIVATE ${LIBUSB_INCLUDE_DIRS})+ target_compile_options(${APP_NAME} PRIVATE ${LIBUSB_CFLAGS_OTHER})+ target_compile_definitions(${APP_NAME} PRIVATE USE_LIBUSB)endif ()
+if (USE_RTMIDI)
+ pkg_check_modules(RTMIDI REQUIRED rtmidi)
+ target_link_options(${APP_NAME} PRIVATE ${RTMIDI_LDFLAGS})+ target_include_directories(${APP_NAME} PRIVATE ${RTMIDI_INCLUDE_DIRS})+ target_compile_options(${APP_NAME} PRIVATE ${RTMIDI_CFLAGS_OTHER})+ target_compile_definitions(${APP_NAME} PRIVATE USE_RTMIDI)+endif ()
+
+if (WIN32)
+target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})+endif ()
+
if (APPLE)
# Destination paths below are relative to ${CMAKE_INSTALL_PREFIX} install(TARGETS ${APP_NAME}BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
- )
+ )
+ set(CODESIGN_CERT_NAME "" CACHE STRING "The developer ID cert name for codesign")
+
set_target_properties(${APP_NAME} PROPERTIESMACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/m8c.app/Contents/Info.plist"MACOSX_BUNDLE_BUNDLE_NAME "m8c"
- MACOSX_BUNDLE_BUNDLE_VERSION "1"
MACOSX_BUNDLE_COPYRIGHT "Copyright © 2021 laamaa. All rights reserved."
MACOSX_BUNDLE_GUI_IDENTIFIER "fi.laamaa.m8c"
- MACOSX_BUNDLE_SHORT_VERSION_STRING "1.7.8"
- MACOSX_BUNDLE_ICON_FILE "m8c.icns")
+ MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"+ MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"+ MACOSX_BUNDLE_ICON_FILE "m8c.icns"
+ )
set(APPS "\${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app")- install(CODE "include(BundleUtilities)
- fixup_bundle(\"${APPS}\" \"\" \"\")- execute_process(COMMAND codesign --force --deep --sign - \${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app)")+ if(CODESIGN_CERT_NAME)
+ # Use the specified certificate
+ install(CODE "include(BundleUtilities)
+ fixup_bundle(\"${APPS}\" \"\" \"\")+ execute_process(COMMAND codesign --force --options runtime --deep --entitlements ../package/macos/Entitlements.plist --sign \"${CODESIGN_CERT_NAME}\" --timestamp \${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app)")+ else()
+ # Use ad-hoc signing (self-signed) when no certificate is specified
+ install(CODE "include(BundleUtilities)
+ fixup_bundle(\"${APPS}\" \"\" \"\")+ execute_process(COMMAND codesign --force --options runtime --deep --entitlements ../package/macos/Entitlements.plist --sign - --timestamp \${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app)")+ endif()
+
+ set(CPACK_PACKAGE_NAME "m8c")
+ set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})+ set(CPACK_PACKAGE_DIRECTORY "package-output")
set(CPACK_GENERATOR "DragNDrop")
include(CPack)
-endif ()
-
+endif ()
\ No newline at end of file
--- a/Dockerfile
+++ /dev/null
@@ -1,2 +1,0 @@
-FROM mcr.microsoft.com/devcontainers/cpp
-RUN apt-get update && apt-get install -y libsdl2-dev libserialport-dev
--- a/Makefile
+++ b/Makefile
@@ -1,23 +1,28 @@
-#Set all your object files (the object files of all the .c files in your project, e.g. main.o my_sub_functions.o )
-OBJ = src/main.o src/serial.o src/slip.o src/command.o src/render.o src/ini.o src/config.o src/input.o src/gamecontrollers.o src/fx_cube.o src/usb.o src/audio.o src/usb_audio.o src/ringbuffer.o src/inprint2.o
+#Set the compiler you are using
+CC = gcc
-#Set any dependant header files so that if they are edited they cause a complete re-compile (e.g. main.h some_subfunctions.h some_definitions_file.h ), or leave blank
-DEPS = src/serial.h src/slip.h src/command.h src/render.h src/ini.h src/config.h src/input.h src/gamecontrollers.h src/fx_cube.h src/audio.h src/ringbuffer.h src/inline_font.h
+#Set the filename extension of your C files
+EXTENSION = .c
-#Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
-INCLUDES = $(shell pkg-config --libs sdl2 libserialport | sed 's/-mwindows//')
+# Location of the source files
+SOURCE_DIR = src/
-#Set any compiler flags you want to use (e.g. -I/usr/include/somefolder `pkg-config --cflags gtk+-3.0` ), or leave blank
-local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl2 libserialport) -Wall -Wextra -O2 -pipe -I.
+# Find all source files in the src directory and subdirectories
+SRC_FILES := $(shell find $(SOURCE_DIR) -type f -name "*$(EXTENSION)")
-#Set the compiler you are using ( gcc for C or g++ for C++ )
-CC = gcc
+# Convert to object files
+OBJ := $(SRC_FILES:.c=.o)
-#Set the filename extensiton of your C files (e.g. .c or .cpp )
-EXTENSION = .c
+# Find all header files for dependencies
+DEPS := $(shell find src -type f -name "*.h")
-SOURCE_DIR = src/
+#Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
+INCLUDES = $(shell pkg-config --libs sdl3 libserialport | sed 's/-mwindows//')
+
+#Set any compiler flags you want to use (e.g. -I/usr/include/somefolder `pkg-config --cflags gtk+-3.0` ), or leave blank
+local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libserialport) -DUSE_LIBSERIALPORT -Wall -Wextra -O2 -pipe -I. -DNDEBUG
+
#define a rule that applies to all files ending in the .o suffix, which says that the .o file depends upon the .c version of the file and all the .h files included in the DEPS macro. Compile each object file
%.o: %$(EXTENSION) $(DEPS)
$(CC) -c -o $@ $< $(local_CFLAGS)
@@ -27,15 +32,19 @@
m8c: $(OBJ)
$(CC) -o $@ $^ $(local_CFLAGS) $(INCLUDES)
-libusb: INCLUDES = $(shell pkg-config --libs sdl2 libusb-1.0)
-libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl2 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
+libusb: INCLUDES = $(shell pkg-config --libs sdl3 libusb-1.0)
+libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libusb-1.0) -Wall -Wextra -O2 -pipe -I. -DUSE_LIBUSB=1 -DNDEBUG
libusb: m8c
+rtmidi: INCLUDES = $(shell pkg-config --libs sdl3 rtmidi)
+rtmidi: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 rtmidi) -Wall -Wextra -O2 -pipe -I. -DUSE_RTMIDI -DNDEBUG
+rtmidi: m8c
+
#Cleanup
.PHONY: clean
clean:
- rm -f src/*.o *~ m8c
+ rm -f src/*.o src/backends/*.o *~ m8c
# PREFIX is environment variable, but if it is not set, then set default value
ifeq ($(PREFIX),)
--- a/README.md
+++ b/README.md
@@ -2,20 +2,31 @@
## Introduction
-The [Dirtywave M8 Tracker](https://dirtywave.com/products/m8-tracker) is a portable sequencer and synthesizer, featuring 8 tracks of assignable instruments such as FM, waveform synthesis, virtual analog, sample playback, and MIDI output. It is powered by a [Teensy](https://www.pjrc.com/teensy/) micro-controller and inspired by the Gameboy tracker [Little Sound DJ](https://www.littlesounddj.com/lsd/index.php).
+The [Dirtywave M8 Tracker](https://dirtywave.com/products/m8-tracker) is a portable sequencer and synthesizer, featuring
+8 tracks of assignable instruments such as FM, waveform synthesis, virtual analog, sample playback, and MIDI output. It
+is powered by a [Teensy](https://www.pjrc.com/teensy/) micro-controller and inspired by the Gameboy
+tracker [Little Sound DJ](https://www.littlesounddj.com/lsd/index.php).
-While Dirtywave makes new batches of units available on a regular basis, M8 is sometimes sold out due to the worldwide chip shortage and high demand of the unit. To fill this gap and and to allow users to freely test this wonderful tracker, [Timothy Lamb](https://github.com/trash80) was kind enough to make the [M8 Headless](https://github.com/Dirtywave/M8HeadlessFirmware) available to everyone.
+While Dirtywave makes new batches of units available on a regular basis, M8 is sometimes sold out due to the worldwide
+chip shortage and high demand of the unit. To fill this gap and and to allow users to freely test this wonderful
+tracker, [Timothy Lamb](https://github.com/trash80) was kind enough to make
+the [M8 Headless](https://github.com/Dirtywave/M8HeadlessFirmware) available to everyone.
-If you like the M8 and you gel with the tracker workflow, please support [Dirtywave](https://dirtywave.com/) by purchasing the actual unit. You can check its availability [here](https://dirtywave.com/products/m8-tracker-model-02). Meanwhile, you can also subscribe to Timothy Lamb's [Patreon](https://www.patreon.com/trash80).
+If you like the M8 and you gel with the tracker workflow, please support [Dirtywave](https://dirtywave.com/) by
+purchasing the actual unit. You can check its availability [here](https://dirtywave.com/products/m8-tracker-model-02).
+Meanwhile, you can also subscribe to Timothy Lamb's [Patreon](https://www.patreon.com/trash80).
-*m8c* is a client for Dirtywave M8 tracker's headless mode. The application should be cross-platform ready and can be built in Linux, Windows (with MSYS2/MINGW64) and Mac OS.
+*m8c* is a client for Dirtywave M8 tracker's headless mode. The application should be cross-platform ready and can be
+built in Linux, Windows (with MSYS2/MINGW64) and Mac OS.
Many thanks to:
-* Trash80 for the great M8 hardware and the original font (stealth57.ttf) that was converted to a bitmap for use in the progam.
-* driedfruit for a wonderful little routine to blit inline bitmap fonts, https://github.com/driedfruit/SDL_inprint/
-* marcinbor85 for the slip handling routine, https://github.com/marcinbor85/slip
-* turbolent for the great Golang-based g0m8 application, which I used as reference on how the M8 serial protocol works.
+* Trash80: For the great M8 hardware and the original fonts that were converted to a bitmap for use in the
+ progam.
+* driedfruit: For a wonderful little routine to blit inline bitmap
+ fonts, [SDL_inprint](https://github.com/driedfruit/SDL_inprint/)
+* marcinbor85: For the slip handling routine, https://github.com/marcinbor85/slip
+* turbolent: For the great Golang-based g0m8 application, which I used as reference on how the M8 serial protocol works.
* *Everyone who's contributed to m8c!*
Disclaimer: I'm not a coder and hardly understand C, use at your own risk :)
@@ -26,9 +37,11 @@
### Windows / MacOS
-There are prebuilt binaries available in the [releases section](https://github.com/laamaa/m8c/releases/) for Windows and recent versions of MacOS.
+There are prebuilt binaries available in the [releases section](https://github.com/laamaa/m8c/releases/) for Windows and
+recent versions of MacOS.
-When running the program for the first time on MacOS, it may not open as it is from an Unidentified Developer. You need to open it from the Applications Folder via Control+Click > Open then select Open from the popup menu.
+When running the program for the first time on MacOS, it may not open as it is from an Unidentified Developer. You need
+to open it from the Applications Folder via Control+Click > Open then select Open from the popup menu.
### Linux
@@ -35,6 +48,7 @@
There are packages available for Fedora Linux and NixOS, or you can build the program from source.
#### Fedora
+
``` sh
sudo dnf copr enable laamaa/m8c
sudo dnf install m8c
@@ -41,6 +55,7 @@
```
#### NixOS
+
``` sh
nix-env -iA m8c-stable -f https://github.com/laamaa/m8c/archive/refs/heads/main.tar.gz
```
@@ -55,18 +70,24 @@
#### Install dependencies
-You will need git, gcc, pkg-config, make and the development headers for libsdl2 and libserialport.
+You will need git, gcc, pkg-config, make and the development headers for SDL3 and libserialport.
-##### Linux (Ubuntu)
+##### Linux (Apt/Ubuntu)
+
+As of writing, there is no official SDL3 package yet in the Ubuntu repositories.
+You'll likely need to build the library
+yourself. https://github.com/libsdl-org/SDL/blob/main/docs/README-cmake.md#building-sdl-on-unix
+
```
-sudo apt update && sudo apt install -y git gcc pkg-config make libsdl2-dev libserialport-dev
+sudo apt update && sudo apt install -y git gcc pkg-config make libserialport-dev
```
##### MacOS
This assumes you have [installed brew](https://docs.brew.sh/Installation)
+
```
-brew update && brew install git gcc make sdl2 libserialport pkg-config
+brew update && brew install git gcc make sdl3 libserialport pkg-config
```
#### Download source code
@@ -85,7 +106,8 @@
#### Start the program
-Connect the M8 or Teensy (with headless firmware) to your computer and start the program. It should automatically detect your device.
+Connect the M8 or Teensy (with headless firmware) to your computer and start the program. It should automatically detect
+your device.
```
./m8c
@@ -95,7 +117,8 @@
#### Choosing a preferred device
-When you have multiple M8 devices connected and you want to choose a specific one or launch m8c multiple times, you can get the list of devices by running
+When you have multiple M8 devices connected and you want to choose a specific one or launch m8c multiple times, you can
+get the list of devices by running
```
./m8c --list
@@ -126,17 +149,21 @@
* s / left ctrl = edit
Additional controls:
+
* Alt + enter = toggle full screen / windowed
* Alt + F4 = quit program
* Delete = opt+edit (deletes a row)
-* Esc = toggle keyjazz on/off
+* Esc = toggle keyjazz on/off
* r / select+start+opt+edit = reset display (if glitches appear on the screen, use this)
* F12 = toggle audio routing on / off
### Keyjazz
-Keyjazz allows to enter notes with keyboard, oldschool tracker-style. The layout is two octaves, starting from keys Z and Q.
-When keyjazz is active, regular a/s/z/x keys are disabled. The base octave can be adjusted with numpad star/divide keys and the velocity can be set
+Keyjazz allows to enter notes with keyboard, oldschool tracker-style. The layout is two octaves, starting from keys Z
+and Q.
+When keyjazz is active, regular a/s/z/x keys are disabled. The base octave can be adjusted with numpad star/divide keys
+and the velocity can be set
+
* Numpad asterisk (\*): increase base octave
* Numpad divide (/): decrease base ooctave
* Numpad plus (+): increase velocity
@@ -144,14 +171,20 @@
## Gamepads
-The program uses SDL's game controller system, which should make it work automagically with most gamepads. On startup, the program tries to load a SDL game controller database named gamecontrollerdb.txt from the same directory as the config file. If your joypad doesn't work out of the box, you might need to create custom bindings to this file, for example with [SDL2 Gamepad Tool](https://generalarcade.com/gamepadtool/).
+The program uses SDL's game controller system, which should make it work automagically with most gamepads.On startup,
+the program tries to load a SDL game controller database named gamecontrollerdb.txt from the same directory as the
+config file. If your joypad doesn't work out of the box, you might need to create custom bindings to this file, for
+example with [SDL2 Gamepad Tool](https://generalarcade.com/gamepadtool/).
## Audio
-Experimental audio routing support can be enabled by setting the config value `"audio_enabled"` to `"true"`. The audio buffer size can also be tweaked from the config file for possible lower latencies.
-If the right audio device is not picked up by default, you can use a specific audio device by using `"audio_device_name"` config parameter.
+Experimental audio routing support can be enabled by setting the config value `"audio_enabled"` to `"true"`. The audio
+buffer size can also be tweaked from the config file for possible lower latencies.
+If the right audio device is not picked up by default, you can use a specific audio device by using
+`"audio_device_name"` config parameter.
-It is possible to toggle audio routing on/off with a key defined in the config (`"key_toggle_audio"`). The default key is F12.
+It is possible to toggle audio routing on/off with a key defined in the config (`"key_toggle_audio"`). The default key
+is F12.
On MacOS you need to grant the program permission to access the Microphone for audio routing to work.
@@ -159,9 +192,11 @@
Application settings and keyboard/game controller bindings can be configured via `config.ini`.
-The keyboard configuration uses SDL2 Scancodes, a reference list can be found at https://wiki.libsdl.org/SDL2/SDLScancodeLookup
+The keyboard configuration uses SDL2 Scancodes, a reference list can be found
+at https://wiki.libsdl.org/SDL2/SDLScancodeLookup
If the file does not exist, it will be created in one of these locations:
+
* Windows: `C:\Users\<username>\AppData\Roaming\m8c\config.ini`
* Linux: `/home/<username>/.local/share/m8c/config.ini`
* MacOS: `/Users/<username>/Library/Application Support/m8c/config.ini`
@@ -172,7 +207,8 @@
m8c --config alternate_config.ini
```
-This looks for a config file with the given name in the same directory as the default config. If you specify a config file that does not exist, a new default config file with the specified name will be created, which you can then edit.
+This looks for a config file with the given name in the same directory as the default config. If you specify a config
+file that does not exist, a new default config file with the specified name will be created, which you can then edit.
Enjoy making some nice music!
@@ -181,6 +217,7 @@
## FAQ
* When starting the program, something like the following appears and the program does not start:
+
```
$ ./m8c
INFO: Looking for USB serial devices.
@@ -189,21 +226,26 @@
ERROR: Error: Failed: Permission denied
```
-This is likely caused because the user running m8c does not have permission to use the serial port. The eaiest way to fix this is to add the current user to a group with permission to use the serial port.
+This is likely caused because the user running m8c does not have permission to use the serial port. The eaiest way to
+fix this is to add the current user to a group with permission to use the serial port.
On Linux systems, look at the permissions on the serial port shown on the line that says "Found M8 in":
+
```
$ ls -la /dev/ttyACM1
crw-rw---- 1 root dialout 166, 0 Jan 8 14:51 /dev/ttyACM0
```
-In this case the serial port is owned by the user 'root' and the group 'dialout'. Both the user and the group have read/write permissions. To add a user to the group, run this command, replacing 'dialout' with the group shown on your own system:
+In this case the serial port is owned by the user 'root' and the group 'dialout'. Both the user and the group have
+read/write permissions. To add a user to the group, run this command, replacing 'dialout' with the group shown on your
+own system:
``` sh
sudo adduser $USER dialout
```
-You may need to log out and back in or even fully reboot the system for this change to take effect, but this will hopefully fix the problem. Please see [this issue for more details](https://github.com/laamaa/m8c/issues/20).
+You may need to log out and back in or even fully reboot the system for this change to take effect, but this will
+hopefully fix the problem. Please see [this issue for more details](https://github.com/laamaa/m8c/issues/20).
--- a/flake.nix
+++ b/flake.nix
@@ -31,7 +31,7 @@
{ stdenv, gnumake
, pkg-config
- , SDL2
+ , SDL3
, libserialport
, fetchFromGitHub
}:
@@ -47,7 +47,7 @@
installFlags = [ "PREFIX=$(out)" ];
nativeBuildInputs = [ gnumake pkg-config ];
- buildInputs = [ SDL2 libserialport ];
+ buildInputs = [ SDL3 libserialport ];
};
eachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f
(import nixpkgs { inherit system; })--- /dev/null
+++ b/package/appimage/icon.svg
@@ -1,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18v-6a9 9 0 0 1 18 0v6"></path><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"></path></svg>
\ No newline at end of file
--- /dev/null
+++ b/package/appimage/m8c.appdata.xml
@@ -1,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop-application">
+ <id>m8c</id>
+ <metadata_license>MIT</metadata_license>
+ <project_license>MIT</project_license>
+ <name>m8c</name>
+ <summary>A Dirtywave M8 remote client</summary>
+ <description>
+ <p>m8c is a client for Dirtywave M8 tracker's remote (headless) mode and can mirror the display and route audio.</p>
+ </description>
+ <launchable type="desktop-id">m8c.desktop</launchable>
+ <url type="homepage">https://m8c.laamaa.fi</url>
+ <screenshots>
+ <screenshot type="default">
+ <image></image>
+ </screenshot>
+ </screenshots>
+ <provides>
+ <id>m8c.desktop</id>
+ </provides>
+</component>
\ No newline at end of file
--- /dev/null
+++ b/package/appimage/m8c.desktop
@@ -1,0 +1,9 @@
+[Desktop Entry]
+Type=Application
+Name=m8c
+Comment=
+Exec=m8c
+Icon=icon
+Terminal=false
+Categories=Audio
+X-AppImage-Version=1.0
--- /dev/null
+++ b/package/appimage/package.sh
@@ -1,0 +1,62 @@
+#!/bin/bash
+set -xe
+
+# Ubuntu 20.04
+
+APP=m8c
+VERSION=2.0.0
+
+if [ "$1" == "build-sdl" ]; then
+
+ ## Build SDL
+ SDL_VERSION=3.2.6
+ SDL_SHA256="096a0b843dd1124afda41c24bd05034af75af37e9a1b9d205cc0a70193b27e1a SDL3-3.2.6.tar.gz"
+
+ sudo apt-get install build-essential git make \
+ pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
+ libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
+ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev \
+ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
+ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libserialport-dev python2
+
+ if [ ! -d SDL3-$SDL_VERSION ]; then
+
+ curl -O https://www.libsdl.org/release/SDL3-$SDL_VERSION.tar.gz
+
+ if ! echo $SDL_SHA256 | sha256sum -c --status -; then
+ echo "SDL archive checksum failed" >&2
+ exit 1
+ fi
+
+ tar zxvf SDL3-$SDL_VERSION.tar.gz
+ pushd SDL3-$SDL_VERSION
+ mkdir build_x86_64
+ cmake -S . -B build_x86_64
+ cmake --build build_x86_64
+ sudo cmake --install build_x86_64
+ popd
+
+ fi
+
+fi
+
+if [ "$2" == "build-m8c" ]; then
+make
+fi
+
+mkdir -p $APP.AppDir/usr/bin
+cp m8c $APP.AppDir/usr/bin/
+cp gamecontrollerdb.txt $APP.AppDir
+
+mkdir -p $APP.AppDir/usr/share/applications/ $APP.AppDir/usr/share/metainfo/
+cp package/appimage/m8c.desktop $APP.AppDir/usr/share/applications/
+#appstreamcli seems to crash
+#cp package/appimage/m8c.appdata.xml $APP.AppDir/usr/share/metainfo/
+cp package/appimage/icon.svg $APP.AppDir
+
+wget -c https://github.com/$(wget -q https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous -O - | grep "appimagetool-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2)
+chmod +x ./appimagetool-*.AppImage
+./appimagetool-*.AppImage deploy ./$APP.AppDir/usr/share/applications/m8c.desktop
+./appimagetool-*.AppImage ./$APP.AppDir
+
+
--- /dev/null
+++ b/package/macos/Entitlements.plist
@@ -1,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.device.audio-input</key>
+ <true/>
+</dict>
+</plist>
\ No newline at end of file
--- a/package/macos/m8c.app/Contents/Info.plist
+++ b/package/macos/m8c.app/Contents/Info.plist
@@ -23,7 +23,7 @@
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string><key>CFBundleSignature</key>
- <string>????</string>
+ <string>APPL</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string><key>CSResourcesFileMapped</key>
--- a/package/rpm/m8c.spec
+++ b/package/rpm/m8c.spec
@@ -1,5 +1,5 @@
Name: m8c
-Version: 1.7.10
+Version: 2.0.0
Release: 1%{?dist}Summary: m8c is a client for Dirtywave M8 music tracker's headless mode
@@ -9,9 +9,9 @@
BuildRequires: gcc
BuildRequires: make
-BuildRequires: SDL2-devel
+BuildRequires: SDL3-devel
BuildRequires: libserialport-devel
-Requires: SDL2
+Requires: SDL3
Requires: libserialport
%description
@@ -35,5 +35,7 @@
%changelog
-* Tue Aug 29 2023 Jonne Kokkonen <jonne.kokkonen@ambientia.fi>
+* Sat Mar 01 2025 Jonne Kokkonen <jonne.kokkonen@gmail.com>
+- Update dependencies (SDL2 -> SDL3)
+* Tue Aug 29 2023 Jonne Kokkonen <jonne.kokkonen@gmail.com>
- First m8c package
--- a/src/SDL2_inprint.h
+++ b/src/SDL2_inprint.h
@@ -4,13 +4,13 @@
#ifndef SDL2_inprint_h
#define SDL2_inprint_h
-#include "inline_font.h"
-#include <SDL.h>
+#include "fonts/inline_font.h"
+#include <SDL3/SDL.h>
-extern void prepare_inline_font(struct inline_font *font);
-extern void kill_inline_font(void);
+extern void inline_font_initialize(struct inline_font *font);
+extern void inline_font_close(void);
-extern void inrenderer(SDL_Renderer *renderer);
+extern void inline_font_set_renderer(SDL_Renderer *renderer);
extern void infont(SDL_Texture *font);
extern void incolor1(const SDL_Color *color);
extern void incolor(Uint32 color); /* Color must be in 0x00RRGGBB format ! */
--- a/src/audio.c
+++ /dev/null
@@ -1,108 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifndef USE_LIBUSB
-#include "audio.h"
-#include <SDL.h>
-#include <stdint.h>
-
-static SDL_AudioDeviceID devid_in = 0;
-static SDL_AudioDeviceID devid_out = 0;
-
-static unsigned int audio_paused = 0;
-static unsigned int audio_initialized = 0;
-
-void toggle_audio(const unsigned int audio_buffer_size, const char *output_device_name) {- if (!audio_initialized) {- audio_init(audio_buffer_size, output_device_name);
- return;
- }
- audio_paused = !audio_paused;
- SDL_PauseAudioDevice(devid_in, audio_paused);
- SDL_PauseAudioDevice(devid_out, audio_paused);
- SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");
-}
-
-void audio_cb_in(void *userdata, uint8_t *stream, int len) {- (void)userdata; // suppress compiler warning
- SDL_QueueAudio(devid_out, stream, len);
-}
-
-int audio_init(const unsigned int audio_buffer_size, const char *output_device_name) {-
- int m8_device_id = -1;
-
- // wait for system to initialize possible new audio devices
- SDL_Delay(500);
-
- const int devcount_in = SDL_GetNumAudioDevices(SDL_TRUE);
-
- if (devcount_in < 1) {- SDL_Log("No audio capture devices, SDL Error: %s", SDL_GetError());- return 0;
- }
- for (int i = 0; i < devcount_in; i++) {- // Check if input device exists before doing anything else
- SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", SDL_GetAudioDeviceName(i, SDL_TRUE));
- if (SDL_strstr(SDL_GetAudioDeviceName(i, SDL_TRUE), "M8") != NULL) {- SDL_Log("M8 Audio Input device found: %s", SDL_GetAudioDeviceName(i, SDL_TRUE));- m8_device_id = i;
- }
- }
- if (m8_device_id == -1) {- // forget about it
- SDL_Log("Cannot find M8 audio input device");- return 0;
- }
-
- SDL_AudioSpec want_in, have_in, want_out, have_out;
-
- // Open output device first to avoid possible Directsound errors
- SDL_zero(want_out);
- want_out.freq = 44100;
- want_out.format = AUDIO_S16;
- want_out.channels = 2;
- want_out.samples = audio_buffer_size;
- devid_out =
- SDL_OpenAudioDevice(output_device_name, 0, &want_out, &have_out, SDL_AUDIO_ALLOW_ANY_CHANGE);
- if (devid_out == 0) {- SDL_Log("Failed to open output: %s", SDL_GetError());- return 0;
- }
-
- SDL_zero(want_in);
- want_in.freq = 44100;
- want_in.format = AUDIO_S16;
- want_in.channels = 2;
- want_in.samples = audio_buffer_size;
- want_in.callback = audio_cb_in;
- devid_in = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(m8_device_id, SDL_TRUE), SDL_TRUE, &want_in,
- &have_in, SDL_AUDIO_ALLOW_ANY_CHANGE);
- if (devid_in == 0) {- SDL_Log("Failed to open M8 audio device, SDL Error: %s", SDL_GetError());- return 0;
- }
-
- // Start audio processing
- SDL_Log("Opening audio devices");- SDL_PauseAudioDevice(devid_in, 0);
- SDL_PauseAudioDevice(devid_out, 0);
-
- audio_paused = 0;
- audio_initialized = 1;
-
- return 1;
-}
-
-void audio_destroy() {- if (!audio_initialized)
- return;
- SDL_Log("Closing audio devices");- SDL_PauseAudioDevice(devid_in, 1);
- SDL_PauseAudioDevice(devid_out, 1);
- SDL_CloseAudioDevice(devid_in);
- SDL_CloseAudioDevice(devid_out);
-
- audio_initialized = 0;
-}
-
-#endif
--- a/src/audio.h
+++ /dev/null
@@ -1,10 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifndef AUDIO_H
-#define AUDIO_H
-
-int audio_init(unsigned int audio_buffer_size, const char *output_device_name);
-void toggle_audio(unsigned int audio_buffer_size, const char *output_device_name);
-void audio_destroy();
-
-#endif
--- /dev/null
+++ b/src/backends/audio.h
@@ -1,0 +1,11 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef AUDIO_H
+#define AUDIO_H
+
+int audio_initialize(const char *output_device_name, unsigned int audio_buffer_size);
+void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size);
+void audio_process(void);
+void audio_close(void);
+
+#endif
--- /dev/null
+++ b/src/backends/audio_libusb.c
@@ -1,0 +1,296 @@
+#include "SDL3/SDL_audio.h"
+#include "SDL3/SDL_error.h"
+#ifdef USE_LIBUSB
+
+#include "m8.h"
+#include "ringbuffer.h"
+#include <SDL3/SDL.h>
+#include <errno.h>
+#include <libusb.h>
+
+#define EP_ISO_IN 0x85
+#define IFACE_NUM 4
+
+#define NUM_TRANSFERS 64
+#define PACKET_SIZE 180
+#define NUM_PACKETS 2
+
+extern libusb_device_handle *devh;
+
+SDL_AudioStream *sdl_audio_stream = NULL;
+int audio_initialized = 0;
+RingBuffer *audio_buffer = NULL;
+static uint8_t *audio_callback_buffer = NULL;
+static size_t audio_callback_buffer_size = 0;
+static int audio_prebuffer_filled = 0;
+#define PREBUFFER_SIZE (8 * 1024) // Wait for 8KB before starting playback
+
+static void audio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {+ (void)userdata; // Suppress unused parameter warning
+ (void)additional_amount; // Suppress unused parameter warning
+
+ // Reallocate callback buffer if needed
+ if (audio_callback_buffer_size < (size_t)total_amount) {+ audio_callback_buffer = SDL_realloc(audio_callback_buffer, total_amount);
+ if (audio_callback_buffer == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to allocate audio buffer");
+ return;
+ }
+ audio_callback_buffer_size = (size_t)total_amount;
+ }
+
+ // Try to get audio data from ring buffer
+ uint32_t available_bytes = audio_buffer->size;
+
+ // Check if we have enough data for initial buffering
+ if (!audio_prebuffer_filled && available_bytes < PREBUFFER_SIZE) {+ // Not enough data yet, output silence and wait
+ SDL_memset(audio_callback_buffer, 0, total_amount);
+ if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+ }
+ return;
+ }
+
+ // Mark prebuffer as filled once we have enough data
+ if (!audio_prebuffer_filled) {+ audio_prebuffer_filled = 1;
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Audio prebuffer filled, starting playback");
+ }
+
+ if (available_bytes >= (uint32_t)total_amount) {+ // We have enough data, read it
+ uint32_t read_len = ring_buffer_pop(audio_buffer, audio_callback_buffer, total_amount);
+ if (read_len > 0) {+ if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, read_len)) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+ }
+ }
+ } else if (available_bytes > 0) {+ // We have some data but not enough - read what we can and pad with silence
+ uint32_t read_len = ring_buffer_pop(audio_buffer, audio_callback_buffer, available_bytes);
+ SDL_memset(audio_callback_buffer + read_len, 0, total_amount - read_len);
+ if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+ }
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Partial buffer: %d/%d bytes", available_bytes, total_amount);
+ } else {+ // No data available - put silence and reset prebuffer flag
+ SDL_memset(audio_callback_buffer, 0, total_amount);
+ if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+ }
+ audio_prebuffer_filled = 0; // Reset prebuffer to avoid continuous dropouts
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow! Resetting prebuffer");
+ }
+}
+
+static void cb_xfr(struct libusb_transfer *xfr) {+ unsigned int i;
+ static int error_count = 0;
+
+ for (i = 0; i < (unsigned int)xfr->num_iso_packets; i++) {+ struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
+
+ if (pack->status != LIBUSB_TRANSFER_COMPLETED) {+ error_count++;
+ if (error_count % 100 == 1) { // Log only every 100th error to avoid spam+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
+ libusb_error_name(pack->status));
+ }
+ continue; // Skip this packet but continue processing others
+ }
+
+ if (pack->actual_length > 0) {+ const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
+ if (sdl_audio_stream != 0 && audio_buffer != NULL) {+ uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
+ if (actual == (uint32_t)-1) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
+ }
+ }
+ }
+ }
+
+ // Reset error count on successful transfer
+ if (xfr->status == LIBUSB_TRANSFER_COMPLETED) {+ error_count = 0;
+ }
+
+ int submit_result = libusb_submit_transfer(xfr);
+ if (submit_result < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB: %s", libusb_error_name(submit_result));
+ SDL_free(xfr->buffer);
+ }
+}
+
+static struct libusb_transfer *xfr[NUM_TRANSFERS];
+
+static int benchmark_in() {+ int i;
+
+ for (i = 0; i < NUM_TRANSFERS; i++) {+ xfr[i] = libusb_alloc_transfer(NUM_PACKETS);
+ if (!xfr[i]) {+ SDL_Log("Could not allocate transfer");+ return -ENOMEM;
+ }
+
+ Uint8 *buffer = SDL_malloc(PACKET_SIZE * NUM_PACKETS);
+
+ libusb_fill_iso_transfer(xfr[i], devh, EP_ISO_IN, buffer, PACKET_SIZE * NUM_PACKETS,
+ NUM_PACKETS, cb_xfr, NULL, 0);
+ libusb_set_iso_packet_lengths(xfr[i], PACKET_SIZE);
+
+ libusb_submit_transfer(xfr[i]);
+ }
+
+ return 1;
+}
+
+int audio_initialize(const char *output_device_name, unsigned int audio_buffer_size) {+ (void)audio_buffer_size; // Suppress unused parameter warning
+
+ SDL_Log("USB audio setup");+
+ if (devh == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Device handle is NULL - cannot initialize audio");
+ return -1;
+ }
+
+ int rc;
+
+ rc = libusb_kernel_driver_active(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error checking kernel driver status: %s", libusb_error_name(rc));
+ return rc;
+ }
+ if (rc == 1) {+ SDL_Log("Detaching kernel driver");+ rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not detach kernel driver: %s", libusb_error_name(rc));
+ return rc;
+ }
+ }
+
+ rc = libusb_claim_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error claiming interface: %s\n", libusb_error_name(rc));
+ return rc;
+ }
+
+ rc = libusb_set_interface_alt_setting(devh, IFACE_NUM, 1);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error setting alt setting: %s\n", libusb_error_name(rc));
+ return rc;
+ }
+
+ if (!SDL_WasInit(SDL_INIT_AUDIO)) {+ if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Init audio failed %s", SDL_GetError());
+ return -1;
+ }
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Audio was already initialised");
+ }
+
+ static SDL_AudioSpec audio_spec;
+ audio_spec.format = SDL_AUDIO_S16;
+ audio_spec.channels = 2;
+ audio_spec.freq = 44100;
+
+ SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),+ output_device_name);
+
+ // Create larger ring buffer for stable audio - about 1.5 seconds at 44.1kHz stereo 16-bit
+ audio_buffer = ring_buffer_create(256 * 1024);
+
+ if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {+ SDL_Log("Using default audio device");+ sdl_audio_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec, &audio_callback, &audio_buffer);
+ } else {+ // TODO: Implement audio device selection
+ sdl_audio_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec, &audio_callback, &audio_buffer);
+ }
+
+ if (sdl_audio_stream == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to open audio stream: %s", SDL_GetError());
+ ring_buffer_free(audio_buffer);
+ return -1;
+ }
+
+ SDL_ResumeAudioStreamDevice(sdl_audio_stream);
+
+ // Good to go
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Starting capture");
+ if ((rc = benchmark_in()) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Capture failed to start: %d", rc);
+ return rc;
+ }
+
+ audio_initialized = 1;
+ audio_prebuffer_filled = 0; // Reset prebuffer state
+ SDL_Log("Successful init");+ return 1;
+
+}
+
+void audio_close() {+ if (devh == NULL) {+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Device handle is NULL - audio already closed or not initialized");
+ return;
+ }
+ if (!audio_initialized) {+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audio not initialized - nothing to close");
+ return;
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Closing audio");
+
+ int rc;
+
+ for (int i = 0; i < NUM_TRANSFERS; i++) {+ rc = libusb_cancel_transfer(xfr[i]);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error cancelling transfer: %s\n",
+ libusb_error_name(rc));
+ }
+ }
+
+ SDL_Log("Freeing interface %d", IFACE_NUM);+
+ rc = libusb_release_interface(devh, IFACE_NUM);
+ if (rc < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error releasing interface: %s\n", libusb_error_name(rc));
+ return;
+ }
+
+ if (sdl_audio_stream != NULL) {+ SDL_Log("Closing audio device");+ SDL_DestroyAudioStream(sdl_audio_stream);
+ sdl_audio_stream = 0;
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Audio closed");
+
+ ring_buffer_free(audio_buffer);
+
+ // Free callback buffer
+ if (audio_callback_buffer) {+ SDL_free(audio_callback_buffer);
+ audio_callback_buffer = NULL;
+ audio_callback_buffer_size = 0;
+ }
+
+ audio_initialized = 0;
+ audio_prebuffer_filled = 0;
+}
+
+void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size) {+ (void)output_device_name; // Suppress unused parameter warning
+ (void)audio_buffer_size; // Suppress unused parameter warning
+ SDL_Log("Libusb audio toggling not implemented yet");+}
+
+#endif
--- /dev/null
+++ b/src/backends/audio_sdl.c
@@ -1,0 +1,208 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef USE_LIBUSB
+#include "audio.h"
+#include <SDL3/SDL.h>
+
+SDL_AudioStream *audio_stream_in, *audio_stream_out;
+
+static unsigned int audio_paused = 0;
+static unsigned int audio_initialized = 0;
+static SDL_AudioSpec audio_spec_in = {SDL_AUDIO_S16LE, 2, 44100};+
+static void SDLCALL audio_cb_out(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {+ // suppress compiler warnings
+ (void)userdata;
+
+ if (additional_amount <= 0) {+ return;
+ }
+
+ const int bytes_available = SDL_GetAudioStreamAvailable(audio_stream_in);
+ if (bytes_available == -1) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+ "Error getting available audio stream bytes: %s, destroying audio",
+ SDL_GetError());
+ audio_close();
+ return;
+ }
+
+ // Decide how much to feed this time.
+ int to_write_goal = additional_amount;
+ if (total_amount > 0) {+ const int prefill_cap = additional_amount * 2;
+ if (to_write_goal < total_amount) {+ to_write_goal = SDL_min(total_amount, prefill_cap);
+ }
+ }
+
+ int to_write = to_write_goal;
+ Uint8 temp[4096];
+
+ while (to_write > 0) {+ int still_avail = SDL_GetAudioStreamAvailable(audio_stream_in);
+ if (still_avail <= 0) {+ break; // nothing more to pull now
+ }
+
+ int chunk = still_avail;
+ if (chunk > (int)sizeof(temp)) chunk = (int)sizeof(temp);
+ if (chunk > to_write) chunk = to_write;
+
+ const int got = SDL_GetAudioStreamData(audio_stream_in, temp, chunk);
+ if (got == -1) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+ "Error reading audio stream data: %s, destroying audio",
+ SDL_GetError());
+ audio_close();
+ return;
+ }
+ if (got == 0) {+ break; // no data currently available
+ }
+
+ if (!SDL_PutAudioStreamData(stream, temp, got)) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+ "Error putting audio stream data: %s, destroying audio",
+ SDL_GetError());
+ audio_close();
+ return;
+ }
+
+ to_write -= got;
+ }
+}
+
+void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size) {+ if (!audio_initialized) {+ audio_initialize(output_device_name, audio_buffer_size);
+ return;
+ }
+ if (audio_paused) {+ SDL_ResumeAudioStreamDevice(audio_stream_out);
+ SDL_ResumeAudioStreamDevice(audio_stream_in);
+ } else {+ SDL_PauseAudioStreamDevice(audio_stream_in);
+ SDL_PauseAudioStreamDevice(audio_stream_out);
+ }
+ audio_paused = !audio_paused;
+ SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");
+}
+
+int audio_initialize(const char *output_device_name, const unsigned int audio_buffer_size) {+
+ int num_devices_in, num_devices_out;
+ SDL_AudioDeviceID m8_device_id = 0;
+ SDL_AudioDeviceID output_device_id = 0;
+
+ if (SDL_Init(SDL_INIT_AUDIO) == false) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "SDL Audio init failed, SDL Error: %s", SDL_GetError());
+ return 0;
+ }
+
+ SDL_AudioDeviceID *devices_in = SDL_GetAudioRecordingDevices(&num_devices_in);
+ if (!devices_in) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio capture devices, SDL Error: %s", SDL_GetError());
+ return 0;
+ }
+
+ SDL_AudioDeviceID *devices_out = SDL_GetAudioPlaybackDevices(&num_devices_out);
+ if (!devices_out) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "No audio playback devices, SDL Error: %s",
+ SDL_GetError());
+ return 0;
+ }
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audio input devices:");
+ for (int i = 0; i < num_devices_in; i++) {+ const SDL_AudioDeviceID instance_id = devices_in[i];
+ const char *device_name = SDL_GetAudioDeviceName(instance_id);
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
+ if (SDL_strstr(device_name, "M8") != NULL) {+ SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "M8 Audio Input device found: %s", device_name);
+ m8_device_id = instance_id;
+ }
+ }
+
+ if (output_device_name != NULL) {+ for (int i = 0; i < num_devices_out; i++) {+ const SDL_AudioDeviceID instance_id = devices_out[i];
+ const char *device_name = SDL_GetAudioDeviceName(instance_id);
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", device_name);
+ if (SDL_strcasestr(device_name, output_device_name) != NULL) {+ SDL_Log("Requested output device found: %s", device_name);+ output_device_id = instance_id;
+ }
+ }
+ }
+
+ SDL_free(devices_in);
+ SDL_free(devices_out);
+
+ if (!output_device_id) {+ output_device_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+ }
+
+ if (!m8_device_id) {+ // forget about it
+ SDL_Log("Cannot find M8 audio input device");+ return 0;
+ }
+
+ char audio_buffer_size_str[256];
+ SDL_snprintf(audio_buffer_size_str, sizeof(audio_buffer_size_str), "%d", audio_buffer_size);
+ if (audio_buffer_size > 0) {+ SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "Setting requested audio device sample frames to %d",
+ audio_buffer_size);
+ SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, audio_buffer_size_str);
+ }
+
+ audio_stream_out = SDL_OpenAudioDeviceStream(output_device_id, NULL, audio_cb_out, NULL);
+
+ SDL_AudioSpec audio_spec_out;
+ int audio_out_buffer_size_real, audio_in_buffer_size_real = 0;
+
+ SDL_GetAudioDeviceFormat(output_device_id, &audio_spec_out, &audio_out_buffer_size_real);
+
+ if (!audio_stream_out) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio output device: %s", SDL_GetError());
+ return 0;
+ }
+ SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO,
+ "Opening audio output: rate %dhz, buffer size: %d frames", audio_spec_out.freq,
+ audio_out_buffer_size_real);
+
+ audio_stream_in = SDL_OpenAudioDeviceStream(m8_device_id, &audio_spec_in, NULL, NULL);
+ if (!audio_stream_in) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio input device: %s", SDL_GetError());
+ SDL_DestroyAudioStream(audio_stream_out);
+ return 0;
+ }
+
+ SDL_SetAudioStreamFormat(audio_stream_in, &audio_spec_in, &audio_spec_out);
+ SDL_GetAudioDeviceFormat(m8_device_id, &audio_spec_in, &audio_in_buffer_size_real);
+ SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audiospec In: format %d, channels %d, rate %d, buffer size %d frames",
+ audio_spec_in.format, audio_spec_in.channels, audio_spec_in.freq, audio_in_buffer_size_real);
+
+
+
+ SDL_ResumeAudioStreamDevice(audio_stream_out);
+ SDL_ResumeAudioStreamDevice(audio_stream_in);
+
+ audio_paused = 0;
+ audio_initialized = 1;
+
+ return 1;
+}
+
+void audio_close(void) {+ if (!audio_initialized)
+ return;
+ SDL_Log("Closing audio devices");+ SDL_DestroyAudioStream(audio_stream_in);
+ SDL_DestroyAudioStream(audio_stream_out);
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ audio_initialized = 0;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/m8.h
@@ -1,0 +1,25 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef M8_H_
+#define M8_H_
+
+#include "../config.h"
+
+enum return_codes {+ DEVICE_DISCONNECTED = 0,
+ DEVICE_PROCESSING = 1,
+ DEVICE_FATAL_ERROR = -1
+};
+
+int m8_initialize(int verbose, const char *preferred_device);
+int m8_list_devices(void);
+int m8_reset_display(void);
+int m8_enable_display(unsigned char reset_display);
+int m8_send_msg_controller(unsigned char input);
+int m8_send_msg_keyjazz(unsigned char note, unsigned char velocity);
+int m8_process_data(const config_params_s *conf);
+int m8_pause_processing(void);
+int m8_resume_processing(void);
+int m8_close(void);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/src/backends/m8_libserialport.c
@@ -1,0 +1,405 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+// Contains portions of code from libserialport's examples released to the
+// public domain
+
+#ifdef USE_LIBSERIALPORT
+#include <SDL3/SDL.h>
+#include <libserialport.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../command.h"
+#include "../config.h"
+#include "m8.h"
+#include "queue.h"
+#include "slip.h"
+
+#define SERIAL_READ_SIZE 1024 // maximum amount of bytes to read from the serial in one pass
+#define SERIAL_READ_DELAY_MS 4 // delay between serial reads in milliseconds
+
+struct sp_port *m8_port = NULL;
+// allocate memory for serial buffers
+static uint8_t serial_buffer[SERIAL_READ_SIZE] = {0};+static uint8_t slip_buffer[SERIAL_READ_SIZE] = {0};+static slip_handler_s slip;
+message_queue_s queue;
+
+SDL_Thread *serial_thread = NULL;
+
+// Structure to pass data to the thread
+typedef struct {+ int should_stop; // Shared stop flag
+} thread_params_s;
+
+thread_params_s thread_params;
+
+// Helper function for error handling
+static int check(enum sp_return result);
+
+static int send_message_to_queue(uint8_t *data, const uint32_t size) {+ push_message(&queue, data, size);
+ return 1;
+}
+
+static int disconnect() {+ SDL_Log("Disconnecting M8");+
+ // wait for the serial processing thread to finish
+ thread_params.should_stop = 1;
+ SDL_WaitThread(serial_thread, NULL);
+ destroy_queue(&queue);
+
+ const unsigned char buf[1] = {'D'};+
+ int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
+ result = 0;
+ }
+
+ sp_close(m8_port);
+ sp_free_port(m8_port);
+ m8_port = NULL;
+ return result;
+}
+
+static int detect_m8_serial_device(const struct sp_port *m8_port) {+ // Check the connection method - we want USB serial devices
+ const enum sp_transport transport = sp_get_port_transport(m8_port);
+
+ if (transport == SP_TRANSPORT_USB) {+ // Get the USB vendor and product IDs.
+ int usb_vid, usb_pid;
+ sp_get_port_usb_vid_pid(m8_port, &usb_vid, &usb_pid);
+
+ if (usb_vid == 0x16C0 && usb_pid == 0x048A)
+ return 1;
+ }
+
+ return 0;
+}
+
+// Checks for connected devices and whether the specified device still exists
+static int serial_port_connected() {+
+ int device_found = 0;
+
+ struct sp_port **port_list;
+
+ const enum sp_return result = sp_list_ports(&port_list);
+
+ if (result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ abort();
+ }
+
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ if (strcmp(sp_get_port_name(port), sp_get_port_name(m8_port)) == 0)
+ device_found = 1;
+ }
+ }
+
+ sp_free_port_list(port_list);
+ return device_found;
+}
+
+static void process_received_bytes(const uint8_t *buffer, int bytes_read, slip_handler_s *slip) {+ const uint8_t *cur = buffer;
+ const uint8_t *end = buffer + bytes_read;
+ while (cur < end) {+ const int slip_result = slip_read_byte(slip, *cur++);
+ if (slip_result != SLIP_NO_ERROR) {+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d", slip_result);
+ }
+ }
+}
+
+static int thread_process_serial_data(void *data) {+ const thread_params_s *thread_params = data;
+
+ while (!thread_params->should_stop) {+ // attempt to read from serial port
+ const int bytes_read = sp_nonblocking_read(m8_port, serial_buffer, SERIAL_READ_SIZE);
+
+ if (bytes_read < 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial.", bytes_read);
+ disconnect();
+ return 0;
+ }
+
+ if (bytes_read > 0) {+ process_received_bytes(serial_buffer, bytes_read, &slip);
+ }
+
+ SDL_Delay(SERIAL_READ_DELAY_MS);
+ }
+ return 1;
+}
+
+static int configure_serial_port(struct sp_port *port) {+ if (check(sp_open(port, SP_MODE_READ_WRITE)) != SP_OK)
+ return 0;
+ if (check(sp_set_baudrate(port, 115200)) != SP_OK)
+ return 0;
+ if (check(sp_set_bits(port, 8)) != SP_OK)
+ return 0;
+ if (check(sp_set_parity(port, SP_PARITY_NONE)) != SP_OK)
+ return 0;
+ if (check(sp_set_stopbits(port, 1)) != SP_OK)
+ return 0;
+ if (check(sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE)) != SP_OK)
+ return 0;
+ return 1;
+}
+
+// Helper function for error handling.
+static int check(const enum sp_return result) {+ char *error_message;
+
+ switch (result) {+ case SP_ERR_ARG:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid argument");
+ break;
+ case SP_ERR_FAIL:
+ error_message = sp_last_error_message();
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Failed: %s", error_message);
+ sp_free_error_message(error_message);
+ break;
+ case SP_ERR_SUPP:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Not supported");
+ break;
+ case SP_ERR_MEM:
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Couldn't allocate memory");
+ break;
+ case SP_OK:
+ default:
+ break;
+ }
+ return result;
+}
+
+// Extracted function for initializing threads and message queue
+static int initialize_serial_thread() {+
+ init_queue(&queue);
+ serial_thread = SDL_CreateThread(thread_process_serial_data, "SerialThread", &thread_params);
+
+ if (!serial_thread) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "SDL_CreateThread Error: %s", SDL_GetError());
+ SDL_Quit();
+ return 0;
+ }
+
+ thread_params.should_stop = 0;
+
+ return 1;
+}
+
+// Extracted function for detecting and selecting the M8 device
+static int find_and_select_device(const char *preferred_device) {+ struct sp_port **port_list;
+ const enum sp_return port_result = sp_list_ports(&port_list);
+
+ if (port_result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!");
+ return 0;
+ }
+
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ char *port_name = sp_get_port_name(port);
+ SDL_Log("Found M8 in %s", port_name);+ sp_copy_port(port, &m8_port);
+
+ // Break if preferred device is found
+ if (preferred_device != NULL && strcmp(preferred_device, port_name) == 0) {+ SDL_Log("Found preferred device, breaking");+ break;
+ }
+ }
+ }
+
+ sp_free_port_list(port_list);
+ return (m8_port != NULL);
+}
+
+int m8_initialize(const int verbose, const char *preferred_device) {+ if (m8_port != NULL) {+ // Port is already initialized
+ return 1;
+ }
+
+ // Initialize slip descriptor
+ static const slip_descriptor_s slip_descriptor = {+ .buf = slip_buffer,
+ .buf_size = sizeof(slip_buffer),
+ .recv_message = send_message_to_queue,
+ };
+ slip_init(&slip, &slip_descriptor);
+
+ if (verbose) {+ SDL_Log("Looking for USB serial devices");+ }
+
+ // Detect and select M8 device
+ if (!find_and_select_device(preferred_device)) {+ if (verbose) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8");
+ }
+ return 0;
+ }
+
+ // Configure serial port
+ if (!configure_serial_port(m8_port)) {+ return 0;
+ }
+
+ // Initialize message queue and threads
+ return initialize_serial_thread();
+}
+
+int m8_send_msg_controller(const uint8_t input) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending controller input %d", input);
+ const unsigned char buf[2] = {'C', input};+ const size_t nbytes = 2;
+ const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
+ return -1;
+ }
+ return 1;
+}
+
+int m8_send_msg_keyjazz(const uint8_t note, uint8_t velocity) {+
+ // Cap velocity to 7bits
+ if (velocity > 0x7F)
+ velocity = 0x7F;
+
+ // Special case for note off
+ if (note == 0xFF && velocity == 0x00) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending keyjazz note off");
+ const unsigned char buf[2] = {'K', 0xFF};+ const size_t nbytes = 2;
+ const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
+ return -1;
+ }
+ return 1;
+ }
+
+ // Regular note on message
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending keyjazz note %d, velocity %d", note, velocity);
+ const unsigned char buf[3] = {'K', note, velocity};+ const size_t nbytes = 3;
+ const int result = sp_blocking_write(m8_port, buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
+ return -1;
+ }
+
+ return 1;
+}
+
+int m8_list_devices() {+ struct sp_port **port_list;
+ const enum sp_return result = sp_list_ports(&port_list);
+
+ if (result != SP_OK) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
+ return 1;
+ }
+
+ for (int i = 0; port_list[i] != NULL; i++) {+ const struct sp_port *port = port_list[i];
+
+ if (detect_m8_serial_device(port)) {+ SDL_Log("Found M8 device: %s", sp_get_port_name(port));+ }
+ }
+
+ sp_free_port_list(port_list);
+ return 0;
+}
+
+int m8_reset_display() {+ SDL_Log("Reset display");+
+ const unsigned char buf[1] = {'R'};+ const int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_display(const unsigned char reset_display) {+ SDL_Log("Enabling and resetting M8 display");+
+ const char buf[1] = {'E'};+ int result = sp_blocking_write(m8_port, buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
+ return 0;
+ }
+
+ if (reset_display) {+ result = m8_reset_display();
+ }
+
+ return result;
+}
+
+int m8_process_data(const config_params_s *conf) {+ static unsigned int empty_cycles = 0;
+
+ // Device likely has been disconnected
+ if (m8_port == NULL) {+ return DEVICE_DISCONNECTED;
+ }
+
+ if (queue_size(&queue) > 0) {+ unsigned char *command;
+ empty_cycles = 0;
+ size_t length = 0;
+ while ((command = pop_message(&queue, &length)) != NULL) {+ if (length > 0) {+ process_command(command, length);
+ }
+ SDL_free(command);
+ }
+ } else {+ empty_cycles++;
+ if (empty_cycles >= conf->wait_packets) {+ // try opening the serial port to check if it's alive
+ if (serial_port_connected()) {+ // the device is still there, carry on
+ empty_cycles = 0;
+ return DEVICE_PROCESSING;
+ }
+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM,
+ "No messages received for %d cycles, assuming device disconnected",
+ empty_cycles);
+ empty_cycles = 0;
+ disconnect();
+ return DEVICE_DISCONNECTED;
+ }
+ }
+ return DEVICE_PROCESSING;
+}
+
+int m8_close() { return disconnect(); }+
+// These shouldn't be needed with serial
+int m8_pause_processing(void) { return 1; }+int m8_resume_processing(void) { return 1; }+#endif
--- /dev/null
+++ b/src/backends/m8_libusb.c
@@ -1,0 +1,572 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+// Contains portions of code from libserialport's examples released to the
+// public domain
+#include "m8.h"
+#ifdef USE_LIBUSB
+
+#include <SDL3/SDL.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../command.h"
+#include "queue.h"
+#include "slip.h"
+
+static int ep_out_addr = 0x03;
+static int ep_in_addr = 0x83;
+
+#define ACM_CTRL_DTR 0x01
+#define ACM_CTRL_RTS 0x02
+
+#define M8_VID 0x16c0
+#define M8_PID 0x048a
+
+#define SERIAL_READ_SIZE 1024 // maximum amount of bytes to read from the serial in one pass
+
+libusb_context *ctx = NULL;
+libusb_device_handle *devh = NULL;
+static uint8_t serial_buffer[SERIAL_READ_SIZE] = {0};+static uint8_t slip_buffer[SERIAL_READ_SIZE] = {0};+static slip_handler_s slip;
+message_queue_s queue;
+static int do_exit = 0;
+static int async_transfer_active = 0;
+static struct libusb_transfer *async_transfer = NULL;
+static int shutdown_in_progress = 0;
+
+static int send_message_to_queue(uint8_t *data, const uint32_t size) {+ push_message(&queue, data, size);
+ return 1;
+}
+
+int m8_list_devices() {+ int r;
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+
+ libusb_device **device_list = NULL;
+ int count = libusb_get_device_list(ctx, &device_list);
+ for (size_t idx = 0; idx < (size_t)count; ++idx) {+ libusb_device *device = device_list[idx];
+ struct libusb_device_descriptor desc;
+ int rc = libusb_get_device_descriptor(device, &desc);
+ if (rc < 0) {+ SDL_Log("Error");+ libusb_free_device_list(device_list, 1);
+ return rc;
+ }
+
+ if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {+ SDL_Log("Found M8 device: %d:%d\n", libusb_get_port_number(device),+ libusb_get_bus_number(device));
+ }
+ }
+ libusb_free_device_list(device_list, 1);
+ return 0;
+}
+
+static int usb_loop(void *data) {+ (void)data; // Suppress unused parameter warning
+
+ SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
+ while (!do_exit) {+ int rc = libusb_handle_events(ctx);
+ if (rc != LIBUSB_SUCCESS) {+ SDL_Log("Audio loop error: %s\n", libusb_error_name(rc));+ break;
+ }
+ }
+ return 0;
+}
+
+static SDL_Thread *usb_thread;
+
+static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer) {+ int *completed = transfer->user_data;
+ *completed = 1;
+}
+
+static int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {+ if (devh == NULL) {+ return -1;
+ }
+
+ int completed = 0;
+
+ struct libusb_transfer *transfer;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, devh, endpoint, serial_buf, count, xfr_cb_in, &completed,
+ timeout_ms);
+ int r = libusb_submit_transfer(transfer);
+
+ if (r < 0) {+ SDL_Log("Error");+ libusb_free_transfer(transfer);
+ return r;
+ }
+
+retry:
+ libusb_lock_event_waiters(ctx);
+ while (!completed) {+ if (!libusb_event_handler_active(ctx)) {+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+ libusb_wait_for_event(ctx, NULL);
+ }
+ libusb_unlock_event_waiters(ctx);
+
+ int actual_length = transfer->actual_length;
+
+ libusb_free_transfer(transfer);
+
+ return actual_length;
+}
+
+int blocking_write(void *buf, int count, unsigned int timeout_ms) {+ return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
+}
+
+// This function is currently unused but kept for potential future use
+__attribute__((unused))
+static int bulk_async_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms,
+ void (*f)(struct libusb_transfer *), void *user_data) {+ struct libusb_transfer *transfer;
+ transfer = libusb_alloc_transfer(1);
+ libusb_fill_bulk_stream_transfer(transfer, devh, endpoint, 0, serial_buf, count, f, user_data,
+ timeout_ms);
+ int r = libusb_submit_transfer(transfer);
+
+ if (r < 0) {+ SDL_Log("Error");+ libusb_free_transfer(transfer);
+ return r;
+ }
+ return 0;
+}
+
+static void async_callback(struct libusb_transfer *xfr) {+ if (shutdown_in_progress) {+ async_transfer_active = 0;
+ return;
+ }
+
+ if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {+ if (xfr->status == LIBUSB_TRANSFER_CANCELLED) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer cancelled");
+ async_transfer_active = 0;
+ return;
+ }
+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Async transfer failed with status: %s",
+ libusb_error_name(xfr->status));
+ if (!shutdown_in_progress && libusb_submit_transfer(xfr) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error re-submitting failed transfer");
+ async_transfer_active = 0;
+ }
+ return;
+ }
+
+ int bytes_read = xfr->actual_length;
+ if (bytes_read < 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial", (int)bytes_read);
+ } else if (bytes_read > 0) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received %d bytes from M8", bytes_read);
+ uint8_t *serial_buf = xfr->buffer;
+ uint8_t *cur = serial_buf;
+ const uint8_t *end = serial_buf + bytes_read;
+ slip_handler_s *slip = (slip_handler_s *)xfr->user_data;
+ while (cur < end) {+ // process the incoming bytes into commands and draw them
+ int n = slip_read_byte(slip, *(cur++));
+ if (n != SLIP_NO_ERROR) {+ if (n == SLIP_ERROR_INVALID_PACKET) {+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Invalid SLIP packet!\n");
+
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
+ }
+ }
+ }
+ }
+ if (!shutdown_in_progress) {+ int submit_result = libusb_submit_transfer(xfr);
+ if (submit_result < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error re-submitting URB: %s",
+ libusb_error_name(submit_result));
+ async_transfer_active = 0;
+ }
+ } else {+ async_transfer_active = 0;
+ }
+}
+
+int async_read_start(uint8_t *serial_buf, int count, slip_handler_s *slip) {+ if (async_transfer_active) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer already active, skipping");
+ return 0; // Already active
+ }
+
+ if (devh == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Device handle is NULL, cannot start async transfer");
+ return -1;
+ }
+
+ async_transfer = libusb_alloc_transfer(1);
+ if (!async_transfer) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to allocate async transfer");
+ return -1;
+ }
+
+ libusb_fill_bulk_stream_transfer(async_transfer, devh, ep_in_addr, 0, serial_buf, count,
+ &async_callback, slip, 300);
+ int r = libusb_submit_transfer(async_transfer);
+
+ if (r < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error starting async transfer: %s", libusb_error_name(r));
+ libusb_free_transfer(async_transfer);
+ async_transfer = NULL;
+ return r;
+ }
+
+ async_transfer_active = 1;
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer started successfully");
+ return 0;
+}
+
+void async_read_stop() {+ shutdown_in_progress = 1;
+
+ if (async_transfer_active && async_transfer) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Stopping async transfer");
+ int cancel_result = libusb_cancel_transfer(async_transfer);
+ if (cancel_result < 0) {+ if (cancel_result == LIBUSB_ERROR_NOT_FOUND) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer already completed or cancelled");
+ } else if (cancel_result == LIBUSB_ERROR_INVALID_PARAM) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer not valid for cancellation");
+ } else {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer cancellation returned: %s",
+ libusb_error_name(cancel_result));
+ }
+ }
+ // Wait briefly for the callback to complete
+ for (int i = 0; i < 10 && async_transfer_active; i++) {+ SDL_Delay(1);
+ }
+ // Force cleanup if still active
+ async_transfer_active = 0;
+ }
+}
+
+int m8_process_data(const config_params_s *conf) {+ (void)conf; // Suppress unused parameter warning
+ // Process any queued messages
+ if (queue_size(&queue) > 0) {+ unsigned char *command;
+ size_t length = 0;
+ while ((command = pop_message(&queue, &length)) != NULL) {+ if (length > 0) {+ process_command(command, length);
+ }
+ SDL_free(command);
+ }
+ }
+ return DEVICE_PROCESSING;
+}
+
+int check_serial_port() {+ // Reading will fail anyway when the device is not present anymore
+ return 1;
+}
+
+int init_interface() {+
+ if (devh == NULL) {+ SDL_Log("Device not initialised!");+ return 0;
+ }
+
+ int rc;
+
+ for (int if_num = 0; if_num < 2; if_num++) {+ if (libusb_kernel_driver_active(devh, if_num)) {+ SDL_Log("Detaching kernel driver for interface %d", if_num);+ libusb_detach_kernel_driver(devh, if_num);
+ }
+ rc = libusb_claim_interface(devh, if_num);
+ if (rc < 0) {+ SDL_Log("Error claiming interface: %s", libusb_error_name(rc));+ libusb_close(devh);
+ devh = NULL;
+ return 0;
+ }
+ }
+
+ /* Start configuring the device:
+ * - set line state
+ */
+ SDL_Log("Setting line state");+ rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, 0);
+ if (rc < 0) {+ SDL_Log("Error during control transfer: %s", libusb_error_name(rc));+ return 0;
+ }
+
+ /* - set line encoding: here 115200 8N1
+ * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
+ */
+ SDL_Log("Set line encoding");+ unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};+ rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding, sizeof(encoding), 0);
+ if (rc < 0) {+ SDL_Log("Error during control transfer: %s", libusb_error_name(rc));+ return 0;
+ }
+
+ init_queue(&queue);
+
+ usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
+
+ // Start async transfer for reading data from M8
+ if (async_read_start(serial_buffer, SERIAL_READ_SIZE, &slip) < 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to start async transfer during initialization");
+ }
+
+ return 1;
+}
+
+int init_serial_with_file_descriptor(int file_descriptor) {+ SDL_Log("Initialising serial with file descriptor");+
+ if (file_descriptor <= 0) {+ SDL_Log("Invalid file descriptor: %d", file_descriptor);+ return 0;
+ }
+
+ int r;
+ r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
+ if (r != LIBUSB_SUCCESS) {+ SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));+ return 0;
+ }
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+ r = libusb_wrap_sys_device(ctx, (intptr_t)file_descriptor, &devh);
+ if (r < 0) {+ SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));+ return 0;
+ } else if (devh == NULL) {+ SDL_Log("libusb_wrap_sys_device returned invalid handle");+ return 0;
+ }
+ SDL_Log("USB device init success");+
+ return init_interface();
+}
+
+int m8_initialize(int verbose, const char *preferred_device) {+ (void)verbose; // Suppress unused parameter warning
+ (void)preferred_device; // Suppress unused parameter warning
+
+ if (devh != NULL) {+ return 1;
+ }
+
+ // Initialize slip descriptor
+ static const slip_descriptor_s slip_descriptor = {+ .buf = slip_buffer,
+ .buf_size = sizeof(slip_buffer),
+ .recv_message = send_message_to_queue,
+ };
+ slip_init(&slip, &slip_descriptor);
+
+ int r;
+ r = libusb_init(&ctx);
+ if (r < 0) {+ SDL_Log("libusb_init failed: %s", libusb_error_name(r));+ return 0;
+ }
+ if (preferred_device == NULL) {+ devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
+ } else {+ char *port;
+ char *saveptr = NULL;
+ char *bus;
+ char *device_copy = SDL_strdup(preferred_device); // Create a copy to avoid const qualifier warning
+ if (device_copy == NULL) {+ SDL_Log("Failed to allocate memory for device string");+ return 0;
+ }
+ port = SDL_strtok_r(device_copy, ":", &saveptr);
+ bus = SDL_strtok_r(NULL, ":", &saveptr);
+ libusb_device **device_list = NULL;
+ int count = libusb_get_device_list(ctx, &device_list);
+ for (size_t idx = 0; idx < (size_t)count; ++idx) {+ libusb_device *device = device_list[idx];
+ struct libusb_device_descriptor desc;
+ r = libusb_get_device_descriptor(device, &desc);
+ if (r < 0) {+ SDL_Log("libusb_get_device_descriptor failed: %s", libusb_error_name(r));+ libusb_free_device_list(device_list, 1);
+ SDL_free(device_copy);
+ return 0;
+ }
+
+ if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {+ SDL_Log("Searching for port %s and bus %s", port, bus);+ if (libusb_get_port_number(device) == SDL_atoi(port) &&
+ libusb_get_bus_number(device) == SDL_atoi(bus)) {+ SDL_Log("Preferred device found, connecting");+ r = libusb_open(device, &devh);
+ if (r < 0) {+ SDL_Log("libusb_open failed: %s", libusb_error_name(r));+ libusb_free_device_list(device_list, 1);
+ SDL_free(device_copy);
+ return 0;
+ }
+ }
+ }
+ }
+ libusb_free_device_list(device_list, 1);
+ SDL_free(device_copy); // Free the allocated copy
+ if (devh == NULL) {+ SDL_Log("Preferred device %s not found, using first available", preferred_device);+ devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
+ }
+ }
+ if (devh == NULL) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,
+ "libusb_open_device_with_vid_pid returned invalid handle");
+ return 0;
+ }
+ SDL_Log("USB device init success");+
+ return init_interface();
+}
+
+int m8_reset_display() {+ int result;
+
+ SDL_Log("Reset display\n");+
+ char buf[1] = {'R'};+
+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_display(const unsigned char reset_display) {+ (void)reset_display; // Suppress unused parameter warning
+ if (devh == NULL) {+ return 0;
+ }
+
+ int result;
+
+ SDL_Log("Enabling and resetting M8 display\n");+
+ char buf[1] = {'E'};+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
+ return 0;
+ }
+
+ SDL_Delay(5);
+ result = m8_reset_display();
+ return result;
+}
+
+int m8_close() {+
+ char buf[1] = {'D'};+ int result;
+
+ SDL_Log("Disconnecting M8\n");+
+ // Stop async transfer first
+ async_read_stop();
+
+ result = blocking_write(buf, 1, 5);
+ if (result != 1) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
+ return -1;
+ }
+
+ int rc;
+
+ if (devh != NULL) {+
+ for (int if_num = 0; if_num < 2; if_num++) {+ rc = libusb_release_interface(devh, if_num);
+ if (rc < 0) {+ SDL_Log("Error releasing interface: %s", libusb_error_name(rc));+ return 0;
+ }
+ }
+
+ do_exit = 1;
+
+ libusb_close(devh);
+ }
+
+ SDL_WaitThread(usb_thread, NULL);
+
+ libusb_exit(ctx);
+
+ destroy_queue(&queue);
+
+ if (async_transfer) {+ libusb_free_transfer(async_transfer);
+ async_transfer = NULL;
+ }
+ async_transfer_active = 0;
+ shutdown_in_progress = 0;
+
+ return 1;
+}
+
+int m8_send_msg_controller(uint8_t input) {+ char buf[2] = {'C', input};+ int nbytes = 2;
+ int result;
+ result = blocking_write(buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
+ return -1;
+ }
+ return 1;
+}
+
+int m8_send_msg_keyjazz(uint8_t note, uint8_t velocity) {+ if (velocity > 0x7F)
+ velocity = 0x7F;
+ char buf[3] = {'K', note, velocity};+ int nbytes = 3;
+ int result;
+ result = blocking_write(buf, nbytes, 5);
+ if (result != nbytes) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
+ return -1;
+ }
+
+ return 1;
+}
+
+// These shouldn't be needed with serial
+int m8_pause_processing(void) { return 1; }+int m8_resume_processing(void) { return 1; }+
+#endif
--- /dev/null
+++ b/src/backends/m8_rtmidi.c
@@ -1,0 +1,371 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifdef USE_RTMIDI
+
+#ifndef NDEBUG
+#define RTMIDI_DEBUG
+#endif
+
+#include "../command.h"
+#include "../config.h"
+#include "m8.h"
+#include "queue.h"
+#include <SDL3/SDL.h>
+#include <rtmidi_c.h>
+#include <stdbool.h>
+#include <string.h>
+
+RtMidiInPtr midi_in;
+RtMidiOutPtr midi_out;
+message_queue_s queue;
+
+const unsigned char m8_sysex_header[5] = {0xF0, 0x00, 0x02, 0x61, 0x00};+const unsigned int m8_sysex_header_size = sizeof(m8_sysex_header);
+const unsigned char sysex_message_end = 0xF7;
+
+bool midi_processing_suspended = false;
+bool midi_sysex_received = false;
+
+static bool message_is_m8_sysex(const unsigned char *message) {+ if (memcmp(m8_sysex_header, message, m8_sysex_header_size) == 0) {+ return true;
+ }
+ return false;
+}
+
+static void midi_decode(const uint8_t *encoded_data, size_t length, uint8_t **decoded_data,
+ size_t *decoded_length) {+ if (length < m8_sysex_header_size) {+ // Invalid data
+ *decoded_data = NULL;
+ *decoded_length = 0;
+ return;
+ }
+
+ // Skip header "F0 00 02 61" and the first MSB byte
+ size_t pos = m8_sysex_header_size + 1;
+
+ // Calculate expected output size (ignoring EOT if present)
+ const size_t expected_output_size =
+ (length - m8_sysex_header_size) - ((length - m8_sysex_header_size) / 8);
+ if (encoded_data[length - 1] == sysex_message_end) {+ length--; // Ignore the EOT byte
+ }
+
+ // Allocate memory for decoded output
+ *decoded_data = (uint8_t *)SDL_malloc(expected_output_size);
+ if (*decoded_data == NULL) {+ *decoded_length = 0;
+ return;
+ }
+
+ uint8_t bit_counter = 0;
+ uint8_t bit_byte_counter = 0;
+ uint8_t *out = *decoded_data;
+ *decoded_length = 0;
+
+ while (pos < length) {+ // Extract MSB from the "bit field" position
+ uint8_t msb = (encoded_data[bit_byte_counter * 8 + m8_sysex_header_size] >> bit_counter) & 0x01;
+
+ // Extract LSB from data byte
+ uint8_t lsb = encoded_data[pos] & 0x7F;
+
+ // Reconstruct original byte, skipping the MSB bytes in output
+ *out = (msb << 7) | lsb;
+ out++;
+ (*decoded_length)++;
+
+ bit_counter++;
+ pos++;
+
+ if (bit_counter == 7) {+ bit_counter = 0;
+ bit_byte_counter++;
+ pos++; // Skip the MSB byte
+ }
+ }
+}
+
+static void midi_callback(double delta_time, const unsigned char *message, size_t message_size,
+ void *user_data) {+ // Unused variables
+ (void)delta_time;
+ (void)user_data;
+
+ if (midi_processing_suspended || message_size < 5 || !message_is_m8_sysex(message))
+ return;
+
+ if (!midi_sysex_received) {+ midi_sysex_received = true;
+ }
+
+ unsigned char *decoded_data;
+ size_t decoded_length;
+ midi_decode(message, message_size, &decoded_data, &decoded_length);
+
+ // If you need to debug incoming MIDI packets, you can uncomment the lines below:
+
+ /* printf("Original data: ");+ for (size_t i = 0; i < message_size; i++) {+ printf("%02X ", message[i]);+ } */
+
+ if (decoded_data) {+ /* printf("\nDecoded MIDI Data: ");+ for (size_t i = 0; i < decoded_length; i++) {+ printf("%02X ", decoded_data[i]);+ }
+ printf("\n"); */+ push_message(&queue, decoded_data, decoded_length);
+ SDL_free(decoded_data);
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Decoding failed.\n");
+ }
+}
+
+static void close_and_free_midi_ports(void) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Freeing MIDI ports");
+ rtmidi_in_cancel_callback(midi_in);
+ rtmidi_close_port(midi_in);
+ rtmidi_close_port(midi_out);
+ rtmidi_in_free(midi_in);
+ rtmidi_out_free(midi_out);
+ midi_in = NULL;
+ midi_out = NULL;
+ midi_sysex_received = false;
+}
+
+static int initialize_rtmidi(void) {+ SDL_Log("Initializing rtmidi");+ midi_in = rtmidi_in_create(RTMIDI_API_UNSPECIFIED, "m8c_in", 2048);
+ midi_out = rtmidi_out_create(RTMIDI_API_UNSPECIFIED, "m8c_out");
+ if (midi_in == NULL || midi_out == NULL) {+ return 0;
+ }
+ return 1;
+}
+
+static int detect_m8_midi_device(const int verbose, const char *preferred_device) {+ int m8_midi_port_number = -1;
+ const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
+ if (verbose)
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Number of MIDI in ports: %d", ports_total_in);
+ for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {+ int port_name_length_in;
+ rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
+ char port_name[port_name_length_in];
+ rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
+ if (verbose)
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "MIDI IN port %d, name: %s", port_number, port_name);
+ if (SDL_strncmp("M8", port_name, 2) == 0) {+ m8_midi_port_number = port_number;
+ if (verbose)
+ SDL_Log("Found M8 Input in MIDI port %d", port_number);+ if (preferred_device != NULL && SDL_strcmp(preferred_device, port_name) == 0) {+ SDL_Log("Found preferred device, breaking");+ break;
+ }
+ }
+ }
+ return m8_midi_port_number;
+}
+
+static int device_still_exists(void) {+ if (midi_in == NULL || midi_out == NULL) {+ return 0;
+ };
+
+ int m8_midi_port_number = 0;
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Checking if opened MIDI port still exists");
+
+ m8_midi_port_number = detect_m8_midi_device(0, NULL);
+ if (m8_midi_port_number >= 0) {+ return 1;
+ }
+ return 0;
+}
+
+static int disconnect(void) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending disconnect message to M8");
+ const unsigned char disconnect_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'D', 0xF7};+ const int result =
+ rtmidi_out_send_message(midi_out, &disconnect_sysex[0], sizeof(disconnect_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send disconnect");
+ }
+ close_and_free_midi_ports();
+ return !result;
+}
+
+int m8_initialize(const int verbose, const char *preferred_device) {+
+ int m8_midi_port_number = 0;
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Initialize M8 via RTMIDI called");
+ if (midi_in == NULL || midi_out == NULL) {+ initialize_rtmidi();
+ };
+ m8_midi_port_number = detect_m8_midi_device(verbose, preferred_device);
+ if (m8_midi_port_number >= 0) {+ rtmidi_in_ignore_types(midi_in, false, true, true); // Allow sysex
+ rtmidi_open_port(midi_in, m8_midi_port_number, "M8");
+ rtmidi_open_port(midi_out, m8_midi_port_number, "M8");
+ init_queue(&queue);
+ return 1;
+ }
+ return 0;
+}
+
+int m8_reset_display(void) {+ SDL_Log("Reset display");+ const unsigned char reset_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'R', 0xF7};+ const int result = rtmidi_out_send_message(midi_out, &reset_sysex[0], sizeof(reset_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, error %s", midi_out->msg);
+ return 0;
+ }
+ return 1;
+}
+
+int m8_enable_display(const unsigned char reset_display) {+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending enable command sysex");
+ rtmidi_in_set_callback(midi_in, midi_callback, NULL);
+ const unsigned char enable_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'E', 0xF7};+ const int result = rtmidi_out_send_message(midi_out, &enable_sysex[0], sizeof(enable_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send remote display enable command");
+ return 0;
+ }
+ // Wait a second to get a reply from the device
+ const Uint64 timer_wait_for_response = SDL_GetTicks();
+ while (!midi_sysex_received) {+ if (SDL_GetTicks() - timer_wait_for_response > 1000) {+ SDL_LogCritical(
+ SDL_LOG_CATEGORY_SYSTEM,
+ "No response from device. Please make sure you're using M8 firmware 6.0.0 or newer.");
+ close_and_free_midi_ports();
+ return 0;
+ }
+ SDL_Delay(5);
+ }
+ if (reset_display) {+ return m8_reset_display();
+ }
+ return 1;
+}
+
+int m8_send_msg_controller(const unsigned char input) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "MIDI key inputs not implemented yet");
+ return 0;
+#if 0
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Sending controller input 0x%02X", input);
+ // Encode a 8bit byte to two 7-bit bytes
+ //const unsigned char sysex_encoded_input[2] = {input & 0x7F, (input >> 7) & 0x01};+ const unsigned char input_sysex[10] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', sysex_encoded_input[0], sysex_encoded_input[1], 0xF7};+ const int result = rtmidi_out_send_message(midi_out, &input_sysex[0], sizeof(input_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send key input message");
+ return 0;
+ }
+ return 1;
+#endif
+}
+
+int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "MIDI keyjazz not implemented yet");
+ return 0;
+#if 0
+ if (velocity > 0x7F) {+ velocity = 0x7F;
+ }
+
+ // Special case for note off
+ if (note == 0xFF && velocity == 0x00) {+ const unsigned char all_notes_off_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'K', 0xFF, 0xF7};+ const int result =
+ rtmidi_out_send_message(midi_out, &all_notes_off_sysex[0], sizeof(all_notes_off_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send all notes off");
+ return 0;
+ }
+ return 1;
+ }
+
+ const unsigned char keyjazz_sysex[10] = {0xF0, 0x00, 0x02, 0x61, 0x00,+ 0x00, 'K', note, velocity, 0xF7};
+ const int result = rtmidi_out_send_message(midi_out, &keyjazz_sysex[0], sizeof(keyjazz_sysex));
+ if (result != 0) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to send keyjazz input message");
+ return 0;
+ }
+ return 1;
+#endif
+}
+
+int m8_process_data(const config_params_s *conf) {+
+ static unsigned int empty_cycles = 0;
+
+ if (queue_size(&queue) > 0) {+ unsigned char *command;
+ empty_cycles = 0;
+ size_t length = 0;
+ while ((command = pop_message(&queue, &length)) != NULL) {+ process_command(command, length);
+ SDL_free(command);
+ }
+ } else {+ empty_cycles++;
+ if (empty_cycles >= conf->wait_packets) {+ if (device_still_exists()) {+ empty_cycles = 0;
+ return DEVICE_PROCESSING;
+ }
+ SDL_Log("No messages received for %d cycles, assuming device disconnected", empty_cycles);+ close_and_free_midi_ports();
+ destroy_queue(&queue);
+ empty_cycles = 0;
+ return DEVICE_DISCONNECTED;
+ }
+ }
+ return DEVICE_PROCESSING;
+}
+
+int m8_close(void) {+ const int result = disconnect();
+ destroy_queue(&queue);
+ return result;
+}
+
+int m8_list_devices(void) {+ if (midi_in == NULL || midi_out == NULL) {+ initialize_rtmidi();
+ };
+ const unsigned int ports_total_in = rtmidi_get_port_count(midi_in);
+ SDL_Log("Number of MIDI in ports: %d", ports_total_in);+ for (unsigned int port_number = 0; port_number < ports_total_in; port_number++) {+ int port_name_length_in;
+ rtmidi_get_port_name(midi_in, port_number, NULL, &port_name_length_in);
+ char port_name[port_name_length_in];
+ rtmidi_get_port_name(midi_in, port_number, &port_name[0], &port_name_length_in);
+ SDL_Log("MIDI IN port %d, name: %s", port_number, port_name);+ }
+ close_and_free_midi_ports();
+ return 1;
+}
+
+int m8_pause_processing(void) {+ midi_processing_suspended = true;
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Pausing MIDI processing");
+ return 1;
+}
+int m8_resume_processing(void) {+ midi_processing_suspended = false;
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Resuming MIDI processing");
+ m8_reset_display();
+ return 1;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/queue.c
@@ -1,0 +1,70 @@
+#include "queue.h"
+#include <SDL3/SDL.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Initialize the message queue
+void init_queue(message_queue_s *queue) {+ queue->front = 0;
+ queue->rear = 0;
+ queue->mutex = SDL_CreateMutex();
+ queue->cond = SDL_CreateCondition();
+}
+
+// Free allocated memory and destroy mutex
+void destroy_queue(message_queue_s *queue) {+ SDL_LockMutex(queue->mutex);
+
+ while (queue->front != queue->rear) {+ SDL_free(queue->messages[queue->front]);
+ queue->front = (queue->front + 1) % MAX_QUEUE_SIZE;
+ }
+
+ SDL_UnlockMutex(queue->mutex);
+ SDL_DestroyMutex(queue->mutex);
+ SDL_DestroyCondition(queue->cond);
+}
+
+// Push a message to the queue
+void push_message(message_queue_s *queue, const unsigned char *message, size_t length) {+ SDL_LockMutex(queue->mutex);
+
+ if ((queue->rear + 1) % MAX_QUEUE_SIZE == queue->front) {+ SDL_LogError(SDL_LOG_CATEGORY_SYSTEM,"Queue is full, cannot add message.");
+ } else {+ // Allocate space for the message and store it
+ queue->messages[queue->rear] = SDL_malloc(length);
+ SDL_memcpy(queue->messages[queue->rear], message, length);
+ queue->lengths[queue->rear] = length;
+ queue->rear = (queue->rear + 1) % MAX_QUEUE_SIZE;
+ SDL_SignalCondition(queue->cond); // Signal consumer thread
+ }
+
+ SDL_UnlockMutex(queue->mutex);
+}
+
+// Pop a message from the queue
+unsigned char *pop_message(message_queue_s *queue, size_t *length) {+ SDL_LockMutex(queue->mutex);
+
+ // Check if the queue is empty
+ if (queue->front == queue->rear) {+ SDL_UnlockMutex(queue->mutex);
+ return NULL; // Return NULL if there are no messages
+ }
+
+ // Otherwise, retrieve the message and its length
+ *length = queue->lengths[queue->front];
+ unsigned char *message = queue->messages[queue->front];
+ queue->front = (queue->front + 1) % MAX_QUEUE_SIZE;
+
+ SDL_UnlockMutex(queue->mutex);
+ return message;
+}
+
+unsigned int queue_size(const message_queue_s *queue) {+ SDL_LockMutex(queue->mutex);
+ const unsigned int size = (queue->rear - queue->front + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE;
+ SDL_UnlockMutex(queue->mutex);
+ return size;
+}
\ No newline at end of file
--- /dev/null
+++ b/src/backends/queue.h
@@ -1,0 +1,63 @@
+//
+// Created by jonne on 3/17/25.
+//
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include <SDL3/SDL.h>
+
+#define MAX_QUEUE_SIZE 8192
+
+typedef struct {+ unsigned char *messages[MAX_QUEUE_SIZE];
+ size_t lengths[MAX_QUEUE_SIZE]; // Store lengths of each message
+ int front;
+ int rear;
+ SDL_Mutex *mutex;
+ SDL_Condition *cond;
+} message_queue_s;
+
+/**
+ * Initializes the message queue structure.
+ *
+ * @param queue A pointer to the message queue structure to be initialized.
+ */
+void init_queue(message_queue_s *queue);
+
+/**
+ * Destroys the message queue and releases associated resources.
+ *
+ * @param queue A pointer to the message queue structure to be destroyed.
+ */
+void destroy_queue(message_queue_s *queue);
+
+/**
+ * Retrieves and removes a message from the front of the message queue.
+ * If the queue is empty, the function returns NULL.
+ *
+ * @param queue A pointer to the message queue structure from which the message is to be retrieved.
+ * @param length A pointer to a variable where the length of the retrieved message will be stored.
+ * @return A pointer to the retrieved message, or NULL if the queue is empty.
+ */
+unsigned char *pop_message(message_queue_s *queue, size_t *length);
+
+/**
+ * Adds a new message to the message queue.
+ * If the queue is full, the message will not be added.
+ *
+ * @param queue A pointer to the message queue structure where the message is to be stored.
+ * @param message A pointer to the message data to be added to the queue.
+ * @param length The length of the message in bytes.
+ */
+void push_message(message_queue_s *queue, const unsigned char *message, size_t length);
+
+/**
+ * Calculates the current size of the message queue.
+ *
+ * @param queue A pointer to the message queue structure whose size is to be determined.
+ * @return The number of messages currently in the queue.
+ */
+unsigned int queue_size(const message_queue_s *queue);
+
+#endif // QUEUE_H
--- /dev/null
+++ b/src/backends/ringbuffer.c
@@ -1,0 +1,55 @@
+#include "ringbuffer.h"
+#include <SDL3/SDL.h>
+
+RingBuffer *ring_buffer_create(uint32_t size) {+ RingBuffer *rb = SDL_malloc(sizeof(*rb));
+ rb->buffer = SDL_malloc(sizeof(*rb->buffer) * size);
+ rb->head = 0;
+ rb->tail = 0;
+ rb->max_size = size;
+ rb->size = 0;
+ return rb;
+}
+
+void ring_buffer_free(RingBuffer *rb) {+ SDL_free(rb->buffer);
+ SDL_free(rb);
+}
+
+uint32_t ring_buffer_empty(RingBuffer *rb) { return rb->size == 0; }+
+uint32_t ring_buffer_full(RingBuffer *rb) { return rb->size == rb->max_size; }+
+uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length) {+ if (ring_buffer_full(rb)) {+ return -1; // buffer full, push fails
+ }
+ uint32_t space1 = rb->max_size - rb->tail;
+ uint32_t n = length <= rb->max_size - rb->size ? length : rb->max_size - rb->size;
+ if (n <= space1) {+ SDL_memcpy(rb->buffer + rb->tail, data, n);
+ } else {+ SDL_memcpy(rb->buffer + rb->tail, data, space1);
+ SDL_memcpy(rb->buffer, data + space1, n - space1);
+ }
+ rb->tail = (rb->tail + n) % rb->max_size;
+ rb->size += n;
+ return n; // push successful, returns number of bytes pushed
+}
+
+uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length) {+ if (ring_buffer_empty(rb)) {+ return -1; // buffer empty, pop fails
+ }
+ uint32_t space1 = rb->max_size - rb->head;
+ uint32_t n = length <= rb->size ? length : rb->size;
+ if (n <= space1) {+ SDL_memcpy(data, rb->buffer + rb->head, n);
+ } else {+ SDL_memcpy(data, rb->buffer + rb->head, space1);
+ SDL_memcpy(data + space1, rb->buffer, n - space1);
+ }
+ rb->head = (rb->head + n) % rb->max_size;
+ rb->size -= n;
+ return n; // pop successful, returns number of bytes popped
+}
--- /dev/null
+++ b/src/backends/ringbuffer.h
@@ -1,0 +1,24 @@
+#ifndef M8C_RINGBUFFER_H
+#define M8C_RINGBUFFER_H
+
+#include <stdint.h>
+
+typedef struct {+ uint8_t *buffer;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t max_size;
+ uint32_t size;
+} RingBuffer;
+
+RingBuffer *ring_buffer_create(uint32_t size);
+
+uint32_t ring_buffer_empty(RingBuffer *rb);
+
+uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length);
+
+uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length);
+
+void ring_buffer_free(RingBuffer *rb);
+
+#endif // M8C_RINGBUFFER_H
--- /dev/null
+++ b/src/backends/slip.c
@@ -1,0 +1,114 @@
+/*
+MIT License
+
+Copyright (c) 2018 Marcin Borowicz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/* This code is originally by marcinbor85, https://github.com/marcinbor85/slip
+It has been simplified a bit as CRC checking etc. is not required in this
+program. */
+
+#include "slip.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+static void reset_rx(slip_handler_s *slip) {+ assert(slip != NULL);
+
+ slip->state = SLIP_STATE_NORMAL;
+ slip->size = 0;
+}
+
+slip_error_t slip_init(slip_handler_s *slip, const slip_descriptor_s *descriptor) {+ assert(slip != NULL);
+ assert(descriptor != NULL);
+ assert(descriptor->buf != NULL);
+ assert(descriptor->recv_message != NULL);
+
+ slip->descriptor = descriptor;
+ reset_rx(slip);
+
+ return SLIP_NO_ERROR;
+}
+
+static slip_error_t put_byte_to_buffer(slip_handler_s *slip, const uint8_t byte) {+ slip_error_t error = SLIP_NO_ERROR;
+
+ assert(slip != NULL);
+
+ if (slip->size >= slip->descriptor->buf_size) {+ error = SLIP_ERROR_BUFFER_OVERFLOW;
+ reset_rx(slip);
+ } else {+ slip->descriptor->buf[slip->size++] = byte;
+ slip->state = SLIP_STATE_NORMAL;
+ }
+
+ return error;
+}
+
+slip_error_t slip_read_byte(slip_handler_s *slip, uint8_t byte) {+ slip_error_t error = SLIP_NO_ERROR;
+
+ assert(slip != NULL);
+
+ switch (slip->state) {+ case SLIP_STATE_NORMAL:
+ switch (byte) {+ case SLIP_SPECIAL_BYTE_END:
+ if (!slip->descriptor->recv_message(slip->descriptor->buf, slip->size)) {+ error = SLIP_ERROR_INVALID_PACKET;
+ }
+ reset_rx(slip);
+ break;
+ case SLIP_SPECIAL_BYTE_ESC:
+ slip->state = SLIP_STATE_ESCAPED;
+ break;
+ default:
+ error = put_byte_to_buffer(slip, byte);
+ break;
+ }
+ break;
+
+ case SLIP_STATE_ESCAPED:
+ switch (byte) {+ case SLIP_ESCAPED_BYTE_END:
+ byte = SLIP_SPECIAL_BYTE_END;
+ break;
+ case SLIP_ESCAPED_BYTE_ESC:
+ byte = SLIP_SPECIAL_BYTE_ESC;
+ break;
+ default:
+ error = SLIP_ERROR_UNKNOWN_ESCAPED_BYTE;
+ reset_rx(slip);
+ break;
+ }
+
+ if (error != SLIP_NO_ERROR)
+ break;
+
+ error = put_byte_to_buffer(slip, byte);
+ break;
+ }
+
+ return error;
+}
--- /dev/null
+++ b/src/backends/slip.h
@@ -1,0 +1,67 @@
+/*
+MIT License
+
+Copyright (c) 2018 Marcin Borowicz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/* This code is originally by marcinbor85, https://github.com/marcinbor85/slip
+It has been simplified a bit as CRC checking etc. is not required in this
+program. */
+
+#ifndef SLIP_H_
+#define SLIP_H_
+
+#include <stdint.h>
+
+#define SLIP_SPECIAL_BYTE_END 0xC0
+#define SLIP_SPECIAL_BYTE_ESC 0xDB
+
+#define SLIP_ESCAPED_BYTE_END 0xDC
+#define SLIP_ESCAPED_BYTE_ESC 0xDD
+
+typedef enum {+ SLIP_STATE_NORMAL = 0x00,
+ SLIP_STATE_ESCAPED
+} slip_state_t;
+
+typedef struct {+ uint8_t *buf;
+ uint32_t buf_size;
+ int (*recv_message)(uint8_t *data, uint32_t size);
+} slip_descriptor_s;
+
+typedef struct {+ slip_state_t state;
+ uint32_t size;
+ const slip_descriptor_s *descriptor;
+} slip_handler_s;
+
+typedef enum {+ SLIP_NO_ERROR = 0x00,
+ SLIP_ERROR_BUFFER_OVERFLOW,
+ SLIP_ERROR_UNKNOWN_ESCAPED_BYTE,
+ SLIP_ERROR_INVALID_PACKET
+} slip_error_t;
+
+slip_error_t slip_init(slip_handler_s *slip, const slip_descriptor_s *descriptor);
+slip_error_t slip_read_byte(slip_handler_s *slip, uint8_t byte);
+
+#endif
\ No newline at end of file
--- a/src/command.c
+++ b/src/command.c
@@ -1,7 +1,7 @@
// Copyright 2021 Jonne Kokkonen
// Released under the MIT licence, https://opensource.org/licenses/MIT
-#include <SDL_log.h>
+#include <SDL3/SDL.h>
#include "command.h"
#include "render.h"
@@ -27,7 +27,7 @@
};
static void dump_packet(const uint32_t size, const uint8_t *recv_buf) {- for (uint16_t a = 0; a < size; a++) {+ for (uint32_t a = 0; a < size; a++) {SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "0x%02X ", recv_buf[a]);
}
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "\n");
@@ -168,7 +168,7 @@
set_m8_model(0);
}
- set_font_mode(recv_buf[5]);
+ renderer_set_font_mode(recv_buf[5]);
return 1;
}
--- /dev/null
+++ b/src/common.h
@@ -1,0 +1,15 @@
+#ifndef COMMON_H_
+#define COMMON_H_
+#include "config.h"
+
+enum app_state { QUIT, INITIALIZE, WAIT_FOR_DEVICE, RUN };+
+struct app_context {+ config_params_s conf;
+ enum app_state app_state;
+ char *preferred_device;
+ unsigned char device_connected;
+ unsigned char app_suspended;
+ };
+
+#endif
\ No newline at end of file
--- a/src/config.c
+++ b/src/config.c
@@ -3,8 +3,9 @@
#include "config.h"
#include "ini.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
#include <assert.h>
+#include <ctype.h>
#include <stdio.h>
/* Case-insensitive string compare from ini.h library */
@@ -18,7 +19,7 @@
}
}
-config_params_s init_config(char *filename) {+config_params_s config_initialize(char *filename) {config_params_s c;
if (filename == NULL) {@@ -28,13 +29,12 @@
}
c.init_fullscreen = 0; // default fullscreen state at load
- c.init_use_gpu = 1; // default to use hardware acceleration
+ c.integer_scaling = 0; // use integer scaling for the user interface
c.idle_ms = 10; // default to high performance
c.wait_for_device = 1; // default to exit if device disconnected
- c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
- // sec for default idle_ms)
+ c.wait_packets = 256; // amount of empty command queue reads before assuming device disconnected
c.audio_enabled = 0; // route M8 audio to default output
- c.audio_buffer_size = 1024; // requested audio buffer size in samples
+ c.audio_buffer_size = 0; // requested audio buffer size in samples: 0 = let SDL decide
c.audio_device_name = NULL; // Use this device, leave NULL to use the default output device
c.key_up = SDL_SCANCODE_UP;
@@ -57,25 +57,25 @@
c.key_jazz_dec_velocity = SDL_SCANCODE_KP_PLUS;
c.key_toggle_audio = SDL_SCANCODE_F12;
- c.gamepad_up = SDL_CONTROLLER_BUTTON_DPAD_UP;
- c.gamepad_left = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
- c.gamepad_down = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
- c.gamepad_right = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
- c.gamepad_select = SDL_CONTROLLER_BUTTON_BACK;
- c.gamepad_start = SDL_CONTROLLER_BUTTON_START;
- c.gamepad_opt = SDL_CONTROLLER_BUTTON_B;
- c.gamepad_edit = SDL_CONTROLLER_BUTTON_A;
- c.gamepad_quit = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
- c.gamepad_reset = SDL_CONTROLLER_BUTTON_LEFTSTICK;
+ c.gamepad_up = SDL_GAMEPAD_BUTTON_DPAD_UP;
+ c.gamepad_left = SDL_GAMEPAD_BUTTON_DPAD_LEFT;
+ c.gamepad_down = SDL_GAMEPAD_BUTTON_DPAD_DOWN;
+ c.gamepad_right = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
+ c.gamepad_select = SDL_GAMEPAD_BUTTON_BACK;
+ c.gamepad_start = SDL_GAMEPAD_BUTTON_START;
+ c.gamepad_opt = SDL_GAMEPAD_BUTTON_EAST;
+ c.gamepad_edit = SDL_GAMEPAD_BUTTON_SOUTH;
+ c.gamepad_quit = SDL_GAMEPAD_BUTTON_RIGHT_STICK;
+ c.gamepad_reset = SDL_GAMEPAD_BUTTON_LEFT_STICK;
c.gamepad_analog_threshold = 30000;
c.gamepad_analog_invert = 0;
- c.gamepad_analog_axis_updown = SDL_CONTROLLER_AXIS_LEFTY;
- c.gamepad_analog_axis_leftright = SDL_CONTROLLER_AXIS_LEFTX;
- c.gamepad_analog_axis_start = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
- c.gamepad_analog_axis_select = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
- c.gamepad_analog_axis_opt = SDL_CONTROLLER_AXIS_INVALID;
- c.gamepad_analog_axis_edit = SDL_CONTROLLER_AXIS_INVALID;
+ c.gamepad_analog_axis_updown = SDL_GAMEPAD_AXIS_LEFTY;
+ c.gamepad_analog_axis_leftright = SDL_GAMEPAD_AXIS_LEFTX;
+ c.gamepad_analog_axis_start = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
+ c.gamepad_analog_axis_select = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
+ c.gamepad_analog_axis_opt = SDL_GAMEPAD_AXIS_INVALID;
+ c.gamepad_analog_axis_edit = SDL_GAMEPAD_AXIS_INVALID;
return c;
}
@@ -86,7 +86,7 @@
// Open the default config file for writing
char config_path[1024] = {0}; snprintf(config_path, sizeof(config_path), "%s%s", SDL_GetPrefPath("", "m8c"), conf->filename);- SDL_RWops *rw = SDL_RWFromFile(config_path, "w");
+ SDL_IOStream *rw = SDL_IOFromFile(config_path, "w");
SDL_Log("Writing config file to %s", config_path);@@ -99,12 +99,12 @@
snprintf(ini_values[initPointer++], LINELEN, "[graphics]\n");
snprintf(ini_values[initPointer++], LINELEN, "fullscreen=%s\n",
conf->init_fullscreen ? "true" : "false");
- snprintf(ini_values[initPointer++], LINELEN, "use_gpu=%s\n",
- conf->init_use_gpu ? "true" : "false");
snprintf(ini_values[initPointer++], LINELEN, "idle_ms=%d\n", conf->idle_ms);
snprintf(ini_values[initPointer++], LINELEN, "wait_for_device=%s\n",
conf->wait_for_device ? "true" : "false");
snprintf(ini_values[initPointer++], LINELEN, "wait_packets=%d\n", conf->wait_packets);
+ snprintf(ini_values[initPointer++], LINELEN, "integer_scaling=%s\n",
+ conf->integer_scaling ? "true" : "false");
snprintf(ini_values[initPointer++], LINELEN, "[audio]\n");
snprintf(ini_values[initPointer++], LINELEN, "audio_enabled=%s\n",
conf->audio_enabled ? "true" : "false");
@@ -171,13 +171,13 @@
// Write ini_values array to config file
for (unsigned int i = 0; i < INI_LINE_COUNT; i++) {const size_t len = SDL_strlen(ini_values[i]);
- if (SDL_RWwrite(rw, ini_values[i], 1, len) != len) {+ if (SDL_WriteIO(rw, ini_values[i], len) != len) {SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Couldn't write line into config file.");
} else {SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Wrote to config: %s", ini_values[i]);
}
}
- SDL_RWclose(rw);
+ SDL_CloseIO(rw);
} else { SDL_Log("Couldn't write into config file.");}
@@ -184,7 +184,7 @@
}
// Read config
-void read_config(config_params_s *conf) {+void config_read(config_params_s *conf) { char config_path[1024] = {0}; snprintf(config_path, sizeof(config_path), "%s%s", SDL_GetPrefPath("", "m8c"), conf->filename);@@ -232,21 +232,15 @@
void read_graphics_config(const ini_t *ini, config_params_s *conf) {const char *param_fs = ini_get(ini, "graphics", "fullscreen");
- const char *param_gpu = ini_get(ini, "graphics", "use_gpu");
const char *idle_ms = ini_get(ini, "graphics", "idle_ms");
const char *param_wait = ini_get(ini, "graphics", "wait_for_device");
const char *wait_packets = ini_get(ini, "graphics", "wait_packets");
+ const char *integer_scaling = ini_get(ini, "graphics", "integer_scaling");
- if (strcmpci(param_fs, "true") == 0) {+ if (param_fs != NULL && strcmpci(param_fs, "true") == 0) {conf->init_fullscreen = 1;
- } else
+ } else {conf->init_fullscreen = 0;
-
- if (param_gpu != NULL) {- if (strcmpci(param_gpu, "true") == 0) {- conf->init_use_gpu = 1;
- } else
- conf->init_use_gpu = 0;
}
if (idle_ms != NULL)
@@ -261,6 +255,12 @@
}
if (wait_packets != NULL)
conf->wait_packets = SDL_atoi(wait_packets);
+
+ if (integer_scaling != NULL && strcmpci(integer_scaling, "true") == 0) {+ conf->integer_scaling = 1;
+ } else {+ conf->integer_scaling = 0;
+ }
}
void read_key_config(const ini_t *ini, config_params_s *conf) {--- a/src/config.h
+++ b/src/config.h
@@ -9,13 +9,13 @@
typedef struct config_params_s {char *filename;
unsigned int init_fullscreen;
- unsigned int init_use_gpu;
+ unsigned int integer_scaling;
unsigned int idle_ms;
unsigned int wait_for_device;
unsigned int wait_packets;
unsigned int audio_enabled;
unsigned int audio_buffer_size;
- const char *audio_device_name;
+ char *audio_device_name;
unsigned int key_up;
unsigned int key_left;
@@ -59,9 +59,8 @@
} config_params_s;
-
-config_params_s init_config(char *filename);
-void read_config(config_params_s *conf);
+config_params_s config_initialize(char *filename);
+void config_read(config_params_s *conf);
void read_audio_config(const ini_t *ini, config_params_s *conf);
void read_graphics_config(const ini_t *ini, config_params_s *conf);
void read_key_config(const ini_t *ini, config_params_s *conf);
--- /dev/null
+++ b/src/events.c
@@ -1,0 +1,87 @@
+#include "events.h"
+#include "backends/m8.h"
+#include "common.h"
+#include "gamepads.h"
+#include "input.h"
+#include "render.h"
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_events.h>
+
+SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {+ struct app_context *ctx = appstate;
+ SDL_AppResult ret_val = SDL_APP_CONTINUE;
+
+ switch (event->type) {+
+ // --- System events ---
+ case SDL_EVENT_QUIT:
+ case SDL_EVENT_TERMINATING:
+ ret_val = SDL_APP_SUCCESS;
+ break;
+ case SDL_EVENT_WINDOW_RESIZED:
+ case SDL_EVENT_WINDOW_MOVED:
+ // If the window size is changed, some systems might need a little nudge to fix scaling
+ renderer_fix_texture_scaling_after_window_resize(&ctx->conf);
+ break;
+
+ // --- iOS specific events ---
+ case SDL_EVENT_DID_ENTER_BACKGROUND:
+ // iOS: Application entered into the background on iOS. About 5 seconds to stop things.
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_BACKGROUND");
+ ctx->app_suspended = 1;
+ if (ctx->device_connected)
+ m8_pause_processing();
+ break;
+ case SDL_EVENT_WILL_ENTER_BACKGROUND:
+ // iOS: App about to enter into the background
+ break;
+ case SDL_EVENT_WILL_ENTER_FOREGROUND:
+ // iOS: App returning to the foreground
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_WILL_ENTER_FOREGROUND");
+ break;
+ case SDL_EVENT_DID_ENTER_FOREGROUND:
+ // iOS: App becomes interactive again
+ SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received SDL_EVENT_DID_ENTER_FOREGROUND");
+ ctx->app_suspended = 0;
+ if (ctx->device_connected) {+ renderer_clear_screen();
+ m8_resume_processing();
+ }
+ break;
+ case SDL_EVENT_FINGER_DOWN:
+ input_handle_finger_down(ctx, event);
+ break;
+
+ // --- Input events ---
+ case SDL_EVENT_GAMEPAD_ADDED:
+ case SDL_EVENT_GAMEPAD_REMOVED:
+ // Reinitialize game controllers on controller add/remove/remap
+ gamepads_initialize();
+ break;
+
+ case SDL_EVENT_KEY_DOWN:
+ input_handle_key_down_event(ctx, event);
+ break;
+
+ case SDL_EVENT_KEY_UP:
+ input_handle_key_up_event(ctx, event);
+ break;
+
+ case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
+ input_handle_gamepad_button(ctx, event->gbutton.button, true);
+ break;
+
+ case SDL_EVENT_GAMEPAD_BUTTON_UP:
+ input_handle_gamepad_button(ctx, event->gbutton.button, false);
+ break;
+
+ case SDL_EVENT_GAMEPAD_AXIS_MOTION:
+ input_handle_gamepad_axis(ctx, event->gaxis.axis, event->gaxis.value);
+ break;
+
+
+ default:
+ break;
+ }
+ return ret_val;
+}
--- /dev/null
+++ b/src/events.h
@@ -1,0 +1,7 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+#ifndef EVENTS_H_
+#define EVENTS_H_
+
+#endif
--- a/src/font1.h
+++ /dev/null
@@ -1,56 +1,0 @@
-#ifndef FONT1_H_
-#define FONT1_H_
-
-#include "inline_font.h"
-
-struct inline_font font_v1_small = {- 470,
- 7,
- 5,
- 7,
- 0,
- 0,
- 3,
- 24,
- 566,
- {- 0x42, 0x4D, 0x36, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
- 0x00, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x80, 0x00, 0x40, 0x34,
- 0x01, 0x80, 0x00, 0x40, 0x12, 0x1F, 0xFF, 0xFE, 0x1F, 0xFC, 0x9E, 0x08, 0x10, 0x00, 0x00,
- 0x8E, 0x8F, 0x9D, 0xEF, 0xC1, 0xD1, 0xFB, 0xA3, 0xF8, 0xC5, 0xD0, 0x6C, 0x7C, 0x47, 0x12,
- 0x31, 0xF7, 0xC6, 0x1C, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0x93, 0xF1, 0x77, 0x22, 0xEA, 0xC7,
- 0xF0, 0x0C, 0x3E, 0x7F, 0xE3, 0xF1, 0xFF, 0xFE, 0x4F, 0x80, 0x00, 0x00, 0x15, 0xF9, 0xC8,
- 0x02, 0x40, 0x08, 0x40, 0x02, 0x11, 0x24, 0x02, 0x10, 0xC4, 0x91, 0x0A, 0x10, 0x20, 0x20,
- 0x10, 0x8C, 0x63, 0x18, 0x42, 0x31, 0x24, 0x65, 0x08, 0xC6, 0x30, 0x94, 0x42, 0x48, 0xAB,
- 0x71, 0x0C, 0x04, 0x14, 0x00, 0x00, 0x8C, 0x61, 0x18, 0x10, 0x31, 0x20, 0xB4, 0x4A, 0xC6,
- 0x3F, 0xFC, 0x02, 0x48, 0xD2, 0xAA, 0x0A, 0x22, 0x48, 0x80, 0x00, 0x80, 0x3E, 0x55, 0xD4,
- 0x02, 0x40, 0x08, 0x00, 0x01, 0x19, 0x24, 0x02, 0x10, 0xC4, 0x91, 0x08, 0x00, 0x6F, 0xB0,
- 0x97, 0x8C, 0x61, 0x18, 0x42, 0x71, 0x24, 0x69, 0x08, 0xC6, 0x30, 0xAC, 0x82, 0x48, 0xAA,
- 0xAA, 0x0A, 0x04, 0x24, 0x00, 0x00, 0xFC, 0x61, 0x1F, 0x93, 0xF1, 0x20, 0xA8, 0x4A, 0xC6,
- 0x31, 0x8C, 0x3E, 0x48, 0xCA, 0xA4, 0xF9, 0x22, 0x48, 0x80, 0x00, 0x80, 0x15, 0xF2, 0x24,
- 0x02, 0x42, 0xBE, 0x0F, 0x80, 0x95, 0x27, 0xFF, 0xFF, 0xFC, 0x5F, 0xF8, 0x00, 0xE0, 0x38,
- 0x55, 0xFF, 0xA1, 0x1F, 0x7A, 0x1F, 0x20, 0x71, 0x08, 0xCE, 0x3E, 0x8F, 0x9C, 0x48, 0xC6,
- 0x24, 0x79, 0x04, 0x44, 0x00, 0x00, 0x0C, 0x61, 0x18, 0x92, 0x31, 0x20, 0xA4, 0x4A, 0xC6,
- 0x31, 0x8C, 0x20, 0x48, 0xC6, 0xAA, 0x88, 0xA2, 0x4A, 0x80, 0x00, 0x80, 0x3F, 0x4D, 0x50,
- 0x02, 0x41, 0x08, 0x00, 0x00, 0x53, 0x20, 0x43, 0x18, 0x40, 0x31, 0x8A, 0x10, 0x6F, 0xB0,
- 0x37, 0x8C, 0x61, 0x18, 0x42, 0x11, 0x20, 0x69, 0x0A, 0xD6, 0x31, 0x8C, 0x60, 0x48, 0xC6,
- 0x2A, 0x88, 0x84, 0x84, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0xA2, 0x4F, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xF8, 0xC6, 0xB1, 0x8F, 0xE2, 0x48, 0xD8, 0x00, 0x82, 0x95, 0xFC, 0xC8,
- 0x82, 0x42, 0x88, 0x00, 0x00, 0x31, 0xE0, 0x43, 0x18, 0x40, 0x31, 0x88, 0x00, 0x20, 0x22,
- 0x31, 0x8C, 0x63, 0x18, 0x42, 0x31, 0x20, 0x65, 0x0D, 0xE6, 0x31, 0x8C, 0x60, 0x48, 0xC6,
- 0x31, 0x88, 0x45, 0x04, 0x28, 0x04, 0x04, 0x00, 0x10, 0x10, 0x10, 0x00, 0x20, 0x40, 0x00,
- 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x22, 0x48, 0xA4, 0x00, 0x82, 0x80, 0x40, 0x30,
- 0x81, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x27, 0xFF, 0x1F, 0xFF, 0xEF, 0xF8, 0x00, 0x00, 0x01,
- 0xCE, 0x77, 0x9D, 0xEF, 0xFD, 0xD1, 0xF8, 0x63, 0x08, 0xC5, 0xDE, 0x77, 0x9F, 0xF8, 0xC6,
- 0x31, 0x8F, 0xC7, 0x0C, 0x10, 0x08, 0x04, 0x00, 0x10, 0x1C, 0x10, 0x20, 0xA0, 0xC0, 0x00,
- 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3E, 0x4F, 0x80, 0x00,
- }};
-#endif // FONT1_H_
\ No newline at end of file
--- a/src/font2.h
+++ /dev/null
@@ -1,85 +1,0 @@
-#ifndef FONT2_H_
-#define FONT2_H_
-#include "inline_font.h"
-
-struct inline_font font_v1_large = {- 752,
- 9,
- 8,
- 9,
- 0,
- -40,
- 4,
- 22,
- 1010,
- {- 0x42, 0x4D, 0xF2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
- 0x00, 0x00, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xE7,
- 0x7D, 0x84, 0xFF, 0xFC, 0x9F, 0xFF, 0xFF, 0x9F, 0xFF, 0x9F, 0xFF, 0x00, 0x00, 0x00, 0x00,
- 0xFC, 0x00, 0x00, 0xE7, 0x01, 0xFC, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xE7, 0x81, 0x3C, 0x01,
- 0x81, 0x03, 0x00, 0x3F, 0x81, 0x3C, 0x00, 0x81, 0x3C, 0x00, 0x3C, 0x3C, 0x81, 0x3F, 0x84,
- 0x3C, 0x81, 0xE7, 0x81, 0xE7, 0x3C, 0x3C, 0xE7, 0x00, 0xF0, 0xFF, 0x0F, 0xFF, 0x80, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x24, 0x3C, 0x00,
- 0x3F, 0xFC, 0x3F, 0x00, 0x80, 0x00, 0x3F, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xE7, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0xFF, 0xDB, 0x00, 0x38, 0x39, 0xFF, 0xF9, 0xCF, 0xFF, 0xFF, 0xCF, 0xFF,
- 0xFF, 0x3F, 0x3C, 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C, 0xE7, 0x3C, 0xFC, 0xFF, 0x9F, 0xFC,
- 0xFF, 0x9F, 0xFF, 0x3F, 0x3C, 0x3C, 0x3C, 0x39, 0x3F, 0x3F, 0x3C, 0x3C, 0xE7, 0x3C, 0x39,
- 0x3F, 0x3C, 0x3C, 0x3C, 0x3F, 0x39, 0x39, 0x3C, 0xE7, 0x3C, 0xC3, 0x18, 0x3C, 0xE7, 0x3F,
- 0xF3, 0xFC, 0xCF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F, 0x9F, 0xFC, 0x3C, 0xE7,
- 0xFC, 0x39, 0xCF, 0x24, 0x3C, 0x3C, 0x3F, 0xFC, 0x3F, 0xFC, 0x9F, 0x3C, 0x1F, 0x24, 0x99,
- 0xFC, 0x9F, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0xE4, 0x9D, 0x30, 0xFF,
- 0xF3, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0x9F, 0x3C, 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C,
- 0xE7, 0x3C, 0xFC, 0xE7, 0x9F, 0xF8, 0x81, 0x8F, 0xE7, 0x21, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F,
- 0x3F, 0x3C, 0x3C, 0xE7, 0x3C, 0x33, 0x3F, 0x3C, 0x3C, 0x3C, 0x3F, 0x32, 0x33, 0xFC, 0xE7,
- 0x3C, 0x99, 0x00, 0x99, 0xE7, 0x9F, 0xF3, 0xF9, 0xCF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3F,
- 0x3C, 0x3F, 0x9F, 0xFC, 0x3C, 0xE7, 0xFC, 0x33, 0xCF, 0x24, 0x3C, 0x3C, 0x3F, 0xFC, 0x3F,
- 0xFC, 0x9F, 0x3C, 0x0F, 0x24, 0xC3, 0xFC, 0xCF, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF,
- 0xFF, 0xDB, 0xE4, 0xCF, 0x87, 0xFF, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xCF, 0x3C,
- 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C, 0xE7, 0x3C, 0xFC, 0xFF, 0xFF, 0xF0, 0xFF, 0x87, 0xE7,
- 0x24, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F, 0x3F, 0x3C, 0x3C, 0xE7, 0xFC, 0x27, 0x3F, 0x3C, 0x38,
- 0x3C, 0x3F, 0x24, 0x27, 0xFC, 0xE7, 0x3C, 0x3C, 0x24, 0xC3, 0xE7, 0xCF, 0xF3, 0xF3, 0xCF,
- 0xFF, 0xFF, 0xFF, 0x00, 0x3C, 0x3F, 0x3C, 0x00, 0x9F, 0x00, 0x3C, 0xE7, 0xFC, 0x07, 0xCF,
- 0x24, 0x3C, 0x3C, 0x00, 0x00, 0x3F, 0x00, 0x9F, 0x3C, 0x27, 0x24, 0xE7, 0x00, 0xE7, 0x3C,
- 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF, 0xFF, 0xDB, 0x00, 0xE7, 0xC7, 0xFF, 0xF3, 0xE7, 0x33,
- 0x81, 0xFF, 0x81, 0xFF, 0xE7, 0x24, 0xE7, 0x00, 0x80, 0x00, 0x00, 0x00, 0xF3, 0x81, 0x00,
- 0xFF, 0xFF, 0xE0, 0xFF, 0x83, 0xE1, 0x24, 0x00, 0x01, 0x3F, 0x3C, 0x03, 0x03, 0x30, 0x00,
- 0xE7, 0xFC, 0x0F, 0x3F, 0x3C, 0x30, 0x3C, 0x01, 0x3C, 0x01, 0x81, 0xE7, 0x3C, 0x3C, 0x3C,
- 0xE7, 0x81, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0x3C, 0x3C, 0x9F,
- 0x3C, 0x3C, 0xE7, 0xFC, 0x33, 0xCF, 0x24, 0x3C, 0x3C, 0x3C, 0x3C, 0x3F, 0x3F, 0x9F, 0x3C,
- 0x33, 0x24, 0xC3, 0x3C, 0xF3, 0x3C, 0xE7, 0x3C, 0x23, 0x00, 0x00, 0xCF, 0xFF, 0x81, 0x27,
- 0xF3, 0x93, 0xFF, 0xF3, 0xE7, 0x87, 0xE7, 0xFF, 0xFF, 0xFF, 0xF3, 0x3C, 0xE7, 0xFC, 0xFC,
- 0x3C, 0x3F, 0x3F, 0xF9, 0x3C, 0x3C, 0xE7, 0x9F, 0xF0, 0xFF, 0x87, 0xFC, 0x20, 0x3C, 0x3B,
- 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3C, 0xE7, 0xFC, 0x27, 0x3F, 0x24, 0x24, 0x3C, 0x3C, 0x3C,
- 0x3C, 0x3F, 0xE7, 0x3C, 0x3C, 0x3C, 0xC3, 0x3C, 0xF3, 0xF3, 0xCF, 0xCF, 0xFF, 0xFF, 0xFF,
- 0xFC, 0x3C, 0x3F, 0x3C, 0x3C, 0x00, 0x3C, 0x3C, 0xE7, 0xFC, 0x39, 0xCF, 0x24, 0x3C, 0x3C,
- 0x3C, 0x3C, 0x3F, 0x3F, 0x9F, 0x3C, 0x39, 0x24, 0x99, 0x3C, 0xF9, 0x3C, 0xE7, 0x3C, 0x89,
- 0x00, 0x00, 0xCF, 0xC9, 0xDB, 0x27, 0xB9, 0x93, 0xE7, 0xF3, 0xE7, 0xCF, 0xE7, 0xFF, 0xFF,
- 0xFF, 0xF9, 0x3C, 0xE7, 0xFC, 0xFC, 0x3C, 0x3F, 0x3F, 0xFC, 0x3C, 0x3C, 0xFF, 0xFF, 0xF8,
- 0x81, 0x8F, 0xFC, 0x3C, 0x3C, 0x39, 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3C, 0xE7, 0xFC, 0x33,
- 0x3F, 0x00, 0x0C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3F, 0xE7, 0x3C, 0x3C, 0x3C, 0x99, 0x3C, 0xF9,
- 0xF3, 0x9F, 0xCF, 0x27, 0xFF, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x87,
- 0x80, 0x3C, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x24, 0x3C,
- 0x3C, 0x00, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF, 0xC9, 0xFF, 0x00, 0x1C, 0xC7, 0xE7,
- 0xF9, 0xCF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3C, 0xE7, 0xFC, 0xFC, 0x3C, 0x3F, 0x3F,
- 0xFC, 0x9C, 0x3C, 0xFF, 0xFF, 0xFC, 0xFF, 0x9F, 0x3C, 0x3C, 0x99, 0x39, 0x3C, 0x39, 0x3F,
- 0x3F, 0x3C, 0x3C, 0xE7, 0xFC, 0x39, 0x3F, 0x18, 0x1C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xE7,
- 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xF3, 0x3F, 0xCF, 0x8F, 0xFF, 0x9F, 0xFF, 0x3F, 0xFF,
- 0xFC, 0xFF, 0x9F, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF,
- 0xC9, 0xFF, 0xE7, 0xBE, 0xFF, 0xE7, 0xFC, 0x9F, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
- 0x87, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
- 0x81, 0xC3, 0x03, 0x81, 0x03, 0x00, 0x00, 0x81, 0x3C, 0x00, 0x00, 0x3C, 0x3F, 0x3C, 0x3C,
- 0x81, 0x01, 0x81, 0x01, 0x81, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0xF0, 0xFF, 0x0F,
- 0xDF, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0xFC, 0xFF, 0x80, 0xFF, 0x3F, 0xE7, 0xFC, 0x3F, 0x0F,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
- 0xE7, 0x00, 0xFF, 0x00, 0x00,
- }};
-#endif // FONT2_H_
\ No newline at end of file
--- a/src/font3.h
+++ /dev/null
@@ -1,93 +1,0 @@
-#ifndef FONT3_H_
-#define FONT3_H_
-
-#include "inline_font.h"
-
-struct inline_font font_v2_small = {- 846,
- 9,
- 9,
- 9,
- 0,
- -2,
- 5,
- 38,
- 1118,
- {- 0x42, 0x4D, 0x5E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
- 0x00, 0x00, 0x00, 0x4E, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xCC, 0x03, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x7E,
- 0xF7, 0xC4, 0x3B, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFD, 0xFE, 0x00, 0x00,
- 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xDE, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x30, 0x03, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0,
- 0x07, 0xF3, 0xF8, 0x00, 0xFF, 0x01, 0x3F, 0x80, 0x1E, 0xF0, 0x07, 0xBD, 0xFC, 0xFE, 0x00,
- 0x00, 0x3C, 0x7F, 0xEE, 0x3F, 0xFC, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC,
- 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x07, 0x73, 0xF8, 0x00, 0xFF, 0xFF, 0x3F, 0xC0, 0x00,
- 0x00, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x07, 0xFC, 0x00, 0x00, 0xFF,
- 0xFF, 0xF7, 0x60, 0x0B, 0xD3, 0xD7, 0xFF, 0xFD, 0xBF, 0xFF, 0xFD, 0xEF, 0xFF, 0xFF, 0xFE,
- 0xFE, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB, 0xFC, 0xFE, 0xFF, 0x7F, 0xDF, 0xFF,
- 0x3F, 0xFF, 0x3F, 0xFE, 0xFF, 0x7F, 0x3F, 0x9F, 0xEF, 0xD7, 0xFB, 0xFD, 0xFC, 0xFE, 0xF7,
- 0xFF, 0x9F, 0xAF, 0xF7, 0xF3, 0xF1, 0xFC, 0xFF, 0x7E, 0xBF, 0x7F, 0xDE, 0xF7, 0xF7, 0x5C,
- 0xF9, 0x7D, 0xFF, 0x5F, 0xFD, 0xFF, 0xDF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x9F, 0xEF,
- 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF7, 0x73, 0xF9, 0xFC, 0xFF, 0xFF,
- 0x3F, 0xFF, 0xCF, 0xF7, 0xF3, 0x7D, 0xDD, 0x7D, 0xFF, 0x3F, 0xDF, 0xCF, 0xF7, 0xF7, 0xFC,
- 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x1E, 0xED, 0xC3, 0xEF, 0xFF, 0xFB, 0xDF, 0xFF, 0xFD, 0xFF,
- 0xFF, 0xFF, 0xFF, 0x7E, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB, 0xFC, 0xFE, 0xFF,
- 0x3F, 0xDF, 0xFE, 0x38, 0x0F, 0x1F, 0xDE, 0x80, 0x7F, 0x3F, 0x9F, 0xEF, 0xE7, 0xFB, 0xFD,
- 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0x6F, 0xF7, 0xF3, 0xE9, 0xFC, 0xFF, 0x7D, 0x3E, 0xFF, 0xDE,
- 0xF7, 0xF6, 0xED, 0x75, 0xBB, 0xFF, 0x6F, 0xFD, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F,
- 0x3F, 0x9F, 0xEF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0x6F, 0xF7, 0x73, 0xF9,
- 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xCF, 0xF7, 0xF3, 0xBD, 0xDD, 0xBB, 0xFF, 0x4F, 0xDF, 0xCF,
- 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x7E, 0xEE, 0xFB, 0xD7, 0xFF, 0xFB, 0xDF,
- 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB,
- 0xFC, 0xFE, 0xFF, 0x7F, 0xFF, 0xFC, 0x3F, 0xFF, 0x0F, 0xDE, 0xBA, 0x7F, 0x3F, 0x9F, 0xEF,
- 0xE7, 0xFB, 0xFD, 0xFC, 0xFE, 0xF7, 0xFF, 0x9E, 0xEF, 0xF7, 0xF3, 0xD9, 0xFC, 0xFF, 0x7B,
- 0x3D, 0xFF, 0xDE, 0xF7, 0xF5, 0xF5, 0xAD, 0xD7, 0xFF, 0x77, 0xFD, 0xFF, 0x7F, 0xBF, 0xFF,
- 0xFF, 0xFF, 0x00, 0x3F, 0x9F, 0xEF, 0xE0, 0x00, 0x0C, 0x00, 0xFE, 0xF7, 0xFF, 0x80, 0xEF,
- 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xC0, 0x0F, 0xF7, 0xF3, 0xDD, 0xDD, 0xC7, 0x00,
- 0x71, 0xDF, 0xCF, 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x60, 0x0F, 0x7D, 0xBB,
- 0xFF, 0xFB, 0xDF, 0xDD, 0xE0, 0x3F, 0xF8, 0x0F, 0xFF, 0xDE, 0xEE, 0xF7, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x3F, 0xFF, 0x07, 0xC0, 0x82, 0x00,
- 0x00, 0x1F, 0xEF, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0xF7, 0xFF, 0x81, 0xEF, 0xF7, 0x73, 0xB9,
- 0xFC, 0x00, 0x7F, 0x00, 0x00, 0x1E, 0xF7, 0xF3, 0xF9, 0xDD, 0xEF, 0x00, 0x7B, 0xFD, 0xFE,
- 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x9F, 0xEF, 0xE7, 0xF3, 0xFD, 0xFC, 0xFE, 0xF7,
- 0xFF, 0x9F, 0x6F, 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xEF, 0xF7, 0xF3, 0xED,
- 0xDD, 0xBB, 0x7F, 0x7E, 0x5F, 0xCF, 0xF7, 0xF3, 0x3C, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x6E,
- 0xFF, 0xBE, 0x7F, 0xFF, 0xFB, 0xDF, 0xEB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0xFE, 0xF7,
- 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F, 0x7F, 0xFF, 0xFC, 0x3F, 0xFF, 0x0F,
- 0xFC, 0xFA, 0x7F, 0x3E, 0xDF, 0xEF, 0xE7, 0xFB, 0xFD, 0xFE, 0xFE, 0xF7, 0xFF, 0x9E, 0xEF,
- 0xF6, 0xB3, 0x79, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE, 0xF7, 0xF3, 0xF9, 0xFD, 0xD7, 0x7F,
- 0x7D, 0xFD, 0xFD, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x9F, 0xEF, 0xE7, 0xF3, 0xFD,
- 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xEF,
- 0xF7, 0xF3, 0xF5, 0xDD, 0x7D, 0x7F, 0x7F, 0x9F, 0xCF, 0xF7, 0xF4, 0xDC, 0x00, 0x00, 0x7F,
- 0xB7, 0xC0, 0x0E, 0xF1, 0xDD, 0xBD, 0xFF, 0xFB, 0xDF, 0xF7, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xF6, 0xFE, 0xF7, 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xFE,
- 0x38, 0x0F, 0x1F, 0xFC, 0x82, 0x7F, 0x3E, 0xDF, 0xEF, 0xE7, 0xFB, 0xFD, 0xFE, 0xFE, 0xF7,
- 0xFF, 0x9F, 0x6F, 0xF5, 0xD2, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE, 0xF7, 0xF3, 0xF9,
- 0xFD, 0xBB, 0x7F, 0x7E, 0xFD, 0xFB, 0xFF, 0xBB, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0xCF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x07, 0xF3, 0xF9, 0xDC, 0xFE, 0x7F, 0x00, 0x1F, 0xCF, 0xF7, 0xF7, 0xFC,
- 0x00, 0x00, 0x7F, 0xB7, 0xF7, 0x60, 0x05, 0xED, 0xBD, 0xFF, 0xFD, 0xBF, 0xEB, 0xFD, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFA, 0xFE, 0xF7, 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F,
- 0x7F, 0xFF, 0xFF, 0x3F, 0xFF, 0x3D, 0xFC, 0xFE, 0x7F, 0x3E, 0xDF, 0xEF, 0xD7, 0xFB, 0xFD,
- 0xFE, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF3, 0xE1, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE,
- 0xF7, 0xF3, 0xF9, 0xFD, 0x7D, 0x7F, 0x7F, 0x7D, 0xF7, 0xFF, 0xBD, 0x7F, 0xFF, 0xBF, 0xFF,
- 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xCF,
- 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xB7, 0xF7, 0x7E, 0xF1, 0xF6, 0x7D, 0xFF, 0xFE, 0x7F,
- 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x07, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0x9F, 0xCF, 0xF7, 0xF3, 0xF8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x07, 0xF3, 0xF9, 0xFC, 0xFE, 0x7F, 0x00, 0x3C, 0x6F, 0xFE, 0x3E, 0xFF,
- 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xC0, 0x0F, 0xF0, 0x07, 0xFC, 0x00, 0x00,
- }};
-#endif // FONT3_H_
\ No newline at end of file
--- a/src/font4.h
+++ /dev/null
@@ -1,108 +1,0 @@
-#ifndef FONT4_H_
-#define FONT4_H_
-
-#include "inline_font.h"
-
-struct inline_font font_v2_large = {- 940,
- 10,
- 10,
- 10,
- 0,
- -2,
- 4,
- 38,
- 1346,
- {- 0x42, 0x4D, 0x42, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
- 0x00, 0x00, 0x00, 0xAC, 0x03, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xB0, 0x04, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCF,
- 0xCF, 0x3E, 0x60, 0xCF, 0xFF, 0xF9, 0x3F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xE7, 0xF3, 0xFC,
- 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x3F, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x03, 0x3F, 0x00, 0x18, 0x00, 0x01, 0x80, 0x0F, 0xF8, 0x04,
- 0xFC, 0x00, 0x00, 0x13, 0xF2, 0x00, 0x3F, 0x0F, 0xC8, 0x04, 0xFF, 0x83, 0x0F, 0xC0, 0x07,
- 0xCF, 0x80, 0x7C, 0xF3, 0xF0, 0xFC, 0x00, 0x40, 0x0F, 0x0F, 0xFC, 0xE1, 0xFF, 0xF0, 0x03,
- 0xFF, 0x80, 0x00, 0x18, 0x02, 0x00, 0x80, 0x33, 0xF0, 0x04, 0xFC, 0x00, 0x00, 0x13, 0xE0,
- 0x00, 0x33, 0x0F, 0xC8, 0x04, 0xFF, 0xFF, 0x0F, 0xF0, 0x07, 0x80, 0x80, 0x43, 0xF8, 0x00,
- 0xFC, 0x00, 0x40, 0x00, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCC,
- 0x01, 0x1E, 0x40, 0x0F, 0xFF, 0xF1, 0x1F, 0xFF, 0xFF, 0x3C, 0xFF, 0xFF, 0xE7, 0xF1, 0xFC,
- 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x3F, 0xFC, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x03, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00,
- 0xFC, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x3F, 0x0F, 0x80, 0x00, 0xFF, 0x06, 0x0F, 0x80, 0x03,
- 0xCF, 0x00, 0x38, 0x71, 0xE0, 0x78, 0x00, 0x00, 0x0F, 0x0F, 0xF8, 0xE1, 0xFF, 0xF0, 0x03,
- 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xF0, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xC4,
- 0x00, 0x33, 0x0F, 0xC0, 0x00, 0xFF, 0xFF, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x01, 0xF0, 0x00,
- 0x78, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00,
- 0x00, 0x8F, 0xCE, 0x1F, 0xFF, 0xE3, 0x8F, 0xFF, 0xFF, 0x3C, 0xFF, 0xFF, 0xFF, 0xF8, 0xFC,
- 0xFC, 0xF3, 0xCF, 0xFF, 0xF3, 0xFC, 0xFF, 0x0F, 0xCF, 0xF0, 0xFC, 0xFF, 0x33, 0xFC, 0xFF,
- 0xE7, 0x80, 0x79, 0xFF, 0xFC, 0xFF, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xF0,
- 0xFC, 0xF3, 0xFF, 0xC3, 0xC4, 0xFF, 0x3F, 0x0F, 0x03, 0xF0, 0xFF, 0x3C, 0x0F, 0x1F, 0xF3,
- 0xCF, 0x3F, 0x30, 0x30, 0xC2, 0x31, 0xFF, 0x23, 0xFF, 0x3F, 0xF1, 0xF9, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xF3, 0xFF, 0xF0, 0xFC, 0xF3, 0xFF, 0xC3, 0x8F,
- 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0x01, 0x80, 0x0F, 0xFF, 0xF3, 0x3F, 0x3F, 0x08, 0xF3, 0x32,
- 0x31, 0xFF, 0x03, 0xF3, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x03,
- 0xCC, 0xC7, 0xCE, 0x1F, 0xFF, 0xE7, 0xCF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C,
- 0xFC, 0xF3, 0xCF, 0xFF, 0xF3, 0xFC, 0xFF, 0x0F, 0xCF, 0xF0, 0xFC, 0xFF, 0x33, 0xFC, 0xFF,
- 0xC7, 0x80, 0x78, 0xFF, 0x3C, 0xC0, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xF0,
- 0xFC, 0xF3, 0xFF, 0xC3, 0x8C, 0xFF, 0x3F, 0x0E, 0x03, 0xF0, 0xFF, 0x38, 0x0E, 0x3F, 0xF3,
- 0xCF, 0x3F, 0x23, 0x10, 0x03, 0x03, 0xFF, 0x31, 0xFF, 0x3F, 0xE3, 0xF9, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x00, 0x0F, 0xC3, 0xFC, 0xFC, 0x00, 0x33, 0xF8, 0x00, 0xFC, 0xF3, 0xFF, 0xC0, 0x1F,
- 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x03, 0x3F, 0x3F, 0x0C, 0x73, 0x33,
- 0x03, 0x80, 0x20, 0x73, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCE,
- 0x00, 0xE3, 0xC0, 0x0F, 0xFF, 0xE7, 0xCF, 0xCE, 0x78, 0x07, 0xFF, 0x80, 0x7F, 0xFE, 0x3C,
- 0xCC, 0xF3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
- 0x87, 0xFF, 0xF8, 0x7F, 0x04, 0xC0, 0x00, 0x00, 0x03, 0xFC, 0xFC, 0x00, 0x00, 0x03, 0x00,
- 0x00, 0xF3, 0xFF, 0xC0, 0x1C, 0xFF, 0x33, 0x0C, 0x43, 0xF0, 0x01, 0x31, 0x00, 0x18, 0x03,
- 0xCF, 0x3F, 0x07, 0x82, 0x13, 0x87, 0x80, 0x38, 0xFF, 0x3F, 0xC7, 0xF9, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x00, 0x0F, 0xC3, 0xFC, 0xFC, 0x00, 0x33, 0xF0, 0x00, 0xFC, 0xF3, 0xFF, 0xC0, 0x3F,
- 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xF0, 0x07, 0x3F, 0x3F, 0x0E, 0x33, 0x33,
- 0x03, 0x00, 0x38, 0x13, 0xF0, 0xFF, 0x3F, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCC,
- 0x01, 0xF1, 0xE0, 0xCF, 0xFF, 0xE7, 0xCF, 0xC4, 0x78, 0x07, 0xFF, 0x80, 0x7F, 0xFF, 0x1C,
- 0xCC, 0xF3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
- 0x07, 0xFF, 0xF8, 0x3F, 0x00, 0xCC, 0x00, 0x00, 0x33, 0xFC, 0xFC, 0x00, 0x00, 0x03, 0x00,
- 0x00, 0xF3, 0xFF, 0xC0, 0x1C, 0xFF, 0x21, 0x08, 0xC3, 0xF0, 0x00, 0x33, 0x00, 0x00, 0x07,
- 0xCF, 0x3F, 0x0F, 0xC3, 0x33, 0x87, 0x00, 0x3C, 0x7F, 0x3F, 0x8F, 0xF9, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0x00, 0x33, 0xF0, 0xFC, 0xF3, 0xFF, 0xC3, 0x1F,
- 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xF3, 0xFF, 0x3F, 0x3F, 0x0F, 0x13, 0x32,
- 0x31, 0x3F, 0x3F, 0x03, 0xF0, 0xFF, 0x3F, 0x00, 0x70, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00,
- 0xCF, 0xF8, 0xE6, 0x7F, 0xFF, 0xE7, 0xCF, 0xF1, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x8C,
- 0xFC, 0xF3, 0xFF, 0xCF, 0xF0, 0xFC, 0x3F, 0xCF, 0xFF, 0xF0, 0xFC, 0x3F, 0x33, 0xFC, 0xFF,
- 0x87, 0x80, 0x78, 0x7F, 0xF0, 0xCC, 0x3F, 0x0F, 0x93, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xFC,
- 0xFC, 0xF3, 0xFF, 0xC3, 0x8C, 0xFF, 0x00, 0x01, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF,
- 0xCF, 0x3F, 0x0F, 0xC3, 0xF3, 0x03, 0x3F, 0x3E, 0x3F, 0x3F, 0x1F, 0xF9, 0xE6, 0x7F, 0xFF,
- 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC3, 0xFF, 0x03, 0x8F,
- 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0F, 0x83, 0x30,
- 0x78, 0x3F, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0x22, 0x70, 0x00, 0x00, 0x3F, 0xC9, 0xF0, 0x00,
- 0x00, 0xFC, 0x66, 0x73, 0xFF, 0xE3, 0x8F, 0xF1, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC4,
- 0xFC, 0xF3, 0xFF, 0xCF, 0xF0, 0xFC, 0x3F, 0xCF, 0xFF, 0xF0, 0xFC, 0x3F, 0x33, 0xFC, 0xFF,
- 0xC7, 0x80, 0x78, 0xF3, 0xF0, 0xFC, 0x3F, 0x0F, 0x93, 0xFC, 0xF8, 0x3F, 0xCF, 0xF3, 0xFC,
- 0xFC, 0xF3, 0xFF, 0xC3, 0xC4, 0xFF, 0x0C, 0x03, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF,
- 0xCF, 0x3F, 0x0F, 0xC3, 0xF2, 0x31, 0x3F, 0x3F, 0x1F, 0x3E, 0x3F, 0xF9, 0xE0, 0x7F, 0xFF,
- 0x3F, 0x00, 0x40, 0x18, 0x02, 0x00, 0x80, 0x73, 0xF8, 0x00, 0x01, 0xC3, 0xFF, 0x03, 0xC7,
- 0xCF, 0x00, 0x40, 0x18, 0x04, 0x01, 0x80, 0x20, 0x08, 0x00, 0x00, 0x3F, 0x0F, 0xC3, 0x30,
- 0xFC, 0x3F, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xC9, 0xFC, 0xCE,
- 0x00, 0x9E, 0x20, 0x73, 0xFF, 0xF1, 0x1F, 0xC4, 0x7F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
- 0x00, 0x03, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
- 0xE7, 0xFF, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0xFC, 0x00, 0x3F, 0xC3, 0xE0, 0xFF, 0x1E, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x3F, 0x0F, 0xC3, 0xF0, 0x78, 0x3F, 0x00, 0x0F, 0x0C, 0x7F, 0xE1, 0xF0, 0xFF, 0xFE,
- 0x3F, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0xFF, 0xF0, 0x0F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,
- 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xC9, 0xFC, 0xCF,
- 0xCF, 0x9F, 0x30, 0xF3, 0xFF, 0xF9, 0x3F, 0xCE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
- 0x00, 0x03, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xF8, 0x06, 0x01, 0x80, 0x40, 0x38, 0x00, 0x03, 0x80, 0x20, 0x08, 0x00,
- 0xFC, 0x00, 0x3F, 0xC3, 0xF0, 0xFF, 0x3F, 0x0F, 0xC8, 0x04, 0x01, 0x80, 0x40, 0x18, 0x00,
- 0x00, 0x3F, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x00, 0x0F, 0x0C, 0xFF, 0xE1, 0xF9, 0xFF, 0xFE,
- 0x7F, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0xFF, 0xF8, 0x0F, 0xFC, 0xFF, 0xF3, 0xFF, 0xC3, 0xFF,
- 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00,
- }};
-#endif // FONT4_H_
\ No newline at end of file
--- a/src/font5.h
+++ /dev/null
@@ -1,143 +1,0 @@
-#ifndef FONT5_H_
-#define FONT5_H_
-
-#include "inline_font.h"
-
-struct inline_font font_v2_huge = {- 1128,
- 12,
- 12,
- 12,
- 0,
- -54,
- 4,
- 24,
- 1874,
- {- 0x42, 0x4D, 0x52, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
- 0x00, 0x00, 0x00, 0x68, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x9F, 0xFF, 0xFC, 0x1C, 0xFF, 0xFF, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF,
- 0xF9, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0xFF,
- 0xC0, 0x00, 0xFF, 0xCF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF8, 0x03, 0x3F,
- 0xC0, 0x01, 0x80, 0x00, 0x01, 0x80, 0x03, 0xFF, 0x80, 0x13, 0xFC, 0x00, 0x00, 0x01, 0x3F,
- 0xC8, 0x00, 0x3F, 0xC3, 0xFC, 0x80, 0x13, 0xFF, 0x80, 0xC3, 0xFC, 0x00, 0x1F, 0x9F, 0x80,
- 0x1F, 0x9F, 0x3F, 0xC3, 0xFC, 0x00, 0x10, 0x00, 0xC0, 0xFF, 0xFC, 0xE0, 0x7F, 0xFF, 0x00,
- 0x0F, 0xFF, 0x80, 0x00, 0x01, 0x80, 0x08, 0x00, 0x80, 0x0C, 0xFF, 0x00, 0x13, 0xFC, 0x00,
- 0x00, 0x01, 0x3F, 0x80, 0x00, 0x39, 0xC3, 0xFC, 0x80, 0x13, 0xFF, 0xFF, 0xC3, 0xFF, 0x00,
- 0x1E, 0x00, 0x80, 0x10, 0xFF, 0x80, 0x03, 0xFC, 0x00, 0x10, 0x00, 0x00, 0x03, 0xFF, 0x00,
- 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6, 0x70, 0x01, 0x9F, 0x18, 0x08, 0xFF,
- 0xFF, 0xE3, 0xE3, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF9, 0xFF, 0x1F, 0xF0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0xFF, 0xCF, 0xFF, 0x8F,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF0, 0x03, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0xC3, 0xF8, 0x00,
- 0x03, 0xFF, 0x01, 0x83, 0xF8, 0x00, 0x0F, 0x9F, 0x00, 0x0F, 0x0F, 0x1F, 0x81, 0xF8, 0x00,
- 0x00, 0x00, 0xC0, 0xFF, 0xF8, 0xE0, 0x7F, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0C, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x10, 0x00, 0x39,
- 0xC3, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xC3, 0xFF, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7F, 0x00,
- 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0xFF,
- 0xFF, 0xFF, 0xE6, 0x70, 0x00, 0x8F, 0x11, 0xC1, 0xFF, 0xFF, 0xC7, 0xF1, 0xFF, 0xFF, 0xF9,
- 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF, 0xCF, 0xFC, 0xFF,
- 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xCF, 0x9F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9,
- 0xFF, 0xFC, 0x3F, 0x13, 0xFF, 0x3F, 0xC3, 0xF0, 0x3F, 0xC3, 0xFF, 0x3F, 0x03, 0xF1, 0xFF,
- 0xCF, 0x9F, 0x3F, 0xCE, 0x07, 0x0F, 0x08, 0xF1, 0xFF, 0xC8, 0xFF, 0xCF, 0xFF, 0xF1, 0xFE,
- 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xFC, 0xFF, 0xFF,
- 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x3F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0xFF,
- 0xC3, 0xFF, 0xFF, 0xCC, 0xFF, 0x3F, 0xC2, 0x3F, 0x39, 0xC8, 0xF1, 0xFF, 0xCC, 0x7F, 0x3F,
- 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x9C, 0xC7,
- 0x13, 0xE3, 0xFF, 0xFF, 0xCF, 0xF9, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7,
- 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF, 0xCF, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF,
- 0xCF, 0x9F, 0xCF, 0xFF, 0xCF, 0xC0, 0x3F, 0x3F, 0xFF, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x33, 0xFF, 0x3F,
- 0xC3, 0xE0, 0x3F, 0xC3, 0xFF, 0x3E, 0x03, 0xE3, 0xFF, 0xCF, 0x9F, 0x3F, 0xCC, 0x63, 0x06,
- 0x0C, 0x63, 0xFF, 0xCC, 0x7F, 0xCF, 0xFF, 0xE3, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
- 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xFC, 0xFF, 0xFF, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3C,
- 0x7F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC0, 0x01, 0x80, 0x03, 0xFF, 0xFF, 0xCC, 0xFF, 0x3F,
- 0xC3, 0x1F, 0x39, 0xCC, 0x63, 0xFF, 0xCE, 0x3F, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00,
- 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x1F, 0x9C, 0xE3, 0xF3, 0xC1, 0xFF, 0xFF, 0xCF, 0xF9,
- 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF,
- 0xCF, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0x8F, 0xC0,
- 0x3F, 0x1F, 0xF9, 0xF3, 0x80, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F,
- 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3C, 0x73, 0xFF, 0x3F, 0xC3, 0xC4, 0x3F, 0xC3, 0xFF, 0x3C,
- 0x43, 0xC7, 0xFF, 0xCF, 0x9F, 0x3F, 0xC8, 0xF1, 0x20, 0x4E, 0x07, 0xFF, 0xCE, 0x3F, 0xCF,
- 0xFF, 0xC7, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFC, 0x3F, 0xF3, 0xFC, 0x00,
- 0x00, 0x03, 0x80, 0x03, 0xFC, 0xF9, 0xFF, 0xFC, 0x00, 0xFF, 0x9F, 0x39, 0xC3, 0xFC, 0x3F,
- 0xC0, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x0C, 0xFF, 0x3F, 0xC3, 0x8F, 0x39, 0xCE, 0x07, 0x80,
- 0x0F, 0x1F, 0x3F, 0xC3, 0xFF, 0x3F, 0xC2, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6,
- 0x78, 0x00, 0xF1, 0xF1, 0x88, 0xFF, 0xFF, 0xCF, 0xF9, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xC0,
- 0x3F, 0xFF, 0xF1, 0xF3, 0x9C, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x0F, 0xF8, 0x13, 0x80, 0x00,
- 0x00, 0x00, 0x3F, 0xF3, 0xFC, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF9, 0xFF, 0xFC, 0x00,
- 0xF3, 0xFF, 0x39, 0xC3, 0x8C, 0x3F, 0xC0, 0x01, 0x3C, 0xC0, 0x01, 0x80, 0x0F, 0x9F, 0x3F,
- 0xC1, 0xF8, 0x30, 0xCF, 0x0F, 0x80, 0x0F, 0x1F, 0xCF, 0xFF, 0x8F, 0xFE, 0x7F, 0xFF, 0xFF,
- 0xFF, 0xFF, 0x00, 0x03, 0xFC, 0x3F, 0xF3, 0xFC, 0x00, 0x00, 0x03, 0x00, 0x03, 0xFC, 0xF9,
- 0xFF, 0xFC, 0x01, 0xFF, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0x00,
- 0x1C, 0xFF, 0x3F, 0xC3, 0xC7, 0x39, 0xCE, 0x07, 0x00, 0x0F, 0x8F, 0x3F, 0xC3, 0xFF, 0x3F,
- 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6, 0x70, 0x01, 0xF8, 0xF8, 0x1C, 0xFF,
- 0xFF, 0xCF, 0xF9, 0xF3, 0x9F, 0xC0, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0xF8, 0xF3, 0x9C, 0xF9,
- 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xCF,
- 0xFE, 0x0F, 0xFF, 0xFF, 0x07, 0xFC, 0x03, 0x9C, 0x00, 0x00, 0x03, 0x3F, 0xF3, 0xFC, 0x00,
- 0x00, 0x00, 0x30, 0x00, 0x00, 0xF9, 0xFF, 0xFC, 0x00, 0xF3, 0xFF, 0x30, 0xC3, 0x1C, 0x3F,
- 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0x9F, 0x3F, 0xC3, 0xFC, 0x39, 0xCF, 0x0F, 0x00,
- 0x0F, 0x8F, 0xCF, 0xFF, 0x1F, 0xFE, 0x79, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F,
- 0xF3, 0xFC, 0x3F, 0xCC, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x38, 0xFF, 0x9F, 0x39,
- 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0x3F, 0xFC, 0xFF, 0x3F, 0xC3, 0xE3, 0x39,
- 0xCC, 0x63, 0x3F, 0xCF, 0xC7, 0x3F, 0xC3, 0xFF, 0x3F, 0xC8, 0x9F, 0x00, 0x00, 0x00, 0x3F,
- 0xFF, 0xFF, 0x80, 0x13, 0x9F, 0xFC, 0x7E, 0x0F, 0xFF, 0xFF, 0xCF, 0xF9, 0xF1, 0x1F, 0xF9,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x73, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF, 0xC3, 0xFC, 0x3F,
- 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xCF, 0xFF, 0xCF, 0xFF, 0x0F, 0xC0, 0x3F, 0x0F, 0xFF,
- 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xF3, 0xFC, 0xF9,
- 0xFF, 0xFC, 0x3C, 0x73, 0xFF, 0x20, 0x42, 0x3C, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F, 0xCE, 0x07, 0x3F, 0xCF, 0xC7, 0xCF, 0xFE, 0x3F, 0xFE,
- 0x78, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xCC, 0xFF, 0x3F,
- 0xC0, 0x00, 0xF9, 0xFF, 0xFC, 0x3C, 0x7F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xC3, 0xFF, 0x3F, 0xFC, 0xFF, 0x3F, 0xC3, 0xF1, 0x39, 0xC8, 0xF1, 0x3F, 0xCF, 0xE3, 0x3F,
- 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x13, 0x9F, 0x8E,
- 0x3C, 0xE7, 0xFF, 0xFF, 0xCF, 0xF9, 0xF8, 0x3F, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
- 0x33, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF, 0xC3, 0xFC, 0x3F, 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F,
- 0xCF, 0x9F, 0xFF, 0xFF, 0x8F, 0xC0, 0x3F, 0x1F, 0xFF, 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F,
- 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xF3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x33, 0xFF, 0x06,
- 0x00, 0x7C, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xCC, 0x63, 0x3F, 0xCF, 0xE3, 0xCF, 0xFC, 0x7F, 0xFE, 0x7C, 0x63, 0xFF, 0xFF, 0xFF, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0x00, 0x00, 0x01, 0xE1, 0xFF, 0xF0, 0x3E,
- 0x3F, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
- 0xC3, 0xF8, 0x39, 0xC1, 0xF8, 0x3F, 0xC0, 0x00, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00,
- 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xE6, 0x70, 0x00, 0x8F, 0x1C, 0xE7, 0x3F, 0xFF, 0xC7, 0xF1,
- 0xF8, 0x3F, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF,
- 0xC3, 0xFC, 0x3F, 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xCF, 0x9F, 0xFF, 0xFF, 0xCF, 0xFF,
- 0xFF, 0x3F, 0x3F, 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F, 0xF3, 0xF8, 0x3F, 0xF3, 0xFF, 0x3F,
- 0xF3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3F, 0x13, 0xFF, 0x0F, 0x00, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xC3, 0xFC, 0x3F, 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F, 0xC8, 0xF1, 0x3F, 0xCF, 0xF1, 0xCF,
- 0xF8, 0xFF, 0xFE, 0x7E, 0x07, 0xFF, 0xFC, 0xFF, 0x00, 0x10, 0x01, 0x80, 0x08, 0x00, 0x80,
- 0x1C, 0xFF, 0x80, 0x03, 0xFF, 0xE1, 0xFF, 0xF0, 0x3F, 0x1F, 0x9F, 0x00, 0x10, 0x01, 0x80,
- 0x10, 0x01, 0x80, 0x08, 0x00, 0x80, 0x00, 0x00, 0x3F, 0xC3, 0xFC, 0x39, 0xC3, 0xFC, 0x3F,
- 0xC0, 0x00, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xE6,
- 0x78, 0x00, 0x8F, 0x9C, 0x07, 0x3F, 0xFF, 0xE3, 0xE3, 0xF1, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xFC, 0x3F,
- 0x83, 0xFF, 0x1F, 0x81, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
- 0xC3, 0xFC, 0x3F, 0xC1, 0xF8, 0x3F, 0xC0, 0x00, 0xC0, 0xF1, 0xFF, 0xE0, 0x7F, 0x0F, 0xFF,
- 0xF8, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFC, 0x00, 0xFF, 0xF3, 0xFF, 0xFF,
- 0xFF, 0xFF, 0x3F, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x00,
- 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xFF, 0xFF, 0x9F, 0xFF, 0xFE, 0x0F, 0x3F,
- 0xFF, 0xF3, 0xE7, 0xF3, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01,
- 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x18, 0x01, 0x80, 0x10, 0x03, 0x80, 0x00, 0x03, 0x80,
- 0x08, 0x00, 0x80, 0x03, 0xFC, 0x00, 0x0F, 0xFC, 0x3F, 0xC3, 0xFF, 0x3F, 0xC3, 0xFC, 0x80,
- 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x00, 0x00, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
- 0xC0, 0x00, 0xC0, 0xF3, 0xFF, 0xE0, 0x7F, 0x9F, 0xFF, 0xF9, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF,
- 0xFF, 0xFC, 0xFF, 0xFE, 0x00, 0xFF, 0xF3, 0xFF, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, 0x1F, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00,
- }};
-#endif // FONT5_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/font1.h
@@ -1,0 +1,56 @@
+#ifndef FONT1_H_
+#define FONT1_H_
+
+#include "inline_font.h"
+
+struct inline_font font_v1_small = {+ 470,
+ 7,
+ 5,
+ 7,
+ 0,
+ 0,
+ 3,
+ 24,
+ 566,
+ {+ 0x42, 0x4D, 0x36, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
+ 0x00, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x80, 0x00, 0x40, 0x34,
+ 0x01, 0x80, 0x00, 0x40, 0x12, 0x1F, 0xFF, 0xFE, 0x1F, 0xFC, 0x9E, 0x08, 0x10, 0x00, 0x00,
+ 0x8E, 0x8F, 0x9D, 0xEF, 0xC1, 0xD1, 0xFB, 0xA3, 0xF8, 0xC5, 0xD0, 0x6C, 0x7C, 0x47, 0x12,
+ 0x31, 0xF7, 0xC6, 0x1C, 0x03, 0xE0, 0xFF, 0xFF, 0xFF, 0x93, 0xF1, 0x77, 0x22, 0xEA, 0xC7,
+ 0xF0, 0x0C, 0x3E, 0x7F, 0xE3, 0xF1, 0xFF, 0xFE, 0x4F, 0x80, 0x00, 0x00, 0x15, 0xF9, 0xC8,
+ 0x02, 0x40, 0x08, 0x40, 0x02, 0x11, 0x24, 0x02, 0x10, 0xC4, 0x91, 0x0A, 0x10, 0x20, 0x20,
+ 0x10, 0x8C, 0x63, 0x18, 0x42, 0x31, 0x24, 0x65, 0x08, 0xC6, 0x30, 0x94, 0x42, 0x48, 0xAB,
+ 0x71, 0x0C, 0x04, 0x14, 0x00, 0x00, 0x8C, 0x61, 0x18, 0x10, 0x31, 0x20, 0xB4, 0x4A, 0xC6,
+ 0x3F, 0xFC, 0x02, 0x48, 0xD2, 0xAA, 0x0A, 0x22, 0x48, 0x80, 0x00, 0x80, 0x3E, 0x55, 0xD4,
+ 0x02, 0x40, 0x08, 0x00, 0x01, 0x19, 0x24, 0x02, 0x10, 0xC4, 0x91, 0x08, 0x00, 0x6F, 0xB0,
+ 0x97, 0x8C, 0x61, 0x18, 0x42, 0x71, 0x24, 0x69, 0x08, 0xC6, 0x30, 0xAC, 0x82, 0x48, 0xAA,
+ 0xAA, 0x0A, 0x04, 0x24, 0x00, 0x00, 0xFC, 0x61, 0x1F, 0x93, 0xF1, 0x20, 0xA8, 0x4A, 0xC6,
+ 0x31, 0x8C, 0x3E, 0x48, 0xCA, 0xA4, 0xF9, 0x22, 0x48, 0x80, 0x00, 0x80, 0x15, 0xF2, 0x24,
+ 0x02, 0x42, 0xBE, 0x0F, 0x80, 0x95, 0x27, 0xFF, 0xFF, 0xFC, 0x5F, 0xF8, 0x00, 0xE0, 0x38,
+ 0x55, 0xFF, 0xA1, 0x1F, 0x7A, 0x1F, 0x20, 0x71, 0x08, 0xCE, 0x3E, 0x8F, 0x9C, 0x48, 0xC6,
+ 0x24, 0x79, 0x04, 0x44, 0x00, 0x00, 0x0C, 0x61, 0x18, 0x92, 0x31, 0x20, 0xA4, 0x4A, 0xC6,
+ 0x31, 0x8C, 0x20, 0x48, 0xC6, 0xAA, 0x88, 0xA2, 0x4A, 0x80, 0x00, 0x80, 0x3F, 0x4D, 0x50,
+ 0x02, 0x41, 0x08, 0x00, 0x00, 0x53, 0x20, 0x43, 0x18, 0x40, 0x31, 0x8A, 0x10, 0x6F, 0xB0,
+ 0x37, 0x8C, 0x61, 0x18, 0x42, 0x11, 0x20, 0x69, 0x0A, 0xD6, 0x31, 0x8C, 0x60, 0x48, 0xC6,
+ 0x2A, 0x88, 0x84, 0x84, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0xA2, 0x4F, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF8, 0xC6, 0xB1, 0x8F, 0xE2, 0x48, 0xD8, 0x00, 0x82, 0x95, 0xFC, 0xC8,
+ 0x82, 0x42, 0x88, 0x00, 0x00, 0x31, 0xE0, 0x43, 0x18, 0x40, 0x31, 0x88, 0x00, 0x20, 0x22,
+ 0x31, 0x8C, 0x63, 0x18, 0x42, 0x31, 0x20, 0x65, 0x0D, 0xE6, 0x31, 0x8C, 0x60, 0x48, 0xC6,
+ 0x31, 0x88, 0x45, 0x04, 0x28, 0x04, 0x04, 0x00, 0x10, 0x10, 0x10, 0x00, 0x20, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x22, 0x48, 0xA4, 0x00, 0x82, 0x80, 0x40, 0x30,
+ 0x81, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x27, 0xFF, 0x1F, 0xFF, 0xEF, 0xF8, 0x00, 0x00, 0x01,
+ 0xCE, 0x77, 0x9D, 0xEF, 0xFD, 0xD1, 0xF8, 0x63, 0x08, 0xC5, 0xDE, 0x77, 0x9F, 0xF8, 0xC6,
+ 0x31, 0x8F, 0xC7, 0x0C, 0x10, 0x08, 0x04, 0x00, 0x10, 0x1C, 0x10, 0x20, 0xA0, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3E, 0x4F, 0x80, 0x00,
+ }};
+#endif // FONT1_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/font2.h
@@ -1,0 +1,85 @@
+#ifndef FONT2_H_
+#define FONT2_H_
+#include "inline_font.h"
+
+struct inline_font font_v1_large = {+ 752,
+ 9,
+ 8,
+ 9,
+ 0,
+ -40,
+ 4,
+ 22,
+ 1010,
+ {+ 0x42, 0x4D, 0xF2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
+ 0x00, 0x00, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xE7,
+ 0x7D, 0x84, 0xFF, 0xFC, 0x9F, 0xFF, 0xFF, 0x9F, 0xFF, 0x9F, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFC, 0x00, 0x00, 0xE7, 0x01, 0xFC, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xE7, 0x81, 0x3C, 0x01,
+ 0x81, 0x03, 0x00, 0x3F, 0x81, 0x3C, 0x00, 0x81, 0x3C, 0x00, 0x3C, 0x3C, 0x81, 0x3F, 0x84,
+ 0x3C, 0x81, 0xE7, 0x81, 0xE7, 0x3C, 0x3C, 0xE7, 0x00, 0xF0, 0xFF, 0x0F, 0xFF, 0x80, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x24, 0x3C, 0x00,
+ 0x3F, 0xFC, 0x3F, 0x00, 0x80, 0x00, 0x3F, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xE7, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF, 0xDB, 0x00, 0x38, 0x39, 0xFF, 0xF9, 0xCF, 0xFF, 0xFF, 0xCF, 0xFF,
+ 0xFF, 0x3F, 0x3C, 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C, 0xE7, 0x3C, 0xFC, 0xFF, 0x9F, 0xFC,
+ 0xFF, 0x9F, 0xFF, 0x3F, 0x3C, 0x3C, 0x3C, 0x39, 0x3F, 0x3F, 0x3C, 0x3C, 0xE7, 0x3C, 0x39,
+ 0x3F, 0x3C, 0x3C, 0x3C, 0x3F, 0x39, 0x39, 0x3C, 0xE7, 0x3C, 0xC3, 0x18, 0x3C, 0xE7, 0x3F,
+ 0xF3, 0xFC, 0xCF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F, 0x9F, 0xFC, 0x3C, 0xE7,
+ 0xFC, 0x39, 0xCF, 0x24, 0x3C, 0x3C, 0x3F, 0xFC, 0x3F, 0xFC, 0x9F, 0x3C, 0x1F, 0x24, 0x99,
+ 0xFC, 0x9F, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0xE4, 0x9D, 0x30, 0xFF,
+ 0xF3, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0x9F, 0x3C, 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C,
+ 0xE7, 0x3C, 0xFC, 0xE7, 0x9F, 0xF8, 0x81, 0x8F, 0xE7, 0x21, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F,
+ 0x3F, 0x3C, 0x3C, 0xE7, 0x3C, 0x33, 0x3F, 0x3C, 0x3C, 0x3C, 0x3F, 0x32, 0x33, 0xFC, 0xE7,
+ 0x3C, 0x99, 0x00, 0x99, 0xE7, 0x9F, 0xF3, 0xF9, 0xCF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3F,
+ 0x3C, 0x3F, 0x9F, 0xFC, 0x3C, 0xE7, 0xFC, 0x33, 0xCF, 0x24, 0x3C, 0x3C, 0x3F, 0xFC, 0x3F,
+ 0xFC, 0x9F, 0x3C, 0x0F, 0x24, 0xC3, 0xFC, 0xCF, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF,
+ 0xFF, 0xDB, 0xE4, 0xCF, 0x87, 0xFF, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xCF, 0x3C,
+ 0xE7, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C, 0xE7, 0x3C, 0xFC, 0xFF, 0xFF, 0xF0, 0xFF, 0x87, 0xE7,
+ 0x24, 0x3C, 0x3C, 0x3F, 0x3C, 0x3F, 0x3F, 0x3C, 0x3C, 0xE7, 0xFC, 0x27, 0x3F, 0x3C, 0x38,
+ 0x3C, 0x3F, 0x24, 0x27, 0xFC, 0xE7, 0x3C, 0x3C, 0x24, 0xC3, 0xE7, 0xCF, 0xF3, 0xF3, 0xCF,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x3C, 0x3F, 0x3C, 0x00, 0x9F, 0x00, 0x3C, 0xE7, 0xFC, 0x07, 0xCF,
+ 0x24, 0x3C, 0x3C, 0x00, 0x00, 0x3F, 0x00, 0x9F, 0x3C, 0x27, 0x24, 0xE7, 0x00, 0xE7, 0x3C,
+ 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF, 0xFF, 0xDB, 0x00, 0xE7, 0xC7, 0xFF, 0xF3, 0xE7, 0x33,
+ 0x81, 0xFF, 0x81, 0xFF, 0xE7, 0x24, 0xE7, 0x00, 0x80, 0x00, 0x00, 0x00, 0xF3, 0x81, 0x00,
+ 0xFF, 0xFF, 0xE0, 0xFF, 0x83, 0xE1, 0x24, 0x00, 0x01, 0x3F, 0x3C, 0x03, 0x03, 0x30, 0x00,
+ 0xE7, 0xFC, 0x0F, 0x3F, 0x3C, 0x30, 0x3C, 0x01, 0x3C, 0x01, 0x81, 0xE7, 0x3C, 0x3C, 0x3C,
+ 0xE7, 0x81, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0x3C, 0x3C, 0x9F,
+ 0x3C, 0x3C, 0xE7, 0xFC, 0x33, 0xCF, 0x24, 0x3C, 0x3C, 0x3C, 0x3C, 0x3F, 0x3F, 0x9F, 0x3C,
+ 0x33, 0x24, 0xC3, 0x3C, 0xF3, 0x3C, 0xE7, 0x3C, 0x23, 0x00, 0x00, 0xCF, 0xFF, 0x81, 0x27,
+ 0xF3, 0x93, 0xFF, 0xF3, 0xE7, 0x87, 0xE7, 0xFF, 0xFF, 0xFF, 0xF3, 0x3C, 0xE7, 0xFC, 0xFC,
+ 0x3C, 0x3F, 0x3F, 0xF9, 0x3C, 0x3C, 0xE7, 0x9F, 0xF0, 0xFF, 0x87, 0xFC, 0x20, 0x3C, 0x3B,
+ 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3C, 0xE7, 0xFC, 0x27, 0x3F, 0x24, 0x24, 0x3C, 0x3C, 0x3C,
+ 0x3C, 0x3F, 0xE7, 0x3C, 0x3C, 0x3C, 0xC3, 0x3C, 0xF3, 0xF3, 0xCF, 0xCF, 0xFF, 0xFF, 0xFF,
+ 0xFC, 0x3C, 0x3F, 0x3C, 0x3C, 0x00, 0x3C, 0x3C, 0xE7, 0xFC, 0x39, 0xCF, 0x24, 0x3C, 0x3C,
+ 0x3C, 0x3C, 0x3F, 0x3F, 0x9F, 0x3C, 0x39, 0x24, 0x99, 0x3C, 0xF9, 0x3C, 0xE7, 0x3C, 0x89,
+ 0x00, 0x00, 0xCF, 0xC9, 0xDB, 0x27, 0xB9, 0x93, 0xE7, 0xF3, 0xE7, 0xCF, 0xE7, 0xFF, 0xFF,
+ 0xFF, 0xF9, 0x3C, 0xE7, 0xFC, 0xFC, 0x3C, 0x3F, 0x3F, 0xFC, 0x3C, 0x3C, 0xFF, 0xFF, 0xF8,
+ 0x81, 0x8F, 0xFC, 0x3C, 0x3C, 0x39, 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3C, 0xE7, 0xFC, 0x33,
+ 0x3F, 0x00, 0x0C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3F, 0xE7, 0x3C, 0x3C, 0x3C, 0x99, 0x3C, 0xF9,
+ 0xF3, 0x9F, 0xCF, 0x27, 0xFF, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x87,
+ 0x80, 0x3C, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x24, 0x3C,
+ 0x3C, 0x00, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF, 0xC9, 0xFF, 0x00, 0x1C, 0xC7, 0xE7,
+ 0xF9, 0xCF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3C, 0xE7, 0xFC, 0xFC, 0x3C, 0x3F, 0x3F,
+ 0xFC, 0x9C, 0x3C, 0xFF, 0xFF, 0xFC, 0xFF, 0x9F, 0x3C, 0x3C, 0x99, 0x39, 0x3C, 0x39, 0x3F,
+ 0x3F, 0x3C, 0x3C, 0xE7, 0xFC, 0x39, 0x3F, 0x18, 0x1C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xE7,
+ 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xF3, 0x3F, 0xCF, 0x8F, 0xFF, 0x9F, 0xFF, 0x3F, 0xFF,
+ 0xFC, 0xFF, 0x9F, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0xE7, 0x3C, 0xFF, 0x00, 0x00, 0xCF,
+ 0xC9, 0xFF, 0xE7, 0xBE, 0xFF, 0xE7, 0xFC, 0x9F, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0x87, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
+ 0x81, 0xC3, 0x03, 0x81, 0x03, 0x00, 0x00, 0x81, 0x3C, 0x00, 0x00, 0x3C, 0x3F, 0x3C, 0x3C,
+ 0x81, 0x01, 0x81, 0x01, 0x81, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0xF0, 0xFF, 0x0F,
+ 0xDF, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0xFC, 0xFF, 0x80, 0xFF, 0x3F, 0xE7, 0xFC, 0x3F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0xE7, 0x00, 0xFF, 0x00, 0x00,
+ }};
+#endif // FONT2_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/font3.h
@@ -1,0 +1,93 @@
+#ifndef FONT3_H_
+#define FONT3_H_
+
+#include "inline_font.h"
+
+struct inline_font font_v2_small = {+ 846,
+ 9,
+ 9,
+ 9,
+ 0,
+ -2,
+ 5,
+ 38,
+ 1118,
+ {+ 0x42, 0x4D, 0x5E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
+ 0x00, 0x00, 0x00, 0x4E, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0x03, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x7E,
+ 0xF7, 0xC4, 0x3B, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFD, 0xFE, 0x00, 0x00,
+ 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xDE, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x30, 0x03, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0,
+ 0x07, 0xF3, 0xF8, 0x00, 0xFF, 0x01, 0x3F, 0x80, 0x1E, 0xF0, 0x07, 0xBD, 0xFC, 0xFE, 0x00,
+ 0x00, 0x3C, 0x7F, 0xEE, 0x3F, 0xFC, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC,
+ 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x07, 0x73, 0xF8, 0x00, 0xFF, 0xFF, 0x3F, 0xC0, 0x00,
+ 0x00, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x07, 0xFC, 0x00, 0x00, 0xFF,
+ 0xFF, 0xF7, 0x60, 0x0B, 0xD3, 0xD7, 0xFF, 0xFD, 0xBF, 0xFF, 0xFD, 0xEF, 0xFF, 0xFF, 0xFE,
+ 0xFE, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB, 0xFC, 0xFE, 0xFF, 0x7F, 0xDF, 0xFF,
+ 0x3F, 0xFF, 0x3F, 0xFE, 0xFF, 0x7F, 0x3F, 0x9F, 0xEF, 0xD7, 0xFB, 0xFD, 0xFC, 0xFE, 0xF7,
+ 0xFF, 0x9F, 0xAF, 0xF7, 0xF3, 0xF1, 0xFC, 0xFF, 0x7E, 0xBF, 0x7F, 0xDE, 0xF7, 0xF7, 0x5C,
+ 0xF9, 0x7D, 0xFF, 0x5F, 0xFD, 0xFF, 0xDF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x9F, 0xEF,
+ 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF7, 0x73, 0xF9, 0xFC, 0xFF, 0xFF,
+ 0x3F, 0xFF, 0xCF, 0xF7, 0xF3, 0x7D, 0xDD, 0x7D, 0xFF, 0x3F, 0xDF, 0xCF, 0xF7, 0xF7, 0xFC,
+ 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x1E, 0xED, 0xC3, 0xEF, 0xFF, 0xFB, 0xDF, 0xFF, 0xFD, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x7E, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB, 0xFC, 0xFE, 0xFF,
+ 0x3F, 0xDF, 0xFE, 0x38, 0x0F, 0x1F, 0xDE, 0x80, 0x7F, 0x3F, 0x9F, 0xEF, 0xE7, 0xFB, 0xFD,
+ 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0x6F, 0xF7, 0xF3, 0xE9, 0xFC, 0xFF, 0x7D, 0x3E, 0xFF, 0xDE,
+ 0xF7, 0xF6, 0xED, 0x75, 0xBB, 0xFF, 0x6F, 0xFD, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F,
+ 0x3F, 0x9F, 0xEF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0x6F, 0xF7, 0x73, 0xF9,
+ 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xCF, 0xF7, 0xF3, 0xBD, 0xDD, 0xBB, 0xFF, 0x4F, 0xDF, 0xCF,
+ 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x7E, 0xEE, 0xFB, 0xD7, 0xFF, 0xFB, 0xDF,
+ 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFE, 0xF7, 0xBF, 0xFF, 0xDF, 0xEF, 0xF3, 0xFB,
+ 0xFC, 0xFE, 0xFF, 0x7F, 0xFF, 0xFC, 0x3F, 0xFF, 0x0F, 0xDE, 0xBA, 0x7F, 0x3F, 0x9F, 0xEF,
+ 0xE7, 0xFB, 0xFD, 0xFC, 0xFE, 0xF7, 0xFF, 0x9E, 0xEF, 0xF7, 0xF3, 0xD9, 0xFC, 0xFF, 0x7B,
+ 0x3D, 0xFF, 0xDE, 0xF7, 0xF5, 0xF5, 0xAD, 0xD7, 0xFF, 0x77, 0xFD, 0xFF, 0x7F, 0xBF, 0xFF,
+ 0xFF, 0xFF, 0x00, 0x3F, 0x9F, 0xEF, 0xE0, 0x00, 0x0C, 0x00, 0xFE, 0xF7, 0xFF, 0x80, 0xEF,
+ 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xC0, 0x0F, 0xF7, 0xF3, 0xDD, 0xDD, 0xC7, 0x00,
+ 0x71, 0xDF, 0xCF, 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x60, 0x0F, 0x7D, 0xBB,
+ 0xFF, 0xFB, 0xDF, 0xDD, 0xE0, 0x3F, 0xF8, 0x0F, 0xFF, 0xDE, 0xEE, 0xF7, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x3F, 0xFF, 0x07, 0xC0, 0x82, 0x00,
+ 0x00, 0x1F, 0xEF, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0xF7, 0xFF, 0x81, 0xEF, 0xF7, 0x73, 0xB9,
+ 0xFC, 0x00, 0x7F, 0x00, 0x00, 0x1E, 0xF7, 0xF3, 0xF9, 0xDD, 0xEF, 0x00, 0x7B, 0xFD, 0xFE,
+ 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x9F, 0xEF, 0xE7, 0xF3, 0xFD, 0xFC, 0xFE, 0xF7,
+ 0xFF, 0x9F, 0x6F, 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xEF, 0xF7, 0xF3, 0xED,
+ 0xDD, 0xBB, 0x7F, 0x7E, 0x5F, 0xCF, 0xF7, 0xF3, 0x3C, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0x6E,
+ 0xFF, 0xBE, 0x7F, 0xFF, 0xFB, 0xDF, 0xEB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0xFE, 0xF7,
+ 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F, 0x7F, 0xFF, 0xFC, 0x3F, 0xFF, 0x0F,
+ 0xFC, 0xFA, 0x7F, 0x3E, 0xDF, 0xEF, 0xE7, 0xFB, 0xFD, 0xFE, 0xFE, 0xF7, 0xFF, 0x9E, 0xEF,
+ 0xF6, 0xB3, 0x79, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE, 0xF7, 0xF3, 0xF9, 0xFD, 0xD7, 0x7F,
+ 0x7D, 0xFD, 0xFD, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x9F, 0xEF, 0xE7, 0xF3, 0xFD,
+ 0xFC, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF7, 0x73, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xEF,
+ 0xF7, 0xF3, 0xF5, 0xDD, 0x7D, 0x7F, 0x7F, 0x9F, 0xCF, 0xF7, 0xF4, 0xDC, 0x00, 0x00, 0x7F,
+ 0xB7, 0xC0, 0x0E, 0xF1, 0xDD, 0xBD, 0xFF, 0xFB, 0xDF, 0xF7, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF6, 0xFE, 0xF7, 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F, 0x3F, 0xDF, 0xFE,
+ 0x38, 0x0F, 0x1F, 0xFC, 0x82, 0x7F, 0x3E, 0xDF, 0xEF, 0xE7, 0xFB, 0xFD, 0xFE, 0xFE, 0xF7,
+ 0xFF, 0x9F, 0x6F, 0xF5, 0xD2, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE, 0xF7, 0xF3, 0xF9,
+ 0xFD, 0xBB, 0x7F, 0x7E, 0xFD, 0xFB, 0xFF, 0xBB, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0xCF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0xF3, 0xF9, 0xDC, 0xFE, 0x7F, 0x00, 0x1F, 0xCF, 0xF7, 0xF7, 0xFC,
+ 0x00, 0x00, 0x7F, 0xB7, 0xF7, 0x60, 0x05, 0xED, 0xBD, 0xFF, 0xFD, 0xBF, 0xEB, 0xFD, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFA, 0xFE, 0xF7, 0xFF, 0xBF, 0xCF, 0xE7, 0xFB, 0xFF, 0xFC, 0xFE, 0x7F,
+ 0x7F, 0xFF, 0xFF, 0x3F, 0xFF, 0x3D, 0xFC, 0xFE, 0x7F, 0x3E, 0xDF, 0xEF, 0xD7, 0xFB, 0xFD,
+ 0xFE, 0xFE, 0xF7, 0xFF, 0x9F, 0xAF, 0xF3, 0xE1, 0xF9, 0xFC, 0xFE, 0x7F, 0x3F, 0x9F, 0xFE,
+ 0xF7, 0xF3, 0xF9, 0xFD, 0x7D, 0x7F, 0x7F, 0x7D, 0xF7, 0xFF, 0xBD, 0x7F, 0xFF, 0xBF, 0xFF,
+ 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xCF,
+ 0xF7, 0xF7, 0xFC, 0x00, 0x00, 0x7F, 0xB7, 0xF7, 0x7E, 0xF1, 0xF6, 0x7D, 0xFF, 0xFE, 0x7F,
+ 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x07, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0x9F, 0xCF, 0xF7, 0xF3, 0xF8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0xF3, 0xF9, 0xFC, 0xFE, 0x7F, 0x00, 0x3C, 0x6F, 0xFE, 0x3E, 0xFF,
+ 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xC0, 0x0F, 0xF0, 0x07, 0xFC, 0x00, 0x00,
+ }};
+#endif // FONT3_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/font4.h
@@ -1,0 +1,108 @@
+#ifndef FONT4_H_
+#define FONT4_H_
+
+#include "inline_font.h"
+
+struct inline_font font_v2_large = {+ 940,
+ 10,
+ 10,
+ 10,
+ 0,
+ -2,
+ 4,
+ 38,
+ 1346,
+ {+ 0x42, 0x4D, 0x42, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
+ 0x00, 0x00, 0x00, 0xAC, 0x03, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0x04, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCF,
+ 0xCF, 0x3E, 0x60, 0xCF, 0xFF, 0xF9, 0x3F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xE7, 0xF3, 0xFC,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x3F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x03, 0x3F, 0x00, 0x18, 0x00, 0x01, 0x80, 0x0F, 0xF8, 0x04,
+ 0xFC, 0x00, 0x00, 0x13, 0xF2, 0x00, 0x3F, 0x0F, 0xC8, 0x04, 0xFF, 0x83, 0x0F, 0xC0, 0x07,
+ 0xCF, 0x80, 0x7C, 0xF3, 0xF0, 0xFC, 0x00, 0x40, 0x0F, 0x0F, 0xFC, 0xE1, 0xFF, 0xF0, 0x03,
+ 0xFF, 0x80, 0x00, 0x18, 0x02, 0x00, 0x80, 0x33, 0xF0, 0x04, 0xFC, 0x00, 0x00, 0x13, 0xE0,
+ 0x00, 0x33, 0x0F, 0xC8, 0x04, 0xFF, 0xFF, 0x0F, 0xF0, 0x07, 0x80, 0x80, 0x43, 0xF8, 0x00,
+ 0xFC, 0x00, 0x40, 0x00, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCC,
+ 0x01, 0x1E, 0x40, 0x0F, 0xFF, 0xF1, 0x1F, 0xFF, 0xFF, 0x3C, 0xFF, 0xFF, 0xE7, 0xF1, 0xFC,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x3F, 0xFC, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x03, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00,
+ 0xFC, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x3F, 0x0F, 0x80, 0x00, 0xFF, 0x06, 0x0F, 0x80, 0x03,
+ 0xCF, 0x00, 0x38, 0x71, 0xE0, 0x78, 0x00, 0x00, 0x0F, 0x0F, 0xF8, 0xE1, 0xFF, 0xF0, 0x03,
+ 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xF0, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xC4,
+ 0x00, 0x33, 0x0F, 0xC0, 0x00, 0xFF, 0xFF, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x01, 0xF0, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00,
+ 0x00, 0x8F, 0xCE, 0x1F, 0xFF, 0xE3, 0x8F, 0xFF, 0xFF, 0x3C, 0xFF, 0xFF, 0xFF, 0xF8, 0xFC,
+ 0xFC, 0xF3, 0xCF, 0xFF, 0xF3, 0xFC, 0xFF, 0x0F, 0xCF, 0xF0, 0xFC, 0xFF, 0x33, 0xFC, 0xFF,
+ 0xE7, 0x80, 0x79, 0xFF, 0xFC, 0xFF, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xF0,
+ 0xFC, 0xF3, 0xFF, 0xC3, 0xC4, 0xFF, 0x3F, 0x0F, 0x03, 0xF0, 0xFF, 0x3C, 0x0F, 0x1F, 0xF3,
+ 0xCF, 0x3F, 0x30, 0x30, 0xC2, 0x31, 0xFF, 0x23, 0xFF, 0x3F, 0xF1, 0xF9, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xF3, 0xFF, 0xF0, 0xFC, 0xF3, 0xFF, 0xC3, 0x8F,
+ 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0x01, 0x80, 0x0F, 0xFF, 0xF3, 0x3F, 0x3F, 0x08, 0xF3, 0x32,
+ 0x31, 0xFF, 0x03, 0xF3, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x03,
+ 0xCC, 0xC7, 0xCE, 0x1F, 0xFF, 0xE7, 0xCF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C,
+ 0xFC, 0xF3, 0xCF, 0xFF, 0xF3, 0xFC, 0xFF, 0x0F, 0xCF, 0xF0, 0xFC, 0xFF, 0x33, 0xFC, 0xFF,
+ 0xC7, 0x80, 0x78, 0xFF, 0x3C, 0xC0, 0x3F, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xF0,
+ 0xFC, 0xF3, 0xFF, 0xC3, 0x8C, 0xFF, 0x3F, 0x0E, 0x03, 0xF0, 0xFF, 0x38, 0x0E, 0x3F, 0xF3,
+ 0xCF, 0x3F, 0x23, 0x10, 0x03, 0x03, 0xFF, 0x31, 0xFF, 0x3F, 0xE3, 0xF9, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x0F, 0xC3, 0xFC, 0xFC, 0x00, 0x33, 0xF8, 0x00, 0xFC, 0xF3, 0xFF, 0xC0, 0x1F,
+ 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x03, 0x3F, 0x3F, 0x0C, 0x73, 0x33,
+ 0x03, 0x80, 0x20, 0x73, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCE,
+ 0x00, 0xE3, 0xC0, 0x0F, 0xFF, 0xE7, 0xCF, 0xCE, 0x78, 0x07, 0xFF, 0x80, 0x7F, 0xFE, 0x3C,
+ 0xCC, 0xF3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
+ 0x87, 0xFF, 0xF8, 0x7F, 0x04, 0xC0, 0x00, 0x00, 0x03, 0xFC, 0xFC, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0xF3, 0xFF, 0xC0, 0x1C, 0xFF, 0x33, 0x0C, 0x43, 0xF0, 0x01, 0x31, 0x00, 0x18, 0x03,
+ 0xCF, 0x3F, 0x07, 0x82, 0x13, 0x87, 0x80, 0x38, 0xFF, 0x3F, 0xC7, 0xF9, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x0F, 0xC3, 0xFC, 0xFC, 0x00, 0x33, 0xF0, 0x00, 0xFC, 0xF3, 0xFF, 0xC0, 0x3F,
+ 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xF0, 0x07, 0x3F, 0x3F, 0x0E, 0x33, 0x33,
+ 0x03, 0x00, 0x38, 0x13, 0xF0, 0xFF, 0x3F, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xCC,
+ 0x01, 0xF1, 0xE0, 0xCF, 0xFF, 0xE7, 0xCF, 0xC4, 0x78, 0x07, 0xFF, 0x80, 0x7F, 0xFF, 0x1C,
+ 0xCC, 0xF3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
+ 0x07, 0xFF, 0xF8, 0x3F, 0x00, 0xCC, 0x00, 0x00, 0x33, 0xFC, 0xFC, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0xF3, 0xFF, 0xC0, 0x1C, 0xFF, 0x21, 0x08, 0xC3, 0xF0, 0x00, 0x33, 0x00, 0x00, 0x07,
+ 0xCF, 0x3F, 0x0F, 0xC3, 0x33, 0x87, 0x00, 0x3C, 0x7F, 0x3F, 0x8F, 0xF9, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x0F, 0xC3, 0xFC, 0xFC, 0x3F, 0x00, 0x33, 0xF0, 0xFC, 0xF3, 0xFF, 0xC3, 0x1F,
+ 0xCF, 0x33, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xF3, 0xFF, 0x3F, 0x3F, 0x0F, 0x13, 0x32,
+ 0x31, 0x3F, 0x3F, 0x03, 0xF0, 0xFF, 0x3F, 0x00, 0x70, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00,
+ 0xCF, 0xF8, 0xE6, 0x7F, 0xFF, 0xE7, 0xCF, 0xF1, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x8C,
+ 0xFC, 0xF3, 0xFF, 0xCF, 0xF0, 0xFC, 0x3F, 0xCF, 0xFF, 0xF0, 0xFC, 0x3F, 0x33, 0xFC, 0xFF,
+ 0x87, 0x80, 0x78, 0x7F, 0xF0, 0xCC, 0x3F, 0x0F, 0x93, 0xFC, 0xFC, 0x3F, 0xCF, 0xF3, 0xFC,
+ 0xFC, 0xF3, 0xFF, 0xC3, 0x8C, 0xFF, 0x00, 0x01, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF,
+ 0xCF, 0x3F, 0x0F, 0xC3, 0xF3, 0x03, 0x3F, 0x3E, 0x3F, 0x3F, 0x1F, 0xF9, 0xE6, 0x7F, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC3, 0xFF, 0x03, 0x8F,
+ 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0F, 0x83, 0x30,
+ 0x78, 0x3F, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0x22, 0x70, 0x00, 0x00, 0x3F, 0xC9, 0xF0, 0x00,
+ 0x00, 0xFC, 0x66, 0x73, 0xFF, 0xE3, 0x8F, 0xF1, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC4,
+ 0xFC, 0xF3, 0xFF, 0xCF, 0xF0, 0xFC, 0x3F, 0xCF, 0xFF, 0xF0, 0xFC, 0x3F, 0x33, 0xFC, 0xFF,
+ 0xC7, 0x80, 0x78, 0xF3, 0xF0, 0xFC, 0x3F, 0x0F, 0x93, 0xFC, 0xF8, 0x3F, 0xCF, 0xF3, 0xFC,
+ 0xFC, 0xF3, 0xFF, 0xC3, 0xC4, 0xFF, 0x0C, 0x03, 0xC3, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF,
+ 0xCF, 0x3F, 0x0F, 0xC3, 0xF2, 0x31, 0x3F, 0x3F, 0x1F, 0x3E, 0x3F, 0xF9, 0xE0, 0x7F, 0xFF,
+ 0x3F, 0x00, 0x40, 0x18, 0x02, 0x00, 0x80, 0x73, 0xF8, 0x00, 0x01, 0xC3, 0xFF, 0x03, 0xC7,
+ 0xCF, 0x00, 0x40, 0x18, 0x04, 0x01, 0x80, 0x20, 0x08, 0x00, 0x00, 0x3F, 0x0F, 0xC3, 0x30,
+ 0xFC, 0x3F, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xC9, 0xFC, 0xCE,
+ 0x00, 0x9E, 0x20, 0x73, 0xFF, 0xF1, 0x1F, 0xC4, 0x7F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
+ 0x00, 0x03, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
+ 0xE7, 0xFF, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xFC, 0x00, 0x3F, 0xC3, 0xE0, 0xFF, 0x1E, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x0F, 0xC3, 0xF0, 0x78, 0x3F, 0x00, 0x0F, 0x0C, 0x7F, 0xE1, 0xF0, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0xFF, 0xF0, 0x0F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,
+ 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xC9, 0xFC, 0xCF,
+ 0xCF, 0x9F, 0x30, 0xF3, 0xFF, 0xF9, 0x3F, 0xCE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+ 0x00, 0x03, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF8, 0x06, 0x01, 0x80, 0x40, 0x38, 0x00, 0x03, 0x80, 0x20, 0x08, 0x00,
+ 0xFC, 0x00, 0x3F, 0xC3, 0xF0, 0xFF, 0x3F, 0x0F, 0xC8, 0x04, 0x01, 0x80, 0x40, 0x18, 0x00,
+ 0x00, 0x3F, 0x0F, 0xC3, 0xF0, 0xFC, 0x3F, 0x00, 0x0F, 0x0C, 0xFF, 0xE1, 0xF9, 0xFF, 0xFE,
+ 0x7F, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0xFF, 0xF8, 0x0F, 0xFC, 0xFF, 0xF3, 0xFF, 0xC3, 0xFF,
+ 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x3F, 0xF0, 0x00, 0x00,
+ }};
+#endif // FONT4_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/font5.h
@@ -1,0 +1,143 @@
+#ifndef FONT5_H_
+#define FONT5_H_
+
+#include "inline_font.h"
+
+struct inline_font font_v2_huge = {+ 1128,
+ 12,
+ 12,
+ 12,
+ 0,
+ -54,
+ 4,
+ 24,
+ 1874,
+ {+ 0x42, 0x4D, 0x52, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x7C,
+ 0x00, 0x00, 0x00, 0x68, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x9F, 0xFF, 0xFC, 0x1C, 0xFF, 0xFF, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF,
+ 0xF9, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0xFF,
+ 0xC0, 0x00, 0xFF, 0xCF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF8, 0x03, 0x3F,
+ 0xC0, 0x01, 0x80, 0x00, 0x01, 0x80, 0x03, 0xFF, 0x80, 0x13, 0xFC, 0x00, 0x00, 0x01, 0x3F,
+ 0xC8, 0x00, 0x3F, 0xC3, 0xFC, 0x80, 0x13, 0xFF, 0x80, 0xC3, 0xFC, 0x00, 0x1F, 0x9F, 0x80,
+ 0x1F, 0x9F, 0x3F, 0xC3, 0xFC, 0x00, 0x10, 0x00, 0xC0, 0xFF, 0xFC, 0xE0, 0x7F, 0xFF, 0x00,
+ 0x0F, 0xFF, 0x80, 0x00, 0x01, 0x80, 0x08, 0x00, 0x80, 0x0C, 0xFF, 0x00, 0x13, 0xFC, 0x00,
+ 0x00, 0x01, 0x3F, 0x80, 0x00, 0x39, 0xC3, 0xFC, 0x80, 0x13, 0xFF, 0xFF, 0xC3, 0xFF, 0x00,
+ 0x1E, 0x00, 0x80, 0x10, 0xFF, 0x80, 0x03, 0xFC, 0x00, 0x10, 0x00, 0x00, 0x03, 0xFF, 0x00,
+ 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6, 0x70, 0x01, 0x9F, 0x18, 0x08, 0xFF,
+ 0xFF, 0xE3, 0xE3, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF9, 0xFF, 0x1F, 0xF0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0xFF, 0xCF, 0xFF, 0x8F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF0, 0x03, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0xC3, 0xF8, 0x00,
+ 0x03, 0xFF, 0x01, 0x83, 0xF8, 0x00, 0x0F, 0x9F, 0x00, 0x0F, 0x0F, 0x1F, 0x81, 0xF8, 0x00,
+ 0x00, 0x00, 0xC0, 0xFF, 0xF8, 0xE0, 0x7F, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x10, 0x00, 0x39,
+ 0xC3, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xC3, 0xFF, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7F, 0x00,
+ 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0xFF,
+ 0xFF, 0xFF, 0xE6, 0x70, 0x00, 0x8F, 0x11, 0xC1, 0xFF, 0xFF, 0xC7, 0xF1, 0xFF, 0xFF, 0xF9,
+ 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF, 0xCF, 0xFC, 0xFF,
+ 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xCF, 0x9F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9,
+ 0xFF, 0xFC, 0x3F, 0x13, 0xFF, 0x3F, 0xC3, 0xF0, 0x3F, 0xC3, 0xFF, 0x3F, 0x03, 0xF1, 0xFF,
+ 0xCF, 0x9F, 0x3F, 0xCE, 0x07, 0x0F, 0x08, 0xF1, 0xFF, 0xC8, 0xFF, 0xCF, 0xFF, 0xF1, 0xFE,
+ 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xFC, 0xFF, 0xFF,
+ 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x3F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0xFF,
+ 0xC3, 0xFF, 0xFF, 0xCC, 0xFF, 0x3F, 0xC2, 0x3F, 0x39, 0xC8, 0xF1, 0xFF, 0xCC, 0x7F, 0x3F,
+ 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x9C, 0xC7,
+ 0x13, 0xE3, 0xFF, 0xFF, 0xCF, 0xF9, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7,
+ 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF, 0xCF, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF,
+ 0xCF, 0x9F, 0xCF, 0xFF, 0xCF, 0xC0, 0x3F, 0x3F, 0xFF, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x33, 0xFF, 0x3F,
+ 0xC3, 0xE0, 0x3F, 0xC3, 0xFF, 0x3E, 0x03, 0xE3, 0xFF, 0xCF, 0x9F, 0x3F, 0xCC, 0x63, 0x06,
+ 0x0C, 0x63, 0xFF, 0xCC, 0x7F, 0xCF, 0xFF, 0xE3, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
+ 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xFC, 0xFF, 0xFF, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3C,
+ 0x7F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC0, 0x01, 0x80, 0x03, 0xFF, 0xFF, 0xCC, 0xFF, 0x3F,
+ 0xC3, 0x1F, 0x39, 0xCC, 0x63, 0xFF, 0xCE, 0x3F, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00,
+ 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x1F, 0x9C, 0xE3, 0xF3, 0xC1, 0xFF, 0xFF, 0xCF, 0xF9,
+ 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF3, 0xFC, 0xF9, 0xF3, 0xFF, 0xFF,
+ 0xCF, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xC3, 0xFC, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0x8F, 0xC0,
+ 0x3F, 0x1F, 0xF9, 0xF3, 0x80, 0x3F, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F,
+ 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3C, 0x73, 0xFF, 0x3F, 0xC3, 0xC4, 0x3F, 0xC3, 0xFF, 0x3C,
+ 0x43, 0xC7, 0xFF, 0xCF, 0x9F, 0x3F, 0xC8, 0xF1, 0x20, 0x4E, 0x07, 0xFF, 0xCE, 0x3F, 0xCF,
+ 0xFF, 0xC7, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFC, 0x3F, 0xF3, 0xFC, 0x00,
+ 0x00, 0x03, 0x80, 0x03, 0xFC, 0xF9, 0xFF, 0xFC, 0x00, 0xFF, 0x9F, 0x39, 0xC3, 0xFC, 0x3F,
+ 0xC0, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x0C, 0xFF, 0x3F, 0xC3, 0x8F, 0x39, 0xCE, 0x07, 0x80,
+ 0x0F, 0x1F, 0x3F, 0xC3, 0xFF, 0x3F, 0xC2, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6,
+ 0x78, 0x00, 0xF1, 0xF1, 0x88, 0xFF, 0xFF, 0xCF, 0xF9, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xC0,
+ 0x3F, 0xFF, 0xF1, 0xF3, 0x9C, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x0F, 0xF8, 0x13, 0x80, 0x00,
+ 0x00, 0x00, 0x3F, 0xF3, 0xFC, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF9, 0xFF, 0xFC, 0x00,
+ 0xF3, 0xFF, 0x39, 0xC3, 0x8C, 0x3F, 0xC0, 0x01, 0x3C, 0xC0, 0x01, 0x80, 0x0F, 0x9F, 0x3F,
+ 0xC1, 0xF8, 0x30, 0xCF, 0x0F, 0x80, 0x0F, 0x1F, 0xCF, 0xFF, 0x8F, 0xFE, 0x7F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x00, 0x03, 0xFC, 0x3F, 0xF3, 0xFC, 0x00, 0x00, 0x03, 0x00, 0x03, 0xFC, 0xF9,
+ 0xFF, 0xFC, 0x01, 0xFF, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0x00,
+ 0x1C, 0xFF, 0x3F, 0xC3, 0xC7, 0x39, 0xCE, 0x07, 0x00, 0x0F, 0x8F, 0x3F, 0xC3, 0xFF, 0x3F,
+ 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xE6, 0x70, 0x01, 0xF8, 0xF8, 0x1C, 0xFF,
+ 0xFF, 0xCF, 0xF9, 0xF3, 0x9F, 0xC0, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0xF8, 0xF3, 0x9C, 0xF9,
+ 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xCF,
+ 0xFE, 0x0F, 0xFF, 0xFF, 0x07, 0xFC, 0x03, 0x9C, 0x00, 0x00, 0x03, 0x3F, 0xF3, 0xFC, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0xF9, 0xFF, 0xFC, 0x00, 0xF3, 0xFF, 0x30, 0xC3, 0x1C, 0x3F,
+ 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0x9F, 0x3F, 0xC3, 0xFC, 0x39, 0xCF, 0x0F, 0x00,
+ 0x0F, 0x8F, 0xCF, 0xFF, 0x1F, 0xFE, 0x79, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F,
+ 0xF3, 0xFC, 0x3F, 0xCC, 0xFF, 0x3F, 0xC3, 0xFC, 0xF9, 0xFF, 0xFC, 0x38, 0xFF, 0x9F, 0x39,
+ 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFF, 0x3F, 0xFC, 0xFF, 0x3F, 0xC3, 0xE3, 0x39,
+ 0xCC, 0x63, 0x3F, 0xCF, 0xC7, 0x3F, 0xC3, 0xFF, 0x3F, 0xC8, 0x9F, 0x00, 0x00, 0x00, 0x3F,
+ 0xFF, 0xFF, 0x80, 0x13, 0x9F, 0xFC, 0x7E, 0x0F, 0xFF, 0xFF, 0xCF, 0xF9, 0xF1, 0x1F, 0xF9,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x73, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF, 0xC3, 0xFC, 0x3F,
+ 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xCF, 0xFF, 0xCF, 0xFF, 0x0F, 0xC0, 0x3F, 0x0F, 0xFF,
+ 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F, 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xF3, 0xFC, 0xF9,
+ 0xFF, 0xFC, 0x3C, 0x73, 0xFF, 0x20, 0x42, 0x3C, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F, 0xCE, 0x07, 0x3F, 0xCF, 0xC7, 0xCF, 0xFE, 0x3F, 0xFE,
+ 0x78, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xF3, 0xFC, 0x3F, 0xCC, 0xFF, 0x3F,
+ 0xC0, 0x00, 0xF9, 0xFF, 0xFC, 0x3C, 0x7F, 0x9F, 0x39, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xC3, 0xFF, 0x3F, 0xFC, 0xFF, 0x3F, 0xC3, 0xF1, 0x39, 0xC8, 0xF1, 0x3F, 0xCF, 0xE3, 0x3F,
+ 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0x80, 0x13, 0x9F, 0x8E,
+ 0x3C, 0xE7, 0xFF, 0xFF, 0xCF, 0xF9, 0xF8, 0x3F, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+ 0x33, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF, 0xC3, 0xFC, 0x3F, 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F,
+ 0xCF, 0x9F, 0xFF, 0xFF, 0x8F, 0xC0, 0x3F, 0x1F, 0xFF, 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F,
+ 0xF3, 0xFC, 0x3F, 0xF3, 0xFF, 0x3F, 0xF3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3E, 0x33, 0xFF, 0x06,
+ 0x00, 0x7C, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F, 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xCC, 0x63, 0x3F, 0xCF, 0xE3, 0xCF, 0xFC, 0x7F, 0xFE, 0x7C, 0x63, 0xFF, 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0x00, 0x00, 0x01, 0xE1, 0xFF, 0xF0, 0x3E,
+ 0x3F, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
+ 0xC3, 0xF8, 0x39, 0xC1, 0xF8, 0x3F, 0xC0, 0x00, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00,
+ 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xE6, 0x70, 0x00, 0x8F, 0x1C, 0xE7, 0x3F, 0xFF, 0xC7, 0xF1,
+ 0xF8, 0x3F, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0xFC, 0xF9, 0xFF, 0xFC, 0xFF,
+ 0xC3, 0xFC, 0x3F, 0xF3, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xCF, 0x9F, 0xFF, 0xFF, 0xCF, 0xFF,
+ 0xFF, 0x3F, 0x3F, 0xC3, 0x9C, 0x3F, 0xC3, 0xF9, 0x3F, 0xF3, 0xF8, 0x3F, 0xF3, 0xFF, 0x3F,
+ 0xF3, 0xFC, 0xF9, 0xFF, 0xFC, 0x3F, 0x13, 0xFF, 0x0F, 0x00, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xC3, 0xFC, 0x3F, 0xFF, 0x9F, 0x3F, 0xC3, 0xFC, 0x3F, 0xC8, 0xF1, 0x3F, 0xCF, 0xF1, 0xCF,
+ 0xF8, 0xFF, 0xFE, 0x7E, 0x07, 0xFF, 0xFC, 0xFF, 0x00, 0x10, 0x01, 0x80, 0x08, 0x00, 0x80,
+ 0x1C, 0xFF, 0x80, 0x03, 0xFF, 0xE1, 0xFF, 0xF0, 0x3F, 0x1F, 0x9F, 0x00, 0x10, 0x01, 0x80,
+ 0x10, 0x01, 0x80, 0x08, 0x00, 0x80, 0x00, 0x00, 0x3F, 0xC3, 0xFC, 0x39, 0xC3, 0xFC, 0x3F,
+ 0xC0, 0x00, 0x3F, 0xC3, 0xFF, 0x3F, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xE6,
+ 0x78, 0x00, 0x8F, 0x9C, 0x07, 0x3F, 0xFF, 0xE3, 0xE3, 0xF1, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xFC, 0x3F,
+ 0x83, 0xFF, 0x1F, 0x81, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
+ 0xC3, 0xFC, 0x3F, 0xC1, 0xF8, 0x3F, 0xC0, 0x00, 0xC0, 0xF1, 0xFF, 0xE0, 0x7F, 0x0F, 0xFF,
+ 0xF8, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFC, 0x00, 0xFF, 0xF3, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x3F, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x00,
+ 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xF3, 0x3F, 0xFF, 0xFF, 0x9F, 0xFF, 0xFE, 0x0F, 0x3F,
+ 0xFF, 0xF3, 0xE7, 0xF3, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01,
+ 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x18, 0x01, 0x80, 0x10, 0x03, 0x80, 0x00, 0x03, 0x80,
+ 0x08, 0x00, 0x80, 0x03, 0xFC, 0x00, 0x0F, 0xFC, 0x3F, 0xC3, 0xFF, 0x3F, 0xC3, 0xFC, 0x80,
+ 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x00, 0x00, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, 0x3F,
+ 0xC0, 0x00, 0xC0, 0xF3, 0xFF, 0xE0, 0x7F, 0x9F, 0xFF, 0xF9, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0xFF, 0xFE, 0x00, 0xFF, 0xF3, 0xFF, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, 0x1F, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00,
+ }};
+#endif // FONT5_H_
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/inline_font.h
@@ -1,0 +1,17 @@
+#ifndef INLINE_FONT_H_
+#define INLINE_FONT_H_
+
+struct inline_font {+ const int width;
+ const int height;
+ const int glyph_x;
+ const int glyph_y;
+ const int screen_offset_x;
+ const int screen_offset_y;
+ const int text_offset_y;
+ const int waveform_max_height;
+ const long image_size;
+ const unsigned char image_data[];
+};
+
+#endif
--- a/src/fx_cube.c
+++ b/src/fx_cube.c
@@ -1,7 +1,10 @@
-#include <SDL.h>
-#include <time.h>
#include "SDL2_inprint.h"
+#include <SDL3/SDL.h>
+#include <math.h>
+
+// Handle screensaver cube effect
+
static SDL_Texture *texture_cube;
static SDL_Texture *texture_text;
static SDL_Renderer *fx_renderer;
@@ -27,10 +30,10 @@
}
static void rotate_cube(const float angle_x, const float angle_y) {- const float sin_x = SDL_sin(angle_x);
- const float cos_x = SDL_cos(angle_x);
- const float sin_y = SDL_sin(angle_y);
- const float cos_y = SDL_cos(angle_y);
+ const float sin_x = SDL_sinf(angle_x);
+ const float cos_x = SDL_cosf(angle_x);
+ const float sin_y = SDL_sinf(angle_y);
+ const float cos_y = SDL_cosf(angle_y);
for (int i = 0; i < 8; i++) {const float x = nodes[i][0];
const float y = nodes[i][1];
@@ -56,7 +59,10 @@
SDL_Texture *og_target = SDL_GetRenderTarget(fx_renderer);
- SDL_QueryTexture(og_target, NULL, NULL, &texture_size.x, &texture_size.y);
+ texture_size.x = (int)SDL_GetNumberProperty(SDL_GetTextureProperties(og_target),
+ SDL_PROP_TEXTURE_WIDTH_NUMBER, 0);
+ texture_size.y = (int)SDL_GetNumberProperty(SDL_GetTextureProperties(og_target),
+ SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0);
texture_cube = SDL_CreateTexture(fx_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
texture_size.x, texture_size.y);
@@ -67,7 +73,7 @@
SDL_SetRenderDrawColor(fx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(fx_renderer);
- inprint(fx_renderer, "DEVICE DISCONNECTED", texture_width - font_glyph_width * 19 - 21,
+ inprint(fx_renderer, "M8 DEVICE NOT DETECTED", texture_width - font_glyph_width * 22 - 23,
texture_height - 12, 0xFFFFFF, 0x000000);
inprint(fx_renderer, "M8C", 2, 2, 0xFFFFFF, 0x000000);
@@ -77,46 +83,60 @@
SDL_memcpy(nodes, default_nodes, sizeof(default_nodes));
scale(50, 50, 50);
- rotate_cube(M_PI / 6, SDL_atan(SDL_sqrt(2)));
+ rotate_cube(M_PI / 6, SDL_atanf(SDL_sqrtf(2)));
SDL_SetTextureBlendMode(texture_cube, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture_text, SDL_BLENDMODE_BLEND);
- center_x = texture_size.x / 2.0;
- center_y = texture_size.y / 2.0;
+ center_x = (int)(texture_size.x / 2.0);
+ center_y = (int)(texture_size.y / 2.0);
}
-void fx_cube_destroy() {+void fx_cube_destroy(void) {+ // Free resources
SDL_DestroyTexture(texture_cube);
SDL_DestroyTexture(texture_text);
-}
-void fx_cube_update() {- SDL_Point points[24];
- int points_counter = 0;
- SDL_Texture *og_texture = SDL_GetRenderTarget(fx_renderer);
-
- SDL_SetRenderTarget(fx_renderer, texture_cube);
+ // Force clear renderer
+ SDL_SetRenderTarget(fx_renderer, NULL);
SDL_SetRenderDrawColor(fx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(fx_renderer);
+}
- const unsigned int seconds = SDL_GetTicks() / 1000;
- const float scalefactor = 1 + SDL_sin(seconds) * 0.005;
+// Update the cube texture every 16ms>. Returns 1 if cube was updated, 0 if no changes were made.
+int fx_cube_update(void) {+ static Uint64 ticks_last_update = 0;
- scale(scalefactor, scalefactor, scalefactor);
- rotate_cube(M_PI / 180, M_PI / 270);
+ if (SDL_GetTicks() - ticks_last_update >= 16) {+ ticks_last_update = SDL_GetTicks();
+ SDL_FPoint points[24];
+ int points_counter = 0;
+ SDL_Texture *og_texture = SDL_GetRenderTarget(fx_renderer);
- for (int i = 0; i < 12; i++) {- const float *p1 = nodes[edges[i][0]];
- const float *p2 = nodes[edges[i][1]];
- points[points_counter++] = (SDL_Point){p1[0] + center_x, nodes[edges[i][0]][1] + center_y};- points[points_counter++] = (SDL_Point){p2[0] + center_x, p2[1] + center_y};- }
+ SDL_SetRenderTarget(fx_renderer, texture_cube);
+ SDL_SetRenderDrawColor(fx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ SDL_RenderClear(fx_renderer);
- SDL_RenderCopy(fx_renderer, texture_text, NULL, NULL);
- SDL_SetRenderDrawColor(fx_renderer, line_color.r, line_color.g, line_color.b, line_color.a);
- SDL_RenderDrawLines(fx_renderer, points, 24);
+ const Uint64 seconds = SDL_GetTicks() / 1000;
+ const float scale_factor = 1 + SDL_sinf((float)seconds) * (float)0.005;
- SDL_SetRenderTarget(fx_renderer, og_texture);
- SDL_RenderCopy(fx_renderer, texture_cube, NULL, NULL);
+ scale(scale_factor, scale_factor, scale_factor);
+ rotate_cube(M_PI / 180, M_PI / 270);
+
+ for (int i = 0; i < 12; i++) {+ const float *p1 = nodes[edges[i][0]];
+ const float *p2 = nodes[edges[i][1]];
+ points[points_counter++] = (SDL_FPoint){p1[0] + center_x, nodes[edges[i][0]][1] + center_y};+ points[points_counter++] = (SDL_FPoint){p2[0] + center_x, p2[1] + center_y};+ }
+
+ SDL_RenderTexture(fx_renderer, texture_text, NULL, NULL);
+ SDL_SetRenderDrawColor(fx_renderer, line_color.r, line_color.g, line_color.b, line_color.a);
+ SDL_RenderLines(fx_renderer, points, 24);
+
+ SDL_SetRenderTarget(fx_renderer, og_texture);
+ SDL_RenderTexture(fx_renderer, texture_cube, NULL, NULL);
+ return 1;
+ }
+ return 0;
}
--- a/src/fx_cube.h
+++ b/src/fx_cube.h
@@ -1,10 +1,10 @@
#ifndef FX_CUBE_H_
#define FX_CUBE_H_
-#include "SDL_render.h"
+#include <SDL3/SDL_render.h>
void fx_cube_init(SDL_Renderer *target_renderer, SDL_Color foreground_color,
unsigned int texture_width, unsigned int texture_height,
unsigned int font_glyph_width);
-void fx_cube_destroy();
-void fx_cube_update();
+void fx_cube_destroy(void);
+int fx_cube_update(void);
#endif
\ No newline at end of file
--- a/src/gamecontrollers.c
+++ /dev/null
@@ -1,150 +1,0 @@
-//
-// Created by jonne on 8/19/24.
-//
-
-#include "gamecontrollers.h"
-#include "config.h"
-#include "input.h"
-
-#include <SDL.h>
-
-static int num_joysticks = 0;
-SDL_GameController *game_controllers[MAX_CONTROLLERS];
-
-// Opens available game controllers and returns the amount of opened controllers
-int gamecontrollers_initialize() {-
- num_joysticks = SDL_NumJoysticks();
- int controller_index = 0;
-
- SDL_Log("Looking for game controllers");- SDL_Delay(10); // Some controllers like XBone wired need a little while to get ready
-
- // Try to load the game controller database file
- char db_filename[1024] = {0};- snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt", SDL_GetPrefPath("", "m8c"));- SDL_Log("Trying to open game controller database from %s", db_filename);- SDL_RWops *db_rw = SDL_RWFromFile(db_filename, "rb");
- if (db_rw == NULL) {- snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt", SDL_GetBasePath());
- SDL_Log("Trying to open game controller database from %s", db_filename);- db_rw = SDL_RWFromFile(db_filename, "rb");
- }
-
- if (db_rw != NULL) {- const int mappings = SDL_GameControllerAddMappingsFromRW(db_rw, 1);
- if (mappings != -1)
- SDL_Log("Found %d game controller mappings", mappings);- else
- SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Error loading game controller mappings.");
- } else {- SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Unable to open game controller database file.");
- }
-
- // Open all available game controllers
- for (int i = 0; i < num_joysticks; i++) {- if (!SDL_IsGameController(i))
- continue;
- if (controller_index >= MAX_CONTROLLERS)
- break;
- game_controllers[controller_index] = SDL_GameControllerOpen(i);
- SDL_Log("Controller %d: %s", controller_index + 1,- SDL_GameControllerName(game_controllers[controller_index]));
- controller_index++;
- }
-
- return controller_index;
-}
-
-// Closes all open game controllers
-void gamecontrollers_close() {-
- for (int i = 0; i < MAX_CONTROLLERS; i++) {- if (game_controllers[i])
- SDL_GameControllerClose(game_controllers[i]);
- }
-}
-
-// Check whether a button is pressed on a gamepad and return 1 if pressed.
-static int get_game_controller_button(const config_params_s *conf, SDL_GameController *controller,
- const int button) {-
- const int button_mappings[8] = {conf->gamepad_up, conf->gamepad_down, conf->gamepad_left,- conf->gamepad_right, conf->gamepad_opt, conf->gamepad_edit,
- conf->gamepad_select, conf->gamepad_start};
-
- // Check digital buttons
- if (SDL_GameControllerGetButton(controller, button_mappings[button])) {- return 1;
- }
-
- // If digital button isn't pressed, check the corresponding analog control
- switch (button) {- case INPUT_UP:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_updown) <
- -conf->gamepad_analog_threshold;
- case INPUT_DOWN:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_updown) >
- conf->gamepad_analog_threshold;
- case INPUT_LEFT:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_leftright) <
- -conf->gamepad_analog_threshold;
- case INPUT_RIGHT:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_leftright) >
- conf->gamepad_analog_threshold;
- case INPUT_OPT:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_opt) >
- conf->gamepad_analog_threshold;
- case INPUT_EDIT:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_edit) >
- conf->gamepad_analog_threshold;
- case INPUT_SELECT:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_select) >
- conf->gamepad_analog_threshold;
- case INPUT_START:
- return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_start) >
- conf->gamepad_analog_threshold;
- default:
- return 0;
- }
-}
-
-// Handle game controllers, simply check all buttons and analog axis on every
-// cycle
-int gamecontrollers_handle_buttons(const config_params_s *conf) {-
- const int keycodes[8] = {key_up, key_down, key_left, key_right,- key_opt, key_edit, key_select, key_start};
-
- int key = 0;
-
- // Cycle through every active game controller
- for (int gc = 0; gc < num_joysticks; gc++) {- // Cycle through all M8 buttons
- for (int button = 0; button < INPUT_MAX; button++) {- // If the button is active, add the keycode to the variable containing
- // active keys
- if (get_game_controller_button(conf, game_controllers[gc], button)) {- key |= keycodes[button];
- }
- }
- }
-
- return key;
-}
-
-input_msg_s gamecontrollers_handle_special_messages(const config_params_s *conf) {- input_msg_s msg = {0};- // Read special case game controller buttons quit and reset
- for (int gc = 0; gc < num_joysticks; gc++) {- if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_quit) &&
- (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
- SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
- msg = (input_msg_s){special, msg_quit, 0, 0};- else if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_reset) &&
- (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
- SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
- msg = (input_msg_s){special, msg_reset_display, 0, 0};- }
- return msg;
-}
\ No newline at end of file
--- a/src/gamecontrollers.h
+++ /dev/null
@@ -1,18 +1,0 @@
-//
-// Created by jonne on 8/19/24.
-//
-
-#ifndef GAMECONTROLLERS_H_
-#define GAMECONTROLLERS_H_
-
-#include "config.h"
-#include "input.h"
-
-#define MAX_CONTROLLERS 4
-
-int gamecontrollers_initialize();
-void gamecontrollers_close();
-int gamecontrollers_handle_buttons(const config_params_s *conf);
-input_msg_s gamecontrollers_handle_special_messages(const config_params_s *conf);
-
-#endif //GAMECONTROLLERS_H_
--- /dev/null
+++ b/src/gamepads.c
@@ -1,0 +1,103 @@
+//
+// Created by jonne on 8/19/24.
+//
+
+#include "gamepads.h"
+
+#include <SDL3/SDL.h>
+#include <stdio.h>
+
+// Maximum number of game controllers to support
+#define MAX_CONTROLLERS 4
+
+SDL_Gamepad *game_controllers[MAX_CONTROLLERS];
+
+/**
+ * Initializes available game controllers and loads game controller mappings.
+ *
+ * This function scans for connected joysticks and attempts to open those that are recognized
+ * as game controllers. It also loads the game controller mapping database to improve compatibility
+ * with various devices. Any errors during initialization are logged.
+ *
+ * @return The number of successfully initialized game controllers. Returns -1 if an error occurs
+ * during the initialization process.
+ */
+int gamepads_initialize() {+
+ int num_joysticks = 0;
+ SDL_JoystickID *joystick_ids = NULL;
+
+ if (SDL_Init(SDL_INIT_GAMEPAD) == false) {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed to initialize SDL_GAMEPAD: %s", SDL_GetError());
+ return -1;
+ }
+
+ int controller_index = 0;
+
+ SDL_Log("Looking for game controllers");+ SDL_Delay(10); // Some controllers like XBone wired need a little while to get ready
+
+ // Try to load the game controller database file
+ char db_filename[2048] = {0};+ if (snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt",
+ SDL_GetPrefPath("", "m8c")) >= (int)sizeof(db_filename)) {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Path too long for buffer");
+ return -1;
+ }
+ SDL_Log("Trying to open game controller database from %s", db_filename);+ SDL_IOStream *db_rw = SDL_IOFromFile(db_filename, "rb");
+ if (db_rw == NULL) {+ snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt", SDL_GetBasePath());
+ SDL_Log("Trying to open game controller database from %s", db_filename);+ db_rw = SDL_IOFromFile(db_filename, "rb");
+ }
+
+ if (db_rw != NULL) {+ const int mappings = SDL_AddGamepadMappingsFromIO(db_rw, true);
+ if (mappings != -1) {+ SDL_Log("Found %d game controller mappings", mappings);+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Error loading game controller mappings.");
+ }
+ } else {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Unable to open game controller database file.");
+ }
+
+ joystick_ids = SDL_GetGamepads(&num_joysticks);
+ if (joystick_ids == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed to get gamepad IDs: %s", SDL_GetError());
+ return -1;
+ }
+
+ // Open all available game controllers
+ SDL_Log("Found %d gamepads", num_joysticks);+ for (int i = 0; i < num_joysticks; i++) {+ if (!SDL_IsGamepad(joystick_ids[i]))
+ continue;
+ if (controller_index >= MAX_CONTROLLERS)
+ break;
+ game_controllers[controller_index] = SDL_OpenGamepad(joystick_ids[i]);
+ if (game_controllers[controller_index] == NULL) {+ SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed to open gamepad %d: %s", i, SDL_GetError());
+ continue;
+ }
+ SDL_Log("Controller %d: %s", controller_index + 1,+ SDL_GetGamepadName(game_controllers[controller_index]));
+ controller_index++;
+ }
+
+ SDL_free(joystick_ids);
+
+ return controller_index;
+}
+
+// Closes all open game controllers
+void gamepads_close() {+
+ for (int i = 0; i < MAX_CONTROLLERS; i++) {+ if (game_controllers[i])
+ SDL_CloseGamepad(game_controllers[i]);
+ }
+
+ SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
+}
--- /dev/null
+++ b/src/gamepads.h
@@ -1,0 +1,16 @@
+//
+// Created by jonne on 8/19/24.
+//
+
+#ifndef GAMECONTROLLERS_H_
+#define GAMECONTROLLERS_H_
+
+#include "config.h"
+#include "input.h"
+
+int gamepads_initialize();
+void gamepads_close();
+int gamecontrollers_handle_buttons(const config_params_s *conf);
+input_msg_s gamecontrollers_handle_special_messages(const config_params_s *conf);
+
+#endif //GAMECONTROLLERS_H_
--- a/src/inline_font.h
+++ /dev/null
@@ -1,17 +1,0 @@
-#ifndef INLINE_FONT_H_
-#define INLINE_FONT_H_
-
-struct inline_font {- const int width;
- const int height;
- const int glyph_x;
- const int glyph_y;
- const int screen_offset_x;
- const int screen_offset_y;
- const int text_offset_y;
- const int waveform_max_height;
- const long image_size;
- const unsigned char image_data[];
-};
-
-#endif
--- a/src/inprint2.c
+++ b/src/inprint2.c
@@ -2,8 +2,8 @@
// https://github.com/driedfruit/SDL_inprint Released into public domain.
// Modified to support multiple fonts & adding a background to text.
-#include "inline_font.h"
-#include <SDL.h>
+#include "fonts/inline_font.h"
+#include <SDL3/SDL.h>
#define CHARACTERS_PER_ROW 94
#define CHARACTERS_PER_COLUMN 1
@@ -17,7 +17,7 @@
static struct inline_font *selected_inline_font;
static Uint16 selected_font_w, selected_font_h;
-void prepare_inline_font(struct inline_font *font) {+void inline_font_initialize(struct inline_font *font) {selected_font_w = font->width;
selected_font_h = font->height;
@@ -28,38 +28,38 @@
return;
}
- SDL_RWops *font_bmp =
- SDL_RWFromConstMem(selected_inline_font->image_data, selected_inline_font->image_size);
+ SDL_IOStream *font_bmp =
+ SDL_IOFromConstMem(selected_inline_font->image_data, selected_inline_font->image_size);
- SDL_Surface *surface = SDL_LoadBMP_RW(font_bmp, 1);
+ SDL_Surface *surface = SDL_LoadBMP_IO(font_bmp, 1);
// Black is transparent
- SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0));
+ SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, 0, 0, 0));
inline_font = SDL_CreateTextureFromSurface(selected_renderer, surface);
- SDL_FreeSurface(surface);
+ SDL_DestroySurface(surface);
selected_font = inline_font;
}
-void kill_inline_font(void) {+void inline_font_close(void) {SDL_DestroyTexture(inline_font);
inline_font = NULL;
}
-void inrenderer(SDL_Renderer *renderer) { selected_renderer = renderer; }+void inline_font_set_renderer(SDL_Renderer *renderer) { selected_renderer = renderer; } void infont(SDL_Texture *font) {- Uint32 format;
- int access;
- int w, h;
if (font == NULL) {return;
}
- SDL_QueryTexture(font, &format, &access, &w, &h);
+ const int w =
+ (int)SDL_GetNumberProperty(SDL_GetTextureProperties(font), SDL_PROP_TEXTURE_WIDTH_NUMBER, 0);
+ const int h =
+ (int)SDL_GetNumberProperty(SDL_GetTextureProperties(font), SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0);
selected_font = font;
selected_font_w = w;
@@ -78,16 +78,16 @@
}
void inprint(SDL_Renderer *dst, const char *str, Uint32 x, Uint32 y, const Uint32 fgcolor,
const Uint32 bgcolor) {- SDL_Rect s_rect;
- SDL_Rect d_rect;
- SDL_Rect bg_rect;
+ SDL_FRect s_rect;
+ SDL_FRect d_rect;
+ SDL_FRect bg_rect;
static uint32_t previous_fgcolor;
- d_rect.x = x;
- d_rect.y = y;
- s_rect.w = selected_font_w / CHARACTERS_PER_ROW;
- s_rect.h = selected_font_h / CHARACTERS_PER_COLUMN;
+ d_rect.x = (float)x;
+ d_rect.y = (float)y;
+ s_rect.w = (float)selected_font_w / CHARACTERS_PER_ROW;
+ s_rect.h = (float)selected_font_h / CHARACTERS_PER_COLUMN;
d_rect.w = s_rect.w;
d_rect.h = s_rect.h;
@@ -104,11 +104,11 @@
s_rect.x = col * s_rect.w;
s_rect.y = row * s_rect.h;
#else
- s_rect.x = id * s_rect.w;
+ s_rect.x = (float)id * s_rect.w;
s_rect.y = 0;
#endif
if (id + font_offset == '\n') {- d_rect.x = x;
+ d_rect.x = (float)x;
d_rect.y += s_rect.h + 1;
continue;
}
@@ -122,15 +122,16 @@
(bgcolor & 0x0000FF00) >> 8, bgcolor & 0x000000FF,
0xFF);
bg_rect = d_rect;
- bg_rect.w = selected_inline_font->glyph_x;
- bg_rect.h = selected_inline_font->glyph_y;
+ bg_rect.w = (float)selected_inline_font->glyph_x;
+ bg_rect.h = (float)selected_inline_font->glyph_y;
SDL_RenderFillRect(dst, &bg_rect);
}
// Do not try to render a whitespace character because the font doesn't have one
if (ascii_code != 32) {- SDL_RenderCopy(dst, selected_font, &s_rect, &d_rect);
- } d_rect.x += selected_inline_font->glyph_x + 1;
+ SDL_RenderTexture(dst, selected_font, &s_rect, &d_rect);
+ }
+ d_rect.x += (float)selected_inline_font->glyph_x + 1;
}
}
SDL_Texture *get_inline_font(void) { return selected_font; }--- a/src/input.c
+++ b/src/input.c
@@ -1,314 +1,383 @@
+//
+// Created by Jonne Kokkonen on 15.4.2025.
+//
#include "input.h"
-#include "config.h"
-#include "gamecontrollers.h"
+#include "backends/audio.h"
+#include "backends/m8.h"
+#include "common.h"
#include "render.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
-uint8_t keyjazz_enabled = 0;
-uint8_t keyjazz_base_octave = 2;
-uint8_t keyjazz_velocity = 0x64;
+static unsigned char keyjazz_enabled = 0;
+static unsigned char keyjazz_base_octave = 2;
+static unsigned char keyjazz_velocity = 0x7F;
-static uint8_t keycode = 0; // value of the pressed key
+static unsigned char keycode = 0; // value of the pressed key
+static input_msg_s key = {normal, 0, 0};-static input_msg_s key = {normal, 0, 0, 0};+// Store gamepad state
+static struct {+ int current_buttons;
+ int analog_values[SDL_GAMEPAD_AXIS_COUNT];
+} gamepad_state = {0};-uint8_t toggle_input_keyjazz() {+static unsigned char toggle_input_keyjazz() {keyjazz_enabled = !keyjazz_enabled;
SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, keyjazz_enabled ? "Keyjazz enabled" : "Keyjazz disabled");
return keyjazz_enabled;
}
-static input_msg_s handle_keyjazz(SDL_Event *event, uint8_t keyvalue, config_params_s *conf) {- input_msg_s key = {keyjazz, keyvalue, keyjazz_velocity, event->type};- switch (event->key.keysym.scancode) {- case SDL_SCANCODE_Z:
- key.value = keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_S:
- key.value = 1 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_X:
- key.value = 2 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_D:
- key.value = 3 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_C:
- key.value = 4 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_V:
- key.value = 5 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_G:
- key.value = 6 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_B:
- key.value = 7 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_H:
- key.value = 8 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_N:
- key.value = 9 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_J:
- key.value = 10 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_M:
- key.value = 11 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_Q:
- key.value = 12 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_2:
- key.value = 13 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_W:
- key.value = 14 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_3:
- key.value = 15 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_E:
- key.value = 16 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_R:
- key.value = 17 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_5:
- key.value = 18 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_T:
- key.value = 19 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_6:
- key.value = 20 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_Y:
- key.value = 21 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_7:
- key.value = 22 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_U:
- key.value = 23 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_I:
- key.value = 24 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_9:
- key.value = 25 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_O:
- key.value = 26 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_0:
- key.value = 27 + keyjazz_base_octave * 12;
- break;
- case SDL_SCANCODE_P:
- key.value = 28 + keyjazz_base_octave * 12;
- break;
- default:
- key.type = normal;
- if (event->key.repeat > 0 || event->key.type == SDL_KEYUP) {- break;
+// Get note value for a scancode, or -1 if not found
+static int get_note_for_scancode(const SDL_Scancode scancode) {+
+ // Map from SDL scancodes to note offsets
+ const struct keyjazz_scancodes_t {+ SDL_Scancode scancode;
+ unsigned char note_offset;
+ } NOTE_MAP[] = {+ {SDL_SCANCODE_Z, 0}, {SDL_SCANCODE_S, 1}, {SDL_SCANCODE_X, 2}, {SDL_SCANCODE_D, 3},+ {SDL_SCANCODE_C, 4}, {SDL_SCANCODE_V, 5}, {SDL_SCANCODE_G, 6}, {SDL_SCANCODE_B, 7},+ {SDL_SCANCODE_H, 8}, {SDL_SCANCODE_N, 9}, {SDL_SCANCODE_J, 10}, {SDL_SCANCODE_M, 11},+ {SDL_SCANCODE_Q, 12}, {SDL_SCANCODE_2, 13}, {SDL_SCANCODE_W, 14}, {SDL_SCANCODE_3, 15},+ {SDL_SCANCODE_E, 16}, {SDL_SCANCODE_R, 17}, {SDL_SCANCODE_5, 18}, {SDL_SCANCODE_T, 19},+ {SDL_SCANCODE_6, 20}, {SDL_SCANCODE_Y, 21}, {SDL_SCANCODE_7, 22}, {SDL_SCANCODE_U, 23},+ {SDL_SCANCODE_I, 24}, {SDL_SCANCODE_9, 25}, {SDL_SCANCODE_O, 26}, {SDL_SCANCODE_0, 27},+ {SDL_SCANCODE_P, 28},+ };
+
+ const size_t NOTE_MAP_SIZE = (sizeof(NOTE_MAP) / sizeof(NOTE_MAP[0]));
+
+ for (size_t i = 0; i < NOTE_MAP_SIZE; i++) {+ if (NOTE_MAP[i].scancode == scancode) {+ return NOTE_MAP[i].note_offset + keyjazz_base_octave * 12;
}
- if (event->key.keysym.scancode == conf->key_jazz_dec_octave) {- if (keyjazz_base_octave > 0) {- keyjazz_base_octave--;
- display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- }
- } else if (event->key.keysym.scancode == conf->key_jazz_inc_octave) {- if (keyjazz_base_octave < 8) {- keyjazz_base_octave++;
- display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- }
- } else if (event->key.keysym.scancode == conf->key_jazz_dec_velocity) {- if ((event->key.keysym.mod & KMOD_ALT) > 0) {- if (keyjazz_velocity > 1)
- keyjazz_velocity -= 1;
- } else {- if (keyjazz_velocity > 0x10)
- keyjazz_velocity -= 0x10;
- }
+ }
+ return -1; // Not a note key
+}
+
+// Handle octave and velocity changes
+static void handle_keyjazz_settings(const SDL_Event *event, const config_params_s *conf) {+
+ // Constants for keyjazz limits and adjustments
+ const unsigned char KEYJAZZ_MIN_OCTAVE = 0;
+ const unsigned char KEYJAZZ_MAX_OCTAVE = 8;
+ const unsigned char KEYJAZZ_MIN_VELOCITY = 0;
+ const unsigned char KEYJAZZ_MAX_VELOCITY = 0x7F;
+ const unsigned char KEYJAZZ_FINE_VELOCITY_STEP = 1;
+ const unsigned char KEYJAZZ_COARSE_VELOCITY_STEP = 0x10;
+
+ if (event->key.repeat > 0 || event->key.type == SDL_EVENT_KEY_UP) {+ return;
+ }
+
+ const SDL_Scancode scancode = event->key.scancode;
+ const bool is_fine_adjustment = (event->key.mod & SDL_KMOD_ALT) > 0;
+
+ if (scancode == conf->key_jazz_dec_octave && keyjazz_base_octave > KEYJAZZ_MIN_OCTAVE) {+ keyjazz_base_octave--;
+ display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
+ } else if (scancode == conf->key_jazz_inc_octave && keyjazz_base_octave < KEYJAZZ_MAX_OCTAVE) {+ keyjazz_base_octave++;
+ display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
+ } else if (scancode == conf->key_jazz_dec_velocity) {+ const int step = is_fine_adjustment ? KEYJAZZ_FINE_VELOCITY_STEP : KEYJAZZ_COARSE_VELOCITY_STEP;
+ if (keyjazz_velocity > (is_fine_adjustment ? KEYJAZZ_MIN_VELOCITY + step : step)) {+ keyjazz_velocity -= step;
display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
- } else if (event->key.keysym.scancode == conf->key_jazz_inc_velocity) {- if ((event->key.keysym.mod & KMOD_ALT) > 0) {- if (keyjazz_velocity < 0x7F)
- keyjazz_velocity += 1;
- } else {- if (keyjazz_velocity < 0x6F)
- keyjazz_velocity += 0x10;
- }
+ }
+ } else if (scancode == conf->key_jazz_inc_velocity) {+ const int step = is_fine_adjustment ? KEYJAZZ_FINE_VELOCITY_STEP : KEYJAZZ_COARSE_VELOCITY_STEP;
+ const int max = is_fine_adjustment ? KEYJAZZ_MAX_VELOCITY : (KEYJAZZ_MAX_VELOCITY - step);
+ if (keyjazz_velocity < max) {+ keyjazz_velocity += step;
display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
}
- break;
}
-
- return key;
}
-static input_msg_s handle_normal_keys(const SDL_Event *event, const config_params_s *conf) {- input_msg_s key = {normal, 0, 0, 0};+static input_msg_s handle_keyjazz(const SDL_Event *event, unsigned char keyvalue,
+ const config_params_s *conf) {+ input_msg_s key = {keyjazz, keyvalue, keyjazz_velocity};- if (event->key.keysym.scancode == conf->key_up) {- key.value = key_up;
- } else if (event->key.keysym.scancode == conf->key_left) {- key.value = key_left;
- } else if (event->key.keysym.scancode == conf->key_down) {- key.value = key_down;
- } else if (event->key.keysym.scancode == conf->key_right) {- key.value = key_right;
- } else if (event->key.keysym.scancode == conf->key_select ||
- event->key.keysym.scancode == conf->key_select_alt) {- key.value = key_select;
- } else if (event->key.keysym.scancode == conf->key_start ||
- event->key.keysym.scancode == conf->key_start_alt) {- key.value = key_start;
- } else if (event->key.keysym.scancode == conf->key_opt ||
- event->key.keysym.scancode == conf->key_opt_alt) {- key.value = key_opt;
- } else if (event->key.keysym.scancode == conf->key_edit ||
- event->key.keysym.scancode == conf->key_edit_alt) {- key.value = key_edit;
- } else if (event->key.keysym.scancode == conf->key_delete) {- key.value = key_opt | key_edit;
- } else if (event->key.keysym.scancode == conf->key_reset) {- key = (input_msg_s){special, msg_reset_display, 0, 0};- } else if (event->key.keysym.scancode == conf->key_toggle_audio) {- key = (input_msg_s){special, msg_toggle_audio, 0, 0};- } else {- key.value = 0;
+ // Check if this is a note key
+ const int note_value = get_note_for_scancode(event->key.scancode);
+ if (note_value >= 0) {+ SDL_Log("vel %d", keyjazz_velocity);+ key.value = note_value;
+ return key;
}
+
+ // Not a note key, handle other settings
+ key.type = normal;
+ handle_keyjazz_settings(event, conf);
+
return key;
}
-// Handles SDL input events
-void handle_sdl_events(config_params_s *conf) {+/**
+ * Handles key inputs based on SDL events and predefined configuration mappings.
+ * Maps SDL keyboard events to corresponding input messages.
+ * Provides a default message if no matching key mapping is found.
+ *
+ * @param event Pointer to the SDL_Event structure representing the current keyboard event.
+ * @param conf Pointer to the config_params_s structure containing key mapping configurations.
+ * @return An input_msg_s structure corresponding to the processed key input or a default message if
+ * no match is found.
+ */
+static input_msg_s handle_m8_keys(const SDL_Event *event, const config_params_s *conf) {+ // Default message with a normal type and no value
+ input_msg_s key = {normal, 0, 0};- static int prev_key_analog = 0;
+ // Get the current scancode
+ const SDL_Scancode scancode = event->key.scancode;
- SDL_Event event;
+ // Handle standard keycodes (single key mapping)
+ const struct {+ SDL_Scancode scancode;
+ unsigned char value;
+ } normal_key_map[] = {+ {conf->key_up, key_up},+ {conf->key_left, key_left},+ {conf->key_down, key_down},+ {conf->key_right, key_right},+ {conf->key_select, key_select},+ {conf->key_select_alt, key_select},+ {conf->key_start, key_start},+ {conf->key_start_alt, key_start},+ {conf->key_opt, key_opt},+ {conf->key_opt_alt, key_opt},+ {conf->key_edit, key_edit},+ {conf->key_edit_alt, key_edit},+ {conf->key_delete, key_opt | key_edit},+ };
- // Read joysticks
- const int key_analog = gamecontrollers_handle_buttons(conf);
- if (prev_key_analog != key_analog) {- keycode = key_analog;
- prev_key_analog = key_analog;
+ // Check normal key mappings
+ for (size_t i = 0; i < sizeof(normal_key_map) / sizeof(normal_key_map[0]); i++) {+ if (scancode == normal_key_map[i].scancode) {+ key.value = normal_key_map[i].value;
+ return key;
+ }
}
- input_msg_s gcmsg = gamecontrollers_handle_special_messages(conf);
- if (gcmsg.type == special) {- key = gcmsg;
+ // No matching key found, return the default key message
+ return key;
+}
+
+/**
+ * Handles the key down events during the application runtime.
+ *
+ * @param ctx Pointer to the app_context structure
+ * @param event Pointer to the SDL_Event structure containing data about the key down
+ * event, including key and modifier states.
+ */
+void input_handle_key_down_event(struct app_context *ctx, const SDL_Event *event) {+ if (event->key.repeat > 0) {+ return;
}
- while (SDL_PollEvent(&event)) {+ if (event->key.key == SDLK_RETURN && (event->key.mod & SDL_KMOD_ALT) > 0) {+ toggle_fullscreen();
+ return;
+ }
+ if (event->key.key == SDLK_F4 && (event->key.mod & SDL_KMOD_ALT) > 0) {+ ctx->app_state = QUIT;
+ return;
+ }
+ if (event->key.key == SDLK_ESCAPE) {+ display_keyjazz_overlay(toggle_input_keyjazz(), keyjazz_base_octave, keyjazz_velocity);
+ return;
+ }
- switch (event.type) {+ if (event->key.scancode == ctx->conf.key_toggle_audio && ctx->device_connected) {+ ctx->conf.audio_enabled = !ctx->conf.audio_enabled;
+ audio_toggle(ctx->conf.audio_device_name, ctx->conf.audio_buffer_size);
+ return;
+ }
- // Reinitialize game controllers on controller add/remove/remap
- case SDL_CONTROLLERDEVICEADDED:
- case SDL_CONTROLLERDEVICEREMOVED:
- gamecontrollers_initialize();
- break;
+ if (event->key.scancode == ctx->conf.key_reset && ctx->device_connected && !keyjazz_enabled) {+ m8_reset_display();
+ return;
+ }
- // Handle SDL quit events (for example, window close)
- case SDL_QUIT:
- key = (input_msg_s){special, msg_quit, 0, 0};- break;
+ key = handle_m8_keys(event, &ctx->conf);
+ if (keyjazz_enabled) {+ key = handle_keyjazz(event, key.value, &ctx->conf);
+ }
+ keycode = (key.type == normal) ? (keycode | key.value) : key.value;
- case SDL_WINDOWEVENT:
- if (event.window.event == SDL_WINDOWEVENT_RESIZED) {- static uint32_t ticks_window_resized = 0;
- if (SDL_GetTicks() - ticks_window_resized > 500) {- SDL_Log("Resizing window...");- key = (input_msg_s){special, msg_reset_display, 0, 0};- ticks_window_resized = SDL_GetTicks();
- }
- }
- break;
+ input_process_and_send(ctx);
+}
- case SDL_KEYDOWN:
+/**
+ * Handles the "key up" SDL event and processes the associated input.
+ * Interprets the event based on key mappings (default and keyjazz mode) and updates the input
+ * state. Sends the processed input message for further handling.
+ *
+ * @param ctx Pointer to the app_context structure containing application state and configurations.
+ * @param event Pointer to the SDL_Event structure representing the "key up" event.
+ */
+void input_handle_key_up_event(const struct app_context *ctx, const SDL_Event *event) {+ key = handle_m8_keys(event, &ctx->conf);
+ if (keyjazz_enabled) {+ key = handle_keyjazz(event, key.value, &ctx->conf);
+ }
+ keycode = (key.type == normal) ? (keycode & ~key.value) : 0;
- if (event.key.repeat > 0) {- break;
- }
+ input_process_and_send(ctx);
+}
- // ALT+ENTER toggles fullscreen
- if (event.key.keysym.sym == SDLK_RETURN && (event.key.keysym.mod & KMOD_ALT) > 0) {- toggle_fullscreen();
- break;
- }
+void input_handle_gamepad_button(struct app_context *ctx, const SDL_GamepadButton button,
+ const bool pressed) {+ const config_params_s *conf = &ctx->conf;
+ static int prev_key_value = 0;
+ int key_value = 0;
- // ALT+F4 quits program
- if (event.key.keysym.sym == SDLK_F4 && (event.key.keysym.mod & KMOD_ALT) > 0) {- key = (input_msg_s){special, msg_quit, 0, 0};- break;
- }
+ // Handle standard buttons
+ const struct {+ int button;
+ unsigned char value;
+ } normal_key_map[] = {+ {conf->gamepad_up, key_up}, {conf->gamepad_left, key_left},+ {conf->gamepad_down, key_down}, {conf->gamepad_right, key_right},+ {conf->gamepad_select, key_select}, {conf->gamepad_start, key_start},+ {conf->gamepad_opt, key_opt}, {conf->gamepad_edit, key_edit},+ };
- // ESC = toggle keyjazz
- if (event.key.keysym.sym == SDLK_ESCAPE) {- display_keyjazz_overlay(toggle_input_keyjazz(), keyjazz_base_octave, keyjazz_velocity);
- break;
- }
+ // Check normal key mappings
+ for (size_t i = 0; i < sizeof(normal_key_map) / sizeof(normal_key_map[0]); i++) {+ if (button == normal_key_map[i].button) {+ key_value = normal_key_map[i].value;
+ }
+ }
- // Intentional fallthrough
- case SDL_KEYUP:
+ if (pressed && key_value != prev_key_value) {+ gamepad_state.current_buttons |= key_value;
+ } else {+ gamepad_state.current_buttons &= ~key_value;
+ }
- // Normal keyboard inputs
- key = handle_normal_keys(&event, conf);
+ // Handle special button combinations
+ if (gamepad_state.current_buttons == (key_start | key_select | key_opt | key_edit)) {+ m8_reset_display();
+ return;
+ }
- if (keyjazz_enabled) {- key = handle_keyjazz(&event, key.value, conf);
- }
- break;
+ if (pressed && button == conf->gamepad_quit && gamepad_state.current_buttons == key_select) {+ ctx->app_state = QUIT;
+ return;
+ }
- default:
- break;
- }
+ keycode = gamepad_state.current_buttons;
- switch (key.type) {- case normal:
- if (event.type == SDL_KEYDOWN) {- keycode |= key.value;
- } else if (event.type == SDL_KEYUP) {- keycode &= ~key.value;
- }
- break;
- case keyjazz:
- // Do not allow pressing multiple keys with keyjazz
- case special:
- if (event.type == SDL_KEYDOWN) {- keycode = key.value;
- } else if (event.type == SDL_KEYUP) {- keycode = 0;
- }
- break;
- default:
- break;
- }
+ input_process_and_send(ctx);
+}
+
+/**
+ * Helper function to update button states based on analog axis value.
+ *
+ * @param axis_value The current value of the axis
+ * @param threshold The threshold that determines when a direction is activated
+ * @param negative_key The key to activate when the axis value is below a negative threshold
+ * @param positive_key The key to activate when the axis value is above a positive threshold
+ */
+static void update_button_state_from_axis(Sint16 axis_value, int threshold,
+ int negative_key, int positive_key) {+ if (axis_value < -threshold) {+ gamepad_state.current_buttons |= negative_key;
+ } else if (axis_value > threshold) {+ gamepad_state.current_buttons |= positive_key;
+ } else {+ gamepad_state.current_buttons &= ~(negative_key | positive_key);
}
}
-// Returns the currently pressed keys to main
-input_msg_s get_input_msg(config_params_s *conf) {+/**
+ * Processes gamepad axis movements and updates internal state accordingly.
+ * Maps gamepad analog axis input to directional or functional button states
+ * based on the configuration and analog value threshold.
+ *
+ * @param ctx Pointer to the app_context structure containing application configuration and state.
+ * @param axis The gamepad axis being processed, specified as an SDL_GamepadAxis.
+ * @param value The analog value of the axis, typically ranging from -32768 to 32767.
+ */
+void input_handle_gamepad_axis(const struct app_context *ctx, const SDL_GamepadAxis axis,
+ const Sint16 value) {+ const config_params_s *conf = &ctx->conf;
+ gamepad_state.analog_values[axis] = value;
- key = (input_msg_s){normal, 0, 0, 0};+ // Process directional axes and update button states
+ if (axis == conf->gamepad_analog_axis_updown) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_up, key_down);
+ } else if (axis == conf->gamepad_analog_axis_leftright) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_left, key_right);
+ } else if (axis == conf->gamepad_analog_axis_select) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_select, key_select);
+ } else if (axis == conf->gamepad_analog_axis_opt) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_opt, key_opt);
+ } else if (axis == conf->gamepad_analog_axis_start) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_start, key_start);
+ } else if (axis == conf->gamepad_analog_axis_edit) {+ update_button_state_from_axis(value, conf->gamepad_analog_threshold, key_edit, key_edit);
+ }
- // Query for SDL events
- handle_sdl_events(conf);
+ keycode = gamepad_state.current_buttons;
- if (!keyjazz_enabled && keycode == (key_start | key_select | key_opt | key_edit)) {- key = (input_msg_s){special, msg_reset_display, 0, 0};+ input_process_and_send(ctx);
+}
+
+/**
+ * Processes the current input message and sends the appropriate data based on its type.
+ *
+ * @param ctx Pointer to the app_context structure containing application configuration and state.
+ * @return An integer indicating the success status of the operation.
+ * Returns 1 for successful processing and sending of messages, or 0 if the device is not connected.
+ */
+int input_process_and_send(const struct app_context *ctx) {+ if (!ctx->device_connected) {+ return 0;
}
+ static unsigned char prev_input = 0;
- if (key.type == normal) {- /* Normal input keys go through some event-based manipulation in
- handle_sdl_events(), the value is stored in keycode variable */
- const input_msg_s input = (input_msg_s){key.type, keycode, 0, 0};- return input;
- } else {- // Special event keys already have the correct keycode baked in
- return key;
+ // get current inputs
+ const input_msg_s input = (input_msg_s){key.type, keycode, 0};+
+ switch (input.type) {+ case normal:
+ if (input.value != prev_input) {+ prev_input = input.value;
+ m8_send_msg_controller(input.value);
+ }
+ break;
+ case keyjazz:
+ if (input.value != 0) {+ if (input.value != prev_input) {+ prev_input = input.value;
+ m8_send_msg_keyjazz(input.value, keyjazz_velocity);
+ }
+ } else {+ m8_send_msg_keyjazz(0xFF, 0);
+ }
+ prev_input = input.value;
+ break;
+ default:;
}
+ return 1;
}
+
+// Touch screen double tap: switch between integer scaling / full screen scaling
+void input_handle_finger_down(struct app_context *ctx, const SDL_Event *event) {+ (void)event; // Suppress unused parameter warning
+ static Uint64 last_finger_down_time = 0;
+ SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Finger down");
+ const Uint64 current_time = SDL_GetTicks();
+ const Uint64 time_diff = current_time - last_finger_down_time;
+ // Check if the screen has been double-tapped and change the scaling mode
+ if (time_diff > 70 && time_diff < 500) {+ ctx->conf.integer_scaling = !ctx->conf.integer_scaling;
+ renderer_fix_texture_scaling_after_window_resize(&ctx->conf);
+ SDL_Log("integer scaling: %d", ctx->conf.integer_scaling);+ last_finger_down_time = 0;
+ } else {+ last_finger_down_time = current_time;
+ }
+}
\ No newline at end of file
--- a/src/input.h
+++ b/src/input.h
@@ -1,12 +1,14 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
+//
+// Created by Jonne Kokkonen on 15.4.2025.
+//
-#ifndef INPUT_H_
-#define INPUT_H_
-
+#ifndef INPUT_H
+#define INPUT_H
+#include "common.h"
#include "config.h"
-#include <stdint.h>
+#include <SDL3/SDL_events.h>
+
typedef enum input_buttons_t {INPUT_UP,
INPUT_DOWN,
@@ -34,18 +36,21 @@
typedef enum input_type_t { normal, keyjazz, special } input_type_t; typedef enum special_messages_t {- msg_quit = 1,
- msg_reset_display = 2,
- msg_toggle_audio = 3
+ msg_reset_display = 2
} special_messages_t;
typedef struct input_msg_s {input_type_t type;
- uint8_t value;
- uint8_t value2;
- uint32_t eventType;
+ unsigned char value;
+ unsigned char value2;
} input_msg_s;
-input_msg_s get_input_msg(config_params_s *conf);
+input_msg_s input_get_msg(config_params_s *conf);
+int input_process_and_send(const struct app_context *ctx);
+void input_handle_key_down_event(struct app_context *ctx, const SDL_Event *event);
+void input_handle_key_up_event(const struct app_context *ctx, const SDL_Event *event);
+void input_handle_gamepad_button(struct app_context *ctx, SDL_GamepadButton button, bool pressed);
+void input_handle_gamepad_axis(const struct app_context *ctx, SDL_GamepadAxis axis, Sint16 value);
+void input_handle_finger_down(struct app_context *ctx, const SDL_Event *event);
-#endif
+#endif // INPUT_H
--- a/src/main.c
+++ b/src/main.c
@@ -5,296 +5,238 @@
CFLAGS=-DDEBUG_MSG` */
// #define DEBUG_MSG
-#include <SDL.h>
-#include <signal.h>
+#include <SDL3/SDL.h>
+#define SDL_MAIN_USE_CALLBACKS
+#include <SDL3/SDL_main.h>
+#include <stdlib.h>
#include "SDL2_inprint.h"
-#include "audio.h"
-#include "command.h"
+#include "backends/audio.h"
+#include "backends/m8.h"
+#include "common.h"
#include "config.h"
-#include "input.h"
-#include "gamecontrollers.h"
+#include "gamepads.h"
#include "render.h"
-#include "serial.h"
-#include "slip.h"
-enum state { QUIT, WAIT_FOR_DEVICE, RUN };+// On MacOS TARGET_OS_IOS is defined as 0, so make sure that it's consistent on other platforms as well
+#ifndef TARGET_OS_IOS
+#define TARGET_OS_IOS 0
+#endif
-enum state run = WAIT_FOR_DEVICE;
-uint8_t need_display_reset = 0;
+static void do_wait_for_device(struct app_context *ctx) {+ static Uint64 ticks_poll_device = 0;
+ static int screensaver_initialized = 0;
-// Handles CTRL+C / SIGINT
-void intHandler(int unused) { (void)unused; run = QUIT; }+ // Handle app suspension
+ if (ctx->app_suspended) {+ return;
+ }
-void close_serial_port() { disconnect(); }+ if (!screensaver_initialized) {+ screensaver_initialized = screensaver_init();
+ }
+ screensaver_draw();
+ render_screen(&ctx->conf);
-int main(const int argc, char *argv[]) {+ // Poll for M8 device every second
+ if (ctx->device_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {+ ticks_poll_device = SDL_GetTicks();
+ if (m8_initialize(0, ctx->preferred_device)) {- if (argc == 2 && SDL_strcmp(argv[1], "--list") == 0) {- return list_devices();
- }
+ if (ctx->conf.audio_enabled) {+ if (!audio_initialize(ctx->conf.audio_device_name, ctx->conf.audio_buffer_size)) {+ SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
+ ctx->conf.audio_enabled = 0;
+ }
+ }
- char *preferred_device = NULL;
- if (argc == 3 && SDL_strcmp(argv[1], "--dev") == 0) {- preferred_device = argv[2];
- SDL_Log("Using preferred device %s.\n", preferred_device);+ const int m8_enabled = m8_enable_display(1);
+ // Device was found; enable display and proceed to the main loop
+ if (m8_enabled == 1) {+ ctx->app_state = RUN;
+ ctx->device_connected = 1;
+ SDL_Delay(100); // Give the display time to initialize
+ screensaver_destroy();
+ screensaver_initialized = 0;
+ m8_reset_display(); // Avoid display glitches.
+ } else {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+ ctx->app_state = QUIT;
+ screensaver_destroy();
+ screensaver_initialized = 0;
+#ifdef USE_RTMIDI
+ show_error_message(
+ "Cannot initialize M8 remote display. Make sure you're running "
+ "firmware 6.0.0 or newer. Please close and restart the application to try again.");
+#endif
+ }
+ }
}
+}
- char *config_filename = NULL;
- if (argc == 3 && SDL_strcmp(argv[1], "--config") == 0) {- config_filename = argv[2];
- SDL_Log("Using config file %s.\n", config_filename);+static config_params_s initialize_config(int argc, char *argv[], char **preferred_device,
+ char **config_filename) {+ for (int i = 1; i < argc; i++) {+ if (SDL_strcmp(argv[i], "--list") == 0) {+ exit(m8_list_devices());
+ }
+ if (SDL_strcmp(argv[i], "--dev") == 0 && i + 1 < argc) {+ *preferred_device = argv[i + 1];
+ SDL_Log("Using preferred device: %s", *preferred_device);+ i++;
+ } else if (SDL_strcmp(argv[i], "--config") == 0 && i + 1 < argc) {+ *config_filename = argv[i + 1];
+ SDL_Log("Using config file: %s", *config_filename);+ i++;
+ }
}
- // Initialize the config to defaults read in the params from the
- // configfile if present
- config_params_s conf = init_config(config_filename);
- read_config(&conf);
+ config_params_s conf = config_initialize(*config_filename);
- // allocate memory for serial buffer
- uint8_t *serial_buf = SDL_malloc(serial_read_size);
+ if (TARGET_OS_IOS == 1) {+ // Predefined settings for iOS
+ conf.init_fullscreen=1;
+ } else {+ // On other platforms, read config normally
+ config_read(&conf);
+ }
+ return conf;
+}
- static uint8_t slip_buffer[serial_read_size]; // SLIP command buffer
+/**
+ * Handles the initialization of a device and verifies its connection state.
+ *
+ * @param wait_for_device A flag indicating whether the system should wait for the device to
+ * connect. If set to 0 and the device is not detected, the application exits.
+ * @param preferred_device A string representing the preferred device to initialize.
+ * @return An unsigned char indicating the connection state of the device.
+ * Returns 1 if the device is connected successfully, or 0 if not connected.
+ */
+static unsigned char handle_m8_connection_init(const unsigned char wait_for_device,
+ const char *preferred_device) {+ const unsigned char device_connected = m8_initialize(1, preferred_device);
+ if (!wait_for_device && device_connected == 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected!");
+ exit(EXIT_FAILURE);
+ }
+ return device_connected;
+}
- SDL_zero(slip_buffer);
+// Main callback loop - read inputs, process data from the device, render screen
+SDL_AppResult SDL_AppIterate(void *appstate) {+ if (appstate == NULL) {+ return SDL_APP_FAILURE;
+ }
- // settings for the slip packet handler
- static const slip_descriptor_s slip_descriptor = {- .buf = slip_buffer,
- .buf_size = sizeof(slip_buffer),
- .recv_message = process_command, // the function where complete slip
- // packets are processed further
- };
+ struct app_context *ctx = appstate;
+ SDL_AppResult app_result = SDL_APP_CONTINUE;
- static slip_handler_s slip;
+ switch (ctx->app_state) {+ case INITIALIZE:
+ break;
- uint8_t prev_input = 0;
- uint8_t prev_note = 0;
- uint16_t zerobyte_packets = 0; // used to detect device disconnection
+ case WAIT_FOR_DEVICE:
+ if (ctx->conf.wait_for_device) {+ do_wait_for_device(ctx);
+ }
+ break;
- signal(SIGINT, intHandler);
- signal(SIGTERM, intHandler);
-#ifdef SIGQUIT
- signal(SIGQUIT, intHandler);
-#endif
- slip_init(&slip, &slip_descriptor);
-
- // First device detection to avoid SDL init if it isn't necessary. To be run
- // only if we shouldn't wait for M8 to be connected.
- if (conf.wait_for_device == 0) {- if (init_serial(1, preferred_device) == 0) {- SDL_free(serial_buf);
- return -1;
+ case RUN: {+ const int result = m8_process_data(&ctx->conf);
+ if (result == DEVICE_DISCONNECTED) {+ ctx->device_connected = 0;
+ ctx->app_state = WAIT_FOR_DEVICE;
+ audio_close();
+ } else if (result == DEVICE_FATAL_ERROR) {+ return SDL_APP_FAILURE;
}
+ render_screen(&ctx->conf);
+ break;
}
- // initialize all SDL systems
- if (initialize_sdl(conf.init_fullscreen, conf.init_use_gpu) == -1)
- run = QUIT;
+ case QUIT:
+ app_result = SDL_APP_SUCCESS;
+ break;
+ }
- // initial scan for (existing) game controllers
- gamecontrollers_initialize();
+ return app_result;
+}
-#ifdef DEBUG_MSG
- SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
-#endif
+// Initialize the app: initialize context, configs, renderer controllers and attempt to find M8
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {+ char *config_filename = NULL;
- // main loop begin
- do {- // try to init serial port
- int port_inited = init_serial(1, preferred_device);
- // if port init was successful, try to enable and reset display
- if (port_inited == 1 && enable_and_reset_display() == 1) {- // if audio routing is enabled, try to initialize audio devices
- if (conf.audio_enabled == 1) {- audio_init(conf.audio_buffer_size, conf.audio_device_name);
- // if audio is enabled, reset the display for second time to avoid glitches
- reset_display();
- }
- run = RUN;
- } else {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected on begin loop.");
- if (conf.wait_for_device == 1) {- run = WAIT_FOR_DEVICE;
- } else {- run = QUIT;
- }
- }
+ // Process the application's main callback roughly at 120 Hz
+ SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "120");
- // wait until device is connected
- if (conf.wait_for_device == 1) {- static uint32_t ticks_poll_device = 0;
- static uint32_t ticks_update_screen = 0;
+ struct app_context *ctx = SDL_calloc(1, sizeof(struct app_context));
+ if (ctx == NULL) {+ SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "SDL_calloc failed: %s", SDL_GetError());
+ return SDL_APP_FAILURE;
+ }
- if (port_inited == 0) {- screensaver_init();
- }
+ *appstate = ctx;
+ ctx->app_state = INITIALIZE;
+ ctx->conf = initialize_config(argc, argv, &ctx->preferred_device, &config_filename);
+ ctx->device_connected =
+ handle_m8_connection_init(ctx->conf.wait_for_device, ctx->preferred_device);
- while (run == WAIT_FOR_DEVICE) {- // get current input
- const input_msg_s input = get_input_msg(&conf);
- if (input.type == special && input.value == msg_quit) {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Input message QUIT.");
- run = QUIT;
- }
+ if (!renderer_initialize(&ctx->conf)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer.");
+ return SDL_APP_FAILURE;
+ }
- if (SDL_GetTicks() - ticks_update_screen > 16) {- ticks_update_screen = SDL_GetTicks();
- screensaver_draw();
- render_screen();
- }
+#ifndef NDEBUG
+ // Show debug messages in the application log
+ SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
+ SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
+#else
+ // Show debug messages in the application log
+ SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
+#endif
- // Poll for M8 device every second
- if (port_inited == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {- ticks_poll_device = SDL_GetTicks();
- if (run == WAIT_FOR_DEVICE && init_serial(0, preferred_device) == 1) {+ if (gamepads_initialize() < 0) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize game controllers.");
+ return SDL_APP_FAILURE;
+ }
- if (conf.audio_enabled == 1) {- if (audio_init(conf.audio_buffer_size, conf.audio_device_name) == 0) {- SDL_Log("Cannot initialize audio");- conf.audio_enabled = 0;
- }
- }
-
- const int result = enable_and_reset_display();
- // Device was found; enable display and proceed to the main loop
- if (result == 1) {- run = RUN;
- port_inited = 1;
- screensaver_destroy();
- } else {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
- run = QUIT;
- screensaver_destroy();
- }
- }
- }
- SDL_Delay(conf.idle_ms);
- }
- } else {- // classic startup behaviour, exit if device is not found
- if (port_inited == 0) {- if (conf.audio_enabled == 1) {- audio_destroy();
- }
- gamecontrollers_close();
- close_renderer();
- kill_inline_font();
- SDL_free(serial_buf);
- SDL_Quit();
- return -1;
- }
+ if (ctx->device_connected && m8_enable_display(1)) {+ if (ctx->conf.audio_enabled) {+ audio_initialize(ctx->conf.audio_device_name, ctx->conf.audio_buffer_size);
}
+ ctx->app_state = RUN;
+ render_screen(&ctx->conf);
+ m8_reset_display(); // Second reset to avoid display glitches.
+ } else {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+ ctx->device_connected = 0;
+ ctx->app_state = ctx->conf.wait_for_device ? WAIT_FOR_DEVICE : QUIT;
+ }
- // main loop
- while (run == RUN) {+ return SDL_APP_CONTINUE;
+}
- // get current inputs
- const input_msg_s input = get_input_msg(&conf);
+void SDL_AppQuit(void *appstate, SDL_AppResult result) {+ (void)result; // Suppress compiler warning
- switch (input.type) {- case normal:
- if (input.value != prev_input) {- prev_input = input.value;
- send_msg_controller(input.value);
- }
- break;
- case keyjazz:
- if (input.value != 0) {- if (input.eventType == SDL_KEYDOWN && input.value != prev_input) {- send_msg_keyjazz(input.value, input.value2);
- prev_note = input.value;
- } else if (input.eventType == SDL_KEYUP && input.value == prev_note) {- send_msg_keyjazz(0xFF, 0);
- }
- }
- prev_input = input.value;
- break;
- case special:
- if (input.value != prev_input) {- prev_input = input.value;
- switch (input.value) {- case msg_quit:
- SDL_Log("Received msg_quit from input device.");- run = 0;
- break;
- case msg_reset_display:
- reset_display();
- break;
- case msg_toggle_audio:
- toggle_audio(conf.audio_buffer_size, conf.audio_device_name);
- break;
- default:
- break;
- }
- break;
- }
- }
+ struct app_context *app = appstate;
- while (1) {- // read serial port
- const int bytes_read = serial_read(serial_buf, serial_read_size);
- if (bytes_read < 0) {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial.", bytes_read);
- run = QUIT;
- break;
- }
- if (bytes_read > 0) {- // input from device: reset the zero byte counter and create a
- // pointer to the serial buffer
- zerobyte_packets = 0;
- const uint8_t *cur = serial_buf;
- const uint8_t *end = serial_buf + bytes_read;
- while (cur < end) {- // process the incoming bytes into commands and draw them
- const int n = slip_read_byte(&slip, *cur++);
- if (n != SLIP_NO_ERROR) {- if (n == SLIP_ERROR_INVALID_PACKET) {- reset_display();
- } else {- SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
- }
- }
- }
- } else {- // zero byte packet, increment counter
- zerobyte_packets++;
- if (zerobyte_packets > conf.wait_packets) {- zerobyte_packets = 0;
-
- // try opening the serial port to check if it's alive
- if (check_serial_port()) {- // the device is still there, carry on
- break;
- }
- port_inited = 0;
- run = WAIT_FOR_DEVICE;
- close_serial_port();
- if (conf.audio_enabled == 1) {- audio_destroy();
- }
- /* we'll make one more loop to see if the device is still there
- * but just sending zero bytes. if it doesn't get detected when
- * resetting the port, it will disconnect */
- }
- break;
- }
- }
- render_screen();
- SDL_Delay(conf.idle_ms);
+ if (app) {+ if (app->app_state == WAIT_FOR_DEVICE) {+ screensaver_destroy();
}
- } while (run > QUIT);
- // main loop end
+ if (app->conf.audio_enabled) {+ audio_close();
+ }
+ gamepads_close();
+ renderer_close();
+ inline_font_close();
+ if (app->device_connected) {+ m8_close();
+ }
+ SDL_free(app);
- // exit, clean up
- SDL_Log("Shutting down\n");- if (conf.audio_enabled == 1) {- audio_destroy();
+ SDL_Log("Shutting down.");+ SDL_Quit();
}
- gamecontrollers_close();
- close_renderer();
- close_serial_port();
- SDL_free(serial_buf);
- SDL_Quit();
- return 0;
-}
+}
\ No newline at end of file
--- a/src/render.c
+++ b/src/render.c
@@ -3,29 +3,34 @@
#include "render.h"
-#include <SDL.h>
-#include <stdio.h>
+#include <SDL3/SDL.h>
#include "SDL2_inprint.h"
#include "command.h"
+#include "config.h"
#include "fx_cube.h"
-#include "font1.h"
-#include "font2.h"
-#include "font3.h"
-#include "font4.h"
-#include "font5.h"
-#include "inline_font.h"
+#include "fonts/font1.h"
+#include "fonts/font2.h"
+#include "fonts/font3.h"
+#include "fonts/font4.h"
+#include "fonts/font5.h"
+#include "fonts/inline_font.h"
-SDL_Window *win;
-SDL_Renderer *rend;
-SDL_Texture *maintexture;
-SDL_Color background_color = (SDL_Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0x00};+#include <stdlib.h>
+static SDL_Window *win;
+static SDL_Renderer *rend;
+static SDL_Texture *main_texture;
+static SDL_Texture *hd_texture = NULL;
+static SDL_Color global_background_color = (SDL_Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0x00};+static SDL_RendererLogicalPresentation window_scaling_mode = SDL_LOGICAL_PRESENTATION_INTEGER_SCALE;
+static SDL_ScaleMode texture_scaling_mode = SDL_SCALEMODE_NEAREST;
+
static uint32_t ticks_fps;
static int fps;
static int font_mode = -1;
-static int m8_hardware_model = 0;
+static unsigned int m8_hardware_model = 0;
static int screen_offset_y = 0;
static int text_offset_y = 0;
static int waveform_max_height = 24;
@@ -32,7 +37,10 @@
static int texture_width = 320;
static int texture_height = 240;
+static int hd_texture_width, hd_texture_height = 0;
+static int screensaver_initialized = 0;
+
struct inline_font *fonts[5] = {&font_v1_small, &font_v1_large, &font_v2_small, &font_v2_large,&font_v2_huge};
@@ -40,93 +48,136 @@
static uint8_t dirty = 0;
-// Initializes SDL and creates a renderer and required surfaces
-int initialize_sdl(const int init_fullscreen, const int init_use_gpu) {+void setup_hd_texture_scaling(void) {+ // Fullscreen scaling: use an intermediate texture with the highest possible integer size factor
- if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {- SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init: %s\n", SDL_GetError());
- return -1;
+ int window_width, window_height;
+ if (!SDL_GetWindowSizeInPixels(win, &window_width, &window_height)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't get window size: %s", SDL_GetError());
}
- // SDL documentation recommends this
- atexit(SDL_Quit);
+ // Determine the texture aspect ratio
+ const float texture_aspect_ratio = (float)texture_width / (float)texture_height;
- win = SDL_CreateWindow(
- "m8c", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, texture_width * 2, texture_height * 2,
- SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | init_fullscreen);
+ // Determine the window aspect ratio
+ const float window_aspect_ratio = (float)window_width / (float)window_height;
- rend =
- SDL_CreateRenderer(win, -1, init_use_gpu ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);
+ SDL_Texture *og_texture = SDL_GetRenderTarget(rend);
+ SDL_SetRenderTarget(rend, NULL);
+ // SDL forces black borders in letterbox mode, so in HD mode the texture scaling is manual
+ SDL_SetRenderLogicalPresentation(rend, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
+ SDL_SetTextureScaleMode(main_texture, SDL_SCALEMODE_NEAREST);
- SDL_RenderSetLogicalSize(rend, texture_width, texture_height);
+ // Check the aspect ratio to avoid unnecessary antialiasing
+ if (texture_aspect_ratio == window_aspect_ratio) {+ SDL_SetTextureScaleMode(hd_texture, SDL_SCALEMODE_NEAREST);
+ } else {+ SDL_SetTextureScaleMode(hd_texture, SDL_SCALEMODE_LINEAR);
+ }
+ SDL_SetRenderTarget(rend, og_texture);
+}
- maintexture = NULL;
+// Creates an intermediate texture dynamically based on window size
+static void create_hd_texture() {+ int window_width, window_height;
- maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
- texture_width, texture_height);
+ // Get the current window size
+ SDL_GetWindowSizeInPixels(win, &window_width, &window_height);
- SDL_SetRenderTarget(rend, maintexture);
+ // Calculate the maximum integer scaling factor
+ int scale_factor = SDL_min(window_width / texture_width, window_height / texture_height);
+ if (scale_factor < 1) {+ scale_factor = 1; // Ensure at least 1x scaling
+ }
- SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
- background_color.a);
+ // Calculate the HD texture size
+ const int new_hd_texture_width = texture_width * scale_factor;
+ const int new_hd_texture_height = texture_height * scale_factor;
+ if (hd_texture != NULL && new_hd_texture_width == hd_texture_width && new_hd_texture_height == hd_texture_height) {+ // Texture exists and there is no change in the size, carry on
+ SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "HD texture size not changed, skipping");
+ return;
+ }
- SDL_RenderClear(rend);
+ hd_texture_width = new_hd_texture_width;
+ hd_texture_height = new_hd_texture_height;
- set_font_mode(0);
+ SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Creating HD texture, scale factor: %d, size: %dx%d", scale_factor,
+ hd_texture_width, hd_texture_height);
- SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO);
+ // Destroy any existing HD texture
+ if (hd_texture != NULL) {+ SDL_DestroyTexture(hd_texture);
+ }
- dirty = 1;
+ // Create a new HD texture with the calculated size
+ hd_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+ hd_texture_width, hd_texture_height);
+ if (!hd_texture) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't create HD texture: %s", SDL_GetError());
+ }
- return 1;
+ // Optionally, set a linear scaling mode for smoother rendering
+ if (!SDL_SetTextureScaleMode(hd_texture, SDL_SCALEMODE_LINEAR)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't set HD texture scale mode: %s",
+ SDL_GetError());
+ }
+
+ setup_hd_texture_scaling();
}
static void change_font(struct inline_font *font) {- kill_inline_font();
- inrenderer(rend);
- prepare_inline_font(font);
+ inline_font_close();
+ inline_font_set_renderer(rend);
+ inline_font_initialize(font);
}
-static void check_and_adjust_window_and_texture_size(const unsigned int new_width,
- const unsigned int new_height) {+static void check_and_adjust_window_and_texture_size(const int new_width, const int new_height) {- int h, w;
+ if (texture_width == new_width && texture_height == new_height) {+ return;
+ }
+ int window_h, window_w;
+
texture_width = new_width;
texture_height = new_height;
// Query window size and resize if smaller than default
- SDL_GetWindowSize(win, &w, &h);
- if (w < texture_width * 2 || h < texture_height * 2) {+ SDL_GetWindowSize(win, &window_w, &window_h);
+ if (window_w < texture_width * 2 || window_h < texture_height * 2) {SDL_SetWindowSize(win, texture_width * 2, texture_height * 2);
}
- SDL_DestroyTexture(maintexture);
+ if (hd_texture != NULL) {+ SDL_DestroyTexture(hd_texture);
+ create_hd_texture(); // Create the texture dynamically based on window size
+ setup_hd_texture_scaling();
+ }
- SDL_RenderSetLogicalSize(rend, texture_width, texture_height);
+ if (main_texture != NULL) {+ SDL_DestroyTexture(main_texture);
+ }
- maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
- texture_width, texture_height);
-
- SDL_SetRenderTarget(rend, maintexture);
+ main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+ texture_width, texture_height);
+ SDL_SetTextureScaleMode(main_texture, texture_scaling_mode);
+ SDL_SetRenderTarget(rend, main_texture);
}
-// Set M8 hardware model in use. 0 = MK1, 1 = MK2
+// Set the M8 hardware model in use. 0 = MK1, 1 = MK2
void set_m8_model(const unsigned int model) {- switch (model) {- case 1:
+ if (model == 1) {m8_hardware_model = 1;
check_and_adjust_window_and_texture_size(480, 320);
- break;
- default:
+ } else {m8_hardware_model = 0;
check_and_adjust_window_and_texture_size(320, 240);
- break;
}
}
-void set_font_mode(int mode) {+void renderer_set_font_mode(int mode) { if (mode < 0 || mode > 2) {// bad font mode
return;
@@ -146,19 +197,31 @@
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Font mode %i, Screen offset %i", mode, screen_offset_y);
}
-void close_renderer() {- kill_inline_font();
- SDL_DestroyTexture(maintexture);
+void renderer_close(void) {+ SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Closing renderer");
+ inline_font_close();
+ if (main_texture != NULL) {+ SDL_DestroyTexture(main_texture);
+ }
+ if (hd_texture != NULL) {+ SDL_DestroyTexture(hd_texture);
+ }
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
}
-void toggle_fullscreen() {+void toggle_fullscreen(void) {- const int fullscreen_state = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN;
+ const unsigned long fullscreen_state = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN;
- SDL_SetWindowFullscreen(win, fullscreen_state ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
- SDL_ShowCursor(fullscreen_state);
+ SDL_SetWindowFullscreen(win, fullscreen_state ? false : true);
+ SDL_SyncWindow(win);
+ if (fullscreen_state) {+ // Show cursor when in a windowed state
+ SDL_ShowCursor();
+ } else {+ SDL_HideCursor();
+ }
dirty = 1;
}
@@ -171,14 +234,13 @@
command->background.r << 16 | command->background.g << 8 | command->background.b;
/* Notes:
- If large font is enabled, offset the screen elements by a fixed amount.
- If background and foreground colors are the same, draw transparent
+ If a large font is enabled, offset the screen elements by a fixed amount.
+ If background and foreground colors are the same, draw a transparent
background. Due to the font bitmaps, a different pixel offset is needed for
both*/
inprint(rend, (char *)&command->c, command->pos.x,
- command->pos.y + text_offset_y + screen_offset_y, fgcolor,
- bgcolor);
+ command->pos.y + text_offset_y + screen_offset_y, fgcolor, bgcolor);
dirty = 1;
@@ -187,23 +249,23 @@
void draw_rectangle(struct draw_rectangle_command *command) {- SDL_Rect render_rect;
+ SDL_FRect render_rect;
- render_rect.x = command->pos.x;
- render_rect.y = command->pos.y + screen_offset_y;
+ render_rect.x = (float)command->pos.x;
+ render_rect.y = (float)(command->pos.y + screen_offset_y);
render_rect.h = command->size.height;
render_rect.w = command->size.width;
// Background color changed
- if (render_rect.x == 0 && render_rect.y <= 0 && render_rect.w == texture_width &&
- render_rect.h >= texture_height) {+ if (render_rect.x == 0 && render_rect.y <= 0 && render_rect.w == (float)texture_width &&
+ render_rect.h >= (float)texture_height) {SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "BG color change: %d %d %d", command->color.r,
command->color.g, command->color.b);
- background_color.r = command->color.r;
- background_color.g = command->color.g;
- background_color.b = command->color.b;
- background_color.a = 0xFF;
- SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "x:%i, y:%i, w:%i, h:%i", render_rect.x,
+ global_background_color.r = command->color.r;
+ global_background_color.g = command->color.g;
+ global_background_color.b = command->color.b;
+ global_background_color.a = 0xFF;
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "x:%f, y:%f, w:%f, h:%f", render_rect.x,
render_rect.y, render_rect.w, render_rect.h);
#ifdef __ANDROID__
@@ -223,44 +285,42 @@
static uint8_t wfm_cleared = 0;
static int prev_waveform_size = 0;
- // If the waveform is not being displayed and it's already been cleared, skip
- // rendering it
+ // If the waveform is not being displayed, and it's already been cleared, skip rendering it
if (!(wfm_cleared && command->waveform_size == 0)) {- SDL_Rect wf_rect;
+ SDL_FRect wf_rect;
if (command->waveform_size > 0) {- wf_rect.x = texture_width - command->waveform_size;
+ wf_rect.x = (float)(texture_width - command->waveform_size);
wf_rect.y = 0;
wf_rect.w = command->waveform_size;
- wf_rect.h = waveform_max_height + 1;
+ wf_rect.h = (float)(waveform_max_height + 1);
} else {- wf_rect.x = texture_width - prev_waveform_size;
+ wf_rect.x = (float)(texture_width - prev_waveform_size);
wf_rect.y = 0;
- wf_rect.w = prev_waveform_size;
- wf_rect.h = waveform_max_height + 1;
+ wf_rect.w = (float)prev_waveform_size;
+ wf_rect.h = (float)(waveform_max_height + 1);
}
prev_waveform_size = command->waveform_size;
- SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
- background_color.a);
+ SDL_SetRenderDrawColor(rend, global_background_color.r, global_background_color.g,
+ global_background_color.b, global_background_color.a);
SDL_RenderFillRect(rend, &wf_rect);
SDL_SetRenderDrawColor(rend, command->color.r, command->color.g, command->color.b, 255);
- // Create a SDL_Point array of the waveform pixels for batch drawing
- SDL_Point waveform_points[command->waveform_size];
+ // Create an SDL_Point array of the waveform pixels for batch drawing
+ SDL_FPoint waveform_points[command->waveform_size];
for (int i = 0; i < command->waveform_size; i++) {- // Limit value because the oscilloscope commands seem to glitch
- // occasionally
+ // Limit value to avoid random glitches
if (command->waveform[i] > waveform_max_height) {command->waveform[i] = waveform_max_height;
}
- waveform_points[i].x = i + wf_rect.x;
+ waveform_points[i].x = (float)i + wf_rect.x;
waveform_points[i].y = command->waveform[i];
}
- SDL_RenderDrawPoints(rend, waveform_points, command->waveform_size);
+ SDL_RenderPoints(rend, waveform_points, command->waveform_size);
// The packet we just drew was an empty waveform
if (command->waveform_size == 0) {@@ -278,59 +338,258 @@
const Uint16 overlay_offset_x = texture_width - (fonts[font_mode]->glyph_x * 7 + 1);
const Uint16 overlay_offset_y = texture_height - (fonts[font_mode]->glyph_y + 1);
- const Uint32 bgcolor =
- background_color.r << 16 | background_color.g << 8 | background_color.b;
+ const Uint32 bg_color =
+ global_background_color.r << 16 | global_background_color.g << 8 | global_background_color.b;
if (show) {char overlay_text[7];
- snprintf(overlay_text, sizeof(overlay_text), "%02X %u", velocity, base_octave);
- inprint(rend, overlay_text, overlay_offset_x, overlay_offset_y, 0xC8C8C8, bgcolor);
+ SDL_snprintf(overlay_text, sizeof(overlay_text), "%02X %u", velocity, base_octave);
+ inprint(rend, overlay_text, overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
inprint(rend, "*", overlay_offset_x + (fonts[font_mode]->glyph_x * 5 + 5), overlay_offset_y,
- 0xFF0000, bgcolor);
+ 0xFF0000, bg_color);
} else {- inprint(rend, " ", overlay_offset_x, overlay_offset_y, 0xC8C8C8, bgcolor);
+ inprint(rend, " ", overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
}
dirty = 1;
}
-void render_screen() {- if (dirty) {- dirty = 0;
- SDL_SetRenderTarget(rend, NULL);
+static void log_fps_stats(void) {+ fps++;
- SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
- background_color.a);
+ if (SDL_GetTicks() - ticks_fps > 5000) {+ ticks_fps = SDL_GetTicks();
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%.1f fps\n", (float)fps / 5);
+ fps = 0;
+ }
+}
- SDL_RenderClear(rend);
- SDL_RenderCopy(rend, maintexture, NULL, NULL);
- SDL_RenderPresent(rend);
- SDL_SetRenderTarget(rend, maintexture);
+// Initializes SDL and creates a renderer and required surfaces
+int renderer_initialize(config_params_s *conf) {- fps++;
+ // SDL documentation recommends this
+ atexit(SDL_Quit);
- if (SDL_GetTicks() - ticks_fps > 5000) {- ticks_fps = SDL_GetTicks();
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%.1f fps\n", (float)fps / 5);
- fps = 0;
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == false) {+ SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init: %s", SDL_GetError());
+ return 0;
+ }
+
+ if (!SDL_CreateWindowAndRenderer("m8c", texture_width * 2, texture_height * 2,+ SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY |
+ SDL_WINDOW_OPENGL | conf->init_fullscreen,
+ &win, &rend)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s",
+ SDL_GetError());
+ return false;
+ }
+
+ SDL_SetRenderVSync(rend, 1);
+
+ if (!SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, window_scaling_mode)) {+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set renderer logical presentation: %s",
+ SDL_GetError());
+ return false;
+ }
+
+ main_texture = NULL;
+ main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+ texture_width, texture_height);
+
+ if (main_texture == NULL) {+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s", SDL_GetError());
+ return false;
+ }
+
+ if (conf->integer_scaling == 0) {+ // Create the HD texture dynamically based on window size
+ create_hd_texture();
+ }
+
+ SDL_SetTextureScaleMode(main_texture, texture_scaling_mode);
+
+ SDL_SetRenderTarget(rend, main_texture);
+
+ SDL_SetRenderDrawColor(rend, global_background_color.r, global_background_color.g,
+ global_background_color.b, global_background_color.a);
+
+ if (!SDL_RenderClear(rend)) {+ SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Couldn't clear renderer: %s", SDL_GetError());
+ return 0;
+ }
+
+ renderer_set_font_mode(0);
+
+ SDL_SetHint(SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1");
+ renderer_fix_texture_scaling_after_window_resize(conf); // iOS needs this, doesn't hurt on others either
+
+ dirty = 1;
+
+ SDL_PumpEvents();
+ render_screen(conf);
+
+ return 1;
+}
+
+void render_screen(config_params_s *conf) {+ if (!dirty) {+ // No draw commands have been issued since the last function call, do nothing
+ return;
+ }
+
+ if (!conf) {+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "render_screen configuration parameter is NULL.");
+ return;
+ }
+
+ dirty = 0;
+
+ if (!SDL_SetRenderTarget(rend, NULL)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't set renderer target to window: %s",
+ SDL_GetError());
+ }
+
+ if (!SDL_SetRenderDrawColor(rend, global_background_color.r, global_background_color.g,
+ global_background_color.b, global_background_color.a)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't set render draw color: %s", SDL_GetError());
+ }
+
+ if (!SDL_RenderClear(rend)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't clear renderer: %s", SDL_GetError());
+ }
+
+ if (conf->integer_scaling) {+ // Direct rendering with integer scaling
+ if (!SDL_RenderTexture(rend, main_texture, NULL, NULL)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't render texture: %s", SDL_GetError());
}
+ } else {+ int window_width, window_height;
+ if (!SDL_GetWindowSizeInPixels(win, &window_width, &window_height)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't get window size: %s", SDL_GetError());
+ }
+
+ // Determine the texture aspect ratio
+ const float texture_aspect_ratio = (float)texture_width / (float)texture_height;
+
+ // Determine the window aspect ratio
+ const float window_aspect_ratio = (float)window_width / (float)window_height;
+
+ // Ensure that HD texture exists
+ if (hd_texture == NULL) {+ create_hd_texture(); // Create the texture dynamically based on window size
+ }
+
+ if (!SDL_SetRenderTarget(rend, hd_texture)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't set HD render target: %s", SDL_GetError());
+ }
+
+ // Fill HD texture with BG color
+ SDL_SetRenderDrawColor(rend, global_background_color.r, global_background_color.g,
+ global_background_color.b, global_background_color.a);
+
+ if (!SDL_RenderClear(rend)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't clear HD texture: %s", SDL_GetError());
+ }
+
+ // Render the main texture to hd_texture. It has the same aspect ratio, so a NULL rect works.
+ if (!SDL_RenderTexture(rend, main_texture, NULL, NULL)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't render main texture to HD texture: %s",
+ SDL_GetError());
+ };
+
+ // Switch the render target back to the window
+ if (!SDL_SetRenderTarget(rend, NULL)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't reset render target to window: %s",
+ SDL_GetError());
+ }
+
+ float texture_width_hd, texture_height_hd;
+ SDL_GetTextureSize(hd_texture, &texture_width_hd, &texture_height_hd);
+
+ SDL_FRect dest_rect;
+
+ if (window_aspect_ratio > texture_aspect_ratio) {+ // Window is relatively wider than the texture
+ dest_rect.h = (float)window_height;
+ dest_rect.w = dest_rect.h * texture_aspect_ratio;
+ dest_rect.x = ((float)window_width - dest_rect.w) / 2.0f;
+ dest_rect.y = 0;
+ SDL_RenderTexture(rend, hd_texture, NULL, &dest_rect);
+ } else if (window_aspect_ratio < texture_aspect_ratio) {+ // Window is relatively taller than the texture
+ dest_rect.w = (float)window_width;
+ dest_rect.h = dest_rect.w / texture_aspect_ratio;
+ dest_rect.x = 0;
+ dest_rect.y = ((float)window_height - dest_rect.h) / 2.0f;
+ // Render the HD texture with the calculated destination rectangle
+ SDL_RenderTexture(rend, hd_texture, NULL, &dest_rect);
+ } else {+ // Window and texture aspect ratios match
+ SDL_RenderTexture(rend, hd_texture, NULL, NULL);
+ }
}
+
+ if (!SDL_RenderPresent(rend)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't present renderer: %s", SDL_GetError());
+ }
+
+ if (!SDL_SetRenderTarget(rend, main_texture)) {+ SDL_LogCritical(SDL_LOG_CATEGORY_RENDER, "Couldn't set renderer target to texture: %s",
+ SDL_GetError());
+ }
+
+ log_fps_stats();
}
-void screensaver_init() {- set_font_mode(1);
+int screensaver_init(void) {+ if (screensaver_initialized) {+ return 1;
+ }
+ SDL_SetRenderTarget(rend, main_texture);
+ renderer_set_font_mode(1);
+ global_background_color.r = 0, global_background_color.g = 0, global_background_color.b = 0;
fx_cube_init(rend, (SDL_Color){255, 255, 255, 255}, texture_width, texture_height,fonts[font_mode]->glyph_x);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver initialized");
+ screensaver_initialized = 1;
+ return 1;
}
-void screensaver_draw() {- fx_cube_update();
- dirty = 1;
-}
+void screensaver_draw(void) { dirty = fx_cube_update(); }-void screensaver_destroy() {+void screensaver_destroy(void) {fx_cube_destroy();
- set_font_mode(0);
+ renderer_set_font_mode(0);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver destroyed");
+ screensaver_initialized = 0;
}
+
+void renderer_fix_texture_scaling_after_window_resize(config_params_s *conf) {+ SDL_SetRenderTarget(rend, NULL);
+ if (conf->integer_scaling) {+ // SDL internal integer scaling works well for this purpose
+ SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height,
+ SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
+ } else {+ if (hd_texture != NULL) {+ create_hd_texture(); // Recreate hd texture if necessary
+ }
+ setup_hd_texture_scaling();
+ }
+ SDL_SetTextureScaleMode(main_texture, texture_scaling_mode);
+}
+
+void show_error_message(const char *message) {+ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "m8c error", message, win);
+}
+
+void renderer_clear_screen(void) {+ SDL_SetRenderDrawColor(rend, global_background_color.r, global_background_color.g,
+ global_background_color.b, global_background_color.a);
+ SDL_SetRenderTarget(rend, main_texture);
+ SDL_RenderClear(rend);
+ SDL_SetRenderTarget(rend, NULL);
+
+ SDL_RenderClear(rend);
+}
\ No newline at end of file
--- a/src/render.h
+++ b/src/render.h
@@ -4,25 +4,31 @@
#ifndef RENDER_H_
#define RENDER_H_
-#include <stdint.h>
#include "command.h"
+#include "config.h"
-int initialize_sdl(int init_fullscreen, int init_use_gpu);
-void close_renderer();
+#include <stdint.h>
+int renderer_initialize(config_params_s *conf);
+void renderer_close(void);
+void renderer_set_font_mode(int mode);
+void renderer_fix_texture_scaling_after_window_resize(config_params_s *conf);
+void renderer_clear_screen(void);
+
void draw_waveform(struct draw_oscilloscope_waveform_command *command);
void draw_rectangle(struct draw_rectangle_command *command);
int draw_character(struct draw_character_command *command);
-void set_font_mode(int mode);
+
void set_m8_model(unsigned int model);
-void view_changed(int view);
-void render_screen();
-void toggle_fullscreen();
+void render_screen(config_params_s *conf);
+void toggle_fullscreen(void);
void display_keyjazz_overlay(uint8_t show, uint8_t base_octave, uint8_t velocity);
-void screensaver_init();
-void screensaver_draw();
-void screensaver_destroy();
+void show_error_message(const char *message);
+
+int screensaver_init(void);
+void screensaver_draw(void);
+void screensaver_destroy(void);
#endif
--- a/src/ringbuffer.c
+++ /dev/null
@@ -1,55 +1,0 @@
-#include "ringbuffer.h"
-#include <SDL.h>
-
-RingBuffer *ring_buffer_create(uint32_t size) {- RingBuffer *rb = SDL_malloc(sizeof(*rb));
- rb->buffer = SDL_malloc(sizeof(*rb->buffer) * size);
- rb->head = 0;
- rb->tail = 0;
- rb->max_size = size;
- rb->size = 0;
- return rb;
-}
-
-void ring_buffer_free(RingBuffer *rb) {- free(rb->buffer);
- free(rb);
-}
-
-uint32_t ring_buffer_empty(RingBuffer *rb) { return rb->size == 0; }-
-uint32_t ring_buffer_full(RingBuffer *rb) { return rb->size == rb->max_size; }-
-uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length) {- if (ring_buffer_full(rb)) {- return -1; // buffer full, push fails
- }
- uint32_t space1 = rb->max_size - rb->tail;
- uint32_t n = length <= rb->max_size - rb->size ? length : rb->max_size - rb->size;
- if (n <= space1) {- SDL_memcpy(rb->buffer + rb->tail, data, n);
- } else {- SDL_memcpy(rb->buffer + rb->tail, data, space1);
- SDL_memcpy(rb->buffer, data + space1, n - space1);
- }
- rb->tail = (rb->tail + n) % rb->max_size;
- rb->size += n;
- return n; // push successful, returns number of bytes pushed
-}
-
-uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length) {- if (ring_buffer_empty(rb)) {- return -1; // buffer empty, pop fails
- }
- uint32_t space1 = rb->max_size - rb->head;
- uint32_t n = length <= rb->size ? length : rb->size;
- if (n <= space1) {- SDL_memcpy(data, rb->buffer + rb->head, n);
- } else {- SDL_memcpy(data, rb->buffer + rb->head, space1);
- SDL_memcpy(data + space1, rb->buffer, n - space1);
- }
- rb->head = (rb->head + n) % rb->max_size;
- rb->size -= n;
- return n; // pop successful, returns number of bytes popped
-}
--- a/src/ringbuffer.h
+++ /dev/null
@@ -1,24 +1,0 @@
-#ifndef M8C_RINGBUFFER_H
-#define M8C_RINGBUFFER_H
-
-#include <stdint.h>
-
-typedef struct {- uint8_t *buffer;
- uint32_t head;
- uint32_t tail;
- uint32_t max_size;
- uint32_t size;
-} RingBuffer;
-
-RingBuffer *ring_buffer_create(uint32_t size);
-
-uint32_t ring_buffer_empty(RingBuffer *rb);
-
-uint32_t ring_buffer_pop(RingBuffer *rb, uint8_t *data, uint32_t length);
-
-uint32_t ring_buffer_push(RingBuffer *rb, const uint8_t *data, uint32_t length);
-
-void ring_buffer_free(RingBuffer *rb);
-
-#endif // M8C_RINGBUFFER_H
--- a/src/serial.c
+++ /dev/null
@@ -1,268 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-// Contains portions of code from libserialport's examples released to the
-// public domain
-
-#ifndef USE_LIBUSB
-#include <SDL.h>
-#include <libserialport.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "serial.h"
-
-struct sp_port *m8_port = NULL;
-
-// Helper function for error handling
-static int check(enum sp_return result);
-
-static int detect_m8_serial_device(const struct sp_port *m8_port) {- // Check the connection method - we want USB serial devices
- const enum sp_transport transport = sp_get_port_transport(m8_port);
-
- if (transport == SP_TRANSPORT_USB) {- // Get the USB vendor and product IDs.
- int usb_vid, usb_pid;
- sp_get_port_usb_vid_pid(m8_port, &usb_vid, &usb_pid);
-
- if (usb_vid == 0x16C0 && usb_pid == 0x048A)
- return 1;
- }
-
- return 0;
-}
-
-int list_devices() {- struct sp_port **port_list;
- const enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- SDL_Log("Found M8 device: %s", sp_get_port_name(port));- }
- }
-
- sp_free_port_list(port_list);
- return 0;
-}
-
-// Checks for connected devices and whether the specified device still exists
-int check_serial_port() {-
- int device_found = 0;
-
- /* A pointer to a null-terminated array of pointers to
- * struct sp_port, which will contain the ports found.*/
- struct sp_port **port_list;
-
- /* Call sp_list_ports() to get the ports. The port_list
- * pointer will be updated to refer to the array created. */
- const enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- /* Iterate through the ports. When port_list[i] is NULL
- * this indicates the end of the list. */
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- if (strcmp(sp_get_port_name(port), sp_get_port_name(m8_port)) == 0)
- device_found = 1;
- }
- }
-
- sp_free_port_list(port_list);
- return device_found;
-}
-
-int init_serial(const int verbose, const char *preferred_device) {- if (m8_port != NULL) {- // Port is already initialized
- return 1;
- }
- /* A pointer to a null-terminated array of pointers to
- * struct sp_port, which will contain the ports found.*/
- struct sp_port **port_list;
-
- if (verbose)
- SDL_Log("Looking for USB serial devices.\n");-
- /* Call sp_list_ports() to get the ports. The port_list
- * pointer will be updated to refer to the array created. */
- enum sp_return result = sp_list_ports(&port_list);
-
- if (result != SP_OK) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "sp_list_ports() failed!\n");
- abort();
- }
-
- /* Iterate through the ports. When port_list[i] is NULL
- * this indicates the end of the list. */
- for (int i = 0; port_list[i] != NULL; i++) {- const struct sp_port *port = port_list[i];
-
- if (detect_m8_serial_device(port)) {- char *port_name = sp_get_port_name(port);
- SDL_Log("Found M8 in %s.\n", port_name);- sp_copy_port(port, &m8_port);
- if (preferred_device != NULL && strcmp(preferred_device, port_name) == 0) {- SDL_Log("Found preferred device, breaking");- break;
- }
- }
- }
-
- sp_free_port_list(port_list);
-
- if (m8_port != NULL) {- // Open the serial port and configure it
- SDL_Log("Opening port.");-
- result = sp_open(m8_port, SP_MODE_READ_WRITE);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_baudrate(m8_port, 115200);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_bits(m8_port, 8);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_parity(m8_port, SP_PARITY_NONE);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_stopbits(m8_port, 1);
- if (check(result) != SP_OK)
- return 0;
-
- result = sp_set_flowcontrol(m8_port, SP_FLOWCONTROL_NONE);
- if (check(result) != SP_OK)
- return 0;
- } else {- if (verbose) {- SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8.\n");
- }
- return 0;
- }
-
- return 1;
-}
-
-// Helper function for error handling.
-static int check(const enum sp_return result) {-
- char *error_message;
-
- switch (result) {- case SP_ERR_ARG:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid argument.\n");
- break;
- case SP_ERR_FAIL:
- error_message = sp_last_error_message();
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Failed: %s\n", error_message);
- sp_free_error_message(error_message);
- break;
- case SP_ERR_SUPP:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Not supported.\n");
- break;
- case SP_ERR_MEM:
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Couldn't allocate memory.\n");
- break;
- case SP_OK:
- default:
- break;
- }
- return result;
-}
-
-int reset_display() {- SDL_Log("Reset display\n");-
- const char buf[1] = {'R'};- int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
- return 0;
- }
- return 1;
-}
-
-int enable_and_reset_display() {-
- SDL_Log("Enabling and resetting M8 display\n");-
- const char buf[1] = {'E'};- int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
- return 0;
- }
-
- result = reset_display();
-
- return result;
-}
-
-int disconnect() {-
- SDL_Log("Disconnecting M8\n");-
- const char buf[1] = {'D'};-
- int result = sp_blocking_write(m8_port, buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
- result = 0;
- }
- sp_close(m8_port);
- sp_free_port(m8_port);
- m8_port = NULL;
- return result;
-}
-
-int serial_read(uint8_t *serial_buf, const int count) {- return sp_nonblocking_read(m8_port, serial_buf, count);
-}
-
-int send_msg_controller(const uint8_t input) {- const char buf[2] = {'C', input};- const size_t nbytes = 2;
- int result = sp_blocking_write(m8_port, buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
- return -1;
- }
- return 1;
-}
-
-int send_msg_keyjazz(const uint8_t note, uint8_t velocity) {- if (velocity > 0x7F)
- velocity = 0x7F;
- const char buf[3] = {'K', note, velocity};- const size_t nbytes = 3;
- int result = sp_blocking_write(m8_port, buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
- return -1;
- }
-
- return 1;
-}
-#endif
--- a/src/serial.h
+++ /dev/null
@@ -1,27 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-#ifndef _SERIAL_H_
-#define _SERIAL_H_
-#include <stdint.h>
-
-#ifdef USE_LIBUSB
-// Max packet length of the USB endpoint
-#define serial_read_size 1024
-int init_serial_with_file_descriptor(int file_descriptor);
-#else
-// maximum amount of bytes to read from the serial in one read()
-#define serial_read_size 1024
-#endif
-
-int init_serial(int verbose, const char *preferred_device);
-int list_devices();
-int check_serial_port();
-int reset_display();
-int enable_and_reset_display();
-int disconnect();
-int serial_read(uint8_t *serial_buf, int count);
-int send_msg_controller(uint8_t input);
-int send_msg_keyjazz(uint8_t note, uint8_t velocity);
-
-#endif
--- a/src/slip.c
+++ /dev/null
@@ -1,114 +1,0 @@
-/*
-MIT License
-
-Copyright (c) 2018 Marcin Borowicz
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-/* This code is originally by marcinbor85, https://github.com/marcinbor85/slip
-It has been simplified a bit as CRC checking etc. is not required in this
-program. */
-
-#include "slip.h"
-
-#include <assert.h>
-#include <stddef.h>
-
-static void reset_rx(slip_handler_s *slip) {- assert(slip != NULL);
-
- slip->state = SLIP_STATE_NORMAL;
- slip->size = 0;
-}
-
-slip_error_t slip_init(slip_handler_s *slip, const slip_descriptor_s *descriptor) {- assert(slip != NULL);
- assert(descriptor != NULL);
- assert(descriptor->buf != NULL);
- assert(descriptor->recv_message != NULL);
-
- slip->descriptor = descriptor;
- reset_rx(slip);
-
- return SLIP_NO_ERROR;
-}
-
-static slip_error_t put_byte_to_buffer(slip_handler_s *slip, const uint8_t byte) {- slip_error_t error = SLIP_NO_ERROR;
-
- assert(slip != NULL);
-
- if (slip->size >= slip->descriptor->buf_size) {- error = SLIP_ERROR_BUFFER_OVERFLOW;
- reset_rx(slip);
- } else {- slip->descriptor->buf[slip->size++] = byte;
- slip->state = SLIP_STATE_NORMAL;
- }
-
- return error;
-}
-
-slip_error_t slip_read_byte(slip_handler_s *slip, uint8_t byte) {- slip_error_t error = SLIP_NO_ERROR;
-
- assert(slip != NULL);
-
- switch (slip->state) {- case SLIP_STATE_NORMAL:
- switch (byte) {- case SLIP_SPECIAL_BYTE_END:
- if (!slip->descriptor->recv_message(slip->descriptor->buf, slip->size)) {- error = SLIP_ERROR_INVALID_PACKET;
- }
- reset_rx(slip);
- break;
- case SLIP_SPECIAL_BYTE_ESC:
- slip->state = SLIP_STATE_ESCAPED;
- break;
- default:
- error = put_byte_to_buffer(slip, byte);
- break;
- }
- break;
-
- case SLIP_STATE_ESCAPED:
- switch (byte) {- case SLIP_ESCAPED_BYTE_END:
- byte = SLIP_SPECIAL_BYTE_END;
- break;
- case SLIP_ESCAPED_BYTE_ESC:
- byte = SLIP_SPECIAL_BYTE_ESC;
- break;
- default:
- error = SLIP_ERROR_UNKNOWN_ESCAPED_BYTE;
- reset_rx(slip);
- break;
- }
-
- if (error != SLIP_NO_ERROR)
- break;
-
- error = put_byte_to_buffer(slip, byte);
- break;
- }
-
- return error;
-}
--- a/src/slip.h
+++ /dev/null
@@ -1,67 +1,0 @@
-/*
-MIT License
-
-Copyright (c) 2018 Marcin Borowicz
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-/* This code is originally by marcinbor85, https://github.com/marcinbor85/slip
-It has been simplified a bit as CRC checking etc. is not required in this
-program. */
-
-#ifndef SLIP_H_
-#define SLIP_H_
-
-#include <stdint.h>
-
-#define SLIP_SPECIAL_BYTE_END 0xC0
-#define SLIP_SPECIAL_BYTE_ESC 0xDB
-
-#define SLIP_ESCAPED_BYTE_END 0xDC
-#define SLIP_ESCAPED_BYTE_ESC 0xDD
-
-typedef enum {- SLIP_STATE_NORMAL = 0x00,
- SLIP_STATE_ESCAPED
-} slip_state_t;
-
-typedef struct {- uint8_t *buf;
- uint32_t buf_size;
- int (*recv_message)(uint8_t *data, uint32_t size);
-} slip_descriptor_s;
-
-typedef struct {- slip_state_t state;
- uint32_t size;
- const slip_descriptor_s *descriptor;
-} slip_handler_s;
-
-typedef enum {- SLIP_NO_ERROR = 0x00,
- SLIP_ERROR_BUFFER_OVERFLOW,
- SLIP_ERROR_UNKNOWN_ESCAPED_BYTE,
- SLIP_ERROR_INVALID_PACKET
-} slip_error_t;
-
-slip_error_t slip_init(slip_handler_s *slip, const slip_descriptor_s *descriptor);
-slip_error_t slip_read_byte(slip_handler_s *slip, uint8_t byte);
-
-#endif
\ No newline at end of file
--- a/src/usb.c
+++ /dev/null
@@ -1,363 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-// Contains portions of code from libserialport's examples released to the
-// public domain
-#ifdef USE_LIBUSB
-
-#include <SDL.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "usb.h"
-
-static int ep_out_addr = 0x03;
-static int ep_in_addr = 0x83;
-
-#define ACM_CTRL_DTR 0x01
-#define ACM_CTRL_RTS 0x02
-
-#define M8_VID 0x16c0
-#define M8_PID 0x048a
-
-libusb_context *ctx = NULL;
-libusb_device_handle *devh = NULL;
-
-static int do_exit = 0;
-
-int list_devices() {- int r;
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
-
- libusb_device **device_list = NULL;
- int count = libusb_get_device_list(ctx, &device_list);
- for (size_t idx = 0; idx < count; ++idx) {- libusb_device *device = device_list[idx];
- struct libusb_device_descriptor desc;
- int rc = libusb_get_device_descriptor(device, &desc);
- if (rc < 0) {- SDL_Log("Error");- libusb_free_device_list(device_list, 1);
- return rc;
- }
-
- if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {- SDL_Log("Found M8 device: %d:%d\n", libusb_get_port_number(device),- libusb_get_bus_number(device));
- }
- }
- libusb_free_device_list(device_list, 1);
- return 0;
-}
-
-int usb_loop(void *data) {- SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
- while (!do_exit) {- int rc = libusb_handle_events(ctx);
- if (rc != LIBUSB_SUCCESS) {- SDL_Log("Audio loop error: %s\n", libusb_error_name(rc));- break;
- }
- }
- return 0;
-}
-
-static SDL_Thread *usb_thread;
-
-static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer) {- int *completed = transfer->user_data;
- *completed = 1;
-}
-
-int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {- if (devh == NULL) {- return -1;
- }
-
- int completed = 0;
-
- struct libusb_transfer *transfer;
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, devh, endpoint, serial_buf, count, xfr_cb_in, &completed,
- timeout_ms);
- int r = libusb_submit_transfer(transfer);
-
- if (r < 0) {- SDL_Log("Error");- libusb_free_transfer(transfer);
- return r;
- }
-
-retry:
- libusb_lock_event_waiters(ctx);
- while (!completed) {- if (!libusb_event_handler_active(ctx)) {- libusb_unlock_event_waiters(ctx);
- goto retry;
- }
- libusb_wait_for_event(ctx, NULL);
- }
- libusb_unlock_event_waiters(ctx);
-
- int actual_length = transfer->actual_length;
-
- libusb_free_transfer(transfer);
-
- return actual_length;
-}
-
-int blocking_write(void *buf, int count, unsigned int timeout_ms) {- return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
-}
-
-int serial_read(uint8_t *serial_buf, int count) {- return bulk_transfer(ep_in_addr, serial_buf, count, 1);
-}
-
-int check_serial_port() {- // Reading will fail anyway when the device is not present anymore
- return 1;
-}
-
-int init_interface() {-
- if (devh == NULL) {- SDL_Log("Device not initialised!");- return 0;
- }
-
- int rc;
-
- for (int if_num = 0; if_num < 2; if_num++) {- if (libusb_kernel_driver_active(devh, if_num)) {- SDL_Log("Detaching kernel driver for interface %d", if_num);- libusb_detach_kernel_driver(devh, if_num);
- }
- rc = libusb_claim_interface(devh, if_num);
- if (rc < 0) {- SDL_Log("Error claiming interface: %s", libusb_error_name(rc));- return 0;
- }
- }
-
- /* Start configuring the device:
- * - set line state
- */
- SDL_Log("Setting line state");- rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, 0);
- if (rc < 0) {- SDL_Log("Error during control transfer: %s", libusb_error_name(rc));- return 0;
- }
-
- /* - set line encoding: here 115200 8N1
- * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
- */
- SDL_Log("Set line encoding");- unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};- rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding, sizeof(encoding), 0);
- if (rc < 0) {- SDL_Log("Error during control transfer: %s", libusb_error_name(rc));- return 0;
- }
-
- usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
-
- return 1;
-}
-
-int init_serial_with_file_descriptor(int file_descriptor) {- SDL_Log("Initialising serial with file descriptor");-
- if (file_descriptor <= 0) {- SDL_Log("Invalid file descriptor: %d", file_descriptor);- return 0;
- }
-
- int r;
- r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
- if (r != LIBUSB_SUCCESS) {- SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));- return 0;
- }
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
- r = libusb_wrap_sys_device(ctx, (intptr_t)file_descriptor, &devh);
- if (r < 0) {- SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));- return 0;
- } else if (devh == NULL) {- SDL_Log("libusb_wrap_sys_device returned invalid handle");- return 0;
- }
- SDL_Log("USB device init success");-
- return init_interface();
-}
-
-int init_serial(int verbose, char *preferred_device) {-
- if (devh != NULL) {- return 1;
- }
-
- int r;
- r = libusb_init(&ctx);
- if (r < 0) {- SDL_Log("libusb_init failed: %s", libusb_error_name(r));- return 0;
- }
- if (preferred_device == NULL) {- devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
- } else {- char *port;
- char *saveptr = NULL;
- char *bus;
- port = SDL_strtokr(preferred_device, ":", &saveptr);
- bus = SDL_strtokr(NULL, ":", &saveptr);
- libusb_device **device_list = NULL;
- int count = libusb_get_device_list(ctx, &device_list);
- for (size_t idx = 0; idx < count; ++idx) {- libusb_device *device = device_list[idx];
- struct libusb_device_descriptor desc;
- r = libusb_get_device_descriptor(device, &desc);
- if (r < 0) {- SDL_Log("libusb_get_device_descriptor failed: %s", libusb_error_name(r));- libusb_free_device_list(device_list, 1);
- return 0;
- }
-
- if (desc.idVendor == M8_VID && desc.idProduct == M8_PID) {- SDL_Log("Searching for port %s and bus %s", port, bus);- if (libusb_get_port_number(device) == SDL_atoi(port) &&
- libusb_get_bus_number(device) == SDL_atoi(bus)) {- SDL_Log("Preferred device found, connecting");- r = libusb_open(device, &devh);
- if (r < 0) {- SDL_Log("libusb_open failed: %s", libusb_error_name(r));- return 0;
- }
- }
- }
- }
- libusb_free_device_list(device_list, 1);
- if (devh == NULL) {- SDL_Log("Preferred device %s not found, using first available", preferred_device);- devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
- }
- }
- if (devh == NULL) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,
- "libusb_open_device_with_vid_pid returned invalid handle");
- return 0;
- }
- SDL_Log("USB device init success");-
- return init_interface();
-}
-
-int reset_display() {- int result;
-
- SDL_Log("Reset display\n");-
- char buf[1] = {'R'};-
- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d", result);
- return 0;
- }
- return 1;
-}
-
-int enable_and_reset_display() {- int result;
-
- SDL_Log("Enabling and resetting M8 display\n");-
- char buf[1] = {'E'};- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d", result);
- return 0;
- }
-
- SDL_Delay(5);
- result = reset_display();
- return result;
-}
-
-int disconnect() {-
- char buf[1] = {'D'};- int result;
-
- SDL_Log("Disconnecting M8\n");-
- result = blocking_write(buf, 1, 5);
- if (result != 1) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
- return -1;
- }
-
- int rc;
-
- if (devh != NULL) {-
- for (int if_num = 0; if_num < 2; if_num++) {- rc = libusb_release_interface(devh, if_num);
- if (rc < 0) {- SDL_Log("Error releasing interface: %s", libusb_error_name(rc));- return 0;
- }
- }
-
- do_exit = 1;
-
- libusb_close(devh);
- }
-
- SDL_WaitThread(usb_thread, NULL);
-
- libusb_exit(ctx);
-
- return 1;
-}
-
-int send_msg_controller(uint8_t input) {- char buf[2] = {'C', input};- int nbytes = 2;
- int result;
- result = blocking_write(buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d", result);
- return -1;
- }
- return 1;
-}
-
-int send_msg_keyjazz(uint8_t note, uint8_t velocity) {- if (velocity > 0x7F)
- velocity = 0x7F;
- char buf[3] = {'K', note, velocity};- int nbytes = 3;
- int result;
- result = blocking_write(buf, nbytes, 5);
- if (result != nbytes) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d", result);
- return -1;
- }
-
- return 1;
-}
-
-#endif
--- a/src/usb.h
+++ /dev/null
@@ -1,9 +1,0 @@
-#ifndef M8C_USB_H_
-#define M8C_USB_H_
-#ifdef USE_LIBUSB
-
-#include <libusb.h>
-extern libusb_device_handle *devh;
-
-#endif // USE_LIBUSB
-#endif // M8C_USB_H_
--- a/src/usb_audio.c
+++ /dev/null
@@ -1,200 +1,0 @@
-#ifdef USE_LIBUSB
-
-#include "ringbuffer.h"
-#include "usb.h"
-#include <SDL.h>
-#include <errno.h>
-#include <libusb.h>
-
-#define EP_ISO_IN 0x85
-#define IFACE_NUM 4
-
-#define NUM_TRANSFERS 64
-#define PACKET_SIZE 180
-#define NUM_PACKETS 2
-
-SDL_AudioDeviceID sdl_audio_device_id = 0;
-RingBuffer *audio_buffer = NULL;
-
-static void audio_callback(void *userdata, Uint8 *stream, int len) {- uint32_t read_len = ring_buffer_pop(audio_buffer, stream, len);
-
- if (read_len == -1) {- SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow!");
- }
-
- // If we didn't read the full len bytes, fill the rest with zeros
- if (read_len < len) {- SDL_memset(&stream[read_len], 0, len - read_len);
- }
-}
-
-static void cb_xfr(struct libusb_transfer *xfr) {- unsigned int i;
-
- for (i = 0; i < xfr->num_iso_packets; i++) {- struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
-
- if (pack->status != LIBUSB_TRANSFER_COMPLETED) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
- libusb_error_name(pack->status));
- /* This doesn't happen, so bail out if it does. */
- return;
- }
-
- const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
- if (sdl_audio_device_id != 0) {- uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
- if (actual == -1) {- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
- }
- }
- }
-
- if (libusb_submit_transfer(xfr) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB\n");
- SDL_free(xfr->buffer);
- }
-}
-
-static struct libusb_transfer *xfr[NUM_TRANSFERS];
-
-static int benchmark_in() {- int i;
-
- for (i = 0; i < NUM_TRANSFERS; i++) {- xfr[i] = libusb_alloc_transfer(NUM_PACKETS);
- if (!xfr[i]) {- SDL_Log("Could not allocate transfer");- return -ENOMEM;
- }
-
- Uint8 *buffer = SDL_malloc(PACKET_SIZE * NUM_PACKETS);
-
- libusb_fill_iso_transfer(xfr[i], devh, EP_ISO_IN, buffer, PACKET_SIZE * NUM_PACKETS,
- NUM_PACKETS, cb_xfr, NULL, 0);
- libusb_set_iso_packet_lengths(xfr[i], PACKET_SIZE);
-
- libusb_submit_transfer(xfr[i]);
- }
-
- return 1;
-}
-
-int audio_init(int audio_buffer_size, const char *output_device_name) {- SDL_Log("USB audio setup");-
- int rc;
-
- rc = libusb_kernel_driver_active(devh, IFACE_NUM);
- if (rc == 1) {- SDL_Log("Detaching kernel driver");- rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
- if (rc < 0) {- SDL_Log("Could not detach kernel driver: %s\n", libusb_error_name(rc));- return rc;
- }
- }
-
- rc = libusb_claim_interface(devh, IFACE_NUM);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error claiming interface: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- rc = libusb_set_interface_alt_setting(devh, IFACE_NUM, 1);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error setting alt setting: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- if (!SDL_WasInit(SDL_INIT_AUDIO)) {- if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Init audio failed %s", SDL_GetError());
- return -1;
- }
- } else {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Audio was already initialised");
- }
-
- static SDL_AudioSpec audio_spec;
- audio_spec.format = AUDIO_S16;
- audio_spec.channels = 2;
- audio_spec.freq = 44100;
- audio_spec.samples = audio_buffer_size;
- audio_spec.callback = audio_callback;
-
- SDL_AudioSpec _obtained;
- SDL_zero(_obtained);
-
- SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),- output_device_name);
-
- if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {- SDL_Log("Using default audio device");- sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, &_obtained, 0);
- } else {- sdl_audio_device_id = SDL_OpenAudioDevice(output_device_name, 0, &audio_spec, &_obtained, 0);
- }
-
- audio_buffer = ring_buffer_create(4 * _obtained.size);
-
- SDL_Log("Obtained audio spec. Sample rate: %d, channels: %d, samples: %d, size: %d",- _obtained.freq, _obtained.channels, _obtained.samples, +_obtained.size);
-
- SDL_PauseAudioDevice(sdl_audio_device_id, 0);
-
- // Good to go
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Starting capture");
- if ((rc = benchmark_in()) < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Capture failed to start: %d", rc);
- return rc;
- }
-
- SDL_Log("Successful init");- return 1;
-}
-
-int audio_destroy() {- if (devh == NULL) {- return -1;
- }
-
- SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Closing audio");
-
- int i, rc;
-
- for (i = 0; i < NUM_TRANSFERS; i++) {- rc = libusb_cancel_transfer(xfr[i]);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error cancelling transfer: %s\n",
- libusb_error_name(rc));
- }
- }
-
- SDL_Log("Freeing interface %d", IFACE_NUM);-
- rc = libusb_release_interface(devh, IFACE_NUM);
- if (rc < 0) {- SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error releasing interface: %s\n", libusb_error_name(rc));
- return rc;
- }
-
- if (sdl_audio_device_id != 0) {- SDL_Log("Closing audio device %d", sdl_audio_device_id);- SDL_AudioDeviceID device = sdl_audio_device_id;
- sdl_audio_device_id = 0;
- SDL_CloseAudioDevice(device);
- }
-
- SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Audio closed");
-
- ring_buffer_free(audio_buffer);
- return 1;
-}
-
-void toggle_audio(unsigned int audio_buffer_size, const char *output_device_name) {- SDL_Log("Libusb audio toggling not implemented yet");-}
-
-#endif
--
⑨