diff --git a/CMakeLists.switch b/CMakeLists.switch
new file mode 100644
index 000000000..8b6ec2b43
--- /dev/null
+++ b/CMakeLists.switch
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 3.0)
+#set(CMAKE_VERBOSE_MAKEFILE ON)
+
+set(CMAKE_SYSTEM_NAME "Generic")
+
+set(DEVKITPRO $ENV{DEVKITPRO})
+set(CMAKE_SYSTEM_PROCESSOR "armv8-a")
+set(CMAKE_C_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc")
+set(CMAKE_CXX_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++")
+set(CMAKE_ASM_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-as")
+set(CMAKE_AR "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ar" CACHE STRING "")
+set(CMAKE_RANLIB "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ranlib" CACHE STRING "")
+set(CMAKE_C_FLAGS "-g -O2 -march=armv8-a -mtune=cortex-a57 -mtp=soft -ftls-model=local-exec -fPIC -I${DEVKITPRO}/libnx/include -I${DEVKITPRO}/portlibs/switch/include" CACHE STRING "C flags")
+set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions" CACHE STRING "C++ flags")
+set(CMAKE_FIND_ROOT_PATH ${DEVKITPRO} ${DEVKITPRO}/devkitA64 ${DEVKITPRO}/libnx ${DEVKITPRO}/portlibs/switch)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available")
+
+project(SDL2)
+
+set(SRC_DIRS
+        src
+        src/atomic
+        src/audio
+        src/audio/switch
+        src/cpuinfo
+        src/events
+        src/file
+        src/filesystem/dummy
+        src/haptic
+        src/haptic/dummy
+        src/joystick
+        src/joystick/switch
+        src/joystick/dummy
+        src/loadso
+        src/loadso/dummy
+        src/libm
+        src/power
+        src/render
+        src/render/software
+        src/render/opengl
+        src/render/opengles
+        src/render/opengles2
+        src/stdlib
+        src/thread
+        src/thread/switch
+        src/timer
+        src/timer/switch
+        src/video
+        src/video/yuv2rgb
+        src/video/switch
+        )
+
+set(SRC_FILES )
+foreach (DIR ${SRC_DIRS})
+    file(GLOB FILES ${DIR}/*.c*)
+    list(APPEND SRC_FILES ${FILES})
+endforeach (DIR)
+
+# SDL2 library
+add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
+target_include_directories(${PROJECT_NAME} PUBLIC ${SRC_DIRS} include)
+target_compile_options(${PROJECT_NAME} PUBLIC -O3 -D__SWITCH__ -DSDL_VIDEO_STATIC_ANGLE)
+
+# SDL2 test
+add_executable(${PROJECT_NAME}.elf test/testswitch.c)
+target_include_directories(${PROJECT_NAME}.elf PRIVATE include)
+target_compile_options(${PROJECT_NAME}.elf PRIVATE -O3 -D__SWITCH__)
+target_link_libraries(${PROJECT_NAME}.elf
+        ${PROJECT_NAME}
+        ${DEVKITPRO}/portlibs/switch/lib/libEGL.a
+        ${DEVKITPRO}/portlibs/switch/lib/libglapi.a
+        ${DEVKITPRO}/portlibs/switch/lib/libdrm_nouveau.a
+        ${DEVKITPRO}/libnx/lib/libnx.a
+        stdc++
+        m
+        )
+set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_FLAGS "-specs=${DEVKITPRO}/libnx/switch.specs")
+add_custom_target(${PROJECT_NAME}.nro
+        DEPENDS ${PROJECT_NAME}.elf
+        COMMAND elf2nro ${PROJECT_NAME}.elf ${PROJECT_NAME}.nro)
diff --git a/configure b/configure
index 1c7f87eb0..5f6f836a2 100755
--- a/configure
+++ b/configure
@@ -745,6 +745,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -923,6 +924,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1175,6 +1177,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1312,7 +1323,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1465,6 +1476,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1572,7 +1584,7 @@ Optional Features:
                           QtWayland server support for Wayland video driver
                           [[default=yes]]
   --enable-wayland-shared dynamically load Wayland support [[default=maybe]]
-  --enable-video-mir      use Mir video driver [[default=yes]]
+  --enable-video-mir      use Mir video driver [[default=no]]
   --enable-mir-shared     dynamically load Mir support [[default=maybe]]
   --enable-video-rpi      use Raspberry Pi video driver [[default=yes]]
   --enable-video-x11      use X11 video driver [[default=yes]]
@@ -19300,7 +19312,7 @@ CheckMir()
 if test "${enable_video_mir+set}" = set; then :
   enableval=$enable_video_mir;
 else
-  enable_video_mir=yes
+  enable_video_mir=no
 fi
 
 
@@ -24621,6 +24633,86 @@ $as_echo "#define SDL_TIMER_UNIX 1" >>confdefs.h
             have_timers=yes
         fi
         ;;
+	aarch64-none-elf*)
+        ARCH=switch
+        SDL_CFLAGS="$SDL_CFLAGS -isystem ${DEVKITPRO}/libnx/include -I${DEVKITPRO}/portlibs/switch/include"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS -D__SWITCH__ -march=armv8-a -mtune=cortex-a57 -mtp=soft -ftls-model=local-exec -fPIC"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS $SDL_CFLAGS -g -O3"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS -DSDL_VIDEO_STATIC_ANGLE"
+        EXTRA_LDFLAGS="-march=armv8-a -fPIE -L${DEVKITPRO}/libnx/lib -lEGL -lglapi -ldrm_nouveau -lnx"
+        CheckDeclarationAfterStatement
+
+        # Set up files for the video library
+        if test x$enable_video = xyes; then
+            SOURCES="$SOURCES $srcdir/src/video/switch/*.c"
+
+$as_echo "#define SDL_VIDEO_DRIVER_SWITCH 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_OPENGL_EGL 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_OPENGL 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_OPENGL_ES2 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_RENDER_OGL_ES2 1" >>confdefs.h
+
+            SUMMARY_video="${SUMMARY_video} switch_opengles2"
+            have_video=yes
+        fi
+        # Set up files for the audio library
+        if test x$enable_audio = xyes; then
+
+$as_echo "#define SDL_AUDIO_DRIVER_SWITCH 1" >>confdefs.h
+
+           SOURCES="$SOURCES $srcdir/src/audio/switch/*.c"
+           SUMMARY_audio="${SUMMARY_audio} switch"
+           have_audio=yes
+        fi
+        # Set up files for the joystick library
+        if test x$enable_joystick = xyes; then
+
+$as_echo "#define SDL_JOYSTICK_SWITCH 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/joystick/switch/*.c"
+            have_joystick=yes
+        fi
+        # Set up files for the timer library
+        if test x$enable_timers = xyes; then
+
+$as_echo "#define SDL_TIMER_SWITCH 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/timer/switch/*.c"
+            have_timers=yes
+        fi
+        # Set up files for the system power library
+        if test x$enable_power = xyes; then
+
+$as_echo "#define SDL_POWER_SWITCH 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/power/switch/*.c"
+            have_power=yes
+        fi
+        # Set up files for the system filesystem library
+        if test x$enable_filesystem = xyes; then
+
+$as_echo "#define SDL_FILESYSTEM_SWITCH 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/filesystem/switch/*.c"
+            have_filesystem=yes
+        fi
+        # Set up files for the thread library
+        if test x$enable_threads = xyes; then
+
+$as_echo "#define SDL_THREAD_SWITCH 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/thread/switch/*.c"
+            have_threads=yes
+        fi
+        ;;
     *)
         as_fn_error $? "
 *** Unsupported host:  Please add to configure.in
diff --git a/configure.in b/configure.in
index 1c7e79338..3660e6b57 100644
--- a/configure.in
+++ b/configure.in
@@ -3905,6 +3905,64 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau
             have_timers=yes
         fi
         ;;
+	aarch64-none-elf*)
+        ARCH=switch
+        SDL_CFLAGS="$SDL_CFLAGS -isystem ${DEVKITPRO}/libnx/include -I${DEVKITPRO}/portlibs/switch/include"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS -D__SWITCH__ -march=armv8-a -mtune=cortex-a57 -mtp=soft -ftls-model=local-exec -fPIC"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS $SDL_CFLAGS -g -O3"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS -DSDL_VIDEO_STATIC_ANGLE"
+        EXTRA_LDFLAGS="-march=armv8-a -fPIE -L${DEVKITPRO}/libnx/lib -lEGL -lglapi -ldrm_nouveau -lnx"
+        CheckDeclarationAfterStatement
+
+        # Set up files for the video library
+        if test x$enable_video = xyes; then
+            SOURCES="$SOURCES $srcdir/src/video/switch/*.c"
+            AC_DEFINE(SDL_VIDEO_DRIVER_SWITCH, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_OPENGL_EGL, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_OPENGL, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_OPENGL_ES2, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_RENDER_OGL_ES2, 1, [ ])
+            SUMMARY_video="${SUMMARY_video} switch_opengles2"
+            have_video=yes
+        fi
+        # Set up files for the audio library
+        if test x$enable_audio = xyes; then
+           AC_DEFINE(SDL_AUDIO_DRIVER_SWITCH, 1, [ ])
+           SOURCES="$SOURCES $srcdir/src/audio/switch/*.c"
+           SUMMARY_audio="${SUMMARY_audio} switch"
+           have_audio=yes
+        fi
+        # Set up files for the joystick library
+        if test x$enable_joystick = xyes; then
+            AC_DEFINE(SDL_JOYSTICK_SWITCH, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/joystick/switch/*.c"
+            have_joystick=yes
+        fi
+        # Set up files for the timer library
+        if test x$enable_timers = xyes; then
+            AC_DEFINE(SDL_TIMER_SWITCH, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/timer/switch/*.c"
+            have_timers=yes
+        fi
+        # Set up files for the system power library
+        if test x$enable_power = xyes; then
+            AC_DEFINE(SDL_POWER_SWITCH, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/power/switch/*.c"
+            have_power=yes
+        fi
+        # Set up files for the system filesystem library
+        if test x$enable_filesystem = xyes; then
+            AC_DEFINE(SDL_FILESYSTEM_SWITCH, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/filesystem/switch/*.c"
+            have_filesystem=yes
+        fi
+        # Set up files for the thread library
+        if test x$enable_threads = xyes; then
+            AC_DEFINE(SDL_THREAD_SWITCH, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/thread/switch/*.c"
+            have_threads=yes
+        fi
+        ;;
     *)
         AC_MSG_ERROR([
 *** Unsupported host:  Please add to configure.in
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index 422f47f78..9cc4de52f 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -262,6 +262,7 @@
 #undef SDL_AUDIO_DRIVER_SUNAUDIO
 #undef SDL_AUDIO_DRIVER_WASAPI
 #undef SDL_AUDIO_DRIVER_WINMM
+#undef SDL_AUDIO_DRIVER_SWITCH
 
 /* Enable various input drivers */
 #undef SDL_INPUT_LINUXEV
@@ -278,6 +279,7 @@
 #undef SDL_JOYSTICK_USBHID
 #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
 #undef SDL_JOYSTICK_EMSCRIPTEN
+#undef SDL_JOYSTICK_SWITCH
 #undef SDL_HAPTIC_DUMMY
 #undef SDL_HAPTIC_ANDROID
 #undef SDL_HAPTIC_LINUX
@@ -296,12 +298,14 @@
 #undef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
 #undef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP
 #undef SDL_THREAD_WINDOWS
+#undef SDL_THREAD_SWITCH
 
 /* Enable various timer systems */
 #undef SDL_TIMER_HAIKU
 #undef SDL_TIMER_DUMMY
 #undef SDL_TIMER_UNIX
 #undef SDL_TIMER_WINDOWS
+#undef SDL_TIMER_SWITCH
 
 /* Enable various video drivers */
 #undef SDL_VIDEO_DRIVER_HAIKU
@@ -350,6 +354,7 @@
 #undef SDL_VIDEO_DRIVER_VIVANTE
 #undef SDL_VIDEO_DRIVER_VIVANTE_VDK
 #undef SDL_VIDEO_DRIVER_QNX
+#undef SDL_VIDEO_DRIVER_SWITCH
 
 #undef SDL_VIDEO_RENDER_D3D
 #undef SDL_VIDEO_RENDER_D3D11
@@ -382,6 +387,7 @@
 #undef SDL_POWER_ANDROID
 #undef SDL_POWER_EMSCRIPTEN
 #undef SDL_POWER_HARDWIRED
+#undef SDL_POWER_SWITCH
 
 /* Enable system filesystem support */
 #undef SDL_FILESYSTEM_HAIKU
@@ -392,6 +398,7 @@
 #undef SDL_FILESYSTEM_NACL
 #undef SDL_FILESYSTEM_ANDROID
 #undef SDL_FILESYSTEM_EMSCRIPTEN
+#undef SDL_FILESYSTEM_SWITCH
 
 /* Enable assembly routines */
 #undef SDL_ASSEMBLY_ROUTINES
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index dcaebea6d..6d930adc1 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -98,6 +98,9 @@ static const AudioBootStrap *const bootstrap[] = {
 #if SDL_AUDIO_DRIVER_EMSCRIPTEN
     &EMSCRIPTENAUDIO_bootstrap,
 #endif
+#if SDL_AUDIO_DRIVER_SWITCH
+    &SWITCHAUDIO_bootstrap,
+#endif
 #if SDL_AUDIO_DRIVER_JACK
     &JACK_bootstrap,
 #endif
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index f0e1f3dad..a7e519281 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -205,6 +205,7 @@ extern AudioBootStrap FUSIONSOUND_bootstrap;
 extern AudioBootStrap ANDROIDAUDIO_bootstrap;
 extern AudioBootStrap PSPAUDIO_bootstrap;
 extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
+extern AudioBootStrap SWITCHAUDIO_bootstrap;
 
 #endif /* SDL_sysaudio_h_ */
 
diff --git a/src/audio/switch/SDL_switchaudio.c b/src/audio/switch/SDL_switchaudio.c
new file mode 100644
index 000000000..961c2cf0d
--- /dev/null
+++ b/src/audio/switch/SDL_switchaudio.c
@@ -0,0 +1,156 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_AUDIO_DRIVER_SWITCH
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include "SDL_audio.h"
+#include "../SDL_audio_c.h"
+#include "../SDL_audiodev_c.h"
+
+#include "SDL_switchaudio.h"
+
+static int
+SWITCHAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+{
+    Result res;
+    SDL_bool supported_format = SDL_FALSE;
+    SDL_AudioFormat test_format;
+
+    res = audoutInitialize();
+    if (res != 0) {
+        return SDL_SetError("audoutInitialize failed (0x%x)", res);
+    }
+
+    res = audoutStartAudioOut();
+    if (res != 0) {
+        audoutExit();
+        return SDL_SetError("audoutStartAudioOut failed (0x%x)", res);
+    }
+
+    this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*this->hidden));
+    if (this->hidden == NULL) {
+        return SDL_OutOfMemory();
+    }
+    SDL_zerop(this->hidden);
+
+    test_format = SDL_FirstAudioFormat(this->spec.format);
+    while ((!supported_format) && (test_format)) {
+        if (test_format == AUDIO_S16LSB) {
+            supported_format = SDL_TRUE;
+        } else {
+            test_format = SDL_NextAudioFormat();
+        }
+    }
+    if (!supported_format) {
+        return SDL_SetError("Unsupported audio format");
+    }
+
+    this->spec.format = test_format;
+    this->spec.freq = 48000;
+    this->spec.channels = 2;
+
+    SDL_CalculateAudioSpec(&this->spec);
+
+    for (int i = 0; i < 2; i++) {
+        u32 size = (u32) (this->spec.size + 0xfff) & ~0xfff;
+        this->hidden->buffer[i] = memalign(0x1000, size);
+        memset(this->hidden->buffer[i], 0, size);
+        this->hidden->source_buffer[i].next = NULL;
+        this->hidden->source_buffer[i].buffer = this->hidden->buffer[i];
+        this->hidden->source_buffer[i].buffer_size =
+            (u64) this->spec.size / this->spec.channels / 4;
+        this->hidden->source_buffer[i].data_size = (u64) this->spec.size;
+        this->hidden->source_buffer[i].data_offset = (u64) 0;
+        audoutAppendAudioOutBuffer(&this->hidden->source_buffer[i]);
+    }
+
+    return 0;
+}
+
+static void
+SWITCHAUDIO_PlayDevice(_THIS)
+{
+    audoutAppendAudioOutBuffer(this->hidden->released_buffer);
+}
+
+static void
+SWITCHAUDIO_WaitDevice(_THIS)
+{
+
+}
+
+static Uint8
+*SWITCHAUDIO_GetDeviceBuf(_THIS)
+{
+    audoutWaitPlayFinish(&this->hidden->released_buffer,
+                         &this->hidden->released_count, U64_MAX);
+
+    return this->hidden->released_buffer->buffer;
+}
+
+static void
+SWITCHAUDIO_CloseDevice(_THIS)
+{
+    if (this->hidden->buffer[0]) {
+        free(this->hidden->buffer[0]);
+    }
+    if (this->hidden->buffer[1]) {
+        free(this->hidden->buffer[1]);
+    }
+
+    audoutStopAudioOut();
+    audoutExit();
+
+    SDL_free(this->hidden);
+}
+
+static void
+SWITCHAUDIO_ThreadInit(_THIS)
+{
+
+}
+
+static int
+SWITCHAUDIO_Init(SDL_AudioDriverImpl *impl)
+{
+    impl->OpenDevice = SWITCHAUDIO_OpenDevice;
+    impl->PlayDevice = SWITCHAUDIO_PlayDevice;
+    impl->WaitDevice = SWITCHAUDIO_WaitDevice;
+    impl->GetDeviceBuf = SWITCHAUDIO_GetDeviceBuf;
+    impl->CloseDevice = SWITCHAUDIO_CloseDevice;
+    impl->ThreadInit = SWITCHAUDIO_ThreadInit;
+
+    impl->OnlyHasDefaultOutputDevice = 1;
+
+    return 1;
+}
+
+AudioBootStrap SWITCHAUDIO_bootstrap = {
+    "switch", "Nintendo Switch audio driver", SWITCHAUDIO_Init, 0
+};
+
+#endif /* SDL_AUDIO_DRIVER_SWITCH */
diff --git a/src/audio/switch/SDL_switchaudio.h b/src/audio/switch/SDL_switchaudio.h
new file mode 100644
index 000000000..aeada608a
--- /dev/null
+++ b/src/audio/switch/SDL_switchaudio.h
@@ -0,0 +1,40 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_switchaudio_h_
+#define SDL_switchaudio_h_
+
+#include <switch.h>
+#include "../SDL_sysaudio.h"
+
+/* Hidden "this" pointer for the audio functions */
+#define _THIS   SDL_AudioDevice *this
+
+struct SDL_PrivateAudioData
+{
+    void *buffer[2];
+    AudioOutBuffer source_buffer[2];
+    AudioOutBuffer *released_buffer;
+    u32 released_count;
+
+};
+
+#endif /* SDL_switchaudio_h_ */
diff --git a/src/dynapi/SDL_dynapi.h b/src/dynapi/SDL_dynapi.h
index 73316f1f8..81021e9e9 100644
--- a/src/dynapi/SDL_dynapi.h
+++ b/src/dynapi/SDL_dynapi.h
@@ -55,6 +55,8 @@
 #define SDL_DYNAMIC_API 0
 #elif defined(__clang_analyzer__)
 #define SDL_DYNAMIC_API 0  /* Turn off for static analysis, so reports are more clear. */
+#elif defined(__SWITCH__) && __SWITCH__
+#define SDL_DYNAMIC_API 0  /* Probably not useful on switch. */
 #endif
 
 /* everyone else. This is where we turn on the API if nothing forced it off. */
diff --git a/src/filesystem/switch/dummy.c b/src/filesystem/switch/dummy.c
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h
index fdc0b592b..8dcc6ca1d 100644
--- a/src/joystick/SDL_gamecontrollerdb.h
+++ b/src/joystick/SDL_gamecontrollerdb.h
@@ -226,6 +226,9 @@ static const char *s_ControllerMappings [] =
 #if defined(SDL_JOYSTICK_EMSCRIPTEN)
     "emscripten,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
 #endif
+#if defined(SDL_JOYSTICK_SWITCH)
+    "53776974636820436F6E74726F6C6C65,Switch Controller,a:b1,b:b0,back:b11,dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+#endif
     NULL
 };
 
diff --git a/src/joystick/switch/SDL_sysjoystick.c b/src/joystick/switch/SDL_sysjoystick.c
new file mode 100644
index 000000000..21e8229fd
--- /dev/null
+++ b/src/joystick/switch/SDL_sysjoystick.c
@@ -0,0 +1,209 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_JOYSTICK_SWITCH
+
+/* This is the dummy implementation of the SDL joystick API */
+
+#include "SDL_events.h"
+#include "../SDL_sysjoystick.h"
+
+#include <switch.h>
+
+#define JOYSTICK_COUNT 8
+
+typedef struct JoystickState
+{
+    HidControllerID id;
+    JoystickPosition l_pos;
+    JoystickPosition r_pos;
+    u64 buttons;
+} JoystickState;
+
+/* Current pad state */
+static JoystickState pad[JOYSTICK_COUNT];
+
+static HidControllerID pad_id[JOYSTICK_COUNT] = {
+    CONTROLLER_P1_AUTO, CONTROLLER_PLAYER_2,
+    CONTROLLER_PLAYER_3, CONTROLLER_PLAYER_4,
+    CONTROLLER_PLAYER_5, CONTROLLER_PLAYER_6,
+    CONTROLLER_PLAYER_7, CONTROLLER_PLAYER_8
+};
+
+static const HidControllerKeys pad_mapping[] = {
+    KEY_A, KEY_B, KEY_X, KEY_Y,
+    KEY_LSTICK, KEY_RSTICK,
+    KEY_L, KEY_R,
+    KEY_ZL, KEY_ZR,
+    KEY_PLUS, KEY_MINUS,
+    KEY_DLEFT, KEY_DUP, KEY_DRIGHT, KEY_DDOWN,
+    KEY_LSTICK_LEFT, KEY_LSTICK_UP, KEY_LSTICK_RIGHT, KEY_LSTICK_DOWN,
+    KEY_RSTICK_LEFT, KEY_RSTICK_UP, KEY_RSTICK_RIGHT, KEY_RSTICK_DOWN,
+    KEY_SL_LEFT, KEY_SR_LEFT, KEY_SL_RIGHT, KEY_SR_RIGHT
+};
+
+/* Function to scan the system for joysticks.
+ * It should return 0, or -1 on an unrecoverable fatal error.
+ */
+int
+SDL_SYS_JoystickInit(void)
+{
+    for (int i = 0; i < JOYSTICK_COUNT; i++) {
+        pad[i].id = pad_id[i];
+    }
+
+    return JOYSTICK_COUNT;
+}
+
+int
+SDL_SYS_NumJoysticks(void)
+{
+    return JOYSTICK_COUNT;
+}
+
+void
+SDL_SYS_JoystickDetect(void)
+{
+}
+
+/* Function to get the device-dependent name of a joystick */
+const char *
+SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+{
+    return "Switch Controller";
+}
+
+/* Function to perform the mapping from device index to the instance id for this index */
+SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+{
+    return device_index;
+}
+
+/* Function to open a joystick for use.
+   The joystick to open is specified by the device index.
+   This should fill the nbuttons and naxes fields of the joystick structure.
+   It returns 0, or -1 if there is an error.
+ */
+int
+SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
+{
+    joystick->nbuttons = sizeof(pad_mapping) / sizeof(*pad_mapping);
+    joystick->naxes = 4;
+    joystick->nhats = 0;
+    joystick->instance_id = device_index;
+
+    return 0;
+}
+
+/* Function to determine if this joystick is attached to the system right now */
+SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+{
+    return SDL_TRUE;
+}
+
+/* Function to update the state of a joystick - called as a device poll.
+ * This function shouldn't update the joystick structure directly,
+ * but instead should call SDL_PrivateJoystick*() to deliver events
+ * and update joystick device state.
+ */
+void
+SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
+{
+    u64 changed;
+    static JoystickState pad_old[JOYSTICK_COUNT];
+
+    int index = (int) SDL_JoystickInstanceID(joystick);
+    if (index > JOYSTICK_COUNT) {
+        return;
+    }
+
+    hidJoystickRead(&pad[index].l_pos, pad[index].id, JOYSTICK_LEFT);
+    hidJoystickRead(&pad[index].r_pos, pad[index].id, JOYSTICK_RIGHT);
+    pad[index].buttons = hidKeysHeld(pad[index].id);
+
+    // Axes
+    if (pad_old[index].l_pos.dx != pad[index].l_pos.dx) {
+        SDL_PrivateJoystickAxis(joystick, 0, (Sint16) pad[index].l_pos.dx);
+        pad_old[index].l_pos.dx = pad[index].l_pos.dx;
+    }
+    if (pad_old[index].l_pos.dy != pad[index].l_pos.dy) {
+        SDL_PrivateJoystickAxis(joystick, 1, (Sint16) -pad[index].l_pos.dy);
+        pad_old[index].l_pos.dy = -pad[index].l_pos.dy;
+    }
+    if (pad_old[index].r_pos.dx != pad[index].r_pos.dx) {
+        SDL_PrivateJoystickAxis(joystick, 2, (Sint16) pad[index].r_pos.dx);
+        pad_old[index].r_pos.dx = pad[index].r_pos.dx;
+    }
+    if (pad_old[index].r_pos.dy != pad[index].r_pos.dy) {
+        SDL_PrivateJoystickAxis(joystick, 3, (Sint16) -pad[index].r_pos.dy);
+        pad_old[index].r_pos.dy = -pad[index].r_pos.dy;
+    }
+
+    // Buttons
+    changed = pad_old[index].buttons ^ pad[index].buttons;
+    pad_old[index].buttons = pad[index].buttons;
+    if (changed) {
+        for (int i = 0; i < joystick->nbuttons; i++) {
+            if (changed & pad_mapping[i]) {
+                SDL_PrivateJoystickButton(
+                    joystick, (Uint8) i,
+                    (Uint8) ((pad[index].buttons & pad_mapping[i]) ? SDL_PRESSED : SDL_RELEASED));
+            }
+        }
+    }
+}
+
+/* Function to close a joystick after use */
+void
+SDL_SYS_JoystickClose(SDL_Joystick *joystick)
+{
+}
+
+/* Function to perform any system-specific joystick related cleanup */
+void
+SDL_SYS_JoystickQuit(void)
+{
+}
+
+SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
+{
+    SDL_JoystickGUID guid;
+    /* the GUID is just the first 16 chars of the name for now */
+    const char *name = SDL_SYS_JoystickNameForDeviceIndex(device_index);
+    SDL_zero(guid);
+    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    return guid;
+}
+
+SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
+{
+    SDL_JoystickGUID guid;
+    /* the GUID is just the first 16 chars of the name for now */
+    const char *name = joystick->name;
+    SDL_zero(guid);
+    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    return guid;
+}
+
+#endif /* SDL_JOYSTICK_SWITCH */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/power/switch/dummy.c b/src/power/switch/dummy.c
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c
index 0cd388c37..e6f22a30a 100644
--- a/src/render/opengles2/SDL_render_gles2.c
+++ b/src/render/opengles2/SDL_render_gles2.c
@@ -43,7 +43,7 @@
 #endif
 
 /* To prevent unnecessary window recreation,
- * these should match the defaults selected in SDL_GL_ResetAttributes 
+ * these should match the defaults selected in SDL_GL_ResetAttributes
  */
 #define RENDERER_CONTEXT_MAJOR 2
 #define RENDERER_CONTEXT_MINOR 0
diff --git a/src/thread/SDL_thread_c.h b/src/thread/SDL_thread_c.h
index b68f90e91..3af243df7 100644
--- a/src/thread/SDL_thread_c.h
+++ b/src/thread/SDL_thread_c.h
@@ -36,6 +36,8 @@
 #include "psp/SDL_systhread_c.h"
 #elif SDL_THREAD_STDCPP
 #include "stdcpp/SDL_systhread_c.h"
+#elif SDL_THREAD_SWITCH
+#include "switch/SDL_systhread_c.h"
 #else
 #error Need thread implementation for this platform
 #include "generic/SDL_systhread_c.h"
diff --git a/src/thread/switch/SDL_syscond.c b/src/thread/switch/SDL_syscond.c
new file mode 100644
index 000000000..7fb3aa7e1
--- /dev/null
+++ b/src/thread/switch/SDL_syscond.c
@@ -0,0 +1,173 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include <switch.h>
+#include <threads.h>
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_SWITCH
+
+/* An implementation of condition variables using semaphores and mutexes */
+/*
+   This implementation borrows heavily from the BeOS condition variable
+   implementation, written by Christopher Tate and Owen Smith.  Thanks!
+ */
+
+#include "SDL_thread.h"
+
+struct SDL_cond
+{
+    cnd_t cnd;
+};
+
+struct SDL_mutex
+{
+    mtx_t mtx;
+};
+
+/* Create a condition variable */
+SDL_cond *
+SDL_CreateCond(void)
+{
+    SDL_cond *cond = NULL;
+
+    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
+    if (cond) {
+        int res = cnd_init(&cond->cnd);
+        if (res != thrd_success) {
+            printf("SDL_CreateCond::cnd_init failed: %i\n", res);
+        }
+    }
+    else {
+        SDL_OutOfMemory();
+    }
+    return (cond);
+}
+
+/* Destroy a condition variable */
+void
+SDL_DestroyCond(SDL_cond *cond)
+{
+    if (cond) {
+        cnd_destroy(&cond->cnd);
+        SDL_free(cond);
+    }
+}
+
+/* Restart one of the threads that are waiting on the condition variable */
+int
+SDL_CondSignal(SDL_cond *cond)
+{
+    int res;
+
+    if (!cond) {
+        return SDL_SetError("Passed a NULL cond");
+    }
+
+    res = cnd_signal(&cond->cnd);
+    if (res != thrd_success) {
+        return SDL_SetError("SDL_CondSignal::cnd_signal failed: %i", res);
+    }
+
+    return 0;
+}
+
+/* Restart all threads that are waiting on the condition variable */
+int
+SDL_CondBroadcast(SDL_cond *cond)
+{
+    int res;
+
+    if (!cond) {
+        return SDL_SetError("Passed a NULL cond");
+    }
+
+    res = cnd_broadcast(&cond->cnd);
+    if (res != thrd_success) {
+        return SDL_SetError("SDL_CondBroadcast::cnd_broadcast failed: %i", res);
+    }
+
+    return 0;
+}
+
+/* Wait on the condition variable for at most 'ms' milliseconds.
+   The mutex must be locked before entering this function!
+   The mutex is unlocked during the wait, and locked again after the wait.
+
+Typical use:
+
+Thread A:
+    SDL_LockMutex(lock);
+    while ( ! condition ) {
+        SDL_CondWait(cond, lock);
+    }
+    SDL_UnlockMutex(lock);
+
+Thread B:
+    SDL_LockMutex(lock);
+    ...
+    condition = true;
+    ...
+    SDL_CondSignal(cond);
+    SDL_UnlockMutex(lock);
+ */
+int
+SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms)
+{
+    struct timespec ts;
+    int res;
+
+    if (!cond || !mutex) {
+        return SDL_SetError("SDL_CondWaitTimeout: passed a NULL cond/mutex");
+    }
+
+    clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_nsec += (long) (ms * 1E6);
+    res = cnd_timedwait(&cond->cnd, &mutex->mtx, &ts);
+    if (res == thrd_timedout) {
+        return SDL_MUTEX_TIMEDOUT;
+    } else if (res != thrd_success) {
+        return SDL_SetError("SDL_CondWaitTimeout::cnd_timedwait failed: %i", res);
+    }
+
+    return 0;
+}
+
+/* Wait on the condition variable forever */
+int
+SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex)
+{
+    int res;
+
+    if (!cond || !mutex) {
+        return SDL_SetError("SDL_CondWaitTimeout: passed a NULL cond/mutex");
+    }
+
+    res = cnd_wait(&cond->cnd, &mutex->mtx);
+    if (res != thrd_success) {
+        return SDL_SetError("SDL_CondWait::cnd_wait failed: %i", res);
+    }
+
+    return 0;
+}
+
+#endif /* SDL_THREAD_SWITCH */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/switch/SDL_sysmutex.c b/src/thread/switch/SDL_sysmutex.c
new file mode 100644
index 000000000..ad61e8c35
--- /dev/null
+++ b/src/thread/switch/SDL_sysmutex.c
@@ -0,0 +1,86 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_SWITCH
+
+/* An implementation of mutexes */
+
+#include "SDL_thread.h"
+#include "SDL_systhread_c.h"
+
+struct SDL_mutex
+{
+    mtx_t mtx;
+};
+
+/* Create a mutex */
+SDL_mutex *
+SDL_CreateMutex(void)
+{
+    SDL_mutex *mutex = (SDL_mutex *) SDL_malloc(sizeof(*mutex));
+    if (mutex) {
+        mtx_init(&mutex->mtx, mtx_plain | mtx_recursive);
+    }
+    else {
+        SDL_OutOfMemory();
+    }
+
+    return mutex;
+}
+
+/* Free the mutex */
+void
+SDL_DestroyMutex(SDL_mutex *mutex)
+{
+    if (mutex) {
+        mtx_destroy(&mutex->mtx);
+        SDL_free(mutex);
+    }
+}
+
+/* Lock the mutex */
+int
+SDL_mutexP(SDL_mutex *mutex)
+{
+    if (mutex == NULL) {
+        return SDL_SetError("Passed a NULL mutex");
+    }
+
+    mtx_lock(&mutex->mtx);
+
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+SDL_mutexV(SDL_mutex *mutex)
+{
+    if (mutex == NULL) {
+        return SDL_SetError("Passed a NULL mutex");
+    }
+
+    mtx_unlock(&mutex->mtx);
+
+    return 0;
+}
+
+#endif /* SDL_THREAD_SWITCH */
diff --git a/src/thread/switch/SDL_sysmutex_c.h b/src/thread/switch/SDL_sysmutex_c.h
new file mode 100644
index 000000000..307537fc9
--- /dev/null
+++ b/src/thread/switch/SDL_sysmutex_c.h
@@ -0,0 +1,22 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
diff --git a/src/thread/switch/SDL_syssem.c b/src/thread/switch/SDL_syssem.c
new file mode 100644
index 000000000..6a8d20701
--- /dev/null
+++ b/src/thread/switch/SDL_syssem.c
@@ -0,0 +1,172 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_SWITCH
+
+/* Semaphore functions for the SWITCH. */
+
+#include <stdio.h>
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+#include "SDL_timer.h"
+
+struct SDL_semaphore
+{
+    Uint32 count;
+    Uint32 waiters_count;
+    SDL_mutex *count_lock;
+    SDL_cond *count_nonzero;
+};
+
+/* Create a semaphore */
+SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
+{
+    SDL_sem *sem;
+
+    sem = (SDL_sem *) SDL_malloc(sizeof(*sem));
+    if (!sem) {
+        SDL_OutOfMemory();
+        return NULL;
+    }
+    sem->count = initial_value;
+    sem->waiters_count = 0;
+
+    sem->count_lock = SDL_CreateMutex();
+    sem->count_nonzero = SDL_CreateCond();
+    if (!sem->count_lock || !sem->count_nonzero) {
+        SDL_DestroySemaphore(sem);
+        return NULL;
+    }
+
+    return sem;
+}
+
+/* Free the semaphore */
+void SDL_DestroySemaphore(SDL_sem *sem)
+{
+    if (sem) {
+        sem->count = 0xFFFFFFFF;
+        while (sem->waiters_count > 0) {
+            SDL_CondSignal(sem->count_nonzero);
+            SDL_Delay(10);
+        }
+        SDL_DestroyCond(sem->count_nonzero);
+        if (sem->count_lock) {
+            SDL_mutexP(sem->count_lock);
+            SDL_mutexV(sem->count_lock);
+            SDL_DestroyMutex(sem->count_lock);
+        }
+        SDL_free(sem);
+    }
+}
+
+/* TODO: This routine is a bit overloaded.
+ * If the timeout is 0 then just poll the semaphore; if it's SDL_MUTEX_MAXWAIT, pass
+ * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
+ * is specified, convert it to microseconds. */
+int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
+{
+    int retval;
+
+    if (!sem) {
+        SDL_SetError("Passed a NULL semaphore");
+        return -1;
+    }
+
+    /* A timeout of 0 is an easy case */
+    if (timeout == 0) {
+        return SDL_SemTryWait(sem);
+    }
+
+    SDL_LockMutex(sem->count_lock);
+    ++sem->waiters_count;
+    retval = 0;
+    while ((sem->count == 0) && (retval != SDL_MUTEX_TIMEDOUT)) {
+        retval = SDL_CondWaitTimeout(sem->count_nonzero, sem->count_lock, timeout);
+    }
+    --sem->waiters_count;
+    if (retval == 0) {
+        --sem->count;
+    }
+    SDL_UnlockMutex(sem->count_lock);
+
+    return retval;
+}
+
+int SDL_SemTryWait(SDL_sem *sem)
+{
+    int retval;
+
+    if (!sem) {
+        SDL_SetError("Passed a NULL semaphore");
+        return -1;
+    }
+
+    retval = SDL_MUTEX_TIMEDOUT;
+    SDL_LockMutex(sem->count_lock);
+    if (sem->count > 0) {
+        --sem->count;
+        retval = 0;
+    }
+    SDL_UnlockMutex(sem->count_lock);
+
+    return retval;
+}
+
+int SDL_SemWait(SDL_sem *sem)
+{
+    return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
+}
+
+/* Returns the current count of the semaphore */
+Uint32 SDL_SemValue(SDL_sem *sem)
+{
+    Uint32 value;
+
+    value = 0;
+    if (sem) {
+        SDL_LockMutex(sem->count_lock);
+        value = sem->count;
+        SDL_UnlockMutex(sem->count_lock);
+    }
+    return value;
+}
+
+int SDL_SemPost(SDL_sem *sem)
+{
+    if (!sem) {
+        SDL_SetError("Passed a NULL semaphore");
+        return -1;
+    }
+
+    SDL_LockMutex(sem->count_lock);
+    if (sem->waiters_count > 0) {
+        SDL_CondSignal(sem->count_nonzero);
+    }
+    ++sem->count;
+    SDL_UnlockMutex(sem->count_lock);
+
+    return 0;
+}
+
+#endif /* SDL_THREAD_SWITCH */
diff --git a/src/thread/switch/SDL_systhread.c b/src/thread/switch/SDL_systhread.c
new file mode 100644
index 000000000..4814f2a76
--- /dev/null
+++ b/src/thread/switch/SDL_systhread.c
@@ -0,0 +1,90 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_SWITCH
+
+/* SWITCH thread management routines for SDL */
+
+#include <stdio.h>
+#include <switch.h>
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+#include "../SDL_systhread.h"
+
+static int
+SDL_SYS_RunThread(void *data)
+{
+    SDL_RunThread(data);
+    return 0;
+}
+
+int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
+{
+    int res = thrd_create(&thread->handle, SDL_SYS_RunThread, args);
+    if (res != thrd_success) {
+        return SDL_SetError("SDL_SYS_CreateThread failed: %i", res);
+    }
+
+    return 0;
+}
+
+void SDL_SYS_SetupThread(const char *name)
+{
+}
+
+SDL_threadID SDL_ThreadID(void)
+{
+    return (SDL_threadID) thrd_current();
+}
+
+void SDL_SYS_WaitThread(SDL_Thread *thread)
+{
+    if (thread) {
+        thrd_join(thread->handle, NULL);
+    }
+}
+
+void SDL_SYS_DetachThread(SDL_Thread *thread)
+{
+}
+
+int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
+{
+    Result res;
+
+    if (priority == SDL_THREAD_PRIORITY_HIGH) {
+        res = svcSetThreadPriority(CUR_THREAD_HANDLE, 0x1C);
+    }
+    else {
+        // 0x3B = preemptive threading
+        res = svcSetThreadPriority(CUR_THREAD_HANDLE, 0x3B);
+    }
+
+    if(R_FAILED(res)) {
+        return SDL_SetError("SDL_SYS_SetThreadPriority failed: %i", res);
+    }
+
+    return 0;
+}
+
+#endif /* SDL_THREAD_SWITCH */
diff --git a/src/thread/switch/SDL_systhread_c.h b/src/thread/switch/SDL_systhread_c.h
new file mode 100644
index 000000000..892186e2c
--- /dev/null
+++ b/src/thread/switch/SDL_systhread_c.h
@@ -0,0 +1,25 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include <switch.h>
+#include <threads.h>
+
+typedef thrd_t SYS_ThreadHandle;
diff --git a/src/thread/switch/SDL_systls.c b/src/thread/switch/SDL_systls.c
new file mode 100644
index 000000000..2472fbd60
--- /dev/null
+++ b/src/thread/switch/SDL_systls.c
@@ -0,0 +1,34 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../SDL_thread_c.h"
+
+SDL_TLSData *
+SDL_SYS_GetTLSData(void)
+{
+    return SDL_Generic_GetTLSData();
+}
+
+int
+SDL_SYS_SetTLSData(SDL_TLSData *data)
+{
+    return SDL_Generic_SetTLSData(data);
+}
diff --git a/src/timer/switch/SDL_systimer.c b/src/timer/switch/SDL_systimer.c
new file mode 100644
index 000000000..5c5d209e0
--- /dev/null
+++ b/src/timer/switch/SDL_systimer.c
@@ -0,0 +1,78 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_TIMER_SWITCH
+
+#include "SDL_thread.h"
+#include "SDL_timer.h"
+#include "../SDL_timer_c.h"
+#include <switch.h>
+
+static bool started = false;
+
+static Uint64 start = 0;
+
+void
+SDL_TicksInit(void)
+{
+    if (started) {
+        return;
+    }
+
+    start = SDL_GetPerformanceCounter();
+    started = true;
+}
+
+void
+SDL_TicksQuit(void)
+{
+    started = false;
+}
+
+Uint32 SDL_GetTicks(void)
+{
+    if (!started) {
+        SDL_TicksInit();
+    }
+
+    return (Uint32) ((SDL_GetPerformanceCounter() - start) * 1000 / SDL_GetPerformanceFrequency());
+}
+
+Uint64
+SDL_GetPerformanceCounter(void)
+{
+    return svcGetSystemTick();
+}
+
+Uint64
+SDL_GetPerformanceFrequency(void)
+{
+    return 19200000;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{
+    svcSleepThread((Uint64) ms * 1000000);
+}
+
+#endif /* SDL_TIMER_SWITCH */
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 9df71c9aa..c37bb012f 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -418,6 +418,7 @@ extern VideoBootStrap NACL_bootstrap;
 extern VideoBootStrap VIVANTE_bootstrap;
 extern VideoBootStrap Emscripten_bootstrap;
 extern VideoBootStrap QNX_bootstrap;
+extern VideoBootStrap SWITCH_bootstrap;
 
 extern SDL_VideoDevice *SDL_GetVideoDevice(void);
 extern int SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode);
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 8cf195d1a..a2df8486a 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -112,6 +112,9 @@ static VideoBootStrap *bootstrap[] = {
 #if SDL_VIDEO_DRIVER_QNX
     &QNX_bootstrap,
 #endif
+#if SDL_VIDEO_DRIVER_SWITCH
+	&SWITCH_bootstrap,
+#endif
 #if SDL_VIDEO_DRIVER_DUMMY
     &DUMMY_bootstrap,
 #endif
@@ -1376,7 +1379,7 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
     }
 
     /* Some platforms have OpenGL enabled by default */
-#if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__
+#if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__ || __SWITCH__
     if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN)) {
         flags |= SDL_WINDOW_OPENGL;
     }
diff --git a/src/video/switch/SDL_switchopengles.c b/src/video/switch/SDL_switchopengles.c
new file mode 100644
index 000000000..58834eafa
--- /dev/null
+++ b/src/video/switch/SDL_switchopengles.c
@@ -0,0 +1,60 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+#include "SDL_log.h"
+
+#if SDL_VIDEO_DRIVER_SWITCH
+
+#include "SDL_video.h"
+#include "SDL_switchopengles.h"
+#include "SDL_switchvideo.h"
+
+/* EGL implementation of SDL OpenGL support */
+
+void
+SWITCH_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor)
+{
+    *mask = SDL_GL_CONTEXT_PROFILE_ES;
+    *major = 2;
+    *minor = 0;
+}
+
+int
+SWITCH_GLES_LoadLibrary(_THIS, const char *path)
+{
+    return SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY, 0);
+}
+
+void
+SWITCH_GLES_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
+{
+    *w = 1920;
+    *h = 1080;
+}
+
+SDL_EGL_CreateContext_impl(SWITCH)
+SDL_EGL_MakeCurrent_impl(SWITCH)
+SDL_EGL_SwapWindow_impl(SWITCH)
+
+#endif /* SDL_VIDEO_DRIVER_SWITCH */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/switch/SDL_switchopengles.h b/src/video/switch/SDL_switchopengles.h
new file mode 100644
index 000000000..3beb5eea7
--- /dev/null
+++ b/src/video/switch/SDL_switchopengles.h
@@ -0,0 +1,49 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef SDL_switchteopengles_h_
+#define SDL_switchteopengles_h_
+
+#if SDL_VIDEO_DRIVER_SWITCH
+
+#include "../SDL_sysvideo.h"
+#include "../SDL_egl_c.h"
+
+/* OpenGLES functions */
+#define SWITCH_GLES_GetAttribute SDL_EGL_GetAttribute
+#define SWITCH_GLES_GetProcAddress SDL_EGL_GetProcAddress
+#define SWITCH_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
+#define SWITCH_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
+#define SWITCH_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
+#define SWITCH_GLES_DeleteContext SDL_EGL_DeleteContext
+
+extern int SWITCH_GLES_LoadLibrary(_THIS, const char *path);
+extern SDL_GLContext SWITCH_GLES_CreateContext(_THIS, SDL_Window *window);
+extern int SWITCH_GLES_SwapWindow(_THIS, SDL_Window *window);
+extern int SWITCH_GLES_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context);
+extern void SWITCH_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor);
+extern void SWITCH_GLES_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h);
+
+#endif /* SDL_VIDEO_DRIVER_SWITCH */
+#endif /* SDL_switchteopengles_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/switch/SDL_switchtouch.c b/src/video/switch/SDL_switchtouch.c
new file mode 100644
index 000000000..9191817c0
--- /dev/null
+++ b/src/video/switch/SDL_switchtouch.c
@@ -0,0 +1,115 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_SWITCH
+
+#include <switch.h>
+
+#include "SDL_events.h"
+#include "SDL_switchtouch.h"
+#include "../../events/SDL_touch_c.h"
+
+#define MAX_TOUCH 16
+
+typedef struct SwitchTouch
+{
+    SDL_FingerID id;
+    touchPosition position;
+} SwitchTouch;
+
+typedef struct TouchState
+{
+    SwitchTouch touch[MAX_TOUCH];
+    u32 count;
+} TouchState;
+
+TouchState touchState;
+
+TouchState touchState_old;
+
+void
+SWITCH_InitTouch(void)
+{
+    SDL_AddTouch((SDL_TouchID) 0, "Switch");
+}
+
+void
+SWITCH_QuitTouch(void)
+{
+
+}
+
+void
+SWITCH_PollTouch(void)
+{
+    memcpy(&touchState_old, &touchState, sizeof(touchState));
+
+    touchState.count = hidTouchCount();
+    if (touchState.count >= MAX_TOUCH) {
+        touchState.count = MAX_TOUCH - 1;
+    }
+
+    if (touchState.count > 0) {
+        for (u32 i = 0; i < touchState.count; i++) {
+
+            touchState.touch[i].id = i;
+            hidTouchRead(&touchState.touch[i].position, i);
+
+            // Send an initial touch
+            SDL_SendTouch(0, (SDL_FingerID) i, SDL_TRUE,
+                          (float) touchState.touch[i].position.px / 1280.0f,
+                          (float) touchState.touch[i].position.py / 720.0f, 1);
+
+            // Always send the motion
+            SDL_SendTouchMotion(0, (SDL_FingerID) i,
+                                (float) touchState.touch[i].position.px / 1280.0f,
+                                (float) touchState.touch[i].position.py / 720.0f, 1);
+        }
+    }
+
+    // some fingers might have been let go
+    if (touchState_old.count > 0) {
+
+        for (int i = 0; i < touchState_old.count; i++) {
+
+            int finger_up = 1;
+            if (touchState.count > 0) {
+                for (int j = 0; j < touchState.count; j++) {
+                    if (touchState.touch[j].id == touchState_old.touch[i].id) {
+                        finger_up = 0;
+                    }
+                }
+            }
+
+            if (finger_up == 1) {
+                // Finger released from screen
+                SDL_SendTouch((SDL_TouchID) 0, (SDL_FingerID) touchState_old.touch[i].id, SDL_FALSE,
+                              (float) touchState_old.touch[i].position.px / 1280.0f,
+                              (float) touchState_old.touch[i].position.py / 720.0f, 1);
+            }
+        }
+    }
+}
+
+#endif /* SDL_VIDEO_DRIVER_SWITCH */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/switch/SDL_switchtouch.h b/src/video/switch/SDL_switchtouch.h
new file mode 100644
index 000000000..ac5908e39
--- /dev/null
+++ b/src/video/switch/SDL_switchtouch.h
@@ -0,0 +1,34 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef _SDL_switchtouch_h
+#define _SDL_switchtouch_h
+
+#include "../../SDL_internal.h"
+
+/* Touch functions */
+extern void SWITCH_InitTouch(void);
+extern void SWITCH_QuitTouch(void);
+extern void SWITCH_PollTouch(void);
+
+#endif /* _SDL_switchtouch_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/switch/SDL_switchvideo.c b/src/video/switch/SDL_switchvideo.c
new file mode 100644
index 000000000..e76b022ce
--- /dev/null
+++ b/src/video/switch/SDL_switchvideo.c
@@ -0,0 +1,366 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_SWITCH
+
+#include "../SDL_sysvideo.h"
+#include "../../render/SDL_sysrender.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "../../events/SDL_mouse_c.h"
+#include "../../events/SDL_windowevents_c.h"
+
+#include "SDL_switchvideo.h"
+#include "SDL_switchopengles.h"
+#include "SDL_switchtouch.h"
+
+static int
+SWITCH_Available(void)
+{
+    return 1;
+}
+
+static void
+SWITCH_Destroy(SDL_VideoDevice *device)
+{
+    if (device) {
+        SDL_free(device);
+    }
+}
+
+static SDL_VideoDevice *
+SWITCH_CreateDevice(int devindex)
+{
+    SDL_VideoDevice *device;
+
+    /* Initialize SDL_VideoDevice structure */
+    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
+    if (device == NULL) {
+        SDL_OutOfMemory();
+        return NULL;
+    }
+
+    /* Setup amount of available displays */
+    device->num_displays = 0;
+
+    /* Set device free function */
+    device->free = SWITCH_Destroy;
+
+    /* Setup all functions which we can handle */
+    device->VideoInit = SWITCH_VideoInit;
+    device->VideoQuit = SWITCH_VideoQuit;
+    device->GetDisplayModes = SWITCH_GetDisplayModes;
+    device->SetDisplayMode = SWITCH_SetDisplayMode;
+    device->CreateSDLWindow = SWITCH_CreateWindow;
+    device->CreateSDLWindowFrom = SWITCH_CreateWindowFrom;
+    device->SetWindowTitle = SWITCH_SetWindowTitle;
+    device->SetWindowIcon = SWITCH_SetWindowIcon;
+    device->SetWindowPosition = SWITCH_SetWindowPosition;
+    device->SetWindowSize = SWITCH_SetWindowSize;
+    device->ShowWindow = SWITCH_ShowWindow;
+    device->HideWindow = SWITCH_HideWindow;
+    device->RaiseWindow = SWITCH_RaiseWindow;
+    device->MaximizeWindow = SWITCH_MaximizeWindow;
+    device->MinimizeWindow = SWITCH_MinimizeWindow;
+    device->RestoreWindow = SWITCH_RestoreWindow;
+    device->SetWindowGrab = SWITCH_SetWindowGrab;
+    device->DestroyWindow = SWITCH_DestroyWindow;
+
+    device->GL_LoadLibrary = SWITCH_GLES_LoadLibrary;
+    device->GL_GetProcAddress = SWITCH_GLES_GetProcAddress;
+    device->GL_UnloadLibrary = SWITCH_GLES_UnloadLibrary;
+    device->GL_CreateContext = SWITCH_GLES_CreateContext;
+    device->GL_MakeCurrent = SWITCH_GLES_MakeCurrent;
+    device->GL_SetSwapInterval = SWITCH_GLES_SetSwapInterval;
+    device->GL_GetSwapInterval = SWITCH_GLES_GetSwapInterval;
+    device->GL_SwapWindow = SWITCH_GLES_SwapWindow;
+    device->GL_DeleteContext = SWITCH_GLES_DeleteContext;
+    device->GL_DefaultProfileConfig = SWITCH_GLES_DefaultProfileConfig;
+    device->GL_GetDrawableSize = SWITCH_GLES_GetDrawableSize;
+
+    device->PumpEvents = SWITCH_PumpEvents;
+
+    return device;
+}
+
+VideoBootStrap SWITCH_bootstrap = {
+    "Switch",
+    "OpenGL ES2 video driver for Nintendo Switch",
+    SWITCH_Available,
+    SWITCH_CreateDevice
+};
+
+/*****************************************************************************/
+/* SDL Video and Display initialization/handling functions                   */
+/*****************************************************************************/
+int
+SWITCH_VideoInit(_THIS)
+{
+    SDL_VideoDisplay display;
+    SDL_DisplayMode current_mode;
+    SDL_DisplayData *data;
+    SDL_DisplayModeData *mdata;
+    Result rc;
+
+    SDL_zero(current_mode);
+    current_mode.w = 1920;
+    current_mode.h = 1080;
+    current_mode.refresh_rate = 60;
+    current_mode.format = SDL_PIXELFORMAT_RGBA8888;
+    mdata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
+    current_mode.driverdata = mdata;
+
+    SDL_zero(display);
+    display.desktop_mode = current_mode;
+    display.current_mode = current_mode;
+
+    /* Allocate display internal data */
+    data = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
+    if (data == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    data->egl_display = EGL_DEFAULT_DISPLAY;
+
+    // init vi
+    rc = viInitialize(ViServiceType_Default);
+    if (R_FAILED(rc)) {
+        return SDL_SetError("Could not initialize vi service: 0x%x", rc);
+    }
+
+    rc = viOpenDefaultDisplay(&data->viDisplay);
+    if (R_FAILED(rc)) {
+        return SDL_SetError("Could not open default display: 0x%x", rc);
+    }
+
+    display.driverdata = data;
+    SDL_AddVideoDisplay(&display);
+
+    // init touch
+    SWITCH_InitTouch();
+
+    return 0;
+}
+
+void
+SWITCH_VideoQuit(_THIS)
+{
+    SDL_DisplayData *data = SDL_GetDisplayDriverData(0);
+    if (data) {
+        viCloseDisplay(&data->viDisplay);
+    }
+    viExit();
+
+    // exit touch
+    SWITCH_QuitTouch();
+}
+
+void
+SWITCH_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
+{
+    SDL_DisplayMode mode;
+    SDL_DisplayModeData *data;
+
+    // 1920x1080 RGBA8888, default mode
+    SDL_AddDisplayMode(display, &display->current_mode);
+
+    // 1280x720 RGBA8888
+    SDL_zero(mode);
+    mode.w = 1280;
+    mode.h = 720;
+    mode.refresh_rate = 60;
+    mode.format = SDL_PIXELFORMAT_RGBA8888;
+    data = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
+    mode.driverdata = data;
+    SDL_AddDisplayMode(display, &mode);
+}
+
+int
+SWITCH_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
+{
+    SDL_WindowData *data;
+    Result rc;
+
+    if (display->fullscreen_window) {
+        data = (SDL_WindowData *) display->fullscreen_window->driverdata;
+    }
+    else {
+        if (!SDL_GetFocusWindow()) {
+            return SDL_SetError("Could not get window focus");
+        }
+        data = (SDL_WindowData *) SDL_GetFocusWindow()->driverdata;
+    }
+
+    rc = nwindowSetCrop(&data->nWindow, 0, 0, mode->w, mode->h);
+    if (rc) {
+        return SDL_SetError("Could not set NWindow crop: 0x%x", rc);
+    }
+
+    return 0;
+}
+
+int
+SWITCH_CreateWindow(_THIS, SDL_Window *window)
+{
+    Result rc;
+    SDL_DisplayData *ddata = SDL_GetDisplayDriverData(0);
+    SDL_WindowData *wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
+    if (wdata == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    if (!_this->egl_data) {
+        return SDL_SetError("EGL not initialized");
+    }
+
+    rc = viCreateLayer(&ddata->viDisplay, &wdata->viLayer);
+    if (R_FAILED(rc)) {
+        return SDL_SetError("Could not create vi layer: 0x%x", rc);
+    }
+
+    rc = viSetLayerScalingMode(&wdata->viLayer, ViScalingMode_FitToLayer);
+    if (R_FAILED(rc)) {
+        viCloseLayer(&wdata->viLayer);
+        return SDL_SetError("Could not set vi scaling mode: 0x%x", rc);
+    }
+
+    rc = nwindowCreateFromLayer(&wdata->nWindow, &wdata->viLayer);
+    if (R_FAILED(rc)) {
+        viCloseLayer(&wdata->viLayer);
+        return SDL_SetError("Could not create NWindow from layer: 0x%x", rc);
+    }
+
+    rc = nwindowSetDimensions(&wdata->nWindow, 1920, 1080);
+    if (R_FAILED(rc)) {
+        nwindowClose(&wdata->nWindow);
+        viCloseLayer(&wdata->viLayer);
+        return SDL_SetError("Could not set NWindow dimensions: 0x%x", rc);
+    }
+
+    rc = nwindowSetCrop(&wdata->nWindow, 0, 0, window->w, window->h);
+    if (R_FAILED(rc)) {
+        nwindowClose(&wdata->nWindow);
+        viCloseLayer(&wdata->viLayer);
+        return SDL_SetError("Could not set NWindow crop: 0x%x", rc);
+    }
+
+    wdata->egl_surface = SDL_EGL_CreateSurface(_this, &wdata->nWindow);
+    if (wdata->egl_surface == EGL_NO_SURFACE) {
+        nwindowClose(&wdata->nWindow);
+        viCloseLayer(&wdata->viLayer);
+        return SDL_SetError("Could not create GLES window surface");
+    }
+
+    /* Setup driver data for this window */
+    window->driverdata = wdata;
+
+    /* One window, it always has focus */
+    SDL_SetMouseFocus(window);
+    SDL_SetKeyboardFocus(window);
+
+    /* Window has been successfully created */
+    return 0;
+}
+
+void
+SWITCH_DestroyWindow(_THIS, SDL_Window *window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+
+    if (data) {
+        if (data->egl_surface != EGL_NO_SURFACE) {
+            SDL_EGL_DestroySurface(_this, data->egl_surface);
+        }
+        nwindowClose(&data->nWindow);
+        viCloseLayer(&data->viLayer);
+        SDL_free(data);
+        window->driverdata = NULL;
+    }
+}
+
+int
+SWITCH_CreateWindowFrom(_THIS, SDL_Window *window, const void *data)
+{
+    return -1;
+}
+void
+SWITCH_SetWindowTitle(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_SetWindowIcon(_THIS, SDL_Window *window, SDL_Surface *icon)
+{
+}
+void
+SWITCH_SetWindowPosition(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_SetWindowSize(_THIS, SDL_Window *window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    nwindowSetCrop(&data->nWindow, 0, 0, window->w, window->h);
+}
+void
+SWITCH_ShowWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_HideWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_RaiseWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_MaximizeWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_MinimizeWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_RestoreWindow(_THIS, SDL_Window *window)
+{
+}
+void
+SWITCH_SetWindowGrab(_THIS, SDL_Window *window, SDL_bool grabbed)
+{
+}
+
+void
+SWITCH_PumpEvents(_THIS)
+{
+    if (!appletMainLoop()) {
+        SDL_Event ev;
+        ev.type = SDL_QUIT;
+        SDL_PushEvent(&ev);
+        return;
+    }
+
+    hidScanInput();
+    SWITCH_PollTouch();
+}
+
+#endif /* SDL_VIDEO_DRIVER_SWITCH */
diff --git a/src/video/switch/SDL_switchvideo.h b/src/video/switch/SDL_switchvideo.h
new file mode 100644
index 000000000..b4ed8ef10
--- /dev/null
+++ b/src/video/switch/SDL_switchvideo.h
@@ -0,0 +1,74 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef __SDL_SWITCHVIDEO_H__
+#define __SDL_SWITCHVIDEO_H__
+
+#if SDL_VIDEO_DRIVER_SWITCH
+
+#include <switch.h>
+
+#include "../../SDL_internal.h"
+#include "../SDL_sysvideo.h"
+
+#include "SDL_egl.h"
+
+typedef struct SDL_DisplayData
+{
+    ViDisplay viDisplay;
+    EGLDisplay egl_display;
+} SDL_DisplayData;
+
+typedef struct SDL_DisplayModeData
+{
+} SDL_DisplayModeData;
+
+typedef struct SDL_WindowData
+{
+    ViLayer viLayer;
+    NWindow nWindow;
+    EGLSurface egl_surface;
+} SDL_WindowData;
+
+int SWITCH_VideoInit(_THIS);
+void SWITCH_VideoQuit(_THIS);
+void SWITCH_GetDisplayModes(_THIS, SDL_VideoDisplay *display);
+int SWITCH_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
+int SWITCH_CreateWindow(_THIS, SDL_Window *window);
+int SWITCH_CreateWindowFrom(_THIS, SDL_Window *window, const void *data);
+void SWITCH_SetWindowTitle(_THIS, SDL_Window *window);
+void SWITCH_SetWindowIcon(_THIS, SDL_Window *window, SDL_Surface *icon);
+void SWITCH_SetWindowPosition(_THIS, SDL_Window *window);
+void SWITCH_SetWindowSize(_THIS, SDL_Window *window);
+void SWITCH_ShowWindow(_THIS, SDL_Window *window);
+void SWITCH_HideWindow(_THIS, SDL_Window *window);
+void SWITCH_RaiseWindow(_THIS, SDL_Window *window);
+void SWITCH_MaximizeWindow(_THIS, SDL_Window *window);
+void SWITCH_MinimizeWindow(_THIS, SDL_Window *window);
+void SWITCH_RestoreWindow(_THIS, SDL_Window *window);
+void SWITCH_SetWindowGrab(_THIS, SDL_Window *window, SDL_bool grabbed);
+void SWITCH_DestroyWindow(_THIS, SDL_Window *window);
+void SWITCH_PumpEvents(_THIS);
+
+#endif /* SDL_VIDEO_DRIVER_SWITCH */
+#endif /* __SDL_SWITCHVIDEO_H__ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/test/configure b/test/configure
index 61c32fba1..d0e04b86e 100755
--- a/test/configure
+++ b/test/configure
@@ -637,6 +637,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -717,6 +718,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE}'
@@ -969,6 +971,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1106,7 +1117,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1259,6 +1270,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
diff --git a/test/testswitch.c b/test/testswitch.c
new file mode 100644
index 000000000..51012e481
--- /dev/null
+++ b/test/testswitch.c
@@ -0,0 +1,239 @@
+/*
+  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <switch.h>
+#include "SDL2/SDL.h"
+
+static SDL_DisplayMode modes[5];
+
+static int mode_count = 0, current_mode = 0;
+
+void print_info(SDL_Window *window, SDL_Renderer *renderer)
+{
+    int w, h;
+    SDL_DisplayMode mode;
+
+    SDL_GetWindowSize(window, &w, &h);
+    SDL_Log("window size: %i x %i\n", w, h);
+    SDL_GetRendererOutputSize(renderer, &w, &h);
+    SDL_Log("renderer size: %i x %i\n", w, h);
+
+    SDL_GetCurrentDisplayMode(0, &mode);
+    SDL_Log("display mode: %i x %i @ %i bpp (%s)",
+            mode.w, mode.h,
+            SDL_BITSPERPIXEL(mode.format),
+            SDL_GetPixelFormatName(mode.format));
+}
+
+void change_mode(SDL_Window *window)
+{
+    current_mode++;
+    if (current_mode == mode_count) {
+        current_mode = 0;
+    }
+
+    SDL_SetWindowDisplayMode(window, &modes[current_mode]);
+}
+
+void draw_rects(SDL_Renderer *renderer, int x, int y)
+{
+    // R
+    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+    SDL_Rect r = {x, y, 64, 64};
+    SDL_RenderFillRect(renderer, &r);
+
+    // G
+    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
+    SDL_Rect g = {x + 64, y, 64, 64};
+    SDL_RenderFillRect(renderer, &g);
+
+    // B
+    SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
+    SDL_Rect b = {x + 128, y, 64, 64};
+    SDL_RenderFillRect(renderer, &b);
+}
+
+int main(int argc, char *argv[])
+{
+    SDL_Event event;
+    SDL_Window *window;
+    SDL_Renderer *renderer;
+    int done = 0, x = 0, w = 0, h = 0;
+
+    // mandatory at least on switch, else gfx is not properly closed
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
+        SDL_Log("SDL_Init: %s\n", SDL_GetError());
+        return -1;
+    }
+
+    /// create a window (OpenGL always enabled)
+    /// available switch SDL2 video modes :
+    /// 1920 x 1080 @ 32 bpp (SDL_PIXELFORMAT_RGBA8888)
+    /// 1280 x 720 @ 32 bpp (SDL_PIXELFORMAT_RGBA8888)
+    ///
+    /// SDL_SetWindowSize to change window size when SDL_WINDOW_FULLSCREEN is NOT used (preferably)
+    /// SDL_SetDisplayMode to change display size after SDL_CreateWindow called with SDL_WINDOW_FULLSCREEN
+    /// (this means window size won't change, you'll need to handle that, as any SDL2 app)
+    window = SDL_CreateWindow("sdl2_gles2", 0, 0, 1280, 720, 0);
+    if (!window) {
+        SDL_Log("SDL_CreateWindow: %s\n", SDL_GetError());
+        SDL_Quit();
+        return -1;
+    }
+
+    // create a renderer (OpenGL ES2)
+    renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
+    if (!renderer) {
+        SDL_Log("SDL_CreateRenderer: %s\n", SDL_GetError());
+        SDL_Quit();
+        return -1;
+    }
+
+    // pint some info about display/window/renderer
+    print_info(window, renderer);
+
+    // list available display modes
+    mode_count = SDL_GetNumDisplayModes(0);
+    for (int i = 0; i < mode_count; i++) {
+        SDL_DisplayMode mode;
+        SDL_GetDisplayMode(0, i, &mode);
+        modes[i] = mode;
+        SDL_Log("found display mode: %i x %i @ %i bpp (%s)",
+                mode.w, mode.h,
+                SDL_BITSPERPIXEL(mode.format),
+                SDL_GetPixelFormatName(mode.format));
+    }
+
+    // open CONTROLLER_PLAYER_1 and CONTROLLER_PLAYER_2
+    // when railed, both joycons are mapped to joystick #0,
+    // else joycons are individually mapped to joystick #0, joystick #1, ...
+    // https://github.com/devkitPro/SDL/blob/switch-sdl2/src/joystick/switch/SDL_sysjoystick.c#L45
+    for (int i = 0; i < 2; i++) {
+        if (SDL_JoystickOpen(i) == NULL) {
+            SDL_Log("SDL_JoystickOpen: %s\n", SDL_GetError());
+            SDL_Quit();
+            return -1;
+        }
+    }
+
+    while (!done) {
+
+        while (SDL_PollEvent(&event)) {
+
+            switch (event.type) {
+
+                case SDL_JOYAXISMOTION:
+                    SDL_Log("Joystick %d axis %d value: %d\n",
+                            event.jaxis.which,
+                            event.jaxis.axis, event.jaxis.value);
+                    break;
+
+                case SDL_JOYBUTTONDOWN:
+                    SDL_Log("Joystick %d button %d down\n",
+                            event.jbutton.which, event.jbutton.button);
+                    // https://github.com/devkitPro/SDL/blob/switch-sdl2/src/joystick/switch/SDL_sysjoystick.c#L52
+                    if (event.jbutton.which == 0) {
+                        if (event.jbutton.button == 0) {
+                            // joystick #0 down (A)
+                            change_mode(window);
+                            print_info(window, renderer);
+                        }
+                        else if (event.jbutton.button == 2) {
+                            // joystick #0 down (X)
+                            if (w == 1920) {
+                                SDL_SetWindowSize(window, 1280, 720);
+                            }
+                            else {
+                                SDL_SetWindowSize(window, 1920, 1080);
+                            }
+                            print_info(window, renderer);
+                        }
+                    }
+                    // joystick #0 down (B)
+                    if (event.jbutton.which == 0 && event.jbutton.button == 1) {
+                        done = 1;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+        SDL_RenderClear(renderer);
+
+        // Fill renderer bounds
+        SDL_SetRenderDrawColor(renderer, 111, 111, 111, 255);
+        SDL_GetWindowSize(window, &w, &h);
+        SDL_Rect f = {0, 0, w, h};
+        SDL_RenderFillRect(renderer, &f);
+
+        draw_rects(renderer, x, 0);
+        draw_rects(renderer, x, h - 64);
+
+        SDL_RenderPresent(renderer);
+
+        x++;
+        if (x > w - 192) {
+            x = 0;
+        }
+    }
+
+    SDL_DestroyRenderer(renderer);
+    SDL_DestroyWindow(window);
+    SDL_Quit();
+
+    return 0;
+}
+
+//-----------------------------------------------------------------------------
+// nxlink support
+//-----------------------------------------------------------------------------
+
+#include <unistd.h>
+
+static int s_nxlinkSock = -1;
+
+static void initNxLink()
+{
+    if (R_FAILED(socketInitializeDefault()))
+        return;
+
+    s_nxlinkSock = nxlinkStdio();
+    if (s_nxlinkSock >= 0)
+        printf("printf output now goes to nxlink server\n");
+    else
+        socketExit();
+}
+
+static void deinitNxLink()
+{
+    if (s_nxlinkSock >= 0) {
+        close(s_nxlinkSock);
+        socketExit();
+        s_nxlinkSock = -1;
+    }
+}
+
+void userAppInit()
+{
+    initNxLink();
+}
+
+void userAppExit()
+{
+    deinitNxLink();
+}
