shithub: m8c

Download patch

ref: 42872cf9dd18c5f9d29add8c1b4e3668b90d6ede
parent: ade0037a6fcb50567e156bf59cdbfb28543de39f
parent: dc9503f082e21098218c2657a3a15c32c2d39b76
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Sun Mar 23 12:47:50 EDT 2025

Merge pull request #186 from laamaa/rtmidi

Refactor systems that handle device connection related things, add MIDI sysex backend

--- a/.github/workflows/build-macos-arm.yml
+++ b/.github/workflows/build-macos-arm.yml
@@ -28,7 +28,7 @@
         id: buildApplication
         continue-on-error: true
         run: |
-          mkdir -p build_arm64 && cd build_arm64 && cmake .. && cpack -V
+          mkdir -p build_arm64 && cd build_arm64 && cmake .. -DCMAKE_BUILD_TYPE=Release && cpack -V
 
       - name: 'View debug log if compilation fails'
         if: failure() && steps.buildApplication.outcome == 'failure'
--- a/.github/workflows/build-macos-intel.yml
+++ b/.github/workflows/build-macos-intel.yml
@@ -24,7 +24,7 @@
         id: buildApplication
         continue-on-error: true
         run: |
-          mkdir -p build_x86_64 && cd build_x86_64 && cmake .. && cpack -V
+          mkdir -p build_x86_64 && cd build_x86_64 && cmake .. -DCMAKE_BUILD_TYPE=Release && cpack -V
 
       - name: 'View debug log if compilation fails'
         if: failure() && steps.buildApplication.outcome == 'failure'
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -30,7 +30,7 @@
         id: cache-x86_64-sdl3-files
         uses: actions/cache@v4
         with:
-          path: 'SDL3-${{ env.SDL_VERSION }}'
+          path: 'SDL3-3.2.8'
           key: linux-x86_64-sdl3-files
 
       - name: 'Download SDL3 sources'
@@ -45,8 +45,13 @@
         run: |
           pushd SDL3-$SDL_VERSION
           mkdir build_x86_64
-          cmake -S . -B 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
 
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -43,7 +43,7 @@
       id: cache-sdl3-files
       uses: actions/cache@v4
       with:
-        path: 'SDL3-3.2.4'
+        path: 'SDL3-3.2.8'
         key: win-sdl3-files-${{ matrix.sys }}
 
     - name: 'Download SDL3 sources'
@@ -95,7 +95,7 @@
 
     - name: 'Build package'
       run: |
-        cmake . -G Ninja -DCMAKE_PREFIX_PATH=C:/Libraries
+        cmake . -G Ninja -DCMAKE_PREFIX_PATH=C:/Libraries -DCMAKE_BUILD_TYPE=Release
         cmake --build .
         strip -g m8c.exe
         if [ ${{ matrix.win }} == "win32" ]
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,25 +2,35 @@
 
 project(m8c LANGUAGES C)
 
-set(CMAKE_C_FLAGS "-O2 -Wall -Wextra")
-
 set(APP_NAME m8c)
 
+if (NOT DEFINED USE_LIBSERIALPORT AND NOT DEFINED USE_LIBUSB AND NOT DEFINED USE_RTMIDI)
+    set(USE_LIBSERIALPORT 1)
+endif ()
+
 find_package(PkgConfig REQUIRED)
 find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
 
 pkg_check_modules(SDL3 REQUIRED sdl3)
+if (USE_LIBSERIALPORT)
+    pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
+    link_directories(${SDL3_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})
+    add_compile_definitions(USE_LIBSERIALPORT)
+endif (USE_LIBSERIALPORT)
 if (USE_LIBUSB)
     pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
     link_directories(${SDL3_LIBRARY_DIRS} ${LIBUSB_LIBRARY_DIRS})
     add_compile_definitions(USE_LIBUSB)
-else ()
-    pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
-    link_directories(${SDL3_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})
 endif (USE_LIBUSB)
+if (USE_RTMIDI)
+    pkg_check_modules(RTMIDI REQUIRED rtmidi)
+    link_directories(${SDL3_LIBRARY_DIRS} ${RTMIDI_LIBRARY_DIRS})
+    add_compile_definitions(USE_RTMIDI)
+endif (USE_RTMIDI)
 
-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(APP_ICON ${MACOS_CONTENTS}/Resources/m8c.icns)
@@ -29,22 +39,30 @@
 
 add_executable(${APP_NAME} WIN32 MACOSX_BUNDLE ${APP_ICON} ${m8c_SRC})
 
+if (USE_LIBSERIALPORT)
+    target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})
+    target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})
+    target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})
+endif ()
+
 if (USE_LIBUSB)
     target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBUSB_LIBRARIES})
     target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBUSB_INCLUDE_DIRS})
     target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBUSB_CFLAGS_OTHER})
-else ()
-    target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})
-    target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})
-    target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})
 endif ()
 
+if (USE_RTMIDI)
+    target_link_libraries(${APP_NAME} ${SDL3_LIBRARIES} ${RTMIDI_LIBRARIES})
+    target_include_directories(${APP_NAME} PUBLIC ${SDL3_INCLUDE_DIRS} ${RTMIDI_INCLUDE_DIRS})
+    target_compile_options(${APP_NAME} PUBLIC ${SDL3_CFLAGS_OTHER} ${RTMIDI_CFLAGS_OTHER})
+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_target_properties(${APP_NAME} PROPERTIES
             MACOSX_BUNDLE TRUE
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,43 @@
 #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
+OBJ = src/main.o \
+src/command.o \
+src/config.o \
+src/fx_cube.o \
+src/gamecontrollers.o \
+src/ini.o \
+src/inprint2.o \
+src/input.o \
+src/backends/queue.o \
+src/render.o \
+src/backends/audio_sdl.o \
+src/backends/audio_libusb.o \
+src/backends/m8_libserialport.o \
+src/backends/m8_libusb.o \
+src/backends/m8_rtmidi.o \
+src/backends/ringbuffer.o \
+src/backends/slip.o \
 
 #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
+DEPS = \
+src/command.h \
+src/config.h \
+src/fx_cube.h \
+src/gamecontrollers.h \
+src/ini.h \
+src/input.h \
+src/render.h \
+src/backends/audio.h \
+src/backends/m8.h \
+src/backends/ringbuffer.h \
+src/backends/queue.h \
+src/backends/slip.h \
+src/fonts/inline_font.h
 
 #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) -Wall -Wextra -O2 -pipe -I.
+local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libserialport) -DUSE_LIBSERIALPORT -Wall -Wextra -O2 -pipe -I. -DNDEBUG
 
 #Set the compiler you are using ( gcc for C or g++ for C++ )
 CC = gcc
@@ -28,14 +57,18 @@
 	$(CC) -o $@ $^ $(local_CFLAGS) $(INCLUDES)
 
 libusb: INCLUDES = $(shell pkg-config --libs sdl3 libusb-1.0)
-libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
+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/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 "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,159 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-#ifndef USE_LIBUSB
-#include "audio.h"
-#include <SDL3/SDL.h>
-#include <stdint.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 SDL_AudioSpec audio_spec_out = {SDL_AUDIO_S16LE, 2, 44100};
-
-static void SDLCALL audio_cb_out(void *userdata, SDL_AudioStream *stream, int length, int unused) {
-  // suppress compiler warnings
-  (void)userdata;
-  (void)length;
-  (void)unused;
-
-  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_destroy();
-    return;
-  }
-
-  Uint8 *src_audio_data = SDL_malloc(bytes_available);
-  const int bytes_read = SDL_GetAudioStreamData(audio_stream_in, src_audio_data, bytes_available);
-  if (bytes_read == bytes_available) {
-    SDL_PutAudioStreamData(stream, src_audio_data, bytes_read);
-  } else {
-    SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
-                 "Error getting available audio stream bytes: %s, destroying audio",
-                 SDL_GetError());
-    audio_destroy();
-  }
-  SDL_free(src_audio_data);
-}
-
-void toggle_audio(const char *output_device_name, unsigned int audio_buffer_size) {
-  if (!audio_initialized) {
-    audio_init(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_init(const char *output_device_name, const unsigned int audio_buffer_size) {
-
-  // wait for system to initialize possible new audio devices
-  SDL_Delay(500);
-
-  int num_devices_in, num_devices_out;
-  SDL_AudioDeviceID m8_device_id = 0;
-  SDL_AudioDeviceID output_device_id = 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;
-  }
-
-  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_Log("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_LogInfo(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);
-  SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, audio_buffer_size_str);
-
-  audio_stream_out =
-      SDL_OpenAudioDeviceStream(output_device_id, &audio_spec_out, audio_cb_out, NULL);
-
-  if (!audio_stream_out) {
-    SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio output device: %s", SDL_GetError());
-    return 0;
-  }
-  SDL_Log("Audiospec Out: format %d, channels %d, rate %d", audio_spec_out.format,
-          audio_spec_out.channels, audio_spec_out.freq);
-
-  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_Log("Audiospec In: format %d, channels %d, rate %d", audio_spec_in.format,
-          audio_spec_in.channels, audio_spec_in.freq);
-
-  SDL_SetAudioStreamFormat(audio_stream_in, &audio_spec_in, &audio_spec_out);
-
-  SDL_ResumeAudioStreamDevice(audio_stream_out);
-  SDL_ResumeAudioStreamDevice(audio_stream_in);
-
-  audio_paused = 0;
-  audio_initialized = 1;
-
-  return 1;
-}
-
-void audio_destroy() {
-  if (!audio_initialized)
-    return;
-  SDL_Log("Closing audio devices");
-  SDL_DestroyAudioStream(audio_stream_in);
-  SDL_DestroyAudioStream(audio_stream_out);
-  audio_initialized = 0;
-}
-
-#endif
--- a/src/audio.h
+++ /dev/null
@@ -1,11 +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(const char *output_device_name, unsigned int audio_buffer_size);
-void toggle_audio(const char *output_device_name, unsigned int audio_buffer_size);
-void process_audio();
-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,206 @@
+#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_AudioDeviceID sdl_audio_device_id = 0;
+int audio_initialized = 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_initialize(int audio_buffer_size, const char *output_device_name) {
+  SDL_LogError(SDL_LOG_CATEGORY_AUDIO,"LIBUSB audio not implemented yet");
+  return -1;
+  /*
+  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 = SDL_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;
+  */
+}
+
+void audio_close() {
+  if (devh == NULL || !audio_initialized) {
+    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_device_id != 0) {
+    SDL_Log("Closing audio device %d", sdl_audio_device_id);
+    const 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);
+}
+
+void audio_toggle(unsigned int audio_buffer_size, const char *output_device_name) {
+  SDL_Log("Libusb audio toggling not implemented yet");
+}
+
+#endif
--- /dev/null
+++ b/src/backends/audio_sdl.c
@@ -1,0 +1,163 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef USE_LIBUSB
+#include "audio.h"
+#include <SDL3/SDL.h>
+#include <stdint.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 SDL_AudioSpec audio_spec_out = {SDL_AUDIO_S16LE, 2, 44100};
+
+static void SDLCALL audio_cb_out(void *userdata, SDL_AudioStream *stream, int length, int unused) {
+  // suppress compiler warnings
+  (void)userdata;
+  (void)length;
+  (void)unused;
+
+  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;
+  }
+
+  Uint8 *src_audio_data = SDL_malloc(bytes_available);
+  const int bytes_read = SDL_GetAudioStreamData(audio_stream_in, src_audio_data, bytes_available);
+  if (bytes_read == bytes_available) {
+    SDL_PutAudioStreamData(stream, src_audio_data, bytes_read);
+  } else {
+    SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
+                 "Error getting available audio stream bytes: %s, destroying audio",
+                 SDL_GetError());
+    audio_close();
+  }
+  SDL_free(src_audio_data);
+}
+
+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) {
+
+  // wait for system to initialize possible new audio devices
+  SDL_Delay(500);
+
+  int num_devices_in, num_devices_out;
+  SDL_AudioDeviceID m8_device_id = 0;
+  SDL_AudioDeviceID output_device_id = 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_Log("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_Log("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, &audio_spec_out, audio_cb_out, NULL);
+
+  if (!audio_stream_out) {
+    SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Error opening audio output device: %s", SDL_GetError());
+    return 0;
+  }
+  SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audiospec Out: format %d, channels %d, rate %d",
+               audio_spec_out.format, audio_spec_out.channels, audio_spec_out.freq);
+
+  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_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audiospec In: format %d, channels %d, rate %d",
+               audio_spec_in.format, audio_spec_in.channels, audio_spec_in.freq);
+
+  SDL_SetAudioStreamFormat(audio_stream_in, &audio_spec_in, &audio_spec_out);
+
+  SDL_ResumeAudioStreamDevice(audio_stream_out);
+  SDL_ResumeAudioStreamDevice(audio_stream_in);
+
+  audio_paused = 0;
+  audio_initialized = 1;
+
+  return 1;
+}
+
+void audio_close() {
+  if (!audio_initialized)
+    return;
+  SDL_Log("Closing audio devices");
+  SDL_DestroyAudioStream(audio_stream_in);
+  SDL_DestroyAudioStream(audio_stream_out);
+  audio_initialized = 0;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/m8.h
@@ -1,0 +1,17 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+#ifndef _M8_H_
+#define _M8_H_
+
+#include "../config.h"
+
+int m8_initialize(int verbose, const char *preferred_device);
+int m8_list_devices(void);
+int m8_reset_display(void);
+int m8_enable_and_reset_display(void);
+int m8_send_msg_controller(const unsigned char input);
+int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity);
+int m8_process_data(config_params_s conf);
+int m8_close(void);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/src/backends/m8_libserialport.c
@@ -1,0 +1,334 @@
+// 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 "slip.h"
+
+// maximum amount of bytes to read from the serial in one read()
+#define serial_read_size 1024
+
+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;
+static uint16_t zero_byte_packets = 0; // used to detect device disconnection
+
+SDL_Thread *serial_thread = 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 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");
+    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 m8_initialize(const int verbose, const char *preferred_device) {
+  if (m8_port != NULL) {
+    // Port is already initialized
+    return 1;
+  }
+
+  // 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
+  };
+
+  slip_init(&slip, &slip_descriptor);
+
+  /* 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 m8_reset_display() {
+  SDL_Log("Reset display\n");
+
+  const 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_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 = m8_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 m8_send_msg_controller(const uint8_t input) {
+  const 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) {
+  if (velocity > 0x7F)
+    velocity = 0x7F;
+  const 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_process_data(const config_params_s conf) {
+  while (1) {
+    // read serial port
+    const int bytes_read = serial_read(serial_buffer, serial_read_size);
+    if (bytes_read < 0) {
+      SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial.", bytes_read);
+      disconnect();
+      return -1;
+    }
+    if (bytes_read > 0) {
+      // input from device: reset the zero byte counter and create a
+      // pointer to the serial buffer
+      zero_byte_packets = 0;
+      const uint8_t *cur = serial_buffer;
+      const uint8_t *end = serial_buffer + 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) {
+            m8_reset_display();
+          } else {
+            SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
+          }
+        }
+      }
+    } else {
+      // zero byte packet, increment counter
+      zero_byte_packets++;
+      if (zero_byte_packets > conf.wait_packets) {
+        zero_byte_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;
+        }
+        disconnect();
+        return 0;
+      }
+      break;
+    }
+  }
+  return 1;
+}
+
+int m8_close() { return disconnect(); }
+#endif
--- /dev/null
+++ b/src/backends/m8_libusb.c
@@ -1,0 +1,367 @@
+// 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 <SDL3/SDL.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.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 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 < 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_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;
+}
+
+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 m8_process_data(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));
+      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;
+  }
+
+  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 m8_initialize(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_strtok_r(preferred_device, ":", &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 < 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 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_and_reset_display() {
+  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");
+
+  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 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;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/m8_rtmidi.c
@@ -1,0 +1,293 @@
+// 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 message_is_m8_sysex(const unsigned char *message) {
+  if (memcmp(m8_sysex_header, message, m8_sysex_header_size) == 0) {
+    return true;
+  }
+  return false;
+}
+
+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 (message_size < 5 || !message_is_m8_sysex(message))
+    return;
+
+  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");
+  }
+}
+
+int initialize_rtmidi() {
+  SDL_Log("Initializing rtmidi");
+  midi_in = rtmidi_in_create(RTMIDI_API_UNSPECIFIED, "m8c_in", 1024);
+  midi_out = rtmidi_out_create(RTMIDI_API_UNSPECIFIED, "m8c_out");
+  if (midi_in == NULL || midi_out == NULL) {
+    return 0;
+  }
+  return 1;
+}
+
+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_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);
+    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;
+}
+
+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_and_reset_display(void) {
+  rtmidi_in_set_callback(midi_in, midi_callback, NULL);
+  const unsigned char enable_sysex[8] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'E', 0xF7};
+  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 enable display");
+    return 0;
+  }
+  result = m8_reset_display();
+  return result;
+}
+
+void close_and_free_midi_ports(void) {
+  SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Freeing MIDI ports");
+  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;
+}
+
+int disconnect(void) {
+  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_send_msg_controller(const unsigned char input) {
+  const unsigned char input_sysex[9] = {0xF0, 0x00, 0x02, 0x61, 0x00, 0x00, 'C', input, 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;
+}
+
+int m8_send_msg_keyjazz(const unsigned char note, unsigned char velocity) {
+  if (velocity > 0x7F) {
+    velocity = 0x7F;
+  }
+  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;
+}
+
+int m8_process_data(config_params_s conf) {
+
+  (void)conf; // unused parameter
+
+  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) {
+      SDL_Log("No messages received for %d cycles, assuming device disconnected", empty_cycles);
+      close_and_free_midi_ports();
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int m8_close() {
+  if (queue.mutex != NULL) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Destroying command queue");
+    SDL_DestroyMutex(queue.mutex);
+    SDL_DestroyCondition(queue.cond);
+  }
+  return disconnect();
+}
+
+int m8_list_devices() {
+  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;
+}
+
+#endif
--- /dev/null
+++ b/src/backends/queue.c
@@ -1,0 +1,71 @@
+#include "queue.h"
+#include <SDL3/SDL.h>
+#include <stdio.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,56 @@
+//
+// Created by jonne on 3/17/25.
+//
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include <SDL3/SDL.h>
+
+#define MAX_QUEUE_SIZE 4096
+
+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);
+
+/**
+ * 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 <SDL3/SDL_log.h>
+#include <SDL3/SDL.h>
 
 #include "command.h"
 #include "render.h"
@@ -168,7 +168,7 @@
       set_m8_model(0);
     }
 
-    set_font_mode(recv_buf[5]);
+    renderer_set_font_mode(recv_buf[5]);
 
     return 1;
   }
--- a/src/config.c
+++ b/src/config.c
@@ -19,7 +19,7 @@
   }
 }
 
-config_params_s init_config(char *filename) {
+config_params_s config_initialize(char *filename) {
   config_params_s c;
 
   if (filename == NULL) {
@@ -34,7 +34,7 @@
   c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
   // sec for default idle_ms)
   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;
@@ -182,7 +182,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);
--- a/src/config.h
+++ b/src/config.h
@@ -58,8 +58,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);
--- 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
@@ -4,6 +4,8 @@
 
 #include <math.h>
 
+// Handle screensaver cube effect
+
 static SDL_Texture *texture_cube;
 static SDL_Texture *texture_text;
 static SDL_Renderer *fx_renderer;
--- 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,7 +2,7 @@
 // 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 "fonts/inline_font.h"
 #include <SDL3/SDL.h>
 
 #define CHARACTERS_PER_ROW 94
@@ -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;
@@ -43,12 +43,12 @@
   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) {
 
--- a/src/input.c
+++ b/src/input.c
@@ -1,4 +1,6 @@
 #include "input.h"
+#include "backends/audio.h"
+#include "backends/m8.h"
 #include "config.h"
 #include "gamecontrollers.h"
 #include "render.h"
@@ -12,6 +14,55 @@
 
 static input_msg_s key = {normal, 0, 0, 0};
 
+int input_process(config_params_s conf, enum app_state *app_state) {
+  static uint8_t prev_input = 0;
+  static uint8_t prev_note = 0;
+
+  // get current inputs
+  const input_msg_s input = input_get_msg(&conf);
+
+  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.eventType == SDL_EVENT_KEY_DOWN && input.value != prev_input) {
+        m8_send_msg_keyjazz(input.value, input.value2);
+        prev_note = input.value;
+      } else if (input.eventType == SDL_EVENT_KEY_UP && input.value == prev_note) {
+        m8_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_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Received msg_quit from input device.");
+        *app_state = 0;
+        break;
+      case msg_reset_display:
+        m8_reset_display();
+        break;
+      case msg_toggle_audio:
+        conf.audio_enabled = !conf.audio_enabled;
+        audio_toggle(conf.audio_device_name, conf.audio_buffer_size);
+        break;
+      default:
+        break;
+      }
+      break;
+    }
+  }
+  return 1;
+}
+
 uint8_t toggle_input_keyjazz() {
   keyjazz_enabled = !keyjazz_enabled;
   SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, keyjazz_enabled ? "Keyjazz enabled" : "Keyjazz disabled");
@@ -289,7 +340,7 @@
 }
 
 // Returns the currently pressed keys to main
-input_msg_s get_input_msg(config_params_s *conf) {
+input_msg_s input_get_msg(config_params_s *conf) {
 
   key = (input_msg_s){normal, 0, 0, 0};
 
--- a/src/input.h
+++ b/src/input.h
@@ -7,6 +7,8 @@
 #include "config.h"
 #include <stdint.h>
 
+enum app_state { QUIT, WAIT_FOR_DEVICE, RUN };
+
 typedef enum input_buttons_t {
   INPUT_UP,
   INPUT_DOWN,
@@ -46,6 +48,7 @@
   uint32_t eventType;
 } 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(config_params_s conf, enum app_state *app_state);
 
 #endif
--- a/src/main.c
+++ b/src/main.c
@@ -9,32 +9,83 @@
 #include <signal.h>
 
 #include "SDL2_inprint.h"
-#include "audio.h"
+#include "backends/audio.h"
+#include "backends/m8.h"
 #include "command.h"
 #include "config.h"
 #include "gamecontrollers.h"
 #include "input.h"
 #include "render.h"
-#include "serial.h"
-#include "slip.h"
 
-enum state { QUIT, WAIT_FOR_DEVICE, RUN };
+enum app_state app_state = WAIT_FOR_DEVICE;
 
-enum state run = WAIT_FOR_DEVICE;
-uint8_t need_display_reset = 0;
+// Handle CTRL+C / SIGINT, SIGKILL etc.
+void signal_handler(int unused) {
+  (void)unused;
+  app_state = QUIT;
+}
 
-// Handles CTRL+C / SIGINT
-void intHandler() { run = QUIT; }
+void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
+                        config_params_s *conf) {
+  static Uint64 ticks_poll_device = 0;
+  static Uint64 ticks_update_screen = 0;
 
-void close_serial_port() { disconnect(); }
+  if (*m8_connected == 0) {
+    screensaver_init();
+  }
 
+  while (app_state == WAIT_FOR_DEVICE) {
+    // get current input
+    const input_msg_s input = input_get_msg(&*conf);
+    if (input.type == special && input.value == msg_quit) {
+      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Input message QUIT.");
+      app_state = QUIT;
+    }
+
+    if (SDL_GetTicks() - ticks_update_screen > 16) {
+      ticks_update_screen = SDL_GetTicks();
+      screensaver_draw();
+      render_screen();
+    }
+
+    // Poll for M8 device every second
+    if (*m8_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {
+      ticks_poll_device = SDL_GetTicks();
+      if (app_state == WAIT_FOR_DEVICE && m8_initialize(0, preferred_device) == 1) {
+
+        if (conf->audio_enabled == 1) {
+          if (audio_initialize(conf->audio_device_name, conf->audio_buffer_size) == 0) {
+            SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
+            conf->audio_enabled = 0;
+          }
+        }
+
+        const int m8_enabled = m8_enable_and_reset_display();
+        // Device was found; enable display and proceed to the main loop
+        if (m8_enabled == 1) {
+          app_state = RUN;
+          *m8_connected = 1;
+          screensaver_destroy();
+        } else {
+          SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
+          app_state = QUIT;
+          screensaver_destroy();
+        }
+      }
+    }
+    SDL_Delay(conf->idle_ms);
+  }
+}
+
 int main(const int argc, char *argv[]) {
 
+  char *preferred_device = NULL;
+  unsigned char m8_connected = 0;
+
   if (argc == 2 && SDL_strcmp(argv[1], "--list") == 0) {
-    return list_devices();
+    return m8_list_devices();
   }
 
-  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);
@@ -46,262 +97,104 @@
     SDL_Log("Using config file %s.\n", config_filename);
   }
 
-  // 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);
+  // Initialize the config to defaults
+  config_params_s conf = config_initialize(config_filename);
+  // Read in the params from the configfile if present
+  config_read(&conf);
 
-  // allocate memory for serial buffer
-  uint8_t *serial_buf = SDL_malloc(serial_read_size);
-
-  static uint8_t slip_buffer[serial_read_size]; // SLIP command buffer
-  SDL_zero(slip_buffer);
-
-  // 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
-  };
-
-  static slip_handler_s slip;
-
-  uint8_t prev_input = 0;
-  uint8_t prev_note = 0;
-  uint16_t zerobyte_packets = 0; // used to detect device disconnection
-  uint8_t port_inited = 0;
-
-  signal(SIGINT, intHandler);
-  signal(SIGTERM, intHandler);
+  signal(SIGINT, signal_handler);
+  signal(SIGTERM, signal_handler);
 #ifdef SIGQUIT
-  signal(SIGQUIT, intHandler);
+  signal(SIGQUIT, signal_handler);
 #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) {
-    port_inited = init_serial(1, preferred_device);
-    if (port_inited == 0) {
-      SDL_free(serial_buf);
+    m8_connected = m8_initialize(1, preferred_device);
+    if (m8_connected == 0) {
       return 1;
     }
   }
 
   // initialize all SDL systems
-  if (initialize_sdl(conf.init_fullscreen) == false) {
-    SDL_free(serial_buf);
+  if (renderer_initialize(conf.init_fullscreen) == false) {
     SDL_Quit();
     return 1;
   }
-  run = QUIT;
+  app_state = QUIT;
 
   // initial scan for (existing) gamepads
   gamecontrollers_initialize();
 
-#ifdef DEBUG_MSG
-  SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
+#ifndef NDEBUG
+  SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
+  SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
 #endif
 
   // main loop begin
   do {
-    // try to init serial port
-    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
+    // try to init connection to M8
+    m8_connected = m8_initialize(1, preferred_device);
+    if (m8_connected == 1 && m8_enable_and_reset_display() == 1) {
       if (conf.audio_enabled == 1) {
-        audio_init(conf.audio_device_name, conf.audio_buffer_size);
+        audio_initialize(conf.audio_device_name, conf.audio_buffer_size);
         // if audio is enabled, reset the display for second time to avoid glitches
-        reset_display();
+        m8_reset_display();
       }
-      run = RUN;
+      app_state = RUN;
     } else {
-      SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected on begin loop.");
+      SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected on initial check.");
       if (conf.wait_for_device == 1) {
-        run = WAIT_FOR_DEVICE;
+        app_state = WAIT_FOR_DEVICE;
       } else {
-        run = QUIT;
+        app_state = QUIT;
       }
     }
 
     // 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;
-
-      if (port_inited == 0) {
-        screensaver_init();
-      }
-
-      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 (SDL_GetTicks() - ticks_update_screen > 16) {
-          ticks_update_screen = SDL_GetTicks();
-          screensaver_draw();
-          render_screen();
-        }
-
-        // 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 (conf.audio_enabled == 1) {
-              if (audio_init(conf.audio_device_name, conf.audio_buffer_size) == 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);
-      }
+      do_wait_for_device(preferred_device, &m8_connected, &conf);
     } else {
       // classic startup behaviour, exit if device is not found
-      if (port_inited == 0) {
+      if (m8_connected == 0) {
         if (conf.audio_enabled == 1) {
-          audio_destroy();
+          audio_close();
         }
         gamecontrollers_close();
-        close_renderer();
-        kill_inline_font();
-        SDL_free(serial_buf);
+        renderer_close();
+        inline_font_close();
         SDL_Quit();
-        return -1;
+        return 1;
       }
     }
 
     // main loop
-    while (run == RUN) {
-
-      // get current inputs
-      const input_msg_s input = get_input_msg(&conf);
-
-      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_EVENT_KEY_DOWN && input.value != prev_input) {
-            send_msg_keyjazz(input.value, input.value2);
-            prev_note = input.value;
-          } else if (input.eventType == SDL_EVENT_KEY_UP && 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_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Received msg_quit from input device.");
-            run = 0;
-            break;
-          case msg_reset_display:
-            reset_display();
-            break;
-          case msg_toggle_audio:
-            conf.audio_enabled = !conf.audio_enabled;
-            toggle_audio(conf.audio_device_name,conf.audio_buffer_size);
-            break;
-          default:
-            break;
-          }
-          break;
-        }
+    while (app_state == RUN) {
+      input_process(conf, &app_state);
+      const int result = m8_process_data(conf);
+      if (result == 0) {
+        // Device disconnected
+        m8_connected = 0;
+        app_state = WAIT_FOR_DEVICE;
+        audio_close();
+      } else if (result == -1) {
+        // Fatal error
+        app_state = QUIT;
       }
-
-      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);
     }
-  } while (run > QUIT);
-  // main loop end
+  } while (app_state > QUIT);
+  // Main loop end
 
-  // exit, clean up
+  // Exit, clean up
   SDL_Log("Shutting down");
-  if (conf.audio_enabled == 1) {
-    audio_destroy();
-  }
+  audio_close();
   gamecontrollers_close();
-  close_renderer();
-  if (port_inited == 1)
-    close_serial_port();
-  SDL_free(serial_buf);
+  renderer_close();
+  if (m8_connected == 1)
+    m8_close();
   SDL_Quit();
   return 0;
 }
--- a/src/render.c
+++ b/src/render.c
@@ -4,18 +4,17 @@
 #include "render.h"
 
 #include <SDL3/SDL.h>
-#include <stdio.h>
 
 #include "SDL2_inprint.h"
 #include "command.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"
 
 #include <stdlib.h>
 
@@ -44,7 +43,7 @@
 static uint8_t dirty = 0;
 
 // Initializes SDL and creates a renderer and required surfaces
-int initialize_sdl(const unsigned int init_fullscreen) {
+int renderer_initialize(const unsigned int init_fullscreen) {
 
   if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD) == false) {
     SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "SDL_Init: %s\n", SDL_GetError());
@@ -74,10 +73,8 @@
 
   SDL_RenderClear(rend);
 
-  set_font_mode(0);
+  renderer_set_font_mode(0);
 
-  SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
-
   dirty = 1;
 
   return 1;
@@ -84,9 +81,9 @@
 }
 
 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 int new_width, const int new_height) {
@@ -126,7 +123,7 @@
   }
 }
 
-void set_font_mode(int mode) {
+void renderer_set_font_mode(int mode) {
   if (mode < 0 || mode > 2) {
     // bad font mode
     return;
@@ -146,8 +143,8 @@
   SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Font mode %i, Screen offset %i", mode, screen_offset_y);
 }
 
-void close_renderer() {
-  kill_inline_font();
+void renderer_close() {
+  inline_font_close();
   SDL_DestroyTexture(main_texture);
   SDL_DestroyRenderer(rend);
   SDL_DestroyWindow(win);
@@ -286,7 +283,7 @@
 
   if (show) {
     char overlay_text[7];
-    snprintf(overlay_text, sizeof(overlay_text), "%02X %u", velocity, base_octave);
+    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, bg_color);
@@ -321,7 +318,7 @@
 }
 
 void screensaver_init() {
-  set_font_mode(1);
+  renderer_set_font_mode(1);
   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");
@@ -334,6 +331,6 @@
 
 void screensaver_destroy() {
   fx_cube_destroy();
-  set_font_mode(0);
+  renderer_set_font_mode(0);
   SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver destroyed");
 }
--- a/src/render.h
+++ b/src/render.h
@@ -7,15 +7,14 @@
 #include <stdint.h>
 #include "command.h"
 
-int initialize_sdl(unsigned int init_fullscreen);
-void close_renderer();
+int renderer_initialize(unsigned int init_fullscreen);
+void renderer_close();
 
 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 renderer_set_font_mode(int mode);
 void set_m8_model(unsigned int model);
-void view_changed(int view);
 
 void render_screen();
 void toggle_fullscreen();
--- a/src/ringbuffer.c
+++ /dev/null
@@ -1,55 +1,0 @@
-#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
-}
--- 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 <SDL3/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'};
-  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 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;
-  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 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;
-  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;
-}
-#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 <SDL3/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 <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
-
-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
--