diff --git a/CHANGES b/CHANGES index 9e46c97d8..829a225d7 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Features: - Discord Rich Presence now supports time elapsed - Additional scaling shaders Emulation fixes: + - GB I/O: Fix incrementing SGB controller when P14 is low (fixes mgba.io/i/2202) - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB Video: Render SGB border when unmasking with ATTR/PAL_SET (fixes mgba.io/i/2261) - GBA: Improve timing when not booting from BIOS @@ -56,6 +57,7 @@ Emulation fixes: - GBA Video: Fix backdrop color if DISPCNT is first set to 0 (fixes mgba.io/i/2260) Other fixes: - Core: Don't attempt to restore rewind diffs past start of rewind + - FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084) - GB Video: Fix memory leak when reseting SGB games - GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB - Libretro: Fix crash when using Game Boy codes (fixes mgba.io/i/2281) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70abeaff0..c09ff0507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ if(NOT LIBMGBA_ONLY) set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") - set(BUILD_GLES3 OFF CACHE BOOL "Build with OpenGL|ES 3") + set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") 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") @@ -123,6 +123,10 @@ else() set(CMAKE_INSTALL_INCLUDEDIR "include") endif() +if(APPLE AND DISTBUILD) + set(CMAKE_INSTALL_DOCDIR ".") +endif() + if(NOT DEFINED LIBDIR) set(LIBDIR "${CMAKE_INSTALL_LIBDIR}") endif() @@ -212,8 +216,7 @@ if(WIN32) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) add_definitions(-D_UNICODE -DUNICODE) else() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -municode") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") + add_definitions(-D_GNU_SOURCE) endif() list(APPEND OS_LIB ws2_32 shlwapi) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c) @@ -331,7 +334,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") find_function(localtime_r) # The strtof_l on Linux not actually exposed nor actually strtof_l set(HAVE_STRTOF_L OFF) -elseif(NOT DEFINED PSP2) +else() find_function(localtime_r) find_function(strtof_l) endif() @@ -398,6 +401,7 @@ if(CMAKE_SYSTEM_NAME MATCHES ".*BSD|DragonFly") else() find_feature(USE_EDITLINE "libedit") endif() + if(BUILD_GL) find_package(OpenGL QUIET) if(NOT OPENGL_FOUND) @@ -406,39 +410,49 @@ if(BUILD_GL) set(OPENGL_LIBRARY OpenGL::GL) endif() endif() -if(NOT BUILD_GL AND NOT LIBMGBA_ONLY) - set(OPENGL_LIBRARY "" CACHE PATH "" FORCE) -endif() -if(BUILD_GLES2 AND NOT BUILD_RASPI AND NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|Darwin|Linux|.*BSD|DragonFly|Haiku)$") - find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) - find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) - if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) - set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE) - endif() -endif() -if(NOT BUILD_GLES2 AND NOT LIBMGBA_ONLY) - set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE) -endif() if(BUILD_GL) list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c) list(APPEND FEATURE_DEFINES BUILD_GL) list(APPEND DEPENDENCY_LIB ${OPENGL_LIBRARY}) include_directories(${OPENGL_INCLUDE_DIR}) endif() +if(NOT BUILD_GL AND NOT LIBMGBA_ONLY) + set(OPENGL_LIBRARY "" CACHE PATH "" FORCE) +endif() + +if(BUILD_GLES2 AND NOT BUILD_GL) + find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) + find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) + if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) + set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE) + endif() +endif() if(BUILD_GLES2) - list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) list(APPEND FEATURE_DEFINES BUILD_GLES2) list(APPEND DEPENDENCY_LIB ${OPENGLES2_LIBRARY}) include_directories(${OPENGLES2_INCLUDE_DIR}) endif() -if(BUILD_GLES3) + +if(BUILD_GLES3 AND NOT BUILD_GL) find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h) find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2) - list(APPEND FEATURE_DEFINES BUILD_GLES3) if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY) set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE) endif() endif() +if(BUILD_GLES3) + list(APPEND FEATURE_DEFINES BUILD_GLES3) + list(APPEND DEPENDENCY_LIB ${OPENGLES3_LIBRARY}) + include_directories(${OPENGLES3_INCLUDE_DIR}) +endif() + +if(BUILD_GLES2 OR BUILD_GLES3) + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) +endif() + +if(NOT BUILD_GLES2 AND NOT BUILD_GLES3 AND NOT LIBMGBA_ONLY) + set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE) +endif() if(DISABLE_DEPS) set(USE_GDB_STUB OFF) @@ -692,7 +706,7 @@ endif() if(USE_EPOXY) list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) - list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2) + list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3) list(APPEND FEATURES EPOXY) include_directories(AFTER ${EPOXY_INCLUDE_DIRS}) link_directories(${EPOXY_LIBRARY_DIRS}) @@ -937,14 +951,16 @@ if(BUILD_OPENEMU) install(TARGETS ${BINARY_NAME}-openemu LIBRARY DESTINATION ${OE_LIBDIR} COMPONENT ${BINARY_NAME}.oecoreplugin NAMELINK_SKIP) endif() -if(BUILD_QT AND WIN32) +if(BUILD_QT AND (WIN32 OR APPLE)) set(BUILD_UPDATER ON) endif() if(BUILD_UPDATER) add_executable(updater-stub WIN32 ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c) target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) - if(NOT MSVC) + if(MSVC) + set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup) + else() set_target_properties(updater-stub PROPERTIES LINK_FLAGS_RELEASE -s) set_target_properties(updater-stub PROPERTIES LINK_FLAGS_RELWITHDEBINFO -s) endif() @@ -1091,6 +1107,9 @@ endif() if(DISTBUILD) set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) + set(CPACK_DMG_FILESYSTEM "HFS+") + set(CPACK_DMG_FORMAT "UDBZ") + set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME} ${VERSION_STRING}") if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND BUILD_SHARED) if(NOT APPLE) add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$" "$.debug") @@ -1101,7 +1120,8 @@ if(DISTBUILD) endif() if(APPLE) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf) - set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/cmake/DMGOverrides.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake @ONLY) + set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake) elseif(WIN32) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf installer) elseif(3DS) diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 8ea8e989f..fb8e30451 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -253,9 +253,9 @@ typedef intptr_t ssize_t; #define ATTRIBUTE_NOINLINE // Adapted from https://stackoverflow.com/a/2390626 #define _CONSTRUCTOR(FN, PRE) \ - static void FN(void); \ - __declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \ - static void FN(void) + static void FN(void); \ + __declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \ + static void FN(void) #ifdef _WIN64 #define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "") #else diff --git a/include/mgba-util/gui/font.h b/include/mgba-util/gui/font.h index 1751898c3..5ad318e65 100644 --- a/include/mgba-util/gui/font.h +++ b/include/mgba-util/gui/font.h @@ -59,6 +59,31 @@ enum GUIIcon { GUI_ICON_UP, GUI_ICON_RIGHT, GUI_ICON_DOWN, + GUI_ICON_9SLICE_EMPTY_NW, + GUI_ICON_9SLICE_EMPTY_N, + GUI_ICON_9SLICE_EMPTY_NE, + GUI_ICON_9SLICE_EMPTY_W, + GUI_ICON_9SLICE_EMPTY_E, + GUI_ICON_9SLICE_EMPTY_SW, + GUI_ICON_9SLICE_EMPTY_S, + GUI_ICON_9SLICE_EMPTY_SE, + GUI_ICON_9SLICE_FILLED_NW, + GUI_ICON_9SLICE_FILLED_N, + GUI_ICON_9SLICE_FILLED_NE, + GUI_ICON_9SLICE_FILLED_W, + GUI_ICON_9SLICE_FILLED_C, + GUI_ICON_9SLICE_FILLED_E, + GUI_ICON_9SLICE_FILLED_SW, + GUI_ICON_9SLICE_FILLED_S, + GUI_ICON_9SLICE_FILLED_SE, + GUI_ICON_9SLICE_CAP_NNW, + GUI_ICON_9SLICE_CAP_NWW, + GUI_ICON_9SLICE_CAP_NNE, + GUI_ICON_9SLICE_CAP_NEE, + GUI_ICON_9SLICE_CAP_SSW, + GUI_ICON_9SLICE_CAP_SWW, + GUI_ICON_9SLICE_CAP_SSE, + GUI_ICON_9SLICE_CAP_SEE, GUI_ICON_MAX, }; @@ -80,6 +105,12 @@ struct GUIIconMetric { int height; }; +enum GUI9SliceStyle { + GUI_9SLICE_FILLED, + GUI_9SLICE_EMPTY, + GUI_9SLICE_EMPTY_CAPPED, +}; + unsigned GUIFontHeight(const struct GUIFont*); unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph); unsigned GUIFontSpanWidth(const struct GUIFont*, const char* text); @@ -96,6 +127,8 @@ void GUIFontDrawIconSize(struct GUIFont* font, int x, int y, int w, int h, uint3 void GUIFontDrawSubmit(struct GUIFont* font); #endif +void GUIFontDraw9Slice(struct GUIFont*, int x, int y, int width, int height, uint32_t color, enum GUI9SliceStyle style); + CXX_GUARD_END #endif diff --git a/include/mgba/feature/updater.h b/include/mgba/feature/updater.h index 323ff749c..bacd45294 100644 --- a/include/mgba/feature/updater.h +++ b/include/mgba/feature/updater.h @@ -17,16 +17,16 @@ struct StringList; struct Table; struct mUpdaterContext { - struct Configuration manifest; + struct Configuration manifest; }; struct mUpdate { - const char* path; - size_t size; - int rev; - const char* version; - const char* commit; - const char* sha256; + const char* path; + size_t size; + int rev; + const char* version; + const char* commit; + const char* sha256; }; bool mUpdaterInit(struct mUpdaterContext*, const char* manifest); diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index 2908e48d9..773d5dee0 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -16,7 +16,7 @@ CXX_GUARD_START #include #include -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #ifdef USE_EPOXY #include diff --git a/res/icons.png b/res/icons.png index b52df6688..1b75cddd9 100644 Binary files a/res/icons.png and b/res/icons.png differ diff --git a/res/icons2x.png b/res/icons2x.png index 036d59257..c4066f4ef 100644 Binary files a/res/icons2x.png and b/res/icons2x.png differ diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 518425b83..706e1fde8 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -662,7 +662,7 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) { break; case 'X': _writeMemoryBinary(stub, message); - break; + break; case 'Z': _setBreakpoint(stub, message); break; diff --git a/src/feature/ffmpeg/ffmpeg-decoder.c b/src/feature/ffmpeg/ffmpeg-decoder.c index 5d3c3aa08..c3bb6d1c5 100644 --- a/src/feature/ffmpeg/ffmpeg-decoder.c +++ b/src/feature/ffmpeg/ffmpeg-decoder.c @@ -81,20 +81,12 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) { decoder->videoStream = i; decoder->width = -1; decoder->height = -1; -#if LIBAVCODEC_VERSION_MAJOR >= 55 decoder->videoFrame = av_frame_alloc(); -#else - decoder->videoFrame = avcodec_alloc_frame(); -#endif } if (type == AVMEDIA_TYPE_AUDIO) { decoder->audioStream = i; -#if LIBAVCODEC_VERSION_MAJOR >= 55 decoder->audioFrame = av_frame_alloc(); -#else - decoder->audioFrame = avcodec_alloc_frame(); -#endif } } return true; @@ -102,11 +94,7 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) { void FFmpegDecoderClose(struct FFmpegDecoder* decoder) { if (decoder->audioFrame) { -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&decoder->audioFrame); -#else - avcodec_free_frame(&decoder->audioFrame); -#endif } if (decoder->audio) { @@ -124,11 +112,7 @@ void FFmpegDecoderClose(struct FFmpegDecoder* decoder) { } if (decoder->videoFrame) { -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&decoder->videoFrame); -#else - avcodec_free_frame(&decoder->videoFrame); -#endif } if (decoder->pixels) { @@ -216,4 +200,4 @@ bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) { #endif } return readPacket; -} \ No newline at end of file +} diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index a55008f0d..32bd1dbc3 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -90,7 +90,6 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) { encoder->audioStream = NULL; encoder->audioFrame = NULL; encoder->audioBuffer = NULL; - encoder->postaudioBuffer = NULL; encoder->video = NULL; encoder->videoStream = NULL; encoder->videoFrame = NULL; @@ -315,17 +314,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { FFmpegEncoderClose(encoder); return false; } -#if LIBAVCODEC_VERSION_MAJOR >= 55 encoder->audioFrame = av_frame_alloc(); -#else - encoder->audioFrame = avcodec_alloc_frame(); -#endif if (!encoder->audio->frame_size) { encoder->audio->frame_size = 1; } encoder->audioFrame->nb_samples = encoder->audio->frame_size; encoder->audioFrame->format = encoder->audio->sample_fmt; encoder->audioFrame->pts = 0; + encoder->audioFrame->channel_layout = AV_CH_LAYOUT_STEREO; #ifdef USE_LIBAVRESAMPLE encoder->resampleContext = avresample_alloc_context(); av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0); @@ -342,14 +338,12 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { #endif encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4; encoder->audioBuffer = av_malloc(encoder->audioBufferSize); - encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0); - encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize); - avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0); + av_frame_get_buffer(encoder->audioFrame, 0); if (encoder->audio->codec->id == AV_CODEC_ID_AAC && - (strcasecmp(encoder->containerFormat, "mp4") || - strcasecmp(encoder->containerFormat, "m4v") || - strcasecmp(encoder->containerFormat, "mov"))) { + (strcasecmp(encoder->containerFormat, "mp4") == 0|| + strcasecmp(encoder->containerFormat, "m4v") == 0 || + strcasecmp(encoder->containerFormat, "mov") == 0)) { // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out #ifdef FFMPEG_USE_NEW_BSF av_bsf_alloc(av_bsf_get_by_name("aac_adtstoasc"), &encoder->absf); @@ -391,9 +385,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { } if (encoder->video->codec->id == AV_CODEC_ID_H264 && - (strcasecmp(encoder->containerFormat, "mp4") || - strcasecmp(encoder->containerFormat, "m4v") || - strcasecmp(encoder->containerFormat, "mov"))) { + (strcasecmp(encoder->containerFormat, "mp4") == 0 || + strcasecmp(encoder->containerFormat, "m4v") == 0 || + strcasecmp(encoder->containerFormat, "mov") == 0)) { // QuickTime and a few other things require YUV420 encoder->video->pix_fmt = AV_PIX_FMT_YUV420P; } @@ -497,11 +491,7 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { return false; } -#if LIBAVCODEC_VERSION_MAJOR >= 55 encoder->sinkFrame = av_frame_alloc(); -#else - encoder->sinkFrame = avcodec_alloc_frame(); -#endif } AVDictionary* opts = 0; av_dict_set(&opts, "strict", "-2", 0); @@ -511,17 +501,13 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { FFmpegEncoderClose(encoder); return false; } -#if LIBAVCODEC_VERSION_MAJOR >= 55 encoder->videoFrame = av_frame_alloc(); -#else - encoder->videoFrame = avcodec_alloc_frame(); -#endif encoder->videoFrame->format = encoder->video->pix_fmt != AV_PIX_FMT_PAL8 ? encoder->video->pix_fmt : encoder->ipixFormat; encoder->videoFrame->width = encoder->video->width; encoder->videoFrame->height = encoder->video->height; encoder->videoFrame->pts = 0; _ffmpegSetVideoDimensions(&encoder->d, encoder->iwidth, encoder->iheight); - av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->videoFrame->width, encoder->videoFrame->height, encoder->videoFrame->format, 32); + av_frame_get_buffer(encoder->videoFrame, 32); #ifdef FFMPEG_USE_CODECPAR avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video); #endif @@ -579,21 +565,13 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) { avio_close(encoder->context->pb); } - if (encoder->postaudioBuffer) { - av_free(encoder->postaudioBuffer); - encoder->postaudioBuffer = NULL; - } if (encoder->audioBuffer) { av_free(encoder->audioBuffer); encoder->audioBuffer = NULL; } if (encoder->audioFrame) { -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&encoder->audioFrame); -#else - avcodec_free_frame(&encoder->audioFrame); -#endif } if (encoder->audio) { #ifdef FFMPEG_USE_CODECPAR @@ -623,20 +601,11 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) { } if (encoder->videoFrame) { - av_freep(encoder->videoFrame->data); -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&encoder->videoFrame); -#else - avcodec_free_frame(&encoder->videoFrame); -#endif } if (encoder->sinkFrame) { -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&encoder->sinkFrame); -#else - avcodec_free_frame(&encoder->sinkFrame); -#endif encoder->sinkFrame = NULL; } @@ -696,7 +665,6 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right return; } - int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt); encoder->currentAudioSample = 0; #ifdef USE_LIBAVRESAMPLE avresample_convert(encoder->resampleContext, 0, 0, 0, @@ -705,19 +673,15 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) { return; } -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_make_writable(encoder->audioFrame); -#endif - int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); + int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples); #else -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_make_writable(encoder->audioFrame); -#endif if (swr_get_out_samples(encoder->resampleContext, 1) < encoder->audioFrame->nb_samples) { swr_convert(encoder->resampleContext, NULL, 0, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); return; } - int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize, + int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); #endif @@ -728,61 +692,76 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right } bool _ffmpegWriteAudioFrame(struct FFmpegEncoder* encoder, struct AVFrame* audioFrame) { - AVPacket packet; - av_init_packet(&packet); - packet.data = 0; - packet.size = 0; + AVPacket* packet; +#ifdef FFMPEG_USE_PACKET_UNREF + packet = av_packet_alloc(); +#else + packet = av_malloc(sizeof(*packet)); + av_init_packet(packet); +#endif + packet->data = 0; + packet->size = 0; int gotData; #ifdef FFMPEG_USE_PACKETS avcodec_send_frame(encoder->audio, audioFrame); - gotData = avcodec_receive_packet(encoder->audio, &packet); - gotData = (gotData == 0) && packet.size; + gotData = avcodec_receive_packet(encoder->audio, packet); + gotData = (gotData == 0) && packet->size; #else - avcodec_encode_audio2(encoder->audio, &packet, audioFrame, &gotData); + avcodec_encode_audio2(encoder->audio, packet, audioFrame, &gotData); #endif - packet.pts = av_rescale_q(packet.pts, encoder->audio->time_base, encoder->audioStream->time_base); - packet.dts = packet.pts; + packet->pts = av_rescale_q(packet->pts, encoder->audio->time_base, encoder->audioStream->time_base); + packet->dts = packet->pts; if (gotData) { if (encoder->absf) { - AVPacket tempPacket; + AVPacket* tempPacket; +#ifdef FFMPEG_USE_PACKETS + tempPacket = av_packet_alloc(); +#else + tempPacket = av_malloc(sizeof(*tempPacket)); + av_init_packet(tempPacket); +#endif #ifdef FFMPEG_USE_NEW_BSF - int success = av_bsf_send_packet(encoder->absf, &packet); + int success = av_bsf_send_packet(encoder->absf, packet); if (success >= 0) { - success = av_bsf_receive_packet(encoder->absf, &tempPacket); + success = av_bsf_receive_packet(encoder->absf, tempPacket); } #else int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0, - &tempPacket.data, &tempPacket.size, - packet.data, packet.size, 0); + &tempPacket->data, &tempPacket->size, + packet->data, packet->size, 0); #endif if (success >= 0) { #if LIBAVUTIL_VERSION_MAJOR >= 53 - tempPacket.buf = av_buffer_create(tempPacket.data, tempPacket.size, av_buffer_default_free, 0, 0); + tempPacket->buf = av_buffer_create(tempPacket->data, tempPacket->size, av_buffer_default_free, 0, 0); #endif #ifdef FFMPEG_USE_PACKET_UNREF - av_packet_move_ref(&packet, &tempPacket); + av_packet_move_ref(packet, tempPacket); + av_packet_free(&packet); #else - av_free_packet(&packet); + av_free_packet(packet); + av_freep(&packet); packet = tempPacket; #endif - packet.stream_index = encoder->audioStream->index; - av_interleaved_write_frame(encoder->context, &packet); + packet->stream_index = encoder->audioStream->index; + av_interleaved_write_frame(encoder->context, packet); } } else { - packet.stream_index = encoder->audioStream->index; - av_interleaved_write_frame(encoder->context, &packet); + packet->stream_index = encoder->audioStream->index; + av_interleaved_write_frame(encoder->context, packet); } } #ifdef FFMPEG_USE_PACKET_UNREF - av_packet_unref(&packet); + av_packet_unref(packet); + av_packet_free(&packet); #else - av_free_packet(&packet); + av_free_packet(packet); + av_freep(&packet); #endif return gotData; } @@ -798,9 +777,7 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size } stride *= BYTES_PER_PIXEL; -#if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_make_writable(encoder->videoFrame); -#endif if (encoder->video->codec->id == AV_CODEC_ID_WEBP) { // TODO: Figure out why WebP is rescaling internally (should video frames not be rescaled externally?) encoder->videoFrame->pts = encoder->currentVideoFrame; @@ -829,32 +806,39 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size } bool _ffmpegWriteVideoFrame(struct FFmpegEncoder* encoder, struct AVFrame* videoFrame) { - AVPacket packet; + AVPacket* packet; - av_init_packet(&packet); - packet.data = 0; - packet.size = 0; +#ifdef FFMPEG_USE_PACKET_UNREF + packet = av_packet_alloc(); +#else + packet = av_malloc(sizeof(*packet)); + av_init_packet(packet); +#endif + packet->data = 0; + packet->size = 0; int gotData; #ifdef FFMPEG_USE_PACKETS avcodec_send_frame(encoder->video, videoFrame); - gotData = avcodec_receive_packet(encoder->video, &packet) == 0; + gotData = avcodec_receive_packet(encoder->video, packet) == 0; #else - avcodec_encode_video2(encoder->video, &packet, videoFrame, &gotData); + avcodec_encode_video2(encoder->video, packet, videoFrame, &gotData); #endif if (gotData) { #ifndef FFMPEG_USE_PACKET_UNREF if (encoder->video->coded_frame->key_frame) { - packet.flags |= AV_PKT_FLAG_KEY; + packet->flags |= AV_PKT_FLAG_KEY; } #endif - packet.stream_index = encoder->videoStream->index; - av_interleaved_write_frame(encoder->context, &packet); + packet->stream_index = encoder->videoStream->index; + av_interleaved_write_frame(encoder->context, packet); } #ifdef FFMPEG_USE_PACKET_UNREF - av_packet_unref(&packet); + av_packet_unref(packet); + av_packet_free(&packet); #else - av_free_packet(&packet); + av_free_packet(packet); + av_freep(&packet); #endif return gotData; diff --git a/src/feature/ffmpeg/ffmpeg-encoder.h b/src/feature/ffmpeg/ffmpeg-encoder.h index 26e21184d..a484a9673 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.h +++ b/src/feature/ffmpeg/ffmpeg-encoder.h @@ -33,8 +33,6 @@ struct FFmpegEncoder { int sampleRate; uint16_t* audioBuffer; size_t audioBufferSize; - uint16_t* postaudioBuffer; - size_t postaudioBufferSize; AVFrame* audioFrame; size_t currentAudioSample; int64_t currentAudioFrame; diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index 0e547cd27..154702637 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -4,7 +4,9 @@ * 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 +#include #include +#include #include #include @@ -16,13 +18,18 @@ #include #include #include +#include #define mkdir(X, Y) _mkdir(X) #elif defined(_POSIX_C_SOURCE) #include #endif -bool extractArchive(struct VDir* archive, const char* root) { +#ifndef W_OK +#define W_OK 02 +#endif + +bool extractArchive(struct VDir* archive, const char* root, bool prefix) { char path[PATH_MAX] = {0}; struct VDirEntry* vde; uint8_t block[8192]; @@ -30,17 +37,37 @@ bool extractArchive(struct VDir* archive, const char* root) { while ((vde = archive->listNext(archive))) { struct VFile* vfIn; struct VFile* vfOut; - const char* fname = strchr(vde->name(vde), '/'); - if (!fname) { + const char* fname; + if (prefix) { + fname = strchr(vde->name(vde), '/'); + if (!fname) { + continue; + } + snprintf(path, sizeof(path), "%s/%s", root, &fname[1]); + } else { + fname = vde->name(vde); + snprintf(path, sizeof(path), "%s/%s", root, fname); + } + if (fname[0] == '.') { continue; } - snprintf(path, sizeof(path), "%s/%s", root, &fname[1]); switch (vde->type(vde)) { case VFS_DIRECTORY: printf("mkdir %s\n", fname); if (mkdir(path, 0755) < 0 && errno != EEXIST) { return false; } + if (!prefix) { + struct VDir* subdir = archive->openDir(archive, fname); + if (!subdir) { + return false; + } + if (!extractArchive(subdir, path, false)) { + subdir->close(subdir); + return false; + } + subdir->close(subdir); + } break; case VFS_FILE: printf("extract %s\n", fname); @@ -48,7 +75,11 @@ bool extractArchive(struct VDir* archive, const char* root) { errno = 0; vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); if (!vfOut && errno == EACCES) { +#ifdef _WIN32 + Sleep(1000); +#else sleep(1); +#endif vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); } if (!vfOut) { @@ -76,6 +107,7 @@ int main(int argc, char* argv[]) { UNUSED(argv); struct mCoreConfig config; char updateArchive[PATH_MAX] = {0}; + char bin[PATH_MAX] = {0}; const char* root; int ok = 1; @@ -87,14 +119,71 @@ int main(int argc, char* argv[]) { } else if (access(root, W_OK)) { puts("Cannot write to update path"); } else { +#ifdef __APPLE__ + char subdir[PATH_MAX]; + char devpath[PATH_MAX] = {0}; + bool needsUnmount = false; +#endif bool isPortable = mCoreConfigIsPortable(); - struct VDir* archive = VDirOpenArchive(updateArchive); + const char* extension = mUpdateGetArchiveExtension(&config); + struct VDir* archive = NULL; + bool prefix = true; + if (strcmp(extension, "dmg") == 0) { +#ifdef __APPLE__ + char mountpoint[PATH_MAX]; + // Make a slightly random directory name for the updater mountpoint + struct timeval t; + gettimeofday(&t, NULL); + int printed = snprintf(mountpoint, sizeof(mountpoint), "/Volumes/%s Updater %04X", projectName, (t.tv_usec >> 2) & 0xFFFF); + + // Fork hdiutil to mount it + char* args[] = {"hdiutil", "attach", "-nobrowse", "-mountpoint", mountpoint, updateArchive, NULL}; + int fds[2]; + pipe(fds); + pid_t pid = fork(); + if (pid == 0) { + dup2(fds[1], STDOUT_FILENO); + execvp("hdiutil", args); + _exit(1); + } else { + // Parse out the disk ID so we can detach it when we're done + char buffer[1024] = {0}; + ssize_t size; + while ((size = read(fds[0], buffer, sizeof(buffer) - 1)) > 0) { // Leave the last byte null + char* devinfo = strnstr(buffer, "\n/dev/disk", size); + if (!devinfo) { + continue; + } + char* devend = strpbrk(&devinfo[9], "s \t"); + if (!devend) { + continue; + } + off_t diff = devend - devinfo - 1; + memcpy(devpath, &devinfo[1], diff); + puts(devpath); + break; + } + int retstat; + wait4(pid, &retstat, 0, NULL); + } + snprintf(&mountpoint[printed], sizeof(mountpoint) - printed, "/%s.app", projectName); + snprintf(subdir, sizeof(subdir), "%s/%s.app", root, projectName); + root = subdir; + archive = VDirOpen(mountpoint); + prefix = false; + needsUnmount = true; +#endif + } else { + archive = VDirOpenArchive(updateArchive); + } if (!archive) { puts("Cannot open update archive"); } else { puts("Extracting update"); - if (extractArchive(archive, root)) { + if (extractArchive(archive, root, prefix)) { puts("Complete"); + const char* command = mUpdateGetCommand(&config); + strlcpy(bin, command, sizeof(bin)); ok = 0; mUpdateDeregister(&config); } else { @@ -103,19 +192,31 @@ int main(int argc, char* argv[]) { archive->close(archive); unlink(updateArchive); } +#ifdef __APPLE__ + if (needsUnmount) { + char* args[] = {"hdiutil", "detach", devpath, NULL}; + pid_t pid = vfork(); + if (pid == 0) { + execvp("hdiutil", args); + _exit(0); + } else { + int retstat; + wait4(pid, &retstat, 0, NULL); + } + } +#endif if (!isPortable) { char portableIni[PATH_MAX] = {0}; snprintf(portableIni, sizeof(portableIni), "%s/portable.ini", root); unlink(portableIni); } } - const char* bin = mUpdateGetCommand(&config); mCoreConfigDeinit(&config); if (ok == 0) { const char* argv[] = { bin, NULL }; #ifdef _WIN32 _execv(bin, argv); -#elif defined(_POSIX_C_SOURCE) +#elif defined(_POSIX_C_SOURCE) || defined(__APPLE__) execv(bin, argv); #endif } diff --git a/src/feature/updater.c b/src/feature/updater.c index 21c4aa146..dec380d9c 100644 --- a/src/feature/updater.c +++ b/src/feature/updater.c @@ -185,6 +185,16 @@ void mUpdateRegister(struct mCoreConfig* config, const char* arg0, const char* u #endif if (last) { last[0] = '\0'; +#ifdef __APPLE__ + ssize_t len = strlen(filename); + if (len > 19 && strcmp(&filename[len - 19], ".app/Contents/MacOS") == 0) { + filename[len - 19] = '\0'; + last = strrchr(filename, '/'); + if (last) { + last[0] = '\0'; + } + } +#endif } ConfigurationSetValue(cfg, UPDATE_SECTION, "bin", arg0); ConfigurationSetValue(cfg, UPDATE_SECTION, "root", filename); diff --git a/src/gb/io.c b/src/gb/io.c index 00f75ffd7..74ce4caee 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -119,19 +119,15 @@ static void _writeSGBBits(struct GB* gb, int bits) { if (bits == gb->currentSgbBits) { return; } - switch (bits) { - case 0: - case 1: - if (gb->currentSgbBits & 2) { - gb->sgbIncrement = !gb->sgbIncrement; - } - break; - case 3: + if (bits & 2) { if (gb->sgbIncrement) { gb->sgbIncrement = false; gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers; } - break; + } else { + if (gb->currentSgbBits & 2) { + gb->sgbIncrement = !gb->sgbIncrement; + } } gb->currentSgbBits = bits; if (gb->sgbBit == 128 && bits == 2) { diff --git a/src/gba/core.c b/src/gba/core.c index 3d1ffffc3..125c7f2c5 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -18,7 +18,7 @@ #ifndef DISABLE_THREADING #include #endif -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #include #endif #include @@ -138,7 +138,7 @@ struct GBACore { struct mCore d; struct GBAVideoRenderer dummyRenderer; struct GBAVideoSoftwareRenderer renderer; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 struct GBAVideoGLRenderer glRenderer; #endif #ifndef MINIMAL_CORE @@ -196,7 +196,7 @@ static bool _GBACoreInit(struct mCore* core) { GBAVideoSoftwareRendererCreate(&gbacore->renderer); gbacore->renderer.outputBuffer = NULL; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 GBAVideoGLRendererCreate(&gbacore->glRenderer); gbacore->glRenderer.outputTex = -1; #endif @@ -256,7 +256,7 @@ static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature UNUSED(core); switch (feature) { case mCORE_FEATURE_OPENGL: -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 return true; #else return false; @@ -370,7 +370,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c } struct GBACore* gbacore = (struct GBACore*) core; -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (strcmp("videoScale", option) == 0) { if (config != &core->config) { mCoreConfigCopyValue(&core->config, config, "videoScale"); @@ -388,7 +388,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c if (gbacore->renderer.outputBuffer) { renderer = &gbacore->renderer.d; } -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); renderer = &gbacore->glRenderer.d; @@ -410,7 +410,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c } static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) { -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 const struct GBACore* gbacore = (const struct GBACore*) core; int scale = gbacore->glRenderer.scale; #else @@ -430,7 +430,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s } static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) { -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 struct GBACore* gbacore = (struct GBACore*) core; gbacore->glRenderer.outputTex = texid; #else @@ -573,7 +573,7 @@ static void _GBACoreReset(struct mCore* core) { struct GBA* gba = (struct GBA*) core->board; int fakeBool; if (gbacore->renderer.outputBuffer -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 || gbacore->glRenderer.outputTex != (unsigned) -1 #endif ) { @@ -581,7 +581,7 @@ static void _GBACoreReset(struct mCore* core) { if (gbacore->renderer.outputBuffer) { renderer = &gbacore->renderer.d; } -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); renderer = &gbacore->glRenderer.d; diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 5e1ee1b56..a977491db 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) +#ifdef BUILD_GLES3 #include #include diff --git a/src/platform/cmake/DMGOverrides.cmake.in b/src/platform/cmake/DMGOverrides.cmake.in new file mode 100644 index 000000000..906853946 --- /dev/null +++ b/src/platform/cmake/DMGOverrides.cmake.in @@ -0,0 +1,6 @@ +message(FATAL ${CPACK_GENERATOR}) +if(CPACK_GENERATOR STREQUAL "DragNDrop") + set(CPACK_COMPONENTS_ALL @BINARY_NAME@ @BINARY_NAME@-qt @BINARY_NAME@-qt-dbg) + set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) + unset(CPACK_RESOURCE_FILE_LICENSE) +endif() diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index d9f6705e5..14f0db18b 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -159,6 +159,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) { mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0); mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0); +#ifdef BUILD_GLES3 if (context->initialShader.vao != (GLuint) -1) { glBindVertexArray(context->initialShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); @@ -168,6 +169,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) { glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindVertexArray(0); } +#endif glDeleteFramebuffers(1, &context->finalShader.fbo); glDeleteTextures(1, &context->finalShader.tex); @@ -297,9 +299,12 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) { glUseProgram(shader->program); glUniform1i(shader->texLocation, 0); glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH); +#ifdef BUILD_GLES3 if (shader->vao != (GLuint) -1) { glBindVertexArray(shader->vao); - } else { + } else +#endif + { glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glEnableVertexAttribArray(shader->positionLocation); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); @@ -391,9 +396,11 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) { } glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); +#ifdef BUILD_GLES3 if (context->finalShader.vao != (GLuint) -1) { glBindVertexArray(0); } +#endif } void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { @@ -508,6 +515,7 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name); } +#ifdef BUILD_GLES3 const GLubyte* extensions = glGetString(GL_EXTENSIONS); if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) { glGenVertexArrays(1, &shader->vao); @@ -515,7 +523,9 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f glEnableVertexAttribArray(shader->positionLocation); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindVertexArray(0); - } else { + } else +#endif + { shader->vao = -1; } @@ -527,9 +537,11 @@ void mGLES2ShaderDeinit(struct mGLES2Shader* shader) { glDeleteShader(shader->fragmentShader); glDeleteProgram(shader->program); glDeleteFramebuffers(1, &shader->fbo); +#ifdef BUILD_GLES3 if (shader->vao != (GLuint) -1) { glDeleteVertexArrays(1, &shader->vao); } +#endif } void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) { @@ -547,16 +559,20 @@ void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shad glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); +#ifdef BUILD_GLES3 if (context->shaders[i].vao != (GLuint) -1) { glBindVertexArray(context->shaders[i].vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glEnableVertexAttribArray(context->shaders[i].positionLocation); glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); } +#endif } +#ifdef BUILD_GLES3 if (context->initialShader.vao != (GLuint) -1) { glBindVertexArray(0); } +#endif glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/platform/opengl/gles2.h b/src/platform/opengl/gles2.h index 8cf5b2b0e..4f9f504f5 100644 --- a/src/platform/opengl/gles2.h +++ b/src/platform/opengl/gles2.h @@ -20,6 +20,8 @@ CXX_GUARD_START #include #include #endif +#elif defined(BUILD_GLES3) +#include #else #include #endif diff --git a/src/platform/psp2/CMakeLists.txt b/src/platform/psp2/CMakeLists.txt index 60d37e070..393f63a4d 100644 --- a/src/platform/psp2/CMakeLists.txt +++ b/src/platform/psp2/CMakeLists.txt @@ -3,7 +3,7 @@ include("${VITASDK}/share/vita.cmake" REQUIRED) find_program(OBJCOPY ${cross_prefix}objcopy) find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share) -set(OS_DEFINES IOAPI_NO_64) +set(OS_DEFINES IOAPI_NO_64 _GNU_SOURCE) set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE) file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/psp2-*.c) diff --git a/src/platform/psp2/sce-vfs.c b/src/platform/psp2/sce-vfs.c index 22b05950f..d5a52f177 100644 --- a/src/platform/psp2/sce-vfs.c +++ b/src/platform/psp2/sce-vfs.c @@ -90,7 +90,7 @@ struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { bool _vfsceClose(struct VFile* vf) { struct VFileSce* vfsce = (struct VFileSce*) vf; - sceIoSyncByFd(vfsce->fd); + sceIoSyncByFd(vfsce->fd, 0); return sceIoClose(vfsce->fd) >= 0; } @@ -128,7 +128,7 @@ static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) { sceIoLseek(vfsce->fd, 0, SEEK_SET); sceIoWrite(vfsce->fd, memory, size); sceIoLseek(vfsce->fd, cur, SEEK_SET); - sceIoSyncByFd(vfsce->fd); + sceIoSyncByFd(vfsce->fd, 0); mappedMemoryFree(memory, size); } @@ -155,7 +155,7 @@ bool _vfsceSync(struct VFile* vf, void* buffer, size_t size) { sceIoLseek(vfsce->fd, cur, SEEK_SET); return res == size; } - return sceIoSyncByFd(vfsce->fd) >= 0; + return sceIoSyncByFd(vfsce->fd, 0) >= 0; } struct VDir* VDirOpen(const char* path) { diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index 2d0e7cccc..9895711e0 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -44,8 +44,7 @@ ApplicationUpdater::ApplicationUpdater(ConfigController* config, QObject* parent config->setQtOption("lastUpdateCheck", m_lastCheck); if (available && currentVersion() < updateInfo()) { -#ifdef Q_OS_WIN - // Only works on Windows at the moment +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) ApplicationUpdatePrompt* prompt = new ApplicationUpdatePrompt; connect(prompt, &QDialog::accepted, GBAApp::app(), &GBAApp::restartForUpdate); prompt->setAttribute(Qt::WA_DeleteOnClose); diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 8b147e282..79e79fdc8 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -25,7 +25,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/input) find_package(Qt5 COMPONENTS Core Widgets Network Multimedia) -if(NOT BUILD_GL AND NOT BUILD_GLES2) +if(NOT BUILD_GL AND NOT BUILD_GLES2 AND NOT BUILD_GLES3) message(WARNING "OpenGL is recommended to build the Qt port") endif() @@ -244,6 +244,11 @@ if(APPLE) set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) set(MACOSX_BUNDLE_GUI_IDENTIFIER com.endrift.${BINARY_NAME}-qt) set_source_files_properties(${CMAKE_SOURCE_DIR}/res/icon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + if(DISTBUILD) + set(APPDIR ".") + else() + set(APPDIR "Applications") + endif() endif() if(WIN32) configure_file(${CMAKE_SOURCE_DIR}/res/win.rc.in ${CMAKE_BINARY_DIR}/res/${BINARY_NAME}.rc) @@ -252,7 +257,7 @@ if(WIN32) endif() if(NOT DEFINED DATADIR) if(APPLE) - set(DATADIR Applications/${PROJECT_NAME}.app/Contents/Resources) + set(DATADIR ${APPDIR}/${PROJECT_NAME}.app/Contents/Resources) elseif(WIN32 AND NOT WIN32_UNIX_PATHS) set(DATADIR ".") else() @@ -308,6 +313,9 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CM if(WIN32) set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + if(NOT MSVC) + target_link_libraries(${BINARY_NAME}-qt -municode) + endif() endif() list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network) @@ -339,11 +347,11 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) install(TARGETS ${BINARY_NAME}-qt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt - BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt) + BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt) if(UNIX AND NOT APPLE) install(FILES ${CMAKE_SOURCE_DIR}/res/qt.desktop DESTINATION share/applications RENAME ${BINARY_NAME}-qt.desktop COMPONENT ${BINARY_NAME}-qt) endif() -if(UNIX) +if(UNIX AND NOT (APPLE AND DISTBUILD)) install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt) endif() if(APPLE OR WIN32) @@ -376,7 +384,7 @@ if(APPLE) if(DEFINED CROSS_ROOT) set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}") endif() - install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")") + install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${APPDIR}/${PROJECT_NAME}.app\")") endif() elseif(WIN32) if(CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION EQUAL 8) diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 0ea500f73..f85c43d75 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Display.h" +#include "CoreController.h" #include "ConfigController.h" #include "DisplayGL.h" #include "DisplayQt.h" @@ -14,23 +15,23 @@ using namespace QGBA; -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) Display::Driver Display::s_driver = Display::Driver::OPENGL; #else Display::Driver Display::s_driver = Display::Driver::QT; #endif Display* Display::create(QWidget* parent) { -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) QSurfaceFormat format; format.setSwapInterval(1); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); #endif switch (s_driver) { -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) case Driver::OPENGL: -#if defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { format.setVersion(2, 0); } else { @@ -65,7 +66,7 @@ Display* Display::create(QWidget* parent) { return new DisplayQt(parent); default: -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) return new DisplayGL(format, parent); #else return new DisplayQt(parent); @@ -118,7 +119,7 @@ void Display::configure(ConfigController* config) { interframeBlending(opts->interframeBlending); filter(opts->resampleVideo); config->updateOption("showOSD"); -#if defined(BUILD_GL) || defined(BUILD_GLES2) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) if (opts->shader) { struct VDir* shader = VDirOpen(opts->shader); if (shader && supportsShaders()) { diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 23ad8dcd4..a682ad931 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayGL.h" -#if defined(BUILD_GL) || defined(BUILD_GLES2) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) #include #include @@ -24,7 +24,7 @@ #ifdef BUILD_GL #include "platform/opengl/gl.h" #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) #include "platform/opengl/gles2.h" #ifdef _WIN32 #include @@ -297,13 +297,13 @@ void PainterGL::create() { #ifdef BUILD_GL mGLContext* glBackend; #endif -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) mGLES2Context* gl2Backend; #endif m_window = std::make_unique(); -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) auto version = m_format.version(); if (version >= qMakePair(2, 0)) { gl2Backend = static_cast(malloc(sizeof(mGLES2Context))); @@ -329,7 +329,7 @@ void PainterGL::create() { }; m_backend->init(m_backend, 0); -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders) { m_shader.preprocessShader = static_cast(&reinterpret_cast(m_backend)->initialShader); } @@ -346,7 +346,7 @@ void PainterGL::destroy() { return; } makeCurrent(); -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_shader.passes) { mGLES2ShaderFree(&m_shader); } @@ -423,7 +423,7 @@ void PainterGL::filter(bool filter) { void PainterGL::start() { makeCurrent(); -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders && m_shader.passes) { mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); } @@ -524,19 +524,17 @@ void PainterGL::unpause() { } void PainterGL::performDraw() { - m_painter.begin(m_window.get()); - m_painter.beginNativePainting(); float r = m_surface->devicePixelRatio(); m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r); if (m_buffer) { m_backend->postFrame(m_backend, m_buffer); } m_backend->drawFrame(m_backend); - m_painter.endNativePainting(); if (m_showOSD && m_messagePainter) { + m_painter.begin(m_window.get()); m_messagePainter->paint(&m_painter); + m_painter.end(); } - m_painter.end(); } void PainterGL::enqueue(const uint32_t* backing) { @@ -601,7 +599,7 @@ void PainterGL::setShaders(struct VDir* dir) { if (!supportsShaders()) { return; } -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); @@ -615,7 +613,7 @@ void PainterGL::clearShaders() { if (!supportsShaders()) { return; } -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); @@ -628,7 +626,7 @@ VideoShader* PainterGL::shaders() { } int PainterGL::glTex() { -#ifdef BUILD_GLES2 +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (supportsShaders()) { mGLES2Context* gl2Backend = reinterpret_cast(m_backend); return gl2Backend->tex; diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 39cdb1d8e..d31921151 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#if defined(BUILD_GL) || defined(BUILD_GLES2) +#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) #include "Display.h" diff --git a/src/platform/sdl/main.h b/src/platform/sdl/main.h index 5f52f5e19..c97e50e33 100644 --- a/src/platform/sdl/main.h +++ b/src/platform/sdl/main.h @@ -29,7 +29,7 @@ CXX_GUARD_START #pragma GCC diagnostic pop #endif -#if defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) #include "gl-common.h" #include "platform/opengl/gles2.h" #endif @@ -72,7 +72,7 @@ struct mSDLRenderer { #ifdef BUILD_GL struct mGLContext gl; #endif -#if defined(BUILD_GLES2) || defined(USE_EPOXY) +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) struct mGLES2Context gl2; #endif diff --git a/src/platform/test/cinema-main.c b/src/platform/test/cinema-main.c index 66c290736..7d4034536 100644 --- a/src/platform/test/cinema-main.c +++ b/src/platform/test/cinema-main.c @@ -716,7 +716,11 @@ static struct VDir* _makeOutDir(const char* testName) { strncpy(pathEnd, testName, len); pathEnd += len; +#ifndef _WIN32 mkdir(path, 0777); +#else + mkdir(path); +#endif if (!pos) { break; diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index e9f5fae1e..639ba0dd6 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -83,8 +83,8 @@ static Socket _server = INVALID_SOCKET; int main(int argc, char** argv) { #ifdef _3DS UNUSED(_mPerfShutdown); - gfxInitDefault(); - osSetSpeedupEnable(true); + gfxInitDefault(); + osSetSpeedupEnable(true); consoleInit(GFX_BOTTOM, NULL); #elif defined(__SWITCH__) UNUSED(_mPerfShutdown); diff --git a/src/util/gui/font-metrics.c b/src/util/gui/font-metrics.c index c3f359b25..bf30cb4f0 100644 --- a/src/util/gui/font-metrics.c +++ b/src/util/gui/font-metrics.c @@ -102,7 +102,7 @@ const struct GUIFontGlyphMetric defaultFontMetrics[128] = { { 6, 11, { 2, 5, 3, 5 }}, // 0x5D "]" { 8, 7, { 2, 4, 7, 4 }}, // 0x5E "^" { 10, 3, { 10, 3, 3, 3 }}, // 0x5F "_" - { 6, 5, { 8, 6, 3, 4 }}, // 0x60 "`" + { 6, 5, { 3, 4, 8, 6 }}, // 0x60 "`" { 8, 7, { 6, 4, 3, 4 }}, // 0x61 "a" { 8, 11, { 2, 4, 3, 4 }}, // 0x62 "b" { 8, 7, { 6, 4, 3, 4 }}, // 0x63 "c" @@ -158,4 +158,29 @@ const struct GUIIconMetric defaultIconMetrics[] = { [GUI_ICON_DOWN] = { 130, 34, 12, 12 }, [GUI_ICON_STATUS_FAST_FORWARD] = { 2, 50, 12, 12 }, [GUI_ICON_STATUS_MUTE] = { 17, 50, 14, 12 }, + [GUI_ICON_9SLICE_EMPTY_NW] = { 162, 1, 8, 8 }, + [GUI_ICON_9SLICE_EMPTY_N] = { 170, 1, 12, 8 }, + [GUI_ICON_9SLICE_EMPTY_NE] = { 182, 1, 8, 8 }, + [GUI_ICON_9SLICE_EMPTY_W] = { 162, 9, 8, 12 }, + [GUI_ICON_9SLICE_EMPTY_E] = { 182, 9, 8, 12 }, + [GUI_ICON_9SLICE_EMPTY_SW] = { 162, 23, 8, 8 }, + [GUI_ICON_9SLICE_EMPTY_S] = { 170, 23, 12, 8 }, + [GUI_ICON_9SLICE_EMPTY_SE] = { 182, 23, 8, 8 }, + [GUI_ICON_9SLICE_FILLED_NW] = { 194, 1, 8, 8 }, + [GUI_ICON_9SLICE_FILLED_N] = { 202, 1, 12, 8 }, + [GUI_ICON_9SLICE_FILLED_NE] = { 214, 1, 8, 8 }, + [GUI_ICON_9SLICE_FILLED_W] = { 194, 9, 8, 12 }, + [GUI_ICON_9SLICE_FILLED_C] = { 202, 9, 12, 12 }, + [GUI_ICON_9SLICE_FILLED_E] = { 214, 9, 8, 12 }, + [GUI_ICON_9SLICE_FILLED_SW] = { 194, 23, 8, 8 }, + [GUI_ICON_9SLICE_FILLED_S] = { 202, 23, 12, 8 }, + [GUI_ICON_9SLICE_FILLED_SE] = { 214, 23, 8, 8 }, + [GUI_ICON_9SLICE_CAP_NNW] = { 240, 1, 8, 7 }, + [GUI_ICON_9SLICE_CAP_NWW] = { 226, 16, 6, 8 }, + [GUI_ICON_9SLICE_CAP_NNE] = { 232, 1, 8, 7 }, + [GUI_ICON_9SLICE_CAP_NEE] = { 248, 16, 6, 8 }, + [GUI_ICON_9SLICE_CAP_SSW] = { 240, 24, 8, 7 }, + [GUI_ICON_9SLICE_CAP_SWW] = { 226, 8, 6, 8 }, + [GUI_ICON_9SLICE_CAP_SSE] = { 232, 24, 8, 7 }, + [GUI_ICON_9SLICE_CAP_SEE] = { 248, 8, 6, 8 }, }; diff --git a/src/util/gui/font.c b/src/util/gui/font.c index 7babc3b5b..fbebd7937 100644 --- a/src/util/gui/font.c +++ b/src/util/gui/font.c @@ -86,3 +86,55 @@ void GUIFontPrintf(struct GUIFont* font, int x, int y, enum GUIAlignment align, va_end(args); GUIFontPrint(font, x, y, align, color, buffer); } + +void GUIFontDraw9Slice(struct GUIFont* font, int x, int y, int width, int height, uint32_t color, enum GUI9SliceStyle style) { + switch (style) { + case GUI_9SLICE_EMPTY: + case GUI_9SLICE_EMPTY_CAPPED: + GUIFontDrawIcon(font, x , y , GUI_ALIGN_LEFT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_NW); + GUIFontDrawIcon(font, x + width, y , GUI_ALIGN_RIGHT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_NE); + GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_SW); + GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_SE); + break; + case GUI_9SLICE_FILLED: + GUIFontDrawIcon(font, x , y , GUI_ALIGN_LEFT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_NW); + GUIFontDrawIcon(font, x + width, y , GUI_ALIGN_RIGHT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_NE); + GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SW); + GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SE); + break; + } + + unsigned offX, offY; + unsigned endX, endY; + GUIFontIconMetrics(font, GUI_ICON_9SLICE_EMPTY_NW, &offX, &offY); + GUIFontIconMetrics(font, GUI_ICON_9SLICE_EMPTY_SE, &endX, &endY); + + switch (style) { + case GUI_9SLICE_EMPTY: + GUIFontDrawIconSize(font, x + offX, y, width - offX - endX, offY, color, GUI_ICON_9SLICE_EMPTY_N); + GUIFontDrawIconSize(font, x, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_EMPTY_W); + GUIFontDrawIconSize(font, x + offX, y + height - endY, width - offX - endX, offY, color, GUI_ICON_9SLICE_EMPTY_S); + GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_EMPTY_E); + break; + case GUI_9SLICE_FILLED: + GUIFontDrawIconSize(font, x + offX, y, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILLED_N); + GUIFontDrawIconSize(font, x, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_W); + GUIFontDrawIconSize(font, x + offX, y + height - endY, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILLED_S); + GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_E); + GUIFontDrawIconSize(font, x + offX, y + offY, width - offX - endX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_C); + break; + case GUI_9SLICE_EMPTY_CAPPED: + GUIFontDrawIcon(font, x + offX, y, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NNW); + GUIFontDrawIcon(font, x, y + offY, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NWW); + + GUIFontDrawIcon(font, x + width - endX, y, GUI_ALIGN_RIGHT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NNE); + GUIFontDrawIcon(font, x + width, y + offY, GUI_ALIGN_RIGHT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NEE); + + GUIFontDrawIcon(font, x + offX, y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SSW); + GUIFontDrawIcon(font, x, y + height - endY, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SWW); + + GUIFontDrawIcon(font, x + width - endX, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SSE); + GUIFontDrawIcon(font, x + width, y + height - endY, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SEE); + break; + } +}