mirror of
https://github.com/LIJI32/SameBoy.git
synced 2026-10-11 22:19:50 +00:00
Add a screenshot hotkey to the SDL port, closes #364
This commit is contained in:
@@ -180,13 +180,19 @@ RGBGFX_FLAGS := $(if $(filter $(shell echo 'println __RGBDS_MAJOR__ || (!__RGBDS
|
||||
# Set compilation and linkage flags based on target, platform and configuration
|
||||
|
||||
OPEN_DIALOG = SDL/open_dialog/gtk.c
|
||||
SAVE_PNG = SDL/save_png/libpng.c
|
||||
NEED_LIBPNG = 1
|
||||
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
OPEN_DIALOG = SDL/open_dialog/windows.c
|
||||
SAVE_PNG = SDL/save_png/windows.c
|
||||
NEED_LIBPNG = 0
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),Darwin)
|
||||
OPEN_DIALOG = SDL/open_dialog/cocoa.m
|
||||
SAVE_PNG = SDL/save_png/appkit.m
|
||||
NEED_LIBPNG = 0
|
||||
endif
|
||||
|
||||
# These must come before the -Wno- flags
|
||||
@@ -271,15 +277,22 @@ endif
|
||||
|
||||
ifeq (,$(PKG_CONFIG))
|
||||
GL_LDFLAGS := -lGL
|
||||
ifeq ($(NEED_LIBPNG),1)
|
||||
SDL_LDFLAGS += -lpng
|
||||
endif
|
||||
else
|
||||
GL_CFLAGS := $(shell $(PKG_CONFIG) --cflags gl)
|
||||
GL_LDFLAGS := $(shell $(PKG_CONFIG) --libs gl || echo -lGL)
|
||||
ifeq ($(NEED_LIBPNG),1)
|
||||
SDL_LDFLAGS += $(shell $(PKG_CONFIG) --cflags libpng)
|
||||
SDL_CFLAGS += $(shell $(PKG_CONFIG) --libs libpng || echo -lpng)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
CFLAGS += -IWindows -Drandom=rand --target=x86_64-pc-windows
|
||||
LDFLAGS += -lmsvcrt -lkernel32 -Wl,/MANIFESTFILE:NUL --target=x86_64-pc-windows
|
||||
SDL_LDFLAGS := -lSDL2 -lcomdlg32 -luser32 -lshell32 -lole32 -ladvapi32 -ldwmapi -lSDL2main
|
||||
SDL_LDFLAGS := -lSDL2 -lcomdlg32 -luser32 -lshell32 -lole32 -ladvapi32 -ldwmapi -lwindowscodecs -lSDL2main
|
||||
GL_LDFLAGS := -lopengl32
|
||||
ifneq ($(REDIST_XAUDIO),)
|
||||
CFLAGS += -DREDIST_XAUDIO
|
||||
@@ -417,7 +430,7 @@ endif
|
||||
|
||||
CORE_SOURCES := $(filter-out $(CORE_FILTER),$(shell ls Core/*.c))
|
||||
CORE_HEADERS := $(shell ls Core/*.h)
|
||||
SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) $(patsubst %,SDL/audio/%.c,$(SDL_AUDIO_DRIVERS))
|
||||
SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) $(SAVE_PNG) $(patsubst %,SDL/audio/%.c,$(SDL_AUDIO_DRIVERS))
|
||||
TESTER_SOURCES := $(shell ls Tester/*.c)
|
||||
IOS_SOURCES := $(filter-out iOS/installer.m, $(shell ls iOS/*.m)) $(shell ls AppleCommon/*.m)
|
||||
COCOA_SOURCES := $(shell ls Cocoa/*.m) $(shell ls HexFiend/*.m) $(shell ls JoyKit/*.m) $(shell ls AppleCommon/*.m)
|
||||
@@ -510,6 +523,11 @@ $(OBJ)/HexFiend/%.m.o: HexFiend/%.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(OCFLAGS) -c $< -o $@ -fno-objc-arc -include HexFiend/HexFiend_2_Framework_Prefix.pch
|
||||
|
||||
# Apple-specific code in SDL
|
||||
$(OBJ)/SDL/%.m.o: SDL/%.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(OCFLAGS) $(SDL_CFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJ)/%.m.o: %.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(OCFLAGS) -c $< -o $@
|
||||
|
||||
@@ -81,21 +81,26 @@ void render_texture(void *pixels, void *previous)
|
||||
}
|
||||
|
||||
static const char *help[] = {
|
||||
// Shortcuts
|
||||
"Keyboard Shortcuts:\n"
|
||||
" Open Menu: Escape\n"
|
||||
" Open menu: Escape\n"
|
||||
" Open ROM: " MODIFIER_NAME "+O\n"
|
||||
" Reset: " MODIFIER_NAME "+R\n"
|
||||
" Pause: " MODIFIER_NAME "+P\n"
|
||||
" Save state: " MODIFIER_NAME "+(0-9)\n"
|
||||
" Load state: " MODIFIER_NAME "+" SHIFT_STRING "+(0-9)\n"
|
||||
" Toggle Fullscreen " MODIFIER_NAME "+F\n"
|
||||
" Toggle fullscreen " MODIFIER_NAME "+F\n"
|
||||
#ifdef __APPLE__
|
||||
" Mute/Unmute: " MODIFIER_NAME "+" SHIFT_STRING "+M\n"
|
||||
" Save screenshot: " MODIFIER_NAME "+S\n"
|
||||
#else
|
||||
" Mute/Unmute: " MODIFIER_NAME "+M\n"
|
||||
" Save screenshot: F12\n"
|
||||
#endif
|
||||
" Toggle channel: " ALT_STRING "+(1-4)\n"
|
||||
" Break Debugger: " CTRL_STRING "+C",
|
||||
" Break debugger: " CTRL_STRING "+C",
|
||||
|
||||
// About
|
||||
"\n"
|
||||
"SameBoy\n"
|
||||
"Version " GB_VERSION "\n\n"
|
||||
|
||||
+54
-6
@@ -14,6 +14,7 @@
|
||||
#include "shader.h"
|
||||
#include "audio/audio.h"
|
||||
#include "console.h"
|
||||
#include "save_png/save_png.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -29,6 +30,7 @@ static bool underclock_down = false, rewind_down = false, do_rewind = false, rew
|
||||
static bool rapid_a = false, rapid_b = false;
|
||||
static uint8_t rapid_a_count = 0, rapid_b_count = 0;
|
||||
static double clock_mutliplier = 1.0;
|
||||
static bool pending_screenshot = false;
|
||||
|
||||
char *filename = NULL;
|
||||
static typeof(free) *free_function = NULL;
|
||||
@@ -305,6 +307,32 @@ static void configure_console(void)
|
||||
GB_set_async_input_callback(&gb, async_input_callback);
|
||||
}
|
||||
|
||||
static void save_screenshot(void)
|
||||
{
|
||||
static char png_path[PATH_MAX] = {0,};
|
||||
time_t now = time(NULL);
|
||||
struct tm *local = localtime(&now);
|
||||
char timestring[20];
|
||||
strftime(timestring, sizeof(timestring), "%Y-%m-%d %H.%M.%S", local);
|
||||
|
||||
replace_extension(filename, strlen(filename), png_path, "");
|
||||
snprintf(png_path + strlen(png_path), sizeof(png_path) - 1 - strlen(png_path), " %s.png", timestring);
|
||||
|
||||
unsigned i = 2;
|
||||
while (access(png_path, F_OK) == 0) {
|
||||
replace_extension(filename, strlen(filename), png_path, "");
|
||||
snprintf(png_path + strlen(png_path), sizeof(png_path) - 1 - strlen(png_path), " %s %u.png", timestring, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (save_png(png_path, GB_get_screen_width(&gb), GB_get_screen_height(&gb), active_pixel_buffer, pixel_format)) {
|
||||
show_osd_text("Screenshot saved");
|
||||
}
|
||||
else {
|
||||
show_osd_text("Failed to save screenshot");
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_events(GB_gameboy_t *gb)
|
||||
{
|
||||
SDL_Event event;
|
||||
@@ -567,6 +595,19 @@ static void handle_events(GB_gameboy_t *gb)
|
||||
GB_audio_set_paused(GB_audio_is_playing());
|
||||
}
|
||||
break;
|
||||
#ifdef __APPLE__
|
||||
case SDL_SCANCODE_S:
|
||||
if (!(event.key.keysym.mod & MODIFIER)) break;
|
||||
#else
|
||||
case SDL_SCANCODE_F12:
|
||||
#endif
|
||||
if (osd_countdown && configuration.osd) {
|
||||
pending_screenshot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
save_screenshot();
|
||||
break;
|
||||
|
||||
case SDL_SCANCODE_F:
|
||||
if (event.key.keysym.mod & MODIFIER) {
|
||||
@@ -688,13 +729,20 @@ static void vblank(GB_gameboy_t *gb, GB_vblank_type_t type)
|
||||
show_osd_text("Rewinding...");
|
||||
}
|
||||
|
||||
if (pending_screenshot) {
|
||||
pending_screenshot = false;
|
||||
save_screenshot();
|
||||
}
|
||||
|
||||
if (osd_countdown && configuration.osd) {
|
||||
unsigned width = GB_get_screen_width(gb);
|
||||
unsigned height = GB_get_screen_height(gb);
|
||||
draw_text(active_pixel_buffer,
|
||||
width, height, 8, height - 8 - osd_text_lines * 12, osd_text,
|
||||
rgb_encode(gb, 255, 255, 255), rgb_encode(gb, 0, 0, 0),
|
||||
true);
|
||||
if (osd_countdown != 1) {
|
||||
unsigned width = GB_get_screen_width(gb);
|
||||
unsigned height = GB_get_screen_height(gb);
|
||||
draw_text(active_pixel_buffer,
|
||||
width, height, 8, height - 8 - osd_text_lines * 12, osd_text,
|
||||
rgb_encode(gb, 255, 255, 255), rgb_encode(gb, 0, 0, 0),
|
||||
true);
|
||||
}
|
||||
osd_countdown--;
|
||||
}
|
||||
if (type != GB_VBLANK_TYPE_REPEAT) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <SDL.h>
|
||||
#import "save_png.h"
|
||||
|
||||
bool save_png(const char *filename, uint32_t width, uint32_t height, const void *pixels, SDL_PixelFormat *pixel_format)
|
||||
{
|
||||
if (pixel_format->format != SDL_PIXELFORMAT_ABGR8888 &&
|
||||
pixel_format->format != SDL_PIXELFORMAT_XBGR8888) {
|
||||
return false;
|
||||
}
|
||||
size_t stride = width * 4;
|
||||
|
||||
@autoreleasepool {
|
||||
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:width
|
||||
pixelsHigh:height
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:3
|
||||
hasAlpha:false
|
||||
isPlanar:false
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bitmapFormat:0
|
||||
bytesPerRow:4 * width
|
||||
bitsPerPixel:32];
|
||||
memcpy(rep.bitmapData, pixels, stride * height);
|
||||
NSData *data = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||
return [data writeToFile:@(filename) atomically:true];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <png.h>
|
||||
#include <SDL.h>
|
||||
#include <stdbool.h>
|
||||
#include "save_png.h"
|
||||
|
||||
bool save_png(const char *filename, uint32_t width, uint32_t height, const void *pixels, SDL_PixelFormat *pixel_format)
|
||||
{
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if (!f) return false;
|
||||
|
||||
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
uint8_t *row = malloc(width * 3);
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_write_struct(&png, &info);
|
||||
fclose(f);
|
||||
free(row);
|
||||
return false;
|
||||
}
|
||||
png_init_io(png, f);
|
||||
|
||||
png_set_IHDR(png,
|
||||
info,
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_write_info(png, info);
|
||||
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
const uint32_t *src = (uint32_t *)pixels + y * width;
|
||||
uint8_t *dest = row;
|
||||
|
||||
for (uint32_t x = 0; x < width; x++) {
|
||||
uint8_t dummy;
|
||||
SDL_GetRGBA(*(src++), pixel_format, &dest[0], &dest[1], &dest[2], &dummy);
|
||||
dest += 3;
|
||||
}
|
||||
|
||||
png_write_row(png, row);
|
||||
}
|
||||
|
||||
png_write_end(png, NULL);
|
||||
png_destroy_write_struct(&png, &info);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL.h>
|
||||
|
||||
bool save_png(const char *filename, uint32_t width, uint32_t height, const void *pixels, SDL_PixelFormat *pixel_format);
|
||||
@@ -0,0 +1,88 @@
|
||||
#define COBJMACROS
|
||||
#include "save_png.h"
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
static wchar_t *utf8_to_wide(const char *utf8)
|
||||
{
|
||||
int length = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
|
||||
if (length <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t *ret = malloc(length * sizeof(*ret));
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, ret, length)) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool save_png(const char *filename, uint32_t width, uint32_t height, const void *pixels, SDL_PixelFormat *pixel_format)
|
||||
{
|
||||
bool success = false;
|
||||
wchar_t *wfilename = utf8_to_wide(filename);
|
||||
|
||||
IWICImagingFactory *factory = NULL;
|
||||
IWICStream *stream = NULL;
|
||||
IWICBitmapEncoder *encoder = NULL;
|
||||
IWICBitmapFrameEncode *frame = NULL;
|
||||
uint8_t *row = malloc(width * 3);
|
||||
|
||||
CoInitialize(NULL);
|
||||
|
||||
|
||||
if (CoCreateInstance(&CLSID_WICImagingFactory,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
&IID_IWICImagingFactory,
|
||||
(void **)&factory)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (IWICImagingFactory_CreateStream(factory, &stream)) goto done;
|
||||
if (IWICStream_InitializeFromFilename(stream, wfilename, GENERIC_WRITE)) goto done;
|
||||
if (IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatPng, NULL, &encoder)) goto done;
|
||||
if (IWICBitmapEncoder_Initialize(encoder, (IStream *)stream, WICBitmapEncoderNoCache)) goto done;
|
||||
if (IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)) goto done;
|
||||
if (IWICBitmapFrameEncode_Initialize(frame, NULL)) goto done;
|
||||
if (IWICBitmapFrameEncode_SetSize(frame, width, height)) goto done;
|
||||
|
||||
WICPixelFormatGUID pixel_format_guid = GUID_WICPixelFormat24bppRGB;
|
||||
if (IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format_guid)) goto done;
|
||||
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
const uint32_t *src = (uint32_t *)pixels + y * width;
|
||||
uint8_t *dest = row;
|
||||
|
||||
for (uint32_t x = 0; x < width; x++) {
|
||||
uint8_t dummy;
|
||||
SDL_GetRGBA(*(src++), pixel_format, &dest[2], &dest[1], &dest[0], &dummy);
|
||||
dest += 3;
|
||||
}
|
||||
|
||||
if (IWICBitmapFrameEncode_WritePixels(frame, 1, width * 3, width * 3, (void *)row)) goto done;
|
||||
}
|
||||
|
||||
if (IWICBitmapFrameEncode_Commit(frame)) goto done;
|
||||
if (IWICBitmapEncoder_Commit(encoder)) goto done;
|
||||
|
||||
success = true;
|
||||
|
||||
done:
|
||||
if (frame) IWICBitmapFrameEncode_Release(frame);
|
||||
if (encoder) IWICBitmapEncoder_Release(encoder);
|
||||
if (stream) IWICStream_Release(stream);
|
||||
if (factory) IWICImagingFactory_Release(factory);
|
||||
|
||||
CoUninitialize();
|
||||
free(wfilename);
|
||||
free(row);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
int access(const char *filename, int mode);
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define F_OK 0
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#ifndef __LIBRETRO__
|
||||
|
||||
Reference in New Issue
Block a user