shithub: m8c

Download patch

ref: 02832de008c7e904f7d1a9847fc2f0094ff149d3
parent: 514e71a670e372703dc8768ed678ae1e623d7767
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Mon Feb 24 12:37:12 EST 2025

initial sdl3 migration, audio still missing

--- a/.github/workflows/build-macos-arm.yml
+++ b/.github/workflows/build-macos-arm.yml
@@ -16,7 +16,7 @@
           uname -m
     
       - name: 'Install dependencies'
-        run: brew install cmake pkg-config sdl2 libserialport 
+        run: brew install cmake pkg-config sdl3 libserialport
           
       - name: 'Checkout'
         uses: actions/checkout@v4
--- a/.github/workflows/build-macos-intel.yml
+++ b/.github/workflows/build-macos-intel.yml
@@ -10,8 +10,8 @@
   build-macos:
     runs-on: macos-14
     env:
-      SDL_VERSION: 2.32.0
-      SDL_SHA256: f5c2b52498785858f3de1e2996eba3c1b805d08fe168a47ea527c7fc339072d0
+      SDL_VERSION: 3.2.4
+      SDL_SHA256: 2938328317301dfbe30176d79c251733aa5e7ec5c436c800b99ed4da7adcb0f0
 
     steps:
       - name: 'Install dependencies'
@@ -40,23 +40,23 @@
       - name: Set current date as env variable
         run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
 
-      - name: 'Cache SDL2 files'
-        id: cache-x86_64-sdl2-files
+      - name: 'Cache SDL3 files'
+        id: cache-x86_64-sdl3-files
         uses: actions/cache@v4
         with:
-          path: 'SDL2-2.30.4'
-          key: mac-x86_64-sdl2-files        
+          path: 'SDL3-3.2.4'
+          key: mac-x86_64-sdl3-files
         
-      - name: 'Download SDL2 sources'
+      - name: 'Download SDL3 sources'
         if: steps.cache-x86_64-sdl2-files.outputs.cache-hit != 'true'
         run: |
-          (curl https://www.libsdl.org/release/SDL2-$SDL_VERSION.tar.gz || curl -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/SDL2-$SDL_VERSION.tar.gz) | tar xvf -
+          (curl https://www.libsdl.org/release/SDL3-$SDL_VERSION.tar.gz || curl -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/SDL3-$SDL_VERSION.tar.gz) | echo $SDL_SHA256 | tar xvf -
         
-      - name: 'Build SDL2'
+      - name: 'Build SDL3'
         if: steps.cache-x86_64-sdl2-files.outputs.cache-hit != 'true'
         run: |
           export MACOSX_DEPLOYMENT_TARGET="10.9"
-          pushd SDL2-$SDL_VERSION
+          pushd SDL3-$SDL_VERSION
           mkdir build_x86_64
           cd build_x86_64
           ../configure CPPFLAGS="-arch x86_64 -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -isysroot $HOME/x86_64/SDKs/MacOSX10.7.sdk" CFLAGS="-arch x86_64" CXXFLAGS="-arch x86_64" LDFLAGS="-arch x86_64 -F$HOME/x86_64/SDKs/MacOSX10.7.sdk/System/Library/Frameworks -L/usr/lib -L/usr/lib/system -Wl,-syslibroot,$HOME/x86_64/SDKs/MacOSX10.7.sdk" --host=x86_64-apple-darwin13 --prefix="$HOME/x86_64prefix"
@@ -63,9 +63,9 @@
           make
           popd
 
-      - name: 'Install SDL2'
+      - name: 'Install SDL3'
         run: |
-          pushd SDL2-$SDL_VERSION/build_x86_64
+          pushd SDL3-$SDL_VERSION/build_x86_64
           make install 
           popd
 
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -14,7 +14,7 @@
       - name: 'Install dependencies'
         run: |
           sudo apt-get update
-          sudo apt-get install --fix-missing build-essential libsdl2-dev libserialport-dev zip
+          sudo apt-get install --fix-missing build-essential libsdl3-dev libserialport-dev zip
       - name: 'Checkout'
         uses: actions/checkout@v4
 
@@ -27,7 +27,7 @@
       - name: 'Upload artifact'
         uses: actions/upload-artifact@v4
         with:
-          name: m8c-${{ env.NOW }}-linux
+          name: m8c-${{ env.NOW }}-linux-x86_64
           path: |
             LICENSE
             README.md
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -36,7 +36,7 @@
       with:
         msystem: ${{ matrix.sys }}
         update: true
-        install: mingw-w64-${{ matrix.env }}-toolchain make mingw-w64-${{ matrix.env }}-SDL2 zip dos2unix autoconf automake-wrapper libtool make unzip
+        install: mingw-w64-${{ matrix.env }}-toolchain make mingw-w64-${{ matrix.env }}-sdl3 zip dos2unix autoconf automake-wrapper libtool make unzip
 
     - name: 'Build libserialport manually'
       run: |
@@ -58,12 +58,12 @@
         strip -g m8c.exe
         if [ ${{ matrix.win }} == "win32" ]
         then        
-          cp /mingw32/bin/SDL2.dll .
+          cp /mingw32/bin/SDL3.dll .
           cp /mingw32/bin/libgcc_s_dw2-1.dll .
           cp /mingw32/bin/libserialport-0.dll .
           cp /mingw32/bin/libwinpthread-1.dll .
         else
-          cp /mingw64/bin/SDL2.dll .
+          cp /mingw64/bin/SDL3.dll .
           cp /mingw64/bin/libserialport-0.dll .
         fi
         unix2dos README.md LICENSE AUDIOGUIDE.md
@@ -74,7 +74,7 @@
         name: m8c-${{ env.NOW }}-${{ matrix.win }}
         path: |
           m8c.exe
-          SDL2.dll
+          SDL3.dll
           libserialport-0.dll
           libgcc_s_dw2-1.dll
           libwinpthread-1.dll
@@ -89,7 +89,7 @@
         name: m8c-${{ env.NOW }}-${{ matrix.win }}
         path: |
           m8c.exe
-          SDL2.dll
+          SDL3.dll
           libserialport-0.dll
           gamecontrollerdb.txt
           LICENSE
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,15 +7,16 @@
 set(APP_NAME m8c)
 
 find_package(PkgConfig REQUIRED)
+find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
 
-pkg_check_modules(SDL2 REQUIRED sdl2)
+pkg_check_modules(SDL3 REQUIRED sdl3)
 if (USE_LIBUSB)
     pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
-    link_directories(${SDL2_LIBRARY_DIRS} ${LIBUSB_LIBRARY_DIRS})
+    link_directories(${SDL3_LIBRARY_DIRS} ${LIBUSB_LIBRARY_DIRS})
     add_compile_definitions(USE_LIBUSB)
 else ()
     pkg_check_modules(LIBSERIALPORT REQUIRED libserialport)
-    link_directories(${SDL2_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})
+    link_directories(${SDL3_LIBRARY_DIRS} ${LIBSERIALPORT_LIBRARY_DIRS})
 endif (USE_LIBUSB)
 
 file(GLOB m8c_SRC "src/*.h" "src/*.c")
@@ -29,13 +30,13 @@
 add_executable(${APP_NAME} WIN32 MACOSX_BUNDLE ${APP_ICON} ${m8c_SRC})
 
 if (USE_LIBUSB)
-    target_link_libraries(${APP_NAME} ${SDL2_LIBRARIES} ${LIBUSB_LIBRARIES})
-    target_include_directories(${APP_NAME} PUBLIC ${SDL2_INCLUDE_DIRS} ${LIBUSB_INCLUDE_DIRS})
-    target_compile_options(${APP_NAME} PUBLIC ${SDL2_CFLAGS_OTHER} ${LIBUSB_CFLAGS_OTHER})
+    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} ${SDL2_LIBRARIES} ${LIBSERIALPORT_LIBRARIES})
-    target_include_directories(${APP_NAME} PUBLIC ${SDL2_INCLUDE_DIRS} ${LIBSERIALPORT_INCLUDE_DIRS})
-    target_compile_options(${APP_NAME} PUBLIC ${SDL2_CFLAGS_OTHER} ${LIBSERIALPORT_CFLAGS_OTHER})
+    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 (APPLE)
@@ -52,7 +53,7 @@
             MACOSX_BUNDLE_BUNDLE_VERSION "1"
             MACOSX_BUNDLE_COPYRIGHT "Copyright © 2021 laamaa. All rights reserved."
             MACOSX_BUNDLE_GUI_IDENTIFIER "fi.laamaa.m8c"
-            MACOSX_BUNDLE_SHORT_VERSION_STRING "1.7.8"
+            MACOSX_BUNDLE_SHORT_VERSION_STRING "2.0.0"
             MACOSX_BUNDLE_ICON_FILE "m8c.icns")
 
     set(APPS "\${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app")
--- a/Makefile
+++ b/Makefile
@@ -5,10 +5,10 @@
 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
 
 #Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
-INCLUDES = $(shell pkg-config --libs sdl2 libserialport | sed 's/-mwindows//')
+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 sdl2 libserialport) -Wall -Wextra -O2 -pipe -I.
+local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libserialport) -Wall -Wextra -O2 -pipe -I.
 
 #Set the compiler you are using ( gcc for C or g++ for C++ )
 CC = gcc
@@ -27,8 +27,8 @@
 m8c: $(OBJ)
 	$(CC) -o $@ $^ $(local_CFLAGS) $(INCLUDES)
 
-libusb: INCLUDES = $(shell pkg-config --libs sdl2 libusb-1.0)
-libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl2 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
+libusb: INCLUDES = $(shell pkg-config --libs sdl3 libusb-1.0)
+libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl3 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
 libusb: m8c
 
 #Cleanup
--- a/src/SDL2_inprint.h
+++ b/src/SDL2_inprint.h
@@ -5,7 +5,7 @@
 #define SDL2_inprint_h
 
 #include "inline_font.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 
 extern void prepare_inline_font(struct inline_font *font);
 extern void kill_inline_font(void);
--- a/src/audio.c
+++ b/src/audio.c
@@ -2,7 +2,7 @@
 // Released under the MIT licence, https://opensource.org/licenses/MIT
 #ifndef USE_LIBUSB
 #include "audio.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <stdint.h>
 
 static SDL_AudioDeviceID devid_in = 0;
@@ -12,7 +12,7 @@
 static unsigned int audio_initialized = 0;
 
 void toggle_audio(const unsigned int audio_buffer_size, const char *output_device_name) {
-  if (!audio_initialized) {
+  /*if (!audio_initialized) {
     audio_init(audio_buffer_size, output_device_name);
     return;
   }
@@ -19,16 +19,17 @@
   audio_paused = !audio_paused;
   SDL_PauseAudioDevice(devid_in, audio_paused);
   SDL_PauseAudioDevice(devid_out, audio_paused);
-  SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");
+  SDL_Log(audio_paused ? "Audio paused" : "Audio resumed");*/
 }
 
-void audio_cb_in(void *userdata, uint8_t *stream, int len) {
-  (void)userdata; // suppress compiler warning
-  SDL_QueueAudio(devid_out, stream, len);
+void SDLCALL audio_cb_in(void *userdata, uint8_t *stream, int len) {
+  /*(void)userdata; // suppress compiler warning
+  SDL_QueueAudio(devid_out, stream, len);*/
 }
 
 int audio_init(const unsigned int audio_buffer_size, const char *output_device_name) {
 
+  /*
   int m8_device_id = -1;
 
   // wait for system to initialize possible new audio devices
@@ -59,9 +60,8 @@
   // Open output device first to avoid possible Directsound errors
   SDL_zero(want_out);
   want_out.freq = 44100;
-  want_out.format = AUDIO_S16;
+  want_out.format = SDL_AUDIO_S16LE;
   want_out.channels = 2;
-  want_out.samples = audio_buffer_size;
   devid_out =
       SDL_OpenAudioDevice(output_device_name, 0, &want_out, &have_out, SDL_AUDIO_ALLOW_ANY_CHANGE);
   if (devid_out == 0) {
@@ -71,10 +71,8 @@
 
   SDL_zero(want_in);
   want_in.freq = 44100;
-  want_in.format = AUDIO_S16;
+  want_in.format = SDL_AUDIO_S16LE;
   want_in.channels = 2;
-  want_in.samples = audio_buffer_size;
-  want_in.callback = audio_cb_in;
   devid_in = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(m8_device_id, SDL_TRUE), SDL_TRUE, &want_in,
                                  &have_in, SDL_AUDIO_ALLOW_ANY_CHANGE);
   if (devid_in == 0) {
@@ -84,25 +82,23 @@
 
   // Start audio processing
   SDL_Log("Opening audio devices");
-  SDL_PauseAudioDevice(devid_in, 0);
-  SDL_PauseAudioDevice(devid_out, 0);
 
   audio_paused = 0;
   audio_initialized = 1;
 
-  return 1;
+  return 1;*/
 }
 
 void audio_destroy() {
-  if (!audio_initialized)
+  /*if (!audio_initialized)
     return;
   SDL_Log("Closing audio devices");
-  SDL_PauseAudioDevice(devid_in, 1);
-  SDL_PauseAudioDevice(devid_out, 1);
+  SDL_PauseAudioDevice(devid_in);
+  SDL_PauseAudioDevice(devid_out);
   SDL_CloseAudioDevice(devid_in);
   SDL_CloseAudioDevice(devid_out);
 
-  audio_initialized = 0;
+  audio_initialized = 0;*/
 }
 
 #endif
--- a/src/command.c
+++ b/src/command.c
@@ -1,7 +1,7 @@
 // Copyright 2021 Jonne Kokkonen
 // Released under the MIT licence, https://opensource.org/licenses/MIT
 
-#include <SDL_log.h>
+#include <SDL3/SDL_log.h>
 
 #include "command.h"
 #include "render.h"
--- a/src/config.c
+++ b/src/config.c
@@ -3,8 +3,9 @@
 
 #include "config.h"
 #include "ini.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <assert.h>
+#include <ctype.h>
 #include <stdio.h>
 
 /* Case-insensitive string compare from ini.h library */
@@ -28,7 +29,6 @@
   }
 
   c.init_fullscreen = 0; // default fullscreen state at load
-  c.init_use_gpu = 1;    // default to use hardware acceleration
   c.idle_ms = 10;        // default to high performance
   c.wait_for_device = 1; // default to exit if device disconnected
   c.wait_packets = 1024; // default zero-byte attempts to disconnect (about 2
@@ -57,25 +57,25 @@
   c.key_jazz_dec_velocity = SDL_SCANCODE_KP_PLUS;
   c.key_toggle_audio = SDL_SCANCODE_F12;
 
-  c.gamepad_up = SDL_CONTROLLER_BUTTON_DPAD_UP;
-  c.gamepad_left = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
-  c.gamepad_down = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
-  c.gamepad_right = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
-  c.gamepad_select = SDL_CONTROLLER_BUTTON_BACK;
-  c.gamepad_start = SDL_CONTROLLER_BUTTON_START;
-  c.gamepad_opt = SDL_CONTROLLER_BUTTON_B;
-  c.gamepad_edit = SDL_CONTROLLER_BUTTON_A;
-  c.gamepad_quit = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
-  c.gamepad_reset = SDL_CONTROLLER_BUTTON_LEFTSTICK;
+  c.gamepad_up = SDL_GAMEPAD_BUTTON_DPAD_UP;
+  c.gamepad_left = SDL_GAMEPAD_BUTTON_DPAD_LEFT;
+  c.gamepad_down = SDL_GAMEPAD_BUTTON_DPAD_DOWN;
+  c.gamepad_right = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
+  c.gamepad_select = SDL_GAMEPAD_BUTTON_BACK;
+  c.gamepad_start = SDL_GAMEPAD_BUTTON_START;
+  c.gamepad_opt = SDL_GAMEPAD_BUTTON_EAST;
+  c.gamepad_edit = SDL_GAMEPAD_BUTTON_SOUTH;
+  c.gamepad_quit = SDL_GAMEPAD_BUTTON_RIGHT_STICK;
+  c.gamepad_reset = SDL_GAMEPAD_BUTTON_LEFT_STICK;
 
   c.gamepad_analog_threshold = 30000;
   c.gamepad_analog_invert = 0;
-  c.gamepad_analog_axis_updown = SDL_CONTROLLER_AXIS_LEFTY;
-  c.gamepad_analog_axis_leftright = SDL_CONTROLLER_AXIS_LEFTX;
-  c.gamepad_analog_axis_start = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
-  c.gamepad_analog_axis_select = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
-  c.gamepad_analog_axis_opt = SDL_CONTROLLER_AXIS_INVALID;
-  c.gamepad_analog_axis_edit = SDL_CONTROLLER_AXIS_INVALID;
+  c.gamepad_analog_axis_updown = SDL_GAMEPAD_AXIS_LEFTY;
+  c.gamepad_analog_axis_leftright = SDL_GAMEPAD_AXIS_LEFTX;
+  c.gamepad_analog_axis_start = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
+  c.gamepad_analog_axis_select = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
+  c.gamepad_analog_axis_opt = SDL_GAMEPAD_AXIS_INVALID;
+  c.gamepad_analog_axis_edit = SDL_GAMEPAD_AXIS_INVALID;
 
   return c;
 }
@@ -86,11 +86,11 @@
   // Open the default config file for writing
   char config_path[1024] = {0};
   snprintf(config_path, sizeof(config_path), "%s%s", SDL_GetPrefPath("", "m8c"), conf->filename);
-  SDL_RWops *rw = SDL_RWFromFile(config_path, "w");
+  SDL_IOStream *rw = SDL_IOFromFile(config_path, "w");
 
   SDL_Log("Writing config file to %s", config_path);
 
-  const unsigned int INI_LINE_COUNT = 50;
+  const unsigned int INI_LINE_COUNT = 49;
   const unsigned int LINELEN = 50;
 
   // Entries for the config file
@@ -99,8 +99,6 @@
   snprintf(ini_values[initPointer++], LINELEN, "[graphics]\n");
   snprintf(ini_values[initPointer++], LINELEN, "fullscreen=%s\n",
            conf->init_fullscreen ? "true" : "false");
-  snprintf(ini_values[initPointer++], LINELEN, "use_gpu=%s\n",
-           conf->init_use_gpu ? "true" : "false");
   snprintf(ini_values[initPointer++], LINELEN, "idle_ms=%d\n", conf->idle_ms);
   snprintf(ini_values[initPointer++], LINELEN, "wait_for_device=%s\n",
            conf->wait_for_device ? "true" : "false");
@@ -171,13 +169,13 @@
     // Write ini_values array to config file
     for (unsigned int i = 0; i < INI_LINE_COUNT; i++) {
       const size_t len = SDL_strlen(ini_values[i]);
-      if (SDL_RWwrite(rw, ini_values[i], 1, len) != len) {
+      if (SDL_WriteIO(rw, ini_values[i], len) != len) {
         SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Couldn't write line into config file.");
       } else {
         SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Wrote to config: %s", ini_values[i]);
       }
     }
-    SDL_RWclose(rw);
+    SDL_CloseIO(rw);
   } else {
     SDL_Log("Couldn't write into config file.");
   }
@@ -232,7 +230,6 @@
 
 void read_graphics_config(const ini_t *ini, config_params_s *conf) {
   const char *param_fs = ini_get(ini, "graphics", "fullscreen");
-  const char *param_gpu = ini_get(ini, "graphics", "use_gpu");
   const char *idle_ms = ini_get(ini, "graphics", "idle_ms");
   const char *param_wait = ini_get(ini, "graphics", "wait_for_device");
   const char *wait_packets = ini_get(ini, "graphics", "wait_packets");
@@ -241,13 +238,6 @@
     conf->init_fullscreen = 1;
   } else
     conf->init_fullscreen = 0;
-
-  if (param_gpu != NULL) {
-    if (strcmpci(param_gpu, "true") == 0) {
-      conf->init_use_gpu = 1;
-    } else
-      conf->init_use_gpu = 0;
-  }
 
   if (idle_ms != NULL)
     conf->idle_ms = SDL_atoi(idle_ms);
--- a/src/config.h
+++ b/src/config.h
@@ -9,7 +9,6 @@
 typedef struct config_params_s {
   char *filename;
   unsigned int init_fullscreen;
-  unsigned int init_use_gpu;
   unsigned int idle_ms;
   unsigned int wait_for_device;
   unsigned int wait_packets;
--- a/src/fx_cube.c
+++ b/src/fx_cube.c
@@ -1,7 +1,9 @@
-#include <SDL.h>
-#include <time.h>
 #include "SDL2_inprint.h"
+#include <SDL3/SDL.h>
+#include <time.h>
 
+#include <math.h>
+
 static SDL_Texture *texture_cube;
 static SDL_Texture *texture_text;
 static SDL_Renderer *fx_renderer;
@@ -27,10 +29,10 @@
 }
 
 static void rotate_cube(const float angle_x, const float angle_y) {
-  const float sin_x = SDL_sin(angle_x);
-  const float cos_x = SDL_cos(angle_x);
-  const float sin_y = SDL_sin(angle_y);
-  const float cos_y = SDL_cos(angle_y);
+  const float sin_x = SDL_sinf(angle_x);
+  const float cos_x = SDL_cosf(angle_x);
+  const float sin_y = SDL_sinf(angle_y);
+  const float cos_y = SDL_cosf(angle_y);
   for (int i = 0; i < 8; i++) {
     const float x = nodes[i][0];
     const float y = nodes[i][1];
@@ -56,7 +58,8 @@
 
   SDL_Texture *og_target = SDL_GetRenderTarget(fx_renderer);
 
-  SDL_QueryTexture(og_target, NULL, NULL, &texture_size.x, &texture_size.y);
+  texture_size.x = SDL_GetNumberProperty(SDL_GetTextureProperties(og_target),SDL_PROP_TEXTURE_WIDTH_NUMBER, 0);
+  texture_size.y = SDL_GetNumberProperty(SDL_GetTextureProperties(og_target),SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0);
 
   texture_cube = SDL_CreateTexture(fx_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
                                    texture_size.x, texture_size.y);
@@ -77,13 +80,13 @@
   SDL_memcpy(nodes, default_nodes, sizeof(default_nodes));
 
   scale(50, 50, 50);
-  rotate_cube(M_PI / 6, SDL_atan(SDL_sqrt(2)));
+  rotate_cube(M_PI / 6, SDL_atanf(SDL_sqrtf(2)));
 
   SDL_SetTextureBlendMode(texture_cube, SDL_BLENDMODE_BLEND);
   SDL_SetTextureBlendMode(texture_text, SDL_BLENDMODE_BLEND);
 
-  center_x = texture_size.x / 2.0;
-  center_y = texture_size.y / 2.0;
+  center_x = (int)(texture_size.x / 2.0);
+  center_y = (int)(texture_size.y / 2.0);
 }
 
 void fx_cube_destroy() {
@@ -92,7 +95,7 @@
 }
 
 void fx_cube_update() {
-  SDL_Point points[24];
+  SDL_FPoint points[24];
   int points_counter = 0;
   SDL_Texture *og_texture = SDL_GetRenderTarget(fx_renderer);
 
@@ -101,7 +104,7 @@
   SDL_RenderClear(fx_renderer);
 
   const unsigned int seconds = SDL_GetTicks() / 1000;
-  const float scalefactor = 1 + SDL_sin(seconds) * 0.005;
+  const float scalefactor = 1 + SDL_sinf(seconds) * 0.005;
 
   scale(scalefactor, scalefactor, scalefactor);
   rotate_cube(M_PI / 180, M_PI / 270);
@@ -109,14 +112,14 @@
   for (int i = 0; i < 12; i++) {
     const float *p1 = nodes[edges[i][0]];
     const float *p2 = nodes[edges[i][1]];
-    points[points_counter++] = (SDL_Point){p1[0] + center_x, nodes[edges[i][0]][1] + center_y};
-    points[points_counter++] = (SDL_Point){p2[0] + center_x, p2[1] + center_y};
+    points[points_counter++] = (SDL_FPoint){p1[0] + center_x, nodes[edges[i][0]][1] + center_y};
+    points[points_counter++] = (SDL_FPoint){p2[0] + center_x, p2[1] + center_y};
   }
 
-  SDL_RenderCopy(fx_renderer, texture_text, NULL, NULL);
+  SDL_RenderTexture(fx_renderer, texture_text, NULL, NULL);
   SDL_SetRenderDrawColor(fx_renderer, line_color.r, line_color.g, line_color.b, line_color.a);
-  SDL_RenderDrawLines(fx_renderer, points, 24);
+  SDL_RenderLines(fx_renderer, points, 24);
 
   SDL_SetRenderTarget(fx_renderer, og_texture);
-  SDL_RenderCopy(fx_renderer, texture_cube, NULL, NULL);
+  SDL_RenderTexture(fx_renderer, texture_cube, NULL, NULL);
 }
--- a/src/fx_cube.h
+++ b/src/fx_cube.h
@@ -1,7 +1,7 @@
 #ifndef FX_CUBE_H_
 #define FX_CUBE_H_
 
-#include "SDL_render.h"
+#include <SDL3/SDL_render.h>
 void fx_cube_init(SDL_Renderer *target_renderer, SDL_Color foreground_color,
                   unsigned int texture_width, unsigned int texture_height,
                   unsigned int font_glyph_width);
--- a/src/gamecontrollers.c
+++ b/src/gamecontrollers.c
@@ -6,15 +6,16 @@
 #include "config.h"
 #include "input.h"
 
-#include <SDL.h>
+#include <SDL3/SDL.h>
+#include <stdio.h>
 
 static int num_joysticks = 0;
-SDL_GameController *game_controllers[MAX_CONTROLLERS];
+SDL_Gamepad *game_controllers[MAX_CONTROLLERS];
 
 // Opens available game controllers and returns the amount of opened controllers
 int gamecontrollers_initialize() {
 
-  num_joysticks = SDL_NumJoysticks();
+  SDL_GetJoysticks(&num_joysticks);
   int controller_index = 0;
 
   SDL_Log("Looking for game controllers");
@@ -24,15 +25,15 @@
   char db_filename[1024] = {0};
   snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt", SDL_GetPrefPath("", "m8c"));
   SDL_Log("Trying to open game controller database from %s", db_filename);
-  SDL_RWops *db_rw = SDL_RWFromFile(db_filename, "rb");
+  SDL_IOStream *db_rw = SDL_IOFromFile(db_filename, "rb");
   if (db_rw == NULL) {
     snprintf(db_filename, sizeof(db_filename), "%sgamecontrollerdb.txt", SDL_GetBasePath());
     SDL_Log("Trying to open game controller database from %s", db_filename);
-    db_rw = SDL_RWFromFile(db_filename, "rb");
+    db_rw = SDL_IOFromFile(db_filename, "rb");
   }
 
   if (db_rw != NULL) {
-    const int mappings = SDL_GameControllerAddMappingsFromRW(db_rw, 1);
+    const int mappings = SDL_AddGamepadMappingsFromIO(db_rw, true);
     if (mappings != -1)
       SDL_Log("Found %d game controller mappings", mappings);
     else
@@ -43,13 +44,13 @@
 
   // Open all available game controllers
   for (int i = 0; i < num_joysticks; i++) {
-    if (!SDL_IsGameController(i))
+    if (!SDL_IsGamepad(i))
       continue;
     if (controller_index >= MAX_CONTROLLERS)
       break;
-    game_controllers[controller_index] = SDL_GameControllerOpen(i);
+    game_controllers[controller_index] = SDL_OpenGamepad(i);
     SDL_Log("Controller %d: %s", controller_index + 1,
-            SDL_GameControllerName(game_controllers[controller_index]));
+            SDL_GetGamepadName(game_controllers[controller_index]));
     controller_index++;
   }
 
@@ -61,12 +62,12 @@
 
   for (int i = 0; i < MAX_CONTROLLERS; i++) {
     if (game_controllers[i])
-      SDL_GameControllerClose(game_controllers[i]);
+      SDL_CloseGamepad(game_controllers[i]);
   }
 }
 
 // Check whether a button is pressed on a gamepad and return 1 if pressed.
-static int get_game_controller_button(const config_params_s *conf, SDL_GameController *controller,
+static int get_game_controller_button(const config_params_s *conf, SDL_Gamepad *controller,
                                       const int button) {
 
   const int button_mappings[8] = {conf->gamepad_up,     conf->gamepad_down, conf->gamepad_left,
@@ -74,7 +75,7 @@
                                   conf->gamepad_select, conf->gamepad_start};
 
   // Check digital buttons
-  if (SDL_GameControllerGetButton(controller, button_mappings[button])) {
+  if (SDL_GetGamepadButton(controller, button_mappings[button])) {
     return 1;
   }
 
@@ -81,28 +82,28 @@
   // If digital button isn't pressed, check the corresponding analog control
   switch (button) {
   case INPUT_UP:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_updown) <
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_updown) <
            -conf->gamepad_analog_threshold;
   case INPUT_DOWN:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_updown) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_updown) >
            conf->gamepad_analog_threshold;
   case INPUT_LEFT:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_leftright) <
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_leftright) <
            -conf->gamepad_analog_threshold;
   case INPUT_RIGHT:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_leftright) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_leftright) >
            conf->gamepad_analog_threshold;
   case INPUT_OPT:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_opt) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_opt) >
            conf->gamepad_analog_threshold;
   case INPUT_EDIT:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_edit) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_edit) >
            conf->gamepad_analog_threshold;
   case INPUT_SELECT:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_select) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_select) >
            conf->gamepad_analog_threshold;
   case INPUT_START:
-    return SDL_GameControllerGetAxis(controller, conf->gamepad_analog_axis_start) >
+    return SDL_GetGamepadAxis(controller, conf->gamepad_analog_axis_start) >
            conf->gamepad_analog_threshold;
   default:
     return 0;
@@ -137,13 +138,13 @@
   input_msg_s msg = {0};
   // Read special case game controller buttons quit and reset
   for (int gc = 0; gc < num_joysticks; gc++) {
-    if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_quit) &&
-        (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
-         SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
+    if (SDL_GetGamepadButton(game_controllers[gc], conf->gamepad_quit) &&
+        (SDL_GetGamepadButton(game_controllers[gc], conf->gamepad_select) ||
+         SDL_GetGamepadAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
       msg = (input_msg_s){special, msg_quit, 0, 0};
-    else if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_reset) &&
-             (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
-              SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
+    else if (SDL_GetGamepadButton(game_controllers[gc], conf->gamepad_reset) &&
+             (SDL_GetGamepadButton(game_controllers[gc], conf->gamepad_select) ||
+              SDL_GetGamepadAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
       msg = (input_msg_s){special, msg_reset_display, 0, 0};
   }
   return msg;
--- a/src/inprint2.c
+++ b/src/inprint2.c
@@ -3,7 +3,7 @@
 // Modified to support multiple fonts & adding a background to text.
 
 #include "inline_font.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 
 #define CHARACTERS_PER_ROW 94
 #define CHARACTERS_PER_COLUMN 1
@@ -28,17 +28,17 @@
     return;
   }
 
-  SDL_RWops *font_bmp =
-      SDL_RWFromConstMem(selected_inline_font->image_data, selected_inline_font->image_size);
+  SDL_IOStream *font_bmp =
+      SDL_IOFromConstMem(selected_inline_font->image_data, selected_inline_font->image_size);
 
-  SDL_Surface *surface = SDL_LoadBMP_RW(font_bmp, 1);
+  SDL_Surface *surface = SDL_LoadBMP_IO(font_bmp, 1);
 
   // Black is transparent
-  SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0));
+  SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, 0, 0, 0));
 
   inline_font = SDL_CreateTextureFromSurface(selected_renderer, surface);
 
-  SDL_FreeSurface(surface);
+  SDL_DestroySurface(surface);
 
   selected_font = inline_font;
 }
@@ -51,8 +51,6 @@
 void inrenderer(SDL_Renderer *renderer) { selected_renderer = renderer; }
 
 void infont(SDL_Texture *font) {
-  Uint32 format;
-  int access;
   int w, h;
 
   if (font == NULL) {
@@ -59,7 +57,8 @@
     return;
   }
 
-  SDL_QueryTexture(font, &format, &access, &w, &h);
+  w = SDL_GetNumberProperty(SDL_GetTextureProperties(font),SDL_PROP_TEXTURE_WIDTH_NUMBER, 0);
+  h = SDL_GetNumberProperty(SDL_GetTextureProperties(font),SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0);
 
   selected_font = font;
   selected_font_w = w;
@@ -78,9 +77,9 @@
 }
 void inprint(SDL_Renderer *dst, const char *str, Uint32 x, Uint32 y, const Uint32 fgcolor,
              const Uint32 bgcolor) {
-  SDL_Rect s_rect;
-  SDL_Rect d_rect;
-  SDL_Rect bg_rect;
+  SDL_FRect s_rect;
+  SDL_FRect d_rect;
+  SDL_FRect bg_rect;
 
   static uint32_t previous_fgcolor;
 
@@ -95,7 +94,9 @@
     dst = selected_renderer;
 
   for (; *str; str++) {
-    int id = (int)*str - font_offset;
+    int ascii_code = (int)*str;
+    int id = ascii_code - font_offset;
+
 #if (CHARACTERS_PER_COLUMN != 1)
     int row = id / CHARACTERS_PER_ROW;
     int col = id % CHARACTERS_PER_ROW;
@@ -125,7 +126,10 @@
 
       SDL_RenderFillRect(dst, &bg_rect);
     }
-    SDL_RenderCopy(dst, selected_font, &s_rect, &d_rect);
+    // Do not try to render a whitespace character because the font doesn't have one
+    if (ascii_code != 32) {
+      SDL_RenderTexture(dst, selected_font, &s_rect, &d_rect);
+    }
     d_rect.x += selected_inline_font->glyph_x + 1;
   }
 }
--- a/src/input.c
+++ b/src/input.c
@@ -2,7 +2,7 @@
 #include "config.h"
 #include "gamecontrollers.h"
 #include "render.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 
 uint8_t keyjazz_enabled = 0;
 uint8_t keyjazz_base_octave = 2;
@@ -20,7 +20,7 @@
 
 static input_msg_s handle_keyjazz(SDL_Event *event, uint8_t keyvalue, config_params_s *conf) {
   input_msg_s key = {keyjazz, keyvalue, keyjazz_velocity, event->type};
-  switch (event->key.keysym.scancode) {
+  switch (event->key.scancode) {
   case SDL_SCANCODE_Z:
     key.value = keyjazz_base_octave * 12;
     break;
@@ -110,21 +110,21 @@
     break;
   default:
     key.type = normal;
-    if (event->key.repeat > 0 || event->key.type == SDL_KEYUP) {
+    if (event->key.repeat > 0 || event->key.type == SDL_EVENT_KEY_UP) {
       break;
     }
-    if (event->key.keysym.scancode == conf->key_jazz_dec_octave) {
+    if (event->key.scancode == conf->key_jazz_dec_octave) {
       if (keyjazz_base_octave > 0) {
         keyjazz_base_octave--;
         display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
       }
-    } else if (event->key.keysym.scancode == conf->key_jazz_inc_octave) {
+    } else if (event->key.scancode == conf->key_jazz_inc_octave) {
       if (keyjazz_base_octave < 8) {
         keyjazz_base_octave++;
         display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
       }
-    } else if (event->key.keysym.scancode == conf->key_jazz_dec_velocity) {
-      if ((event->key.keysym.mod & KMOD_ALT) > 0) {
+    } else if (event->key.scancode == conf->key_jazz_dec_velocity) {
+      if ((event->key.mod & SDL_KMOD_ALT) > 0) {
         if (keyjazz_velocity > 1)
           keyjazz_velocity -= 1;
       } else {
@@ -132,8 +132,8 @@
           keyjazz_velocity -= 0x10;
       }
       display_keyjazz_overlay(1, keyjazz_base_octave, keyjazz_velocity);
-    } else if (event->key.keysym.scancode == conf->key_jazz_inc_velocity) {
-      if ((event->key.keysym.mod & KMOD_ALT) > 0) {
+    } else if (event->key.scancode == conf->key_jazz_inc_velocity) {
+      if ((event->key.mod & SDL_KMOD_ALT) > 0) {
         if (keyjazz_velocity < 0x7F)
           keyjazz_velocity += 1;
       } else {
@@ -151,31 +151,31 @@
 static input_msg_s handle_normal_keys(const SDL_Event *event, const config_params_s *conf) {
   input_msg_s key = {normal, 0, 0, 0};
 
-  if (event->key.keysym.scancode == conf->key_up) {
+  if (event->key.scancode == conf->key_up) {
     key.value = key_up;
-  } else if (event->key.keysym.scancode == conf->key_left) {
+  } else if (event->key.scancode == conf->key_left) {
     key.value = key_left;
-  } else if (event->key.keysym.scancode == conf->key_down) {
+  } else if (event->key.scancode == conf->key_down) {
     key.value = key_down;
-  } else if (event->key.keysym.scancode == conf->key_right) {
+  } else if (event->key.scancode == conf->key_right) {
     key.value = key_right;
-  } else if (event->key.keysym.scancode == conf->key_select ||
-             event->key.keysym.scancode == conf->key_select_alt) {
+  } else if (event->key.scancode == conf->key_select ||
+             event->key.scancode == conf->key_select_alt) {
     key.value = key_select;
-  } else if (event->key.keysym.scancode == conf->key_start ||
-             event->key.keysym.scancode == conf->key_start_alt) {
+  } else if (event->key.scancode == conf->key_start ||
+             event->key.scancode == conf->key_start_alt) {
     key.value = key_start;
-  } else if (event->key.keysym.scancode == conf->key_opt ||
-             event->key.keysym.scancode == conf->key_opt_alt) {
+  } else if (event->key.scancode == conf->key_opt ||
+             event->key.scancode == conf->key_opt_alt) {
     key.value = key_opt;
-  } else if (event->key.keysym.scancode == conf->key_edit ||
-             event->key.keysym.scancode == conf->key_edit_alt) {
+  } else if (event->key.scancode == conf->key_edit ||
+             event->key.scancode == conf->key_edit_alt) {
     key.value = key_edit;
-  } else if (event->key.keysym.scancode == conf->key_delete) {
+  } else if (event->key.scancode == conf->key_delete) {
     key.value = key_opt | key_edit;
-  } else if (event->key.keysym.scancode == conf->key_reset) {
+  } else if (event->key.scancode == conf->key_reset) {
     key = (input_msg_s){special, msg_reset_display, 0, 0};
-  } else if (event->key.keysym.scancode == conf->key_toggle_audio) {
+  } else if (event->key.scancode == conf->key_toggle_audio) {
     key = (input_msg_s){special, msg_toggle_audio, 0, 0};
   } else {
     key.value = 0;
@@ -207,18 +207,17 @@
     switch (event.type) {
 
     // Reinitialize game controllers on controller add/remove/remap
-    case SDL_CONTROLLERDEVICEADDED:
-    case SDL_CONTROLLERDEVICEREMOVED:
+    case SDL_EVENT_GAMEPAD_ADDED:
+    case SDL_EVENT_GAMEPAD_REMOVED:
       gamecontrollers_initialize();
       break;
 
     // Handle SDL quit events (for example, window close)
-    case SDL_QUIT:
+    case SDL_EVENT_QUIT:
       key = (input_msg_s){special, msg_quit, 0, 0};
       break;
 
-    case SDL_WINDOWEVENT:
-      if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
+    case SDL_EVENT_WINDOW_RESIZED:
         static uint32_t ticks_window_resized = 0;
         if (SDL_GetTicks() - ticks_window_resized > 500) {
           SDL_Log("Resizing window...");
@@ -225,10 +224,9 @@
           key = (input_msg_s){special, msg_reset_display, 0, 0};
           ticks_window_resized = SDL_GetTicks();
         }
-      }
       break;
 
-    case SDL_KEYDOWN:
+    case SDL_EVENT_KEY_DOWN:
 
       if (event.key.repeat > 0) {
         break;
@@ -235,25 +233,25 @@
       }
 
       // ALT+ENTER toggles fullscreen
-      if (event.key.keysym.sym == SDLK_RETURN && (event.key.keysym.mod & KMOD_ALT) > 0) {
+      if (event.key.key == SDLK_RETURN && (event.key.mod & SDL_KMOD_ALT) > 0) {
         toggle_fullscreen();
         break;
       }
 
       // ALT+F4 quits program
-      if (event.key.keysym.sym == SDLK_F4 && (event.key.keysym.mod & KMOD_ALT) > 0) {
+      if (event.key.key == SDLK_F4 && (event.key.mod & SDL_KMOD_ALT) > 0) {
         key = (input_msg_s){special, msg_quit, 0, 0};
         break;
       }
 
       // ESC = toggle keyjazz
-      if (event.key.keysym.sym == SDLK_ESCAPE) {
+      if (event.key.key == SDLK_ESCAPE) {
         display_keyjazz_overlay(toggle_input_keyjazz(), keyjazz_base_octave, keyjazz_velocity);
         break;
       }
 
     // Intentional fallthrough
-    case SDL_KEYUP:
+    case SDL_EVENT_KEY_UP:
 
       // Normal keyboard inputs
       key = handle_normal_keys(&event, conf);
@@ -269,9 +267,9 @@
 
     switch (key.type) {
     case normal:
-      if (event.type == SDL_KEYDOWN) {
+      if (event.type == SDL_EVENT_KEY_DOWN) {
         keycode |= key.value;
-      } else if (event.type == SDL_KEYUP) {
+      } else if (event.type == SDL_EVENT_KEY_UP) {
         keycode &= ~key.value;
       }
       break;
@@ -278,9 +276,9 @@
     case keyjazz:
       // Do not allow pressing multiple keys with keyjazz
     case special:
-      if (event.type == SDL_KEYDOWN) {
+      if (event.type == SDL_EVENT_KEY_DOWN) {
         keycode = key.value;
-      } else if (event.type == SDL_KEYUP) {
+      } else if (event.type == SDL_EVENT_KEY_UP) {
         keycode = 0;
       }
       break;
--- a/src/main.c
+++ b/src/main.c
@@ -5,7 +5,7 @@
    CFLAGS=-DDEBUG_MSG` */
 // #define DEBUG_MSG
 
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <signal.h>
 
 #include "SDL2_inprint.h"
@@ -55,7 +55,6 @@
   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
@@ -71,6 +70,7 @@
   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);
@@ -82,14 +82,19 @@
   // First device detection to avoid SDL init if it isn't necessary. To be run
   // only if we shouldn't wait for M8 to be connected.
   if (conf.wait_for_device == 0) {
-    if (init_serial(1, preferred_device) == 0) {
+    port_inited = init_serial(1, preferred_device);
+    if (port_inited == 0) {
       SDL_free(serial_buf);
-      return -1;
+      return 1;
     }
   }
 
   // initialize all SDL systems
-  if (initialize_sdl(conf.init_fullscreen, conf.init_use_gpu) == -1)
+  if (initialize_sdl(conf.init_fullscreen) == false) {
+    SDL_free(serial_buf);
+    SDL_Quit();
+    return 1;
+  }
     run = QUIT;
 
   // initial scan for (existing) game controllers
@@ -102,7 +107,7 @@
   // main loop begin
   do {
     // try to init serial port
-    int port_inited = init_serial(1, preferred_device);
+    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
@@ -201,10 +206,10 @@
         break;
       case keyjazz:
         if (input.value != 0) {
-          if (input.eventType == SDL_KEYDOWN && input.value != prev_input) {
+          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_KEYUP && input.value == prev_note) {
+          } else if (input.eventType == SDL_EVENT_KEY_UP && input.value == prev_note) {
             send_msg_keyjazz(0xFF, 0);
           }
         }
@@ -293,7 +298,7 @@
   }
   gamecontrollers_close();
   close_renderer();
-  close_serial_port();
+  if (port_inited == 1) close_serial_port();
   SDL_free(serial_buf);
   SDL_Quit();
   return 0;
--- a/src/render.c
+++ b/src/render.c
@@ -3,7 +3,7 @@
 
 #include "render.h"
 
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <stdio.h>
 
 #include "SDL2_inprint.h"
@@ -17,15 +17,18 @@
 #include "font5.h"
 #include "inline_font.h"
 
+#include <stdlib.h>
+
 SDL_Window *win;
 SDL_Renderer *rend;
-SDL_Texture *maintexture;
+SDL_Texture *main_texture;
 SDL_Color background_color = (SDL_Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0x00};
+SDL_RendererLogicalPresentation scaling_mode = SDL_LOGICAL_PRESENTATION_INTEGER_SCALE;
 
 static uint32_t ticks_fps;
 static int fps;
 static int font_mode = -1;
-static int m8_hardware_model = 0;
+static unsigned int m8_hardware_model = 0;
 static int screen_offset_y = 0;
 static int text_offset_y = 0;
 static int waveform_max_height = 24;
@@ -41,11 +44,11 @@
 static uint8_t dirty = 0;
 
 // Initializes SDL and creates a renderer and required surfaces
-int initialize_sdl(const int init_fullscreen, const int init_use_gpu) {
+int initialize_sdl(const unsigned int init_fullscreen) {
 
-  if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
+  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());
-    return -1;
+    return false;
   }
 
   // SDL documentation recommends this
@@ -52,20 +55,20 @@
   atexit(SDL_Quit);
 
   win = SDL_CreateWindow(
-      "m8c", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, texture_width * 2, texture_height * 2,
-      SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | init_fullscreen);
+      "m8c", texture_width * 2, texture_height * 2,
+      SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | init_fullscreen);
 
   rend =
-      SDL_CreateRenderer(win, -1, init_use_gpu ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);
+      SDL_CreateRenderer(win, NULL);
 
-  SDL_RenderSetLogicalSize(rend, texture_width, texture_height);
+  SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, scaling_mode);
 
-  maintexture = NULL;
+  main_texture = NULL;
 
-  maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+  main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
                                   texture_width, texture_height);
 
-  SDL_SetRenderTarget(rend, maintexture);
+  SDL_SetRenderTarget(rend, main_texture);
 
   SDL_SetRenderDrawColor(rend, background_color.r, background_color.g, background_color.b,
                          background_color.a);
@@ -74,7 +77,7 @@
 
   set_font_mode(0);
 
-  SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO);
+  SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
 
   dirty = 1;
 
@@ -87,8 +90,8 @@
   prepare_inline_font(font);
 }
 
-static void check_and_adjust_window_and_texture_size(const unsigned int new_width,
-                                                     const unsigned int new_height) {
+static void check_and_adjust_window_and_texture_size(const int new_width,
+                                                     const int new_height) {
 
   int h, w;
 
@@ -101,28 +104,25 @@
     SDL_SetWindowSize(win, texture_width * 2, texture_height * 2);
   }
 
-  SDL_DestroyTexture(maintexture);
+  SDL_DestroyTexture(main_texture);
 
-  SDL_RenderSetLogicalSize(rend, texture_width, texture_height);
+  SDL_SetRenderLogicalPresentation(rend, texture_width, texture_height, scaling_mode);
 
-  maintexture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+  main_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
                                   texture_width, texture_height);
 
-  SDL_SetRenderTarget(rend, maintexture);
+  SDL_SetRenderTarget(rend, main_texture);
 }
 
 // Set M8 hardware model in use. 0 = MK1, 1 = MK2
 void set_m8_model(const unsigned int model) {
 
-  switch (model) {
-  case 1:
+  if (model == 1) {
     m8_hardware_model = 1;
     check_and_adjust_window_and_texture_size(480, 320);
-    break;
-  default:
+  } else {
     m8_hardware_model = 0;
     check_and_adjust_window_and_texture_size(320, 240);
-    break;
   }
 }
 
@@ -148,7 +148,7 @@
 
 void close_renderer() {
   kill_inline_font();
-  SDL_DestroyTexture(maintexture);
+  SDL_DestroyTexture(main_texture);
   SDL_DestroyRenderer(rend);
   SDL_DestroyWindow(win);
 }
@@ -155,10 +155,16 @@
 
 void toggle_fullscreen() {
 
-  const int fullscreen_state = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN;
+  const unsigned long fullscreen_state = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN;
 
-  SDL_SetWindowFullscreen(win, fullscreen_state ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
-  SDL_ShowCursor(fullscreen_state);
+  SDL_SetWindowFullscreen(win, fullscreen_state ? false : true);
+  SDL_SyncWindow(win);
+  if (fullscreen_state) {
+    // Show cursor when in windowed state
+    SDL_ShowCursor();
+  } else {
+    SDL_HideCursor();
+  }
 
   dirty = 1;
 }
@@ -187,7 +193,7 @@
 
 void draw_rectangle(struct draw_rectangle_command *command) {
 
-  SDL_Rect render_rect;
+  SDL_FRect render_rect;
 
   render_rect.x = command->pos.x;
   render_rect.y = command->pos.y + screen_offset_y;
@@ -227,7 +233,7 @@
   // rendering it
   if (!(wfm_cleared && command->waveform_size == 0)) {
 
-    SDL_Rect wf_rect;
+    SDL_FRect wf_rect;
     if (command->waveform_size > 0) {
       wf_rect.x = texture_width - command->waveform_size;
       wf_rect.y = 0;
@@ -248,7 +254,7 @@
     SDL_SetRenderDrawColor(rend, command->color.r, command->color.g, command->color.b, 255);
 
     // Create a SDL_Point array of the waveform pixels for batch drawing
-    SDL_Point waveform_points[command->waveform_size];
+    SDL_FPoint waveform_points[command->waveform_size];
 
     for (int i = 0; i < command->waveform_size; i++) {
       // Limit value because the oscilloscope commands seem to glitch
@@ -260,7 +266,7 @@
       waveform_points[i].y = command->waveform[i];
     }
 
-    SDL_RenderDrawPoints(rend, waveform_points, command->waveform_size);
+    SDL_RenderPoints(rend, waveform_points, command->waveform_size);
 
     // The packet we just drew was an empty waveform
     if (command->waveform_size == 0) {
@@ -303,9 +309,9 @@
                            background_color.a);
 
     SDL_RenderClear(rend);
-    SDL_RenderCopy(rend, maintexture, NULL, NULL);
+    SDL_RenderTexture(rend, main_texture, NULL, NULL);
     SDL_RenderPresent(rend);
-    SDL_SetRenderTarget(rend, maintexture);
+    SDL_SetRenderTarget(rend, main_texture);
 
     fps++;
 
--- a/src/render.h
+++ b/src/render.h
@@ -7,7 +7,7 @@
 #include <stdint.h>
 #include "command.h"
 
-int initialize_sdl(int init_fullscreen, int init_use_gpu);
+int initialize_sdl(unsigned int init_fullscreen);
 void close_renderer();
 
 void draw_waveform(struct draw_oscilloscope_waveform_command *command);
--- a/src/ringbuffer.c
+++ b/src/ringbuffer.c
@@ -1,5 +1,5 @@
 #include "ringbuffer.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 
 RingBuffer *ring_buffer_create(uint32_t size) {
   RingBuffer *rb = SDL_malloc(sizeof(*rb));
@@ -12,8 +12,8 @@
 }
 
 void ring_buffer_free(RingBuffer *rb) {
-  free(rb->buffer);
-  free(rb);
+  SDL_free(rb->buffer);
+  SDL_free(rb);
 }
 
 uint32_t ring_buffer_empty(RingBuffer *rb) { return rb->size == 0; }
--- a/src/serial.c
+++ b/src/serial.c
@@ -5,7 +5,7 @@
 // public domain
 
 #ifndef USE_LIBUSB
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <libserialport.h>
 #include <stdio.h>
 #include <stdlib.h>
--- a/src/usb.c
+++ b/src/usb.c
@@ -5,7 +5,7 @@
 // public domain
 #ifdef USE_LIBUSB
 
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <libusb.h>
 #include <stdlib.h>
 #include <string.h>
--- a/src/usb_audio.c
+++ b/src/usb_audio.c
@@ -2,7 +2,7 @@
 
 #include "ringbuffer.h"
 #include "usb.h"
-#include <SDL.h>
+#include <SDL3/SDL.h>
 #include <errno.h>
 #include <libusb.h>
 
--