mirror of https://github.com/mgba-emu/mgba.git
Switch: Initial mGUI port
This commit is contained in:
parent
c0a94967ca
commit
af03ad75be
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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 |
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue