all repos — mgba @ af03ad75be05379bbecbc326e849d26d25237652

mGBA Game Boy Advance Emulator

Switch: Initial mGUI port
Vicki Pfau vi@endrift.com
Thu, 13 Sep 2018 18:12:32 -0700
commit

af03ad75be05379bbecbc326e849d26d25237652

parent

c0a94967ca1295f747d69325787f33bc15f51e76

M CMakeLists.txtCMakeLists.txt

@@ -46,8 +46,8 @@ set(BUILD_PYTHON OFF CACHE BOOL "Build Python bindings")

set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") -set(BUILD_GL ON CACHE STRING "Build with OpenGL") -set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") +set(BUILD_GL ON CACHE BOOL "Build with OpenGL") +set(BUILD_GLES2 OFF CACHE BOOL "Build with OpenGL|ES 2") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages")

@@ -293,6 +293,10 @@ endif()

if(DEFINED 3DS) add_definitions(-DFIXED_ROM_BUFFER) +endif() + +if(DEFINED SWITCH) + set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2" FORCE) endif() if(NOT M_CORE_GBA)
M include/mgba-util/socket.hinclude/mgba-util/socket.h

@@ -55,7 +55,7 @@ #define SOCU_BUFFERSIZE 0x100000

extern u32* SOCUBuffer; #endif -#ifdef SWITCH +#ifdef __SWITCH__ #include <switch.h> #endif

@@ -68,7 +68,7 @@ if (!SOCUBuffer) {

SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE); socInit(SOCUBuffer, SOCU_BUFFERSIZE); } -#elif defined(SWITCH) +#elif defined(__SWITCH__) socketInitializeDefault(); #endif }

@@ -80,7 +80,7 @@ #elif defined(_3DS)

socExit(); free(SOCUBuffer); SOCUBuffer = NULL; -#elif defined(SWITCH) +#elif defined(__SWITCH__) socketExit(); #endif }

@@ -110,7 +110,7 @@ #endif

} static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) { -#if defined(_WIN32) || defined(SWITCH) +#if defined(_WIN32) || defined(__SWITCH__) return recv(socket, (char*) buffer, size, 0); #else return read(socket, buffer, size);
M include/mgba-util/threading.hinclude/mgba-util/threading.h

@@ -13,12 +13,14 @@

#ifndef DISABLE_THREADING #ifdef USE_PTHREADS #include <mgba-util/platform/posix/threading.h> -#elif _WIN32 +#elif defined(_WIN32) #include <mgba-util/platform/windows/threading.h> -#elif PSP2 +#elif defined(PSP2) #include <mgba-util/platform/psp2/threading.h> -#elif _3DS +#elif defined(_3DS) #include <mgba-util/platform/3ds/threading.h> +#elif defined(__SWITCH__) +#include <mgba-util/platform/switch/threading.h> #else #define DISABLE_THREADING #endif
M src/core/config.csrc/core/config.c

@@ -179,7 +179,7 @@ PathRemoveFileSpecW(wpath);

WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0); StringCchCatA(out, MAX_PATH, "\\portable.ini"); portable = VFileOpen(out, O_WRONLY | O_CREAT); -#elif defined(PSP2) || defined(_3DS) || defined(GEKKO) +#elif defined(PSP2) || defined(_3DS) || defined(__SWITCH__) || defined(GEKKO) // Already portable #else char out[PATH_MAX];

@@ -219,7 +219,7 @@ #elif defined(PSP2)

UNUSED(portable); snprintf(out, outLength, "ux0:data/%s", projectName); sceIoMkdir(out, 0777); -#elif defined(GEKKO) +#elif defined(GEKKO) || defined(__SWITCH__) UNUSED(portable); snprintf(out, outLength, "/%s", projectName); mkdir(out, 0777);
M src/platform/switch/CMakeLists.txtsrc/platform/switch/CMakeLists.txt

@@ -1,12 +1,21 @@

find_program(ELF2NRO elf2nro) +find_program(NACPTOOL nacptool) +find_program(BUILD_ROMFS build_romfs) +find_library(GLAPI_LIBRARY glapi REQUIRED) +find_library(EGL_LIBRARY EGL REQUIRED) set(OS_DEFINES USE_VFS_FILE IOAPI_NO_64) list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) +list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c) + +include_directories(AFTER ${OPENGLES2_INCLUDE_DIR} ${OPENGL_EGL_INCLUDE_DIR}) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c) if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) + find_library(NOUVEAU_LIBRARY drm_nouveaud REQUIRED) list(APPEND OS_LIB nxd) else() + find_library(NOUVEAU_LIBRARY drm_nouveau REQUIRED) list(APPEND OS_LIB nx) endif() set(CORE_VFS_SRC ${CORE_VFS_SRC} PARENT_SCOPE)

@@ -22,3 +31,21 @@ install(FILES

${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.nro DESTINATION . COMPONENT ${BINARY_NAME}-perf) endif() + +add_executable(${BINARY_NAME}.elf ${GUI_SRC} ${PLATFORM_SRC} main.c) +set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") +target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${OS_LIB} ${EGL_LIBRARY} ${OPENGLES2_LIBRARY} ${GLAPI_LIBRARY} ${NOUVEAU_LIBRARY} stdc++) + +add_custom_command(OUTPUT control.nacp + COMMAND ${NACPTOOL} --create "${PROJECT_NAME}" "endrift" "${LIB_VERSION_STRING}" control.nacp) + +add_custom_command(OUTPUT romfs.bin + COMMAND ${CMAKE_COMMAND} -E make_directory romfs + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/res/font-new.png" romfs/ + COMMAND ${BUILD_ROMFS} romfs romfs.bin + COMMAND ${CMAKE_COMMAND} -E remove_directory romfs + DEPENDS "${CMAKE_SOURCE_DIR}/res/font-new.png") + +add_custom_target(${BINARY_NAME}.nro ALL + ${ELF2NRO} ${BINARY_NAME}.elf ${BINARY_NAME}.nro --romfs=romfs.bin --nacp=control.nacp --icon="${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg" + DEPENDS ${BINARY_NAME}.elf control.nacp ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg romfs.bin)
M src/platform/switch/CMakeToolchain.txtsrc/platform/switch/CMakeToolchain.txt

@@ -53,4 +53,4 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")

set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) set(SWITCH ON) -add_definitions(-DSWITCH) +add_definitions(-D__SWITCH__)
A src/platform/switch/gui-font.c

@@ -0,0 +1,320 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba-util/gui/font.h> +#include <mgba-util/gui/font-metrics.h> +#include <mgba-util/png-io.h> +#include <mgba-util/string.h> +#include <mgba-util/vfs.h> + +#include <GLES2/gl2.h> + +#define GLYPH_HEIGHT 24 +#define CELL_HEIGHT 32 +#define CELL_WIDTH 32 + +static const GLfloat _offsets[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, +}; + +static const GLchar* const _gles2Header = + "#version 100\n" + "precision mediump float;\n"; + +static const char* const _vertexShader = + "attribute vec2 offset;\n" + "uniform vec3 origin;\n" + "uniform vec2 glyph;\n" + "uniform vec2 dims;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " vec2 scaledOffset = offset * dims;\n" + " gl_Position = vec4((origin.x + scaledOffset.x) / 640.0 - 1.0, -(origin.y + scaledOffset.y) / 384.0 + 1.0, origin.z, 1.0);\n" + " texCoord = (glyph + scaledOffset) / 512.0;\n" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + "uniform vec4 color;\n" + "uniform float cutoff;\n" + + "void main() {\n" + " vec4 texColor = color;\n" + " texColor.a *= texture2D(tex, texCoord).a;\n" + " texColor.a = clamp((texColor.a - cutoff) / (1.0 - cutoff), 0.0, 1.0);\n" + " gl_FragColor = texColor;\n" + "}"; + +struct GUIFont { + GLuint font; + GLuint program; + GLuint vbo; + GLuint offsetLocation; + GLuint texLocation; + GLuint dimsLocation; + GLuint colorLocation; + GLuint originLocation; + GLuint glyphLocation; + GLuint cutoffLocation; +}; + +static bool _loadTexture(const char* path) { + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (!vf) { + return false; + } + png_structp png = PNGReadOpen(vf, 0); + png_infop info = png_create_info_struct(png); + png_infop end = png_create_info_struct(png); + bool success = false; + if (png && info && end) { + success = PNGReadHeader(png, info); + } + void* pixels = NULL; + if (success) { + unsigned height = png_get_image_height(png, info); + unsigned width = png_get_image_width(png, info); + pixels = malloc(width * height); + if (pixels) { + success = PNGReadPixels8(png, info, pixels, width, height, width); + success = success && PNGReadFooter(png, end); + } else { + success = false; + } + if (success) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); + } + } + PNGReadClose(png, info, end); + if (pixels) { + free(pixels); + } + vf->close(vf); + return success; +} + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* font = malloc(sizeof(struct GUIFont)); + if (!font) { + return NULL; + } + glGenTextures(1, &font->font); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, font->font); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (!_loadTexture("romfs:/font-new.png")) { + GUIFontDestroy(font); + return NULL; + } + + font->program = glCreateProgram(); + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const GLchar* shaderBuffer[2]; + + shaderBuffer[0] = _gles2Header; + + shaderBuffer[1] = _vertexShader; + glShaderSource(vertexShader, 2, shaderBuffer, NULL); + + shaderBuffer[1] = _fragmentShader; + glShaderSource(fragmentShader, 2, shaderBuffer, NULL); + + glAttachShader(font->program, vertexShader); + glAttachShader(font->program, fragmentShader); + + glCompileShader(fragmentShader); + + GLint success; + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg); + puts(msg); + } + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg); + puts(msg); + } + glLinkProgram(font->program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + font->texLocation = glGetUniformLocation(font->program, "tex"); + font->colorLocation = glGetUniformLocation(font->program, "color"); + font->dimsLocation = glGetUniformLocation(font->program, "dims"); + font->originLocation = glGetUniformLocation(font->program, "origin"); + font->glyphLocation = glGetUniformLocation(font->program, "glyph"); + font->cutoffLocation = glGetUniformLocation(font->program, "cutoff"); + font->offsetLocation = glGetAttribLocation(font->program, "offset"); + + glGenBuffers(1, &font->vbo); + glBindBuffer(GL_ARRAY_BUFFER, font->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return font; +} + +void GUIFontDestroy(struct GUIFont* font) { + glDeleteBuffers(1, &font->vbo); + glDeleteProgram(font->program); + glDeleteTextures(1, &font->font); + free(font); +} + +unsigned GUIFontHeight(const struct GUIFont* font) { + UNUSED(font); + return GLYPH_HEIGHT; +} + +unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) { + UNUSED(font); + if (glyph > 0x7F) { + glyph = '?'; + } + return defaultFontMetrics[glyph].width * 2; +} + +void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) { + UNUSED(font); + if (icon >= GUI_ICON_MAX) { + if (w) { + *w = 0; + } + if (h) { + *h = 0; + } + } else { + if (w) { + *w = defaultIconMetrics[icon].width * 2; + } + if (h) { + *h = defaultIconMetrics[icon].height * 2; + } + } +} + +void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) { + if (glyph > 0x7F) { + glyph = '?'; + } + struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph]; + + glUseProgram(font->program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, font->font); + glBindBuffer(GL_ARRAY_BUFFER, font->vbo); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glUniform1i(font->texLocation, 0); + glUniform2f(font->glyphLocation, (glyph & 15) * CELL_WIDTH + metric.padding.left * 2, (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2); + glUniform2f(font->dimsLocation, CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2, CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2); + glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0); + + glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glEnableVertexAttribArray(font->offsetLocation); + + glUniform1f(font->cutoffLocation, 0.1f); + glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glUniform1f(font->cutoffLocation, 0.7f); + glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(font->offsetLocation); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); +} + +void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) { + if (icon >= GUI_ICON_MAX) { + return; + } + struct GUIIconMetric metric = defaultIconMetrics[icon]; + switch (align & GUI_ALIGN_HCENTER) { + case GUI_ALIGN_HCENTER: + x -= metric.width; + break; + case GUI_ALIGN_RIGHT: + x -= metric.width * 2; + break; + } + switch (align & GUI_ALIGN_VCENTER) { + case GUI_ALIGN_VCENTER: + y -= metric.height; + break; + case GUI_ALIGN_BOTTOM: + y -= metric.height * 2; + break; + } + + glUseProgram(font->program); + switch (orient) { + case GUI_ORIENT_HMIRROR: + // TODO + break; + case GUI_ORIENT_VMIRROR: + // TODO + break; + case GUI_ORIENT_0: + default: + // TODO: Rotate + break; + } + + glUseProgram(font->program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, font->font); + glBindBuffer(GL_ARRAY_BUFFER, font->vbo); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glUniform1i(font->texLocation, 0); + glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256); + glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2); + glUniform3f(font->originLocation, x, y, 0); + + glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glEnableVertexAttribArray(font->offsetLocation); + + glUniform1f(font->cutoffLocation, 0.1f); + glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glUniform1f(font->cutoffLocation, 0.7f); + glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(font->offsetLocation); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); +} + +void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) { + if (icon >= GUI_ICON_MAX) { + return; + } + struct GUIIconMetric metric = defaultIconMetrics[icon]; + // +}
A src/platform/switch/main.c

@@ -0,0 +1,343 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "feature/gui/gui-runner.h" +#include <mgba/core/core.h> +#include <mgba/internal/gba/input.h> +#include <mgba-util/gui.h> +#include <mgba-util/gui/font.h> + +#include <switch.h> +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#define AUTO_INPUT 0x4E585031 + +static EGLDisplay s_display; +static EGLContext s_context; +static EGLSurface s_surface; + +static const GLfloat _offsets[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, +}; + +static const GLchar* const _gles2Header = + "#version 100\n" + "precision mediump float;\n"; + +static const char* const _vertexShader = + "attribute vec2 offset;\n" + "uniform vec2 dims;\n" + "uniform vec2 insize;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " vec2 ratio = insize / 256.0;\n" + " vec2 scaledOffset = offset * dims;\n" + " gl_Position = vec4(scaledOffset.x * 2.0 - dims.x, scaledOffset.y * -2.0 + dims.y, 0.0, 1.0);\n" + " texCoord = offset * ratio;\n" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + "uniform vec4 color;\n" + + "void main() {\n" + " vec4 texColor = vec4(texture2D(tex, texCoord).rgb, 1.0);\n" + " texColor *= color;\n" + " gl_FragColor = texColor;\n" + "}"; + +static GLuint program; +static GLuint vbo; +static GLuint offsetLocation; +static GLuint texLocation; +static GLuint dimsLocation; +static GLuint insizeLocation; +static GLuint colorLocation; +static GLuint tex; + +static color_t frameBuffer[256 * 256]; + +static bool initEgl() { + s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!s_display) { + goto _fail0; + } + + eglInitialize(s_display, NULL, NULL); + + EGLConfig config; + EGLint numConfigs; + static const EGLint attributeList[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_NONE + }; + eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs); + if (!numConfigs) { + goto _fail1; + } + + s_surface = eglCreateWindowSurface(s_display, config, "", NULL); + if (!s_surface) { + goto _fail1; + } + + s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL); + if (!s_context) { + goto _fail2; + } + + eglMakeCurrent(s_display, s_surface, s_surface, s_context); + return true; + +_fail2: + eglDestroySurface(s_display, s_surface); + s_surface = NULL; +_fail1: + eglTerminate(s_display); + s_display = NULL; +_fail0: + return false; +} + +static void deinitEgl() { + if (s_display) { + if (s_context) { + eglDestroyContext(s_display, s_context); + } + if (s_surface) { + eglDestroySurface(s_display, s_surface); + } + eglTerminate(s_display); + } +} + +static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) { + mInputBindKey(map, binding, __builtin_ctz(nativeKey), key); +} + +static void _drawStart(void) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +static void _drawEnd(void) { + eglSwapBuffers(s_display, s_surface); +} + +static uint32_t _pollInput(const struct mInputMap* map) { + int keys = 0; + hidScanInput(); + u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); + keys |= mInputMapKeyBits(map, AUTO_INPUT, padkeys, 0); + return keys; +} + +static void _setup(struct mGUIRunner* runner) { + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_A, GBA_KEY_A); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_B, GBA_KEY_B); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_PLUS, GBA_KEY_START); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_MINUS, GBA_KEY_SELECT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DUP, GBA_KEY_UP); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DDOWN, GBA_KEY_DOWN); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DLEFT, GBA_KEY_LEFT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DRIGHT, GBA_KEY_RIGHT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_L, GBA_KEY_L); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R); + + runner->core->setVideoBuffer(runner->core, frameBuffer, 256); +} + +static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glUseProgram(program); + float aspectX = width / (float) runner->params.width; + float aspectY = height / (float) runner->params.height; + float max; + if (aspectX > aspectY) { + max = floor(1.0 / aspectX); + } else { + max = floor(1.0 / aspectY); + } + + aspectX *= max; + aspectY *= max; + + glUniform1i(texLocation, 0); + glUniform2f(dimsLocation, aspectX, aspectY); + glUniform2f(insizeLocation, width, height); + if (!faded) { + glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f); + } else { + glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f); + } + + glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glEnableVertexAttribArray(offsetLocation); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(offsetLocation); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); +} + +static void _drawFrame(struct mGUIRunner* runner, bool faded) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameBuffer); + + unsigned width, height; + runner->core->desiredVideoDimensions(runner->core, &width, &height); + _drawTex(runner, width, height, faded); +} + +static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + _drawTex(runner, width, height, faded); +} + +static uint16_t _pollGameInput(struct mGUIRunner* runner) { + int keys = 0; + hidScanInput(); + u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); + keys |= mInputMapKeyBits(&runner->core->inputMap, AUTO_INPUT, padkeys, 0); + return keys; +} + +static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { + UNUSED(runner); +} + +static bool _running(struct mGUIRunner* runner) { + UNUSED(runner); + return appletMainLoop(); +} + +int main(int argc, char* argv[]) { + socketInitializeDefault(); + nxlinkStdio(); + initEgl(); + romfsInit(); + + struct GUIFont* font = GUIFontCreate(); + + u32 width = 1280; + u32 height = 720; + + glViewport(0, 0, width, height); + glClearColor(0.f, 0.f, 0.f, 1.f); + + glGenTextures(1, &tex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + program = glCreateProgram(); + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const GLchar* shaderBuffer[2]; + + shaderBuffer[0] = _gles2Header; + + shaderBuffer[1] = _vertexShader; + glShaderSource(vertexShader, 2, shaderBuffer, NULL); + + shaderBuffer[1] = _fragmentShader; + glShaderSource(fragmentShader, 2, shaderBuffer, NULL); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + glCompileShader(fragmentShader); + + GLint success; + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg); + puts(msg); + } + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg); + puts(msg); + } + glLinkProgram(program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + texLocation = glGetUniformLocation(program, "tex"); + colorLocation = glGetUniformLocation(program, "color"); + dimsLocation = glGetUniformLocation(program, "dims"); + insizeLocation = glGetUniformLocation(program, "insize"); + offsetLocation = glGetAttribLocation(program, "offset"); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + struct mGUIRunner runner = { + .params = { + width, height, + font, "/", + _drawStart, _drawEnd, + _pollInput, NULL, + NULL, + NULL, NULL, + }, + .nConfigExtra = 0, + .setup = _setup, + .teardown = NULL, + .gameLoaded = NULL, + .gameUnloaded = NULL, + .prepareForFrame = NULL, + .drawFrame = _drawFrame, + .drawScreenshot = _drawScreenshot, + .paused = NULL, + .unpaused = NULL, + .incrementScreenMode = NULL, + .setFrameLimiter = _setFrameLimiter, + .pollGameInput = _pollGameInput, + .running = _running + }; + mGUIInit(&runner, "switch"); + + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_A, GUI_INPUT_SELECT); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_B, GUI_INPUT_BACK); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_X, GUI_INPUT_CANCEL); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DUP, GUI_INPUT_UP); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DDOWN, GUI_INPUT_DOWN); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DLEFT, GUI_INPUT_LEFT); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT); + + mGUIRunloop(&runner); + + deinitEgl(); + socketExit(); + return 0; +}
M src/platform/test/perf-main.csrc/platform/test/perf-main.c

@@ -19,7 +19,7 @@

#ifdef _3DS #include <3ds.h> #endif -#ifdef SWITCH +#ifdef __SWITCH__ #include <switch.h> #endif

@@ -52,7 +52,7 @@ #ifdef _3DS

extern bool allocateRomBuffer(void); FS_Archive sdmcArchive; #endif -#ifdef SWITCH +#ifdef __SWITCH__ TimeType __nx_time_type = TimeType_LocalSystemClock; #endif

@@ -77,7 +77,7 @@ consoleInit(GFX_BOTTOM, NULL);

if (!allocateRomBuffer()) { return 1; } -#elif defined(SWITCH) +#elif defined(__SWITCH__) UNUSED(_mPerfShutdown); gfxInitDefault(); consoleInit(NULL);

@@ -138,7 +138,7 @@

#ifdef _3DS gfxExit(); acExit(); -#elif defined(SWITCH) +#elif defined(__SWITCH__) gfxExit(); #endif
M src/util/gui/menu.csrc/util/gui/menu.c

@@ -10,6 +10,8 @@ #include <mgba-util/gui/font.h>

#ifdef _3DS #include <3ds.h> +#elif defined(__SWITCH__) +#include <switch.h> #endif DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);

@@ -29,6 +31,10 @@ GUIInvalidateKeys(params);

while (true) { #ifdef _3DS if (!aptMainLoop()) { + return GUI_MENU_EXIT_CANCEL; + } +#elif defined(__SWITCH__) + if (!appletMainLoop()) { return GUI_MENU_EXIT_CANCEL; } #endif