Switch: Initial mGUI port

This commit is contained in:
Vicki Pfau 2018-09-13 18:12:32 -07:00
parent c0a94967ca
commit af03ad75be
12 changed files with 718 additions and 16 deletions

View File

@ -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")
@ -295,6 +295,10 @@ 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)
set(USE_GDB_STUB OFF)
endif()

View File

@ -55,7 +55,7 @@ struct Address {
extern u32* SOCUBuffer;
#endif
#ifdef SWITCH
#ifdef __SWITCH__
#include <switch.h>
#endif
@ -68,7 +68,7 @@ static inline void SocketSubsystemInit() {
SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE);
socInit(SOCUBuffer, SOCU_BUFFERSIZE);
}
#elif defined(SWITCH)
#elif defined(__SWITCH__)
socketInitializeDefault();
#endif
}
@ -80,7 +80,7 @@ static inline void SocketSubsystemDeinit() {
socExit();
free(SOCUBuffer);
SOCUBuffer = NULL;
#elif defined(SWITCH)
#elif defined(__SWITCH__)
socketExit();
#endif
}
@ -110,7 +110,7 @@ static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size)
}
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);

View File

@ -13,12 +13,14 @@ CXX_GUARD_START
#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

BIN
res/font-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -179,7 +179,7 @@ void mCoreConfigMakePortable(const struct mCoreConfig* config) {
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 @@ void mCoreConfigDirectory(char* out, size_t outLength) {
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);

View File

@ -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 @@ if(BUILD_PERF)
${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)

View File

@ -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__)

View File

@ -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];
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

343
src/platform/switch/main.c Normal file
View File

@ -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;
}

View File

@ -19,7 +19,7 @@
#ifdef _3DS
#include <3ds.h>
#endif
#ifdef SWITCH
#ifdef __SWITCH__
#include <switch.h>
#endif
@ -52,7 +52,7 @@ struct PerfOpts {
extern bool allocateRomBuffer(void);
FS_Archive sdmcArchive;
#endif
#ifdef SWITCH
#ifdef __SWITCH__
TimeType __nx_time_type = TimeType_LocalSystemClock;
#endif
@ -77,7 +77,7 @@ int main(int argc, char** argv) {
if (!allocateRomBuffer()) {
return 1;
}
#elif defined(SWITCH)
#elif defined(__SWITCH__)
UNUSED(_mPerfShutdown);
gfxInitDefault();
consoleInit(NULL);
@ -138,7 +138,7 @@ int main(int argc, char** argv) {
#ifdef _3DS
gfxExit();
acExit();
#elif defined(SWITCH)
#elif defined(__SWITCH__)
gfxExit();
#endif

View File

@ -10,6 +10,8 @@
#ifdef _3DS
#include <3ds.h>
#elif defined(__SWITCH__)
#include <switch.h>
#endif
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
@ -31,6 +33,10 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (!aptMainLoop()) {
return GUI_MENU_EXIT_CANCEL;
}
#elif defined(__SWITCH__)
if (!appletMainLoop()) {
return GUI_MENU_EXIT_CANCEL;
}
#endif
uint32_t newInput = 0;
GUIPollInput(params, &newInput, 0);