Add the switch port first code
This commit is contained in:
parent
981f360371
commit
dd152f6232
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -58,6 +58,14 @@ packages/
|
|||
*.bc
|
||||
*.so
|
||||
|
||||
# Ignore switch files
|
||||
examples/*/*.d
|
||||
examples/*/*.elf
|
||||
examples/*/*.lst
|
||||
examples/*/*.map
|
||||
examples/*/*.nacp
|
||||
examples/*/*.nro
|
||||
|
||||
# Ignore files build by xcode
|
||||
*.mode*v*
|
||||
*.pbxuser
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ RAYLIB_PATH ?= ..
|
|||
|
||||
# Define default options
|
||||
|
||||
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
|
||||
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
|
||||
PLATFORM ?= PLATFORM_DESKTOP
|
||||
|
||||
# Locations of your newly installed library and associated headers. See ../src/Makefile
|
||||
|
|
@ -101,6 +101,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
|
|||
PLATFORM_OS=LINUX
|
||||
endif
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
UNAMEOS = $(shell uname)
|
||||
ifeq ($(UNAMEOS),Linux)
|
||||
PLATFORM_OS = LINUX
|
||||
endif
|
||||
RAYLIB_LIBTYPE = STATIC
|
||||
USE_EXTERNAL_GLFW = TRUE
|
||||
endif
|
||||
|
||||
# RAYLIB_PATH adjustment for different platforms.
|
||||
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
|
||||
|
|
@ -131,6 +139,11 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH)
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
RAYLIB_PREFIX ?= ..
|
||||
RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX))
|
||||
endif
|
||||
|
||||
# Define raylib release directory for compiled library.
|
||||
# RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version
|
||||
RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src
|
||||
|
|
@ -176,6 +189,12 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
# to use emscripten.h and emscripten_set_main_loop()
|
||||
CC = emcc
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
endif
|
||||
|
||||
# Define default make program
|
||||
MAKE = make
|
||||
|
|
@ -205,6 +224,8 @@ ifeq ($(BUILD_MODE),DEBUG)
|
|||
else
|
||||
ifeq ($(PLATFORM),PLATFORM_WEB)
|
||||
CFLAGS += -Os
|
||||
else ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
CFLAGS += -O2
|
||||
else
|
||||
CFLAGS += -s -O1
|
||||
endif
|
||||
|
|
@ -260,6 +281,12 @@ endif
|
|||
# NOTE: Some external/extras libraries could be required (stb, physac, easings...)
|
||||
INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external -I$(RAYLIB_PATH)/src/extras
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
CFLAGS += -ffunction-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
|
||||
endif
|
||||
|
||||
# Define additional directories containing required header files
|
||||
ifeq ($(PLATFORM),PLATFORM_RPI)
|
||||
# RPI required libraries
|
||||
|
|
@ -308,6 +335,10 @@ ifeq ($(PLATFORM),PLATFORM_RPI)
|
|||
LDFLAGS += -L/opt/vc/lib
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
LDFLAGS += -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$*.map $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
endif
|
||||
|
||||
# Define any libraries required on linking
|
||||
# if you want to link libraries (libname.so or libname.a), use the -lname
|
||||
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
|
||||
|
|
@ -369,6 +400,10 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
# Libraries for web (HTML5) compiling
|
||||
LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
LDLIBS = -lraylib -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm
|
||||
LDLIBS += `$(PREFIX)pkg-config --libs sdl2`
|
||||
endif
|
||||
|
||||
# Define all object files required
|
||||
CORE = \
|
||||
|
|
@ -521,6 +556,12 @@ physics: $(PHYSICS)
|
|||
%: %.c
|
||||
ifeq ($(PLATFORM),PLATFORM_ANDROID)
|
||||
$(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$<
|
||||
else ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
$(CC) -MMD -MP -MF $@.d -o $@.o -c $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
|
||||
$(CXX) $@.o $(LDFLAGS) $(LDLIBS) -o $@.elf -D$(PLATFORM)
|
||||
$(NM) -CSn $@.elf > $@.lst
|
||||
nacptool --create "$(notdir $@)" "Raylib Example" "1.0.0" $@.nacp
|
||||
elf2nro $@.elf $@.nro --icon=textures/resources/raylib_logo.jpg --nacp=$@.nacp
|
||||
else
|
||||
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
|
||||
endif
|
||||
|
|
@ -550,6 +591,16 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
|
|||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_WEB)
|
||||
del *.o *.html *.js
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
find . -type f \( \
|
||||
-name "*.o" -o \
|
||||
-name "*.d" -o \
|
||||
-name "*.elf" -o \
|
||||
-name "*.lst" -o \
|
||||
-name "*.map" -o \
|
||||
-name "*.nacp" -o \
|
||||
-name "*.nro" \) -delete
|
||||
endif
|
||||
@echo Cleaning done
|
||||
|
||||
|
|
|
|||
BIN
examples/textures/resources/raylib_logo.jpg
Normal file
BIN
examples/textures/resources/raylib_logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
44
src/Makefile
44
src/Makefile
|
|
@ -11,6 +11,7 @@
|
|||
# PLATFORM_RPI: Raspberry Pi (Raspbian)
|
||||
# PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
|
||||
# PLATFORM_WEB: HTML5 (Chrome, Firefox)
|
||||
# PLATFORM_NX: Switch LibNX
|
||||
#
|
||||
# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline.
|
||||
# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline.
|
||||
|
|
@ -66,7 +67,7 @@ RAYLIB_LIB_NAME ?= raylib
|
|||
RAYLIB_RES_FILE ?= ./raylib.dll.rc.data
|
||||
|
||||
# Define raylib platform
|
||||
# Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
|
||||
# Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
|
||||
PLATFORM ?= PLATFORM_DESKTOP
|
||||
|
||||
# Include raylib modules on compilation
|
||||
|
|
@ -144,6 +145,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
|
|||
PLATFORM_OS = LINUX
|
||||
endif
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
UNAMEOS = $(shell uname)
|
||||
ifeq ($(UNAMEOS),Linux)
|
||||
PLATFORM_OS = LINUX
|
||||
endif
|
||||
RAYLIB_LIBTYPE = STATIC
|
||||
USE_EXTERNAL_GLFW = TRUE
|
||||
endif
|
||||
|
||||
# RAYLIB_SRC_PATH adjustment for different platforms.
|
||||
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
|
||||
|
|
@ -223,6 +232,9 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
|
|||
# By default use OpenGL ES 2.0 on Android
|
||||
GRAPHICS = GRAPHICS_API_OPENGL_ES2
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
GRAPHICS = GRAPHICS_API_OPENGL_ES2
|
||||
endif
|
||||
|
||||
# Define default C compiler and archiver to pack library
|
||||
CC = gcc
|
||||
|
|
@ -271,6 +283,12 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
|
|||
AR = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android-ar
|
||||
endif
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
endif
|
||||
|
||||
# Define compiler flags:
|
||||
# -O1 defines optimization level
|
||||
|
|
@ -309,6 +327,9 @@ ifeq ($(RAYLIB_BUILD_MODE),RELEASE)
|
|||
ifeq ($(PLATFORM),PLATFORM_ANDROID)
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
endif
|
||||
|
||||
# Additional flags for compiler (if desired)
|
||||
|
|
@ -387,6 +408,13 @@ endif
|
|||
# NOTE: Several external required libraries (stb and others)
|
||||
INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
|
||||
CFLAGS += -ffunction-sections -fdata-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
|
||||
INCLUDE_PATHS = -I.
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
|
||||
ifeq ($(PLATFORM_OS),BSD)
|
||||
INCLUDE_PATHS += -I/usr/local/include
|
||||
|
|
@ -475,7 +503,9 @@ endif
|
|||
ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE)
|
||||
OBJS += physac.o
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
OBJS += nxusb.o
|
||||
endif
|
||||
ifeq ($(PLATFORM),PLATFORM_ANDROID)
|
||||
OBJS += android_native_app_glue.o
|
||||
endif
|
||||
|
|
@ -550,7 +580,7 @@ else
|
|||
else
|
||||
# Compile raylib static library version $(RAYLIB_VERSION)
|
||||
# WARNING: You should type "make clean" before doing this target.
|
||||
$(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS)
|
||||
$(AR) rc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS)
|
||||
@echo "raylib static library generated (lib$(RAYLIB_LIB_NAME).a) in $(RAYLIB_RELEASE_PATH)!"
|
||||
endif
|
||||
endif
|
||||
|
|
@ -589,6 +619,10 @@ models.o : models.c raylib.h rlgl.h raymath.h
|
|||
raudio.o : raudio.c raylib.h
|
||||
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
|
||||
|
||||
# Compile switch usb module
|
||||
nxusb.o : nxusb.c nxusb.h raylib.h
|
||||
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
|
||||
|
||||
# Compile raygui module
|
||||
# NOTE: raygui header should be distributed with raylib.h
|
||||
raygui.o : raygui.c raygui.h gui_textbox_extended.h ricons.h
|
||||
|
|
@ -621,6 +655,10 @@ android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c
|
|||
# See below and ../examples/Makefile for more information.
|
||||
# TODO: Add other platforms. Remove sudo requirement, i.e. add USER mode.
|
||||
|
||||
ifeq ($(PLATFORM),PLATFORM_NX)
|
||||
DESTDIR ?= $(PORTLIBS)
|
||||
endif
|
||||
|
||||
# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths.
|
||||
DESTDIR ?= /usr/local
|
||||
RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@
|
|||
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
|
||||
// Enabling this flag allows manual control of the frame processes, use at your own risk
|
||||
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
|
||||
// Enabling this flag allows gamepad to set keyboard and mouse states
|
||||
#define NX_SUPPORT_GAMEPAD_EMULATION 1
|
||||
// Enabling this flag allows debugging by USB, the application will wait for an USB connection to start
|
||||
//#define NX_USB_DEBUGGER 1
|
||||
|
||||
// core: Configuration values
|
||||
//------------------------------------------------------------------------------------
|
||||
|
|
|
|||
275
src/core.c
275
src/core.c
|
|
@ -11,6 +11,7 @@
|
|||
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode)
|
||||
* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
|
||||
* - PLATFORM_WEB: HTML5 with WebAssembly
|
||||
* - PLATFORM_NX: Switch LibNX
|
||||
*
|
||||
* CONFIGURATION:
|
||||
*
|
||||
|
|
@ -34,6 +35,10 @@
|
|||
* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
|
||||
* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
|
||||
*
|
||||
* #define PLATFORM_NX
|
||||
* Windowing and input system configured for libnx (Nintendo Switch)
|
||||
* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
|
||||
*
|
||||
* #define SUPPORT_DEFAULT_FONT (default)
|
||||
* Default font is loaded on window initialization to be available for the user to render simple text.
|
||||
* NOTE: If enabled, uses external module functions to load default raylib font (module: text)
|
||||
|
|
@ -264,6 +269,16 @@
|
|||
#include <emscripten/html5.h> // Emscripten HTML5 library
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_NX)
|
||||
#include <switch.h>
|
||||
#include <EGL/egl.h> // EGL library
|
||||
#include <EGL/eglext.h> // EGL extensions
|
||||
#include <GLES2/gl2.h> // OpenGL ES 2.0 library
|
||||
#if defined(NX_USB_DEBUGGER)
|
||||
#include "nxusb.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Defines and Macros
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
@ -353,7 +368,7 @@ typedef struct CoreData {
|
|||
#if defined(PLATFORM_RPI)
|
||||
EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
|
||||
#endif
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
#if defined(PLATFORM_DRM)
|
||||
int fd; // File descriptor for /dev/dri/...
|
||||
drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
|
||||
|
|
@ -364,6 +379,9 @@ typedef struct CoreData {
|
|||
struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
|
||||
uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
|
||||
#endif // PLATFORM_DRM
|
||||
#if defined(PLATFORM_NX)
|
||||
NWindow *gbmSurface; // GBM surface
|
||||
#endif
|
||||
EGLDisplay device; // Native display device (physical screen connection)
|
||||
EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
|
||||
EGLContext context; // Graphic context, mode in which drawing can be done
|
||||
|
|
@ -458,6 +476,9 @@ typedef struct CoreData {
|
|||
pthread_t threadId; // Gamepad reading thread id
|
||||
int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
|
||||
char name[64]; // Gamepad name holder
|
||||
#endif
|
||||
#if defined(PLATFORM_NX)
|
||||
PadState nxPad[MAX_GAMEPADS]; // Gamepad state holder
|
||||
#endif
|
||||
} Gamepad;
|
||||
} Input;
|
||||
|
|
@ -468,7 +489,7 @@ typedef struct CoreData {
|
|||
double draw; // Time measure for frame draw
|
||||
double frame; // Time measure for one frame
|
||||
double target; // Desired time for one frame, if 0 not applied
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
unsigned long long base; // Base time measure for hi-res timer
|
||||
#endif
|
||||
unsigned int frameCounter; // Frame counter
|
||||
|
|
@ -691,6 +712,9 @@ struct android_app *GetAndroidApp(void)
|
|||
// NOTE: data parameter could be used to pass any kind of required data to the initialization
|
||||
void InitWindow(int width, int height, const char *title)
|
||||
{
|
||||
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
|
||||
NxUsbDebuggerInit();
|
||||
#endif
|
||||
TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
|
||||
|
||||
if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
|
||||
|
|
@ -765,7 +789,7 @@ void InitWindow(int width, int height, const char *title)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
// Initialize graphics device (display device and OpenGL context)
|
||||
// NOTE: returns true if window and graphic device has been initialized successfully
|
||||
CORE.Window.ready = InitGraphicsDevice(width, height);
|
||||
|
|
@ -812,6 +836,13 @@ void InitWindow(int width, int height, const char *title)
|
|||
InitKeyboard(); // Keyboard init (stdin)
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_NX)
|
||||
// Configure our supported input layout
|
||||
padConfigureInput(MAX_GAMEPADS, HidNpadStyleSet_NpadStandard);
|
||||
// Initialize the gamepads
|
||||
padInitializeDefault(&CORE.Input.Gamepad.nxPad[0]);
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_WEB)
|
||||
// Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
|
||||
//emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
|
||||
|
|
@ -872,7 +903,7 @@ void CloseWindow(void)
|
|||
timeEndPeriod(1); // Restore time period
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_NX)
|
||||
// Close surface, context and display
|
||||
if (CORE.Window.device != EGL_NO_DISPLAY)
|
||||
{
|
||||
|
|
@ -991,6 +1022,9 @@ void CloseWindow(void)
|
|||
|
||||
CORE.Window.ready = false;
|
||||
TRACELOG(LOG_INFO, "Window closed successfully");
|
||||
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
|
||||
NxUsbDebuggerEnd();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check if KEY_ESCAPE pressed or Close icon pressed
|
||||
|
|
@ -1022,7 +1056,10 @@ bool WindowShouldClose(void)
|
|||
else return true;
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
#if defined(PLATFORM_NX)
|
||||
if (!appletMainLoop()) return true;
|
||||
#endif
|
||||
if (CORE.Window.ready) return CORE.Window.shouldClose;
|
||||
else return true;
|
||||
#endif
|
||||
|
|
@ -2601,7 +2638,7 @@ double GetTime(void)
|
|||
return glfwGetTime(); // Elapsed time since glfwInit()
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
struct timespec ts = { 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
|
||||
|
|
@ -3835,7 +3872,7 @@ static bool InitGraphicsDevice(int width, int height)
|
|||
}
|
||||
#endif // PLATFORM_DESKTOP || PLATFORM_WEB
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
CORE.Window.fullscreen = true;
|
||||
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
|
||||
|
||||
|
|
@ -4025,8 +4062,10 @@ static bool InitGraphicsDevice(int width, int height)
|
|||
//EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
|
||||
EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
|
||||
//EGL_STENCIL_SIZE, 8, // Stencil buffer size
|
||||
#if !defined(PLATFORM_NX)
|
||||
EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
|
||||
EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
|
||||
#endif
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
|
|
@ -4036,7 +4075,7 @@ static bool InitGraphicsDevice(int width, int height)
|
|||
EGL_NONE
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
EGLint numConfigs = 0;
|
||||
|
||||
// Get an EGL device connection
|
||||
|
|
@ -4203,7 +4242,21 @@ static bool InitGraphicsDevice(int width, int height)
|
|||
//---------------------------------------------------------------------------------
|
||||
#endif // PLATFORM_RPI
|
||||
|
||||
#if defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_NX)
|
||||
CORE.Window.gbmSurface = nwindowGetDefault();
|
||||
if (appletGetOperationMode() == AppletOperationMode_Console)
|
||||
{
|
||||
CORE.Window.display.width = 1920;
|
||||
CORE.Window.display.height = 1080;
|
||||
}
|
||||
else if (appletGetOperationMode() == AppletOperationMode_Handheld)
|
||||
{
|
||||
CORE.Window.display.width = 1280;
|
||||
CORE.Window.display.height = 720;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
|
||||
if (EGL_NO_SURFACE == CORE.Window.surface)
|
||||
{
|
||||
|
|
@ -4470,7 +4523,7 @@ void SwapScreenBuffer(void)
|
|||
glfwSwapBuffers(CORE.Window.handle);
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
||||
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
|
||||
eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
|
||||
|
||||
#if defined(PLATFORM_DRM)
|
||||
|
|
@ -4522,7 +4575,7 @@ void SwapScreenBuffer(void)
|
|||
|
||||
CORE.Window.prevBO = bo;
|
||||
#endif // PLATFORM_DRM
|
||||
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
|
||||
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM || PLATFORM_NX
|
||||
}
|
||||
|
||||
// Register all input events
|
||||
|
|
@ -4773,6 +4826,206 @@ void PollInputEvents(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_NX)
|
||||
int nxGamepadIndex = 0;
|
||||
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
|
||||
|
||||
CORE.Input.Gamepad.ready[nxGamepadIndex] = padIsConnected(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
|
||||
|
||||
if (CORE.Input.Gamepad.ready[nxGamepadIndex]) {
|
||||
// Returns the set of buttons that are currently pressed
|
||||
u64 kHeld = padGetButtons(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
|
||||
u64 kButton;
|
||||
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
|
||||
{
|
||||
// Register previous gamepad states
|
||||
CORE.Input.Gamepad.previousButtonState[nxGamepadIndex][k] = CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k];
|
||||
|
||||
// Check digital buttons
|
||||
kButton = 0;
|
||||
switch (k)
|
||||
{
|
||||
case GAMEPAD_BUTTON_LEFT_FACE_UP: kButton = HidNpadButton_Up; break;
|
||||
case GAMEPAD_BUTTON_LEFT_FACE_RIGHT: kButton = HidNpadButton_Right; break;
|
||||
case GAMEPAD_BUTTON_LEFT_FACE_DOWN: kButton = HidNpadButton_Down; break;
|
||||
case GAMEPAD_BUTTON_LEFT_FACE_LEFT: kButton = HidNpadButton_Left; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_FACE_UP: kButton = HidNpadButton_X; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_FACE_RIGHT: kButton = HidNpadButton_A; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_FACE_DOWN: kButton = HidNpadButton_B; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_FACE_LEFT: kButton = HidNpadButton_Y; break;
|
||||
case GAMEPAD_BUTTON_LEFT_TRIGGER_1: kButton = HidNpadButton_L; break;
|
||||
case GAMEPAD_BUTTON_LEFT_TRIGGER_2: kButton = HidNpadButton_ZL; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_TRIGGER_1: kButton = HidNpadButton_R; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_TRIGGER_2: kButton = HidNpadButton_ZR; break;
|
||||
case GAMEPAD_BUTTON_MIDDLE_LEFT: kButton = HidNpadButton_Minus; break;
|
||||
case GAMEPAD_BUTTON_MIDDLE_RIGHT: kButton = HidNpadButton_Plus; break;
|
||||
case GAMEPAD_BUTTON_LEFT_THUMB: kButton = HidNpadButton_StickL; break;
|
||||
case GAMEPAD_BUTTON_RIGHT_THUMB: kButton = HidNpadButton_StickR; break;
|
||||
}
|
||||
if (kHeld & kButton) {
|
||||
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 1;
|
||||
CORE.Input.Gamepad.lastButtonPressed = k;
|
||||
} else {
|
||||
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check analogic axis and buttons
|
||||
HidAnalogStickState kAxisL = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 0);
|
||||
HidAnalogStickState kAxisR = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 1);
|
||||
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_X] = (float)kAxisL.x / 32767.0f;
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_Y] = (float)kAxisL.y / 32767.0f;
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] = (float)kAxisR.x / 32767.0f;
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] = (float)kAxisR.y / 32767.0f;
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = (kHeld & HidNpadButton_ZL) ? 1.0f : 0.0f;
|
||||
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = (kHeld & HidNpadButton_ZR) ? 1.0f : 0.0f;
|
||||
|
||||
#if defined(NX_SUPPORT_GAMEPAD_EMULATION)
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_RIGHT] = CORE.Input.Keyboard.currentKeyState[KEY_RIGHT];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_D] = CORE.Input.Keyboard.currentKeyState[KEY_D];
|
||||
if (kHeld & HidNpadButton_Right || kHeld & HidNpadButton_StickLRight) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 1;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_D] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_D] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_LEFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_A] = CORE.Input.Keyboard.currentKeyState[KEY_A];
|
||||
if (kHeld & HidNpadButton_Left || kHeld & HidNpadButton_StickLLeft) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 1;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_A] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_A] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_DOWN] = CORE.Input.Keyboard.currentKeyState[KEY_DOWN];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_S] = CORE.Input.Keyboard.currentKeyState[KEY_S];
|
||||
if (kHeld & HidNpadButton_Down || kHeld & HidNpadButton_StickLDown) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 1;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_S] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_S] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_UP] = CORE.Input.Keyboard.currentKeyState[KEY_UP];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_W] = CORE.Input.Keyboard.currentKeyState[KEY_W];
|
||||
if (kHeld & HidNpadButton_Up || kHeld & HidNpadButton_StickLUp) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 1;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_W] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_W] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_Q] = CORE.Input.Keyboard.currentKeyState[KEY_Q];
|
||||
if (kHeld & HidNpadButton_Y) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_E] = CORE.Input.Keyboard.currentKeyState[KEY_E];
|
||||
if (kHeld & HidNpadButton_A) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_E] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_E] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_R] = CORE.Input.Keyboard.currentKeyState[KEY_R];
|
||||
if (kHeld & HidNpadButton_X) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_R] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_R] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_F] = CORE.Input.Keyboard.currentKeyState[KEY_F];
|
||||
if (kHeld & HidNpadButton_B) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_F] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_F] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_ENTER] = CORE.Input.Keyboard.currentKeyState[KEY_ENTER];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_SPACE] = CORE.Input.Keyboard.currentKeyState[KEY_SPACE];
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_ESCAPE] = CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE];
|
||||
if (kHeld & HidNpadButton_Plus && kHeld & HidNpadButton_Minus) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE] = 1;
|
||||
} else {
|
||||
if (kHeld & HidNpadButton_Plus) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
|
||||
}
|
||||
if (kHeld & HidNpadButton_Minus) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CORE.Input.Keyboard.previousKeyState[KEY_LEFT_SHIFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT];
|
||||
if (kHeld & GAMEPAD_BUTTON_LEFT_THUMB) {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 1;
|
||||
} else {
|
||||
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT];
|
||||
if (kHeld & HidNpadButton_ZL) {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 1;
|
||||
} else {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_MIDDLE] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE];
|
||||
if (kHeld & GAMEPAD_BUTTON_RIGHT_THUMB) {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 1;
|
||||
} else {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_RIGHT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT];
|
||||
if (kHeld & HidNpadButton_ZR) {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 1;
|
||||
} else {
|
||||
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 0;
|
||||
}
|
||||
|
||||
CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
|
||||
if (kHeld & HidNpadButton_L) {
|
||||
CORE.Input.Mouse.currentWheelMove = -1.0f;
|
||||
} else if (kHeld & HidNpadButton_R) {
|
||||
CORE.Input.Mouse.currentWheelMove = 1.0f;
|
||||
} else {
|
||||
CORE.Input.Mouse.currentWheelMove = 0.0f;
|
||||
}
|
||||
|
||||
CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x;
|
||||
CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y;
|
||||
|
||||
CORE.Input.Mouse.currentPosition.x += CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] * 10;
|
||||
CORE.Input.Mouse.currentPosition.y -= CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] * 10;
|
||||
|
||||
if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
|
||||
else if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
|
||||
|
||||
if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
|
||||
else if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
|
||||
|
||||
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
|
||||
// NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
|
||||
// stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
|
||||
|
|
|
|||
293
src/external/miniaudio.h
vendored
293
src/external/miniaudio.h
vendored
|
|
@ -1556,6 +1556,10 @@ extern "C" {
|
|||
#ifdef __EMSCRIPTEN__
|
||||
#define MA_EMSCRIPTEN
|
||||
#endif
|
||||
#ifdef __SWITCH__
|
||||
#define MA_SWITCH
|
||||
#define MA_NO_RUNTIME_LINKING
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stddef.h> /* For size_t. */
|
||||
|
|
@ -2988,6 +2992,9 @@ This section contains the APIs for device playback and capture. Here is where yo
|
|||
#if defined(MA_EMSCRIPTEN)
|
||||
#define MA_SUPPORT_WEBAUDIO
|
||||
#endif
|
||||
#if defined(MA_SWITCH)
|
||||
#define MA_SUPPORT_SDL
|
||||
#endif
|
||||
|
||||
/* All platforms should support custom backends. */
|
||||
#define MA_SUPPORT_CUSTOM
|
||||
|
|
@ -3037,6 +3044,9 @@ This section contains the APIs for device playback and capture. Here is where yo
|
|||
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
|
||||
#define MA_HAS_WEBAUDIO
|
||||
#endif
|
||||
#if defined(MA_SUPPORT_SDL) && !defined(MA_NO_SDL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL))
|
||||
#define MA_HAS_SDL
|
||||
#endif
|
||||
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
|
||||
#define MA_HAS_CUSTOM
|
||||
#endif
|
||||
|
|
@ -3076,6 +3086,7 @@ typedef enum
|
|||
ma_backend_aaudio,
|
||||
ma_backend_opensl,
|
||||
ma_backend_webaudio,
|
||||
ma_backend_sdl,
|
||||
ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
|
||||
ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
|
||||
} ma_backend;
|
||||
|
|
@ -3306,6 +3317,7 @@ typedef union
|
|||
ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
|
||||
ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
|
||||
char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
|
||||
int sdl; /* SDL devices are identified with an index. */
|
||||
union
|
||||
{
|
||||
int i;
|
||||
|
|
@ -3960,6 +3972,19 @@ struct ma_context
|
|||
int _unused;
|
||||
} webaudio;
|
||||
#endif
|
||||
#ifdef MA_SUPPORT_SDL
|
||||
struct
|
||||
{
|
||||
ma_handle hSDL; // SDL
|
||||
ma_proc SDL_InitSubSystem;
|
||||
ma_proc SDL_QuitSubSystem;
|
||||
ma_proc SDL_GetNumAudioDevices;
|
||||
ma_proc SDL_GetAudioDeviceName;
|
||||
ma_proc SDL_CloseAudioDevice;
|
||||
ma_proc SDL_OpenAudioDevice;
|
||||
ma_proc SDL_PauseAudioDevice;
|
||||
} sdl;
|
||||
#endif
|
||||
#ifdef MA_SUPPORT_NULL
|
||||
struct
|
||||
{
|
||||
|
|
@ -4247,6 +4272,12 @@ struct ma_device
|
|||
int indexCapture;
|
||||
} webaudio;
|
||||
#endif
|
||||
#ifdef MA_SUPPORT_SDL
|
||||
struct
|
||||
{
|
||||
ma_uint32 deviceID;
|
||||
} sdl;
|
||||
#endif
|
||||
#ifdef MA_SUPPORT_NULL
|
||||
struct
|
||||
{
|
||||
|
|
@ -10391,7 +10422,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority
|
|||
int result;
|
||||
pthread_attr_t* pAttr = NULL;
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
|
||||
/* Try setting the thread priority. It's not critical if anything fails here. */
|
||||
pthread_attr_t attr;
|
||||
if (pthread_attr_init(&attr) == 0) {
|
||||
|
|
@ -10900,8 +10931,10 @@ DEVICE I/O
|
|||
#ifdef MA_POSIX
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifndef MA_NO_RUNTIME_LINKING
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
|
||||
|
|
@ -10919,6 +10952,24 @@ not officially supporting this, but I'm leaving it here in case it's useful for
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MA_ENABLE_SDL
|
||||
#define ML_HAS_SDL
|
||||
|
||||
// SDL headers are necessary if using compile-time linking.
|
||||
#ifdef MA_NO_RUNTIME_LINKING
|
||||
#ifdef __has_include
|
||||
#ifdef MA_EMSCRIPTEN
|
||||
#if !__has_include(<SDL/SDL_audio.h>)
|
||||
#undef MA_HAS_SDL
|
||||
#endif
|
||||
#else
|
||||
#if !__has_include(<SDL2/SDL_audio.h>)
|
||||
#undef MA_HAS_SDL
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
|
||||
{
|
||||
|
|
@ -10953,6 +11004,7 @@ MA_API const char* ma_get_backend_name(ma_backend backend)
|
|||
case ma_backend_aaudio: return "AAudio";
|
||||
case ma_backend_opensl: return "OpenSL|ES";
|
||||
case ma_backend_webaudio: return "Web Audio";
|
||||
case ma_backend_sdl: return "SDL";
|
||||
case ma_backend_custom: return "Custom";
|
||||
case ma_backend_null: return "Null";
|
||||
default: return "Unknown";
|
||||
|
|
@ -11045,6 +11097,12 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
|
|||
#else
|
||||
return MA_FALSE;
|
||||
#endif
|
||||
case ma_backend_sdl:
|
||||
#if defined(MA_HAS_SDL)
|
||||
return MA_TRUE;
|
||||
#else
|
||||
return MA_FALSE;
|
||||
#endif
|
||||
case ma_backend_custom:
|
||||
#if defined(MA_HAS_CUSTOM)
|
||||
return MA_TRUE;
|
||||
|
|
@ -11113,6 +11171,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
|
|||
case ma_backend_aaudio: return MA_FALSE;
|
||||
case ma_backend_opensl: return MA_FALSE;
|
||||
case ma_backend_webaudio: return MA_FALSE;
|
||||
case ma_backend_sdl: return MA_FALSE;
|
||||
case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
|
||||
case ma_backend_null: return MA_FALSE;
|
||||
default: return MA_FALSE;
|
||||
|
|
@ -11596,6 +11655,7 @@ Timing
|
|||
Dynamic Linking
|
||||
|
||||
*******************************************************************************/
|
||||
#ifndef MA_NO_RUNTIME_LINKING
|
||||
MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
|
||||
{
|
||||
ma_handle handle;
|
||||
|
|
@ -11687,7 +11747,7 @@ MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symb
|
|||
(void)pContext; /* It's possible for pContext to be unused. */
|
||||
return proc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
|
||||
|
|
@ -31985,6 +32045,227 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
|
|||
#endif /* Web Audio */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
SDL Backend
|
||||
|
||||
******************************************************************************/
|
||||
#ifdef MA_HAS_SDL
|
||||
|
||||
#define MA_SDL_INIT_AUDIO 0x00000010
|
||||
#define MA_AUDIO_U8 0x0008
|
||||
#define MA_AUDIO_S16 0x8010
|
||||
#define MA_AUDIO_S32 0x8020
|
||||
#define MA_AUDIO_F32 0x8120
|
||||
#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
|
||||
#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
|
||||
#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
|
||||
#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags);
|
||||
typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags);
|
||||
typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture);
|
||||
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
|
||||
typedef void (* MA_PFN_SDL_CloseAudioDevice)(SDL_AudioDeviceID dev);
|
||||
typedef SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const SDL_AudioSpec* desired, SDL_AudioSpec* obtained, int allowed_changes);
|
||||
typedef void (* MA_PFN_SDL_PauseAudioDevice)(SDL_AudioDeviceID dev, int pause_on);
|
||||
|
||||
static SDL_AudioFormat ma_format_to_sdl(ma_format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ma_format_unknown: return 0;
|
||||
case ma_format_u8: return MA_AUDIO_U8;
|
||||
case ma_format_s16: return MA_AUDIO_S16;
|
||||
case ma_format_s24: return MA_AUDIO_S32; // Closest match.
|
||||
case ma_format_s32: return MA_AUDIO_S32;
|
||||
case ma_format_f32: return MA_AUDIO_F32;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ma_format ma_format_from_sdl(SDL_AudioFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case MA_AUDIO_U8: return ma_format_u8;
|
||||
case MA_AUDIO_S16: return ma_format_s16;
|
||||
case MA_AUDIO_S32: return ma_format_s32;
|
||||
case MA_AUDIO_F32: return ma_format_f32;
|
||||
default: return ma_format_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
||||
{
|
||||
MA_ASSERT(pContext != NULL);
|
||||
MA_ASSERT(deviceType == ma_device_type_playback);
|
||||
|
||||
pDeviceInfo->id.sdl = 0;
|
||||
pDeviceInfo->isDefault = MA_TRUE;
|
||||
|
||||
SDL_AudioSpec desiredSpec, obtainedSpec;
|
||||
MA_ZERO_OBJECT(&desiredSpec);
|
||||
|
||||
SDL_AudioDeviceID tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
((MA_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
|
||||
|
||||
ma_format format = ma_format_from_sdl(obtainedSpec.format);
|
||||
if (format == ma_format_unknown) {
|
||||
format = ma_format_f32;
|
||||
}
|
||||
|
||||
pDeviceInfo->nativeDataFormatCount = 0;
|
||||
ma_device_info_add_native_data_format(pDeviceInfo, format, obtainedSpec.channels, obtainedSpec.freq, 0);
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static ma_result ma_device_uninit__sdl(ma_device* pDevice)
|
||||
{
|
||||
MA_ASSERT(pDevice != NULL);
|
||||
|
||||
((MA_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void ma_audio_callback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
|
||||
{
|
||||
ma_device* pDevice = (ma_device*)pUserData;
|
||||
MA_ASSERT(pDevice != NULL);
|
||||
|
||||
ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
||||
ma_uint32 frameCount = (ma_uint32)bufferSizeInBytes / bytesPerFrame;
|
||||
|
||||
ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, frameCount);
|
||||
}
|
||||
|
||||
static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
||||
{
|
||||
MA_ASSERT(pDevice != NULL);
|
||||
MA_ASSERT(pConfig != NULL);
|
||||
MA_ASSERT(pConfig->deviceType == ma_device_type_playback);
|
||||
|
||||
if (pDescriptorPlayback->sampleRate == 0) {
|
||||
pDescriptorPlayback->sampleRate = MA_DEFAULT_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
pDescriptorPlayback->periodSizeInFrames = pDescriptorPlayback->periodCount * ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
|
||||
|
||||
/* SDL wants the buffer size to be a power of 2 for some reason. */
|
||||
if (pDescriptorPlayback->periodSizeInFrames > 32768) {
|
||||
pDescriptorPlayback->periodSizeInFrames = 32768;
|
||||
} else {
|
||||
pDescriptorPlayback->periodSizeInFrames = ma_next_power_of_2(pDescriptorPlayback->periodSizeInFrames);
|
||||
}
|
||||
|
||||
MA_ASSERT(pDescriptorPlayback->periodSizeInFrames <= 32768);
|
||||
|
||||
/* We now have enough information to set up the device. */
|
||||
SDL_AudioSpec desiredSpec, obtainedSpec;
|
||||
MA_ZERO_OBJECT(&desiredSpec);
|
||||
desiredSpec.freq = (int)pDescriptorPlayback->sampleRate;
|
||||
desiredSpec.format = ma_format_to_sdl(pDescriptorPlayback->format);
|
||||
desiredSpec.channels = (ma_uint8)pDescriptorPlayback->channels;
|
||||
desiredSpec.samples = (ma_uint16)pDescriptorPlayback->periodSizeInFrames;
|
||||
desiredSpec.callback = ma_audio_callback__sdl;
|
||||
desiredSpec.userdata = pDevice;
|
||||
|
||||
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
|
||||
if (desiredSpec.format == ma_format_unknown) {
|
||||
desiredSpec.format = MA_AUDIO_F32;
|
||||
}
|
||||
|
||||
pDevice->sdl.deviceID = ((MA_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
|
||||
/* The descriptor needs to be updated with our actual settings. */
|
||||
pDescriptorPlayback->format = ma_format_from_sdl(obtainedSpec.format);
|
||||
pDescriptorPlayback->channels = obtainedSpec.channels;
|
||||
pDescriptorPlayback->sampleRate = (ma_uint32)obtainedSpec.freq;
|
||||
ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
|
||||
pDescriptorPlayback->periodSizeInFrames = obtainedSpec.samples;
|
||||
pDescriptorPlayback->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_device_start__sdl(ma_device* pDevice)
|
||||
{
|
||||
MA_ASSERT(pDevice != NULL);
|
||||
|
||||
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_device_stop__sdl(ma_device* pDevice)
|
||||
{
|
||||
MA_ASSERT(pDevice != NULL);
|
||||
|
||||
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
|
||||
|
||||
ma_device__set_state(pDevice, MA_STATE_STOPPED);
|
||||
ma_stop_proc onStop = pDevice->onStop;
|
||||
if (onStop) {
|
||||
onStop(pDevice);
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static ma_result ma_context_uninit__sdl(ma_context* pContext)
|
||||
{
|
||||
MA_ASSERT(pContext != NULL);
|
||||
MA_ASSERT(pContext->backend == ma_backend_sdl);
|
||||
|
||||
((MA_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO);
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
||||
{
|
||||
MA_ASSERT(pContext != NULL);
|
||||
|
||||
(void)pConfig; /* Unused. */
|
||||
|
||||
pContext->sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem;
|
||||
pContext->sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem;
|
||||
pContext->sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices;
|
||||
pContext->sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName;
|
||||
pContext->sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice;
|
||||
pContext->sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice;
|
||||
pContext->sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice;
|
||||
|
||||
int resultSDL = ((MA_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO);
|
||||
if (resultSDL != 0) {
|
||||
return MA_ERROR;
|
||||
}
|
||||
|
||||
pCallbacks->onContextInit = ma_context_init__sdl;
|
||||
pCallbacks->onContextUninit = ma_context_uninit__sdl;
|
||||
pCallbacks->onContextEnumerateDevices = NULL;
|
||||
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl;
|
||||
pCallbacks->onDeviceInit = ma_device_init__sdl;
|
||||
pCallbacks->onDeviceUninit = ma_device_uninit__sdl;
|
||||
pCallbacks->onDeviceStart = ma_device_start__sdl;
|
||||
pCallbacks->onDeviceStop = ma_device_stop__sdl;
|
||||
pCallbacks->onDeviceRead = NULL;
|
||||
pCallbacks->onDeviceWrite = NULL;
|
||||
pCallbacks->onDeviceDataLoop = NULL;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#endif /* SDL */
|
||||
|
||||
|
||||
|
||||
|
||||
static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
|
||||
{
|
||||
|
|
@ -32340,7 +32621,7 @@ static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
|
|||
pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
|
||||
pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
|
||||
pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
|
||||
pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
|
||||
pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
|
||||
pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
|
||||
|
|
@ -32536,6 +32817,12 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
|
|||
pContext->callbacks.onContextInit = ma_context_init__webaudio;
|
||||
} break;
|
||||
#endif
|
||||
#ifdef MA_HAS_SDL
|
||||
case ma_backend_sdl:
|
||||
{
|
||||
pContext->callbacks.onContextInit = ma_context_init__sdl;
|
||||
} break;
|
||||
#endif
|
||||
#ifdef MA_HAS_CUSTOM
|
||||
case ma_backend_custom:
|
||||
{
|
||||
|
|
|
|||
502
src/nxusb.c
Normal file
502
src/nxusb.c
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
#include "nxusb.h"
|
||||
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <sys/iosupport.h>
|
||||
|
||||
#define U64_MAX ((u64)~0ULL)
|
||||
#define TOTAL_INTERFACES 4
|
||||
#define TOTAL_ENDPOINTS 4
|
||||
#define EP_IN 0
|
||||
#define EP_OUT 1
|
||||
|
||||
typedef enum {
|
||||
UsbDirectionRead = 0,
|
||||
UsbDirectionWrite = 1,
|
||||
} UsbDirection;
|
||||
|
||||
typedef struct {
|
||||
struct usb_interface_descriptor *interface_desc;
|
||||
struct usb_endpoint_descriptor *endpoint_desc[4];
|
||||
const char *string_descriptor;
|
||||
} UsbInterfaceDesc;
|
||||
|
||||
typedef struct {
|
||||
UsbDsEndpoint *endpoint;
|
||||
u8 *buffer;
|
||||
RwLock lock;
|
||||
} UsbCommsEndpoint;
|
||||
|
||||
typedef struct {
|
||||
RwLock lock;
|
||||
bool initialized;
|
||||
UsbDsInterface* interface;
|
||||
u32 endpoint_number;
|
||||
UsbCommsEndpoint endpoint[TOTAL_ENDPOINTS];
|
||||
} UsbCommsInterface;
|
||||
|
||||
static bool g_usbCommsInitialized = false;
|
||||
static UsbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES];
|
||||
static RwLock g_usbCommsLock;
|
||||
static int ep_in = 1;
|
||||
static int ep_out = 1;
|
||||
|
||||
static Result UsbCommsInterfaceInit(u32 intf_ind, const UsbInterfaceDesc *info)
|
||||
{
|
||||
Result rc = 0;
|
||||
UsbCommsInterface *interface = &g_usbCommsInterfaces[intf_ind];
|
||||
|
||||
u8 index = 0;
|
||||
if (info->string_descriptor != NULL)
|
||||
{
|
||||
usbDsAddUsbStringDescriptor(&index, info->string_descriptor);
|
||||
}
|
||||
info->interface_desc->iInterface = index;
|
||||
|
||||
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
|
||||
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
|
||||
.bMaxBurst = 0x0F,
|
||||
.bmAttributes = 0x00,
|
||||
.wBytesPerInterval = 0x00,
|
||||
};
|
||||
|
||||
interface->initialized = 1;
|
||||
|
||||
//The buffer for PostBufferAsync commands must be 0x1000-byte aligned.
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
interface->endpoint[i].buffer = (u8*)memalign(0x1000, 0x1000);
|
||||
if (interface->endpoint[i].buffer == NULL)
|
||||
{
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||
break;
|
||||
}
|
||||
memset(interface->endpoint[i].buffer, 0, 0x1000);
|
||||
}
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsRegisterInterface(&interface->interface);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
info->interface_desc->bInterfaceNumber = interface->interface->interface_index;
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
if((info->endpoint_desc[i]->bEndpointAddress & USB_ENDPOINT_IN) != 0)
|
||||
{
|
||||
info->endpoint_desc[i]->bEndpointAddress |= ep_in;
|
||||
ep_in++;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->endpoint_desc[i]->bEndpointAddress |= ep_out;
|
||||
ep_out++;
|
||||
}
|
||||
}
|
||||
|
||||
// Full Speed Config
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->interface_desc, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
|
||||
info->endpoint_desc[i]->wMaxPacketSize = 0x40;
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
// High Speed Config
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->interface_desc, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
|
||||
info->endpoint_desc[i]->wMaxPacketSize = 0x200;
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
// Super Speed Config
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->interface_desc, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
|
||||
info->endpoint_desc[i]->wMaxPacketSize = 0x400;
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
//Setup endpoints.
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
rc = usbDsInterface_RegisterEndpoint(interface->interface, &interface->endpoint[i].endpoint, info->endpoint_desc[i]->bEndpointAddress);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
rc = usbDsInterface_EnableInterface(interface->interface);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result UsbCommsTransfer(UsbCommsEndpoint *ep, UsbDirection dir, const void* buffer, size_t size, u64 timeout, size_t *transferredSize)
|
||||
{
|
||||
Result rc=0;
|
||||
u32 urbId=0;
|
||||
u32 chunksize=0;
|
||||
u8 transfer_type=0;
|
||||
u8 *bufptr = (u8*)buffer;
|
||||
u8 *transfer_buffer = NULL;
|
||||
u32 tmp_transferredSize = 0;
|
||||
size_t total_transferredSize=0;
|
||||
UsbDsReportData reportdata;
|
||||
|
||||
//Makes sure endpoints are ready for data-transfer / wait for init if needed.
|
||||
rc = usbDsWaitReady(U64_MAX);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
while(size)
|
||||
{
|
||||
//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly.
|
||||
if(((u64)bufptr) & 0xfff)
|
||||
{
|
||||
transfer_buffer = ep->buffer;
|
||||
memset(ep->buffer, 0, 0x1000);
|
||||
|
||||
chunksize = 0x1000;
|
||||
//After this transfer, bufptr will be page-aligned (if size is large enough for another transfer).
|
||||
chunksize-= ((u64)bufptr) & 0xfff;
|
||||
if (size<chunksize) chunksize = size;
|
||||
|
||||
if(dir == UsbDirectionWrite)
|
||||
memcpy(ep->buffer, bufptr, chunksize);
|
||||
|
||||
transfer_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
transfer_buffer = bufptr;
|
||||
chunksize = size;
|
||||
transfer_type = 1;
|
||||
}
|
||||
|
||||
//Start transfer.
|
||||
rc = usbDsEndpoint_PostBufferAsync(ep->endpoint, transfer_buffer, chunksize, &urbId);
|
||||
if(R_FAILED(rc)) return rc;
|
||||
|
||||
//Wait for the transfer to finish.
|
||||
rc = eventWait(&ep->endpoint->CompletionEvent, timeout);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
usbDsEndpoint_Cancel(ep->endpoint);
|
||||
eventWait(&ep->endpoint->CompletionEvent, U64_MAX);
|
||||
eventClear(&ep->endpoint->CompletionEvent);
|
||||
return rc;
|
||||
}
|
||||
eventClear(&ep->endpoint->CompletionEvent);
|
||||
|
||||
rc = usbDsEndpoint_GetReportData(ep->endpoint, &reportdata);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize;
|
||||
|
||||
total_transferredSize+= (size_t)tmp_transferredSize;
|
||||
|
||||
if ((transfer_type==0) && (dir == UsbDirectionRead))
|
||||
memcpy(bufptr, transfer_buffer, tmp_transferredSize);
|
||||
|
||||
bufptr+= tmp_transferredSize;
|
||||
size-= tmp_transferredSize;
|
||||
|
||||
if (tmp_transferredSize < chunksize) break;
|
||||
}
|
||||
|
||||
if (transferredSize) *transferredSize = total_transferredSize;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void UsbCommsInterfaceFree(UsbCommsInterface *interface)
|
||||
{
|
||||
rwlockWriteLock(&interface->lock);
|
||||
if (!interface->initialized)
|
||||
{
|
||||
rwlockWriteUnlock(&interface->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
interface->initialized = 0;
|
||||
interface->interface = NULL;
|
||||
|
||||
for (u32 i = 0; i < interface->endpoint_number; i++)
|
||||
{
|
||||
rwlockWriteLock(&interface->endpoint[i].lock);
|
||||
interface->endpoint[i].endpoint = NULL;
|
||||
free(interface->endpoint[i].buffer);
|
||||
interface->endpoint[i].buffer = NULL;
|
||||
rwlockWriteUnlock(&interface->endpoint[i].lock);
|
||||
}
|
||||
|
||||
rwlockWriteUnlock(&interface->lock);
|
||||
}
|
||||
|
||||
static size_t UsbTransfer(u32 interface, u32 endpoint, UsbDirection dir, void* buffer, size_t size, u64 timeout)
|
||||
{
|
||||
size_t transferredSize=-1;
|
||||
u32 state=0;
|
||||
Result rc, rc2;
|
||||
bool initialized;
|
||||
|
||||
UsbCommsInterface *inter = &g_usbCommsInterfaces[interface];
|
||||
UsbCommsEndpoint *ep = &inter->endpoint[endpoint];
|
||||
rwlockReadLock(&inter->lock);
|
||||
initialized = inter->initialized;
|
||||
rwlockReadUnlock(&inter->lock);
|
||||
if (!initialized) return 0;
|
||||
|
||||
rwlockWriteLock(&ep->lock);
|
||||
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
|
||||
rwlockWriteUnlock(&ep->lock);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
rc2 = usbDsGetState(&state);
|
||||
if (R_SUCCEEDED(rc2))
|
||||
{
|
||||
if (state!=5)
|
||||
{
|
||||
rwlockWriteLock(&ep->lock);
|
||||
// If state changed during transfer, try again. usbDsWaitReady() will be called from this.
|
||||
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
|
||||
rwlockWriteUnlock(&ep->lock);
|
||||
}
|
||||
}
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
transferredSize = 0;
|
||||
}
|
||||
}
|
||||
return transferredSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* REDIRECT IO FUNCTIONS
|
||||
*/
|
||||
|
||||
static ssize_t _write_stdout(struct _reent *r,void *fd,const char *ptr, size_t len)
|
||||
{
|
||||
return UsbTransfer(0, EP_IN, UsbDirectionWrite, (void*)ptr, len, U64_MAX);
|
||||
}
|
||||
|
||||
static const devoptab_t dotab_stdout = {
|
||||
"usb",
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
_write_stdout,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void RedirectOutput(void)
|
||||
{
|
||||
devoptab_list[STD_OUT] = &dotab_stdout;
|
||||
devoptab_list[STD_ERR] = &dotab_stdout;
|
||||
setvbuf(stdout, NULL , _IONBF, 0);
|
||||
setvbuf(stderr, NULL , _IONBF, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* PUBLIC FUNCTIONS
|
||||
*/
|
||||
|
||||
bool NxUsbDebuggerInit(void)
|
||||
{
|
||||
u32 num_interfaces = 1;
|
||||
|
||||
struct usb_device_descriptor device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.idVendor = 0x057e,
|
||||
.idProduct = 0x4000,
|
||||
.bcdDevice = 0x0100,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
struct usb_interface_descriptor serial_interface_descriptor = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor serial_endpoint_descriptor_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor serial_endpoint_descriptor_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
UsbInterfaceDesc infos[1];
|
||||
infos[0].interface_desc = &serial_interface_descriptor;
|
||||
infos[0].endpoint_desc[EP_IN] = &serial_endpoint_descriptor_in;
|
||||
infos[0].endpoint_desc[EP_OUT] = &serial_endpoint_descriptor_out;
|
||||
infos[0].string_descriptor = NULL;
|
||||
|
||||
Result rc = 0;
|
||||
rwlockWriteLock(&g_usbCommsLock);
|
||||
|
||||
if (g_usbCommsInitialized)
|
||||
{
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
|
||||
}
|
||||
else if (num_interfaces > TOTAL_INTERFACES)
|
||||
{
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = usbDsInitialize();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (hosversionAtLeast(5,0,0))
|
||||
{
|
||||
u8 iManufacturer, iProduct, iSerialNumber;
|
||||
static const u16 supported_langs[1] = {0x0409};
|
||||
// Send language descriptor
|
||||
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16));
|
||||
// Send manufacturer
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iManufacturer, "Nintendo");
|
||||
// Send product
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iProduct, "Nintendo Switch");
|
||||
// Send serial number
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iSerialNumber, "SerialNumber");
|
||||
|
||||
// Send device descriptors
|
||||
device_descriptor.iManufacturer = iManufacturer;
|
||||
device_descriptor.iProduct = iProduct;
|
||||
device_descriptor.iSerialNumber = iSerialNumber;
|
||||
|
||||
// Full Speed is USB 1.1
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor);
|
||||
|
||||
// High Speed is USB 2.0
|
||||
device_descriptor.bcdUSB = 0x0200;
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
|
||||
|
||||
// Super Speed is USB 3.0
|
||||
device_descriptor.bcdUSB = 0x0300;
|
||||
// Upgrade packet size to 512
|
||||
device_descriptor.bMaxPacketSize0 = 0x09;
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor);
|
||||
|
||||
// Define Binary Object Store
|
||||
u8 bos[0x16] = {
|
||||
0x05, // .bLength
|
||||
USB_DT_BOS, // .bDescriptorType
|
||||
0x16, 0x00, // .wTotalLength
|
||||
0x02, // .bNumDeviceCaps
|
||||
|
||||
// USB 2.0
|
||||
0x07, // .bLength
|
||||
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
|
||||
0x02, // .bDevCapabilityType
|
||||
0x02, 0x00, 0x00, 0x00, // dev_capability_data
|
||||
|
||||
// USB 3.0
|
||||
0x0A, // .bLength
|
||||
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
|
||||
0x03, // .bDevCapabilityType
|
||||
0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00
|
||||
};
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
for (u32 i = 0; i < num_interfaces; i++)
|
||||
{
|
||||
UsbCommsInterface *intf = &g_usbCommsInterfaces[i];
|
||||
const UsbInterfaceDesc *info = &infos[i];
|
||||
intf->endpoint_number = info->interface_desc->bNumEndpoints;
|
||||
rwlockWriteLock(&intf->lock);
|
||||
for (u32 j = 0; j < intf->endpoint_number; j++)
|
||||
{
|
||||
rwlockWriteLock(&intf->endpoint[j].lock);
|
||||
}
|
||||
rc = UsbCommsInterfaceInit(i, info);
|
||||
for (u32 j = 0; j < intf->endpoint_number; j++)
|
||||
{
|
||||
rwlockWriteUnlock(&intf->endpoint[j].lock);
|
||||
}
|
||||
rwlockWriteUnlock(&intf->lock);
|
||||
if (R_FAILED(rc)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc) && hosversionAtLeast(5,0,0))
|
||||
{
|
||||
rc = usbDsEnable();
|
||||
}
|
||||
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
NxUsbDebuggerEnd();
|
||||
}
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
g_usbCommsInitialized = true;
|
||||
}
|
||||
|
||||
rwlockWriteUnlock(&g_usbCommsLock);
|
||||
|
||||
if (g_usbCommsInitialized) RedirectOutput();
|
||||
return g_usbCommsInitialized;
|
||||
}
|
||||
|
||||
void NxUsbDebuggerEnd(void)
|
||||
{
|
||||
rwlockWriteLock(&g_usbCommsLock);
|
||||
|
||||
usbDsExit();
|
||||
|
||||
g_usbCommsInitialized = false;
|
||||
|
||||
rwlockWriteUnlock(&g_usbCommsLock);
|
||||
|
||||
for (u32 i = 0; i < TOTAL_INTERFACES; i++)
|
||||
{
|
||||
UsbCommsInterfaceFree(&g_usbCommsInterfaces[i]);
|
||||
}
|
||||
}
|
||||
10
src/nxusb.h
Normal file
10
src/nxusb.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef NXUSB_H
|
||||
#define NXUSB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
bool NxUsbDebuggerInit(void);
|
||||
void NxUsbDebuggerEnd(void);
|
||||
|
||||
#endif // NXUSB_H
|
||||
Loading…
Reference in New Issue
Block a user