630 lines
20 KiB
C
630 lines
20 KiB
C
#define SDL_MAIN_USE_CALLBACKS 1
|
|
#include "SDL3/SDL_blendmode.h"
|
|
#include "SDL3/SDL_camera.h"
|
|
#include "SDL3/SDL_error.h"
|
|
#include "SDL3/SDL_events.h"
|
|
#include "SDL3/SDL_init.h"
|
|
#include "SDL3/SDL_keycode.h"
|
|
#include "SDL3/SDL_log.h"
|
|
#include "SDL3/SDL_main.h"
|
|
#include "SDL3/SDL_pixels.h"
|
|
#include "SDL3/SDL_render.h"
|
|
#include "SDL3/SDL_stdinc.h"
|
|
#include "SDL3/SDL_surface.h"
|
|
#include "SDL3/SDL_timer.h"
|
|
#include "SDL3/SDL_video.h"
|
|
|
|
#include "mgba/core/config.h"
|
|
#include "mgba/core/core.h"
|
|
#include "mgba/core/interface.h"
|
|
#include "mgba/feature/commandline.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define NUM_CHANNELS 3
|
|
#define FRAMESKIP_LIMIT 20
|
|
|
|
#if BYTES_PER_PIXEL == 4
|
|
#define SCREEN_FMT SDL_PIXELFORMAT_XBGR8888
|
|
#elif BYTES_PER_PIXEL == 2
|
|
#define SCREEN_FMT SDL_PIXELFORMAT_XBGR1555
|
|
#else
|
|
#error "unknown pixel format"
|
|
#endif
|
|
|
|
static SDL_Texture* textures[NUM_CHANNELS] = { NULL, NULL, NULL };
|
|
static bool texture_updated = false;
|
|
static SDL_CameraID front_camera = 0;
|
|
static SDL_CameraID back_camera = 0;
|
|
static SDL_Surface* scaled = NULL;
|
|
static SDL_Surface* filtered[NUM_CHANNELS] = { NULL, NULL, NULL };
|
|
static SDL_Surface* screens[NUM_CHANNELS] = { NULL, NULL, NULL };
|
|
|
|
static struct mCore* cores[NUM_CHANNELS] = { NULL, NULL, NULL };
|
|
static unsigned keys = 0;
|
|
|
|
struct AppState {
|
|
SDL_Camera* camera;
|
|
SDL_Window* window;
|
|
SDL_Renderer* renderer;
|
|
int cam_idx, spec_idx;
|
|
};
|
|
|
|
enum mColorFormat pixfmt_sdl_to_mgba(uint32_t sdl_fmt) {
|
|
switch (sdl_fmt) {
|
|
case SDL_PIXELFORMAT_ABGR1555: return mCOLOR_BGR5;
|
|
case SDL_PIXELFORMAT_ABGR8888: return mCOLOR_ABGR8;
|
|
case SDL_PIXELFORMAT_ARGB1555: return mCOLOR_RGB5;
|
|
case SDL_PIXELFORMAT_ARGB8888: return mCOLOR_ARGB8;
|
|
case SDL_PIXELFORMAT_BGR565: return mCOLOR_BGR565;
|
|
case SDL_PIXELFORMAT_BGRA8888: return mCOLOR_BGRA8;
|
|
case SDL_PIXELFORMAT_BGRX8888: return mCOLOR_BGRX8;
|
|
case SDL_PIXELFORMAT_RGB565: return mCOLOR_RGB565;
|
|
case SDL_PIXELFORMAT_RGBA8888: return mCOLOR_RGBA8;
|
|
case SDL_PIXELFORMAT_RGBX8888: return mCOLOR_RGBX8;
|
|
case SDL_PIXELFORMAT_XBGR1555: return mCOLOR_BGR5;
|
|
case SDL_PIXELFORMAT_XBGR8888: return mCOLOR_XBGR8;
|
|
case SDL_PIXELFORMAT_XRGB1555: return mCOLOR_RGB5;
|
|
case SDL_PIXELFORMAT_XRGB8888: return mCOLOR_XRGB8;
|
|
/* others don't quite match between mgba and SDL */
|
|
default: return mCOLOR_ANY;
|
|
}
|
|
}
|
|
|
|
void myStartRequestImageRed(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
|
SDL_DestroySurface(scaled);
|
|
SDL_DestroySurface(filtered[0]);
|
|
scaled = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
filtered[0] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
SDL_Log("red: %d x %d", w, h);
|
|
}
|
|
|
|
void myStartRequestImageGreen(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
|
SDL_DestroySurface(filtered[1]);
|
|
filtered[1] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
SDL_Log("green: %d x %d", w, h);
|
|
}
|
|
|
|
void myStartRequestImageBlue(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
|
SDL_DestroySurface(filtered[2]);
|
|
filtered[2] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
SDL_Log("blue: %d x %d", w, h);
|
|
}
|
|
|
|
void myStopRequestImageRed(struct mImageSource* self) {
|
|
SDL_DestroySurface(scaled);
|
|
SDL_DestroySurface(filtered[0]);
|
|
scaled = NULL;
|
|
filtered[0] = NULL;
|
|
}
|
|
|
|
void myStopRequestImageGreen(struct mImageSource* self) {
|
|
SDL_DestroySurface(filtered[1]);
|
|
filtered[1] = NULL;
|
|
}
|
|
|
|
void myStopRequestImageBlue(struct mImageSource* self) {
|
|
SDL_DestroySurface(filtered[2]);
|
|
filtered[2] = NULL;
|
|
}
|
|
|
|
void myRequestImageRed(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
|
SDL_Surface* source = filtered[0];
|
|
if (source != NULL) {
|
|
*fmt = pixfmt_sdl_to_mgba(source->format);
|
|
*stride = source->pitch / (BYTES_PER_PIXEL / SDL_BYTESPERPIXEL(source->format));
|
|
*buf = source->pixels;
|
|
}
|
|
}
|
|
|
|
void myRequestImageGreen(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
|
SDL_Surface* source = filtered[1];
|
|
if (source != NULL) {
|
|
*fmt = pixfmt_sdl_to_mgba(source->format);
|
|
*stride = source->pitch / (BYTES_PER_PIXEL / SDL_BYTESPERPIXEL(source->format));
|
|
*buf = source->pixels;
|
|
}
|
|
}
|
|
|
|
void myRequestImageBlue(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
|
SDL_Surface* source = filtered[2];
|
|
if (source != NULL) {
|
|
*fmt = pixfmt_sdl_to_mgba(source->format);
|
|
*stride = source->pitch / (BYTES_PER_PIXEL / SDL_BYTESPERPIXEL(source->format));
|
|
*buf = source->pixels;
|
|
}
|
|
}
|
|
|
|
struct mImageSource gb_img_src[3];
|
|
|
|
bool SelectCamera(struct AppState* st) {
|
|
int devcount = 0;
|
|
SDL_CameraID *devices = SDL_GetCameras(&devcount);
|
|
|
|
SDL_Renderer* renderer = st->renderer;
|
|
|
|
if (st->cam_idx >= devcount) {
|
|
st->cam_idx = 0;
|
|
} else if (st->cam_idx < 0) {
|
|
st->cam_idx = devcount - 1;
|
|
}
|
|
|
|
if (!devices) {
|
|
SDL_Log("SDL_GetCameras failed: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
if (devcount == 0) {
|
|
SDL_free(devices);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
|
SDL_RenderClear(renderer);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
SDL_RenderDebugText(renderer, 4, 4, "connect a camera");
|
|
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
|
|
const SDL_CameraID device = devices[st->cam_idx];
|
|
const char *name = SDL_GetCameraName(device);
|
|
|
|
const SDL_CameraPosition position = SDL_GetCameraPosition(device);
|
|
const char *posstr = "";
|
|
|
|
char snprintfbuf[20];
|
|
|
|
int formats_len = 0;
|
|
|
|
SDL_CameraSpec** formats = SDL_GetCameraSupportedFormats(device, &formats_len);
|
|
if (st->spec_idx >= formats_len) {
|
|
st->spec_idx = 0;
|
|
} else if (st->spec_idx < 0) {
|
|
st->spec_idx = formats_len - 1;
|
|
}
|
|
SDL_CameraSpec* format = formats[st->spec_idx];
|
|
const char* pixfmt_name = SDL_GetPixelFormatName(format->format);
|
|
if (strncmp("SDL_PIXELFORMAT_", pixfmt_name, 16) == 0) {
|
|
pixfmt_name += 16;
|
|
}
|
|
|
|
if (position == SDL_CAMERA_POSITION_FRONT_FACING) {
|
|
front_camera = device;
|
|
posstr = "[frontfacing]";
|
|
} else if (position == SDL_CAMERA_POSITION_BACK_FACING) {
|
|
back_camera = device;
|
|
posstr = "[backfacing]";
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
|
SDL_RenderClear(renderer);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
SDL_RenderDebugText(renderer, 4, 4, "camera: (up/down)");
|
|
snprintf(snprintfbuf, sizeof(snprintfbuf), "%d. %s", st->cam_idx, posstr);
|
|
SDL_RenderDebugText(renderer, 8, 16, snprintfbuf);
|
|
SDL_RenderDebugText(renderer, 4, 28, name);
|
|
SDL_free((void*)name);
|
|
|
|
SDL_RenderDebugText(renderer, 4, 52, "mode: (left/right)");
|
|
snprintf(
|
|
snprintfbuf,
|
|
sizeof(snprintfbuf),
|
|
"%d.",
|
|
st->spec_idx
|
|
);
|
|
SDL_RenderDebugText(renderer, 8, 64, snprintfbuf);
|
|
|
|
snprintf(
|
|
snprintfbuf,
|
|
sizeof(snprintfbuf),
|
|
"%dx%d",
|
|
format->width,
|
|
format->height
|
|
);
|
|
SDL_RenderDebugText(renderer, 4, 76, snprintfbuf);
|
|
|
|
snprintf(
|
|
snprintfbuf,
|
|
sizeof(snprintfbuf),
|
|
"%.1f Hz",
|
|
((float)format->framerate_numerator / (float)format->framerate_denominator)
|
|
);
|
|
SDL_RenderDebugText(renderer, 4, 88, snprintfbuf);
|
|
SDL_RenderDebugText(renderer, 4, 100, pixfmt_name);
|
|
|
|
SDL_RenderDebugText(renderer, 4, 124, "confirm: (enter)");
|
|
|
|
SDL_free(formats);
|
|
SDL_free(devices);
|
|
|
|
SDL_RenderPresent(renderer);
|
|
|
|
return true;
|
|
}
|
|
|
|
SDL_AppResult SDL_AppInit(void** appstate, int argc, char *argv[]) {
|
|
int i;
|
|
unsigned w, h;
|
|
|
|
struct mArguments args;
|
|
bool parsed = mArgumentsParse(&args, argc, argv, NULL, 0);
|
|
|
|
struct AppState* st = malloc(sizeof(struct AppState));
|
|
memset(st, 0, sizeof(struct AppState));
|
|
*appstate = st;
|
|
|
|
/* Enable standard application logging */
|
|
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
|
|
|
if (!parsed) {
|
|
SDL_Log("Couldn't parse arguments");
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
gb_img_src[0].requestImage = myRequestImageRed;
|
|
gb_img_src[0].startRequestImage = myStartRequestImageRed;
|
|
gb_img_src[0].stopRequestImage = myStopRequestImageRed;
|
|
gb_img_src[1].requestImage = myRequestImageGreen;
|
|
gb_img_src[1].startRequestImage = myStartRequestImageGreen;
|
|
gb_img_src[1].stopRequestImage = myStopRequestImageGreen;
|
|
gb_img_src[2].requestImage = myRequestImageBlue;
|
|
gb_img_src[2].startRequestImage = myStartRequestImageBlue;
|
|
gb_img_src[2].stopRequestImage = myStopRequestImageBlue;
|
|
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
struct mCore* core = mCoreFind(args.fname);
|
|
if (!core || !core->init(core)) {
|
|
SDL_Log("Couldn't initialize mgba core!");
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
if (!mCoreLoadFile(core, args.fname)) {
|
|
SDL_Log("Failed to load ROM");
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
mCoreConfigInit(&core->config, NULL);
|
|
/*mCoreConfigLoad(&core->config);*/
|
|
|
|
mArgumentsApply(&args, NULL, 0, &core->config);
|
|
mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect");
|
|
mCoreConfigSetDefaultValue(&core->config, "frameskip", "4");
|
|
mCoreConfigSetDefaultValue(&core->config, "sgb.borders", "0");
|
|
|
|
mCoreLoadConfig(core);
|
|
|
|
cores[i] = core;
|
|
}
|
|
|
|
/* without loss of generality */
|
|
cores[0]->desiredVideoDimensions(cores[0], &w, &h);
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA)) {
|
|
SDL_Log("Couldn't initialize SDL3: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
st->window = SDL_CreateWindow("cgbwebcam", w, h, SDL_WINDOW_RESIZABLE);
|
|
if (!st->window) {
|
|
SDL_Log("Couldn't create window: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
st->renderer = SDL_CreateRenderer(st->window, NULL);
|
|
if (!st->renderer) {
|
|
SDL_Log("Couldn't create renderer: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
|
|
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
int j;
|
|
struct mCore* core = cores[i];
|
|
SDL_Texture* texture;
|
|
SDL_Surface* screen = SDL_CreateSurface(w, h, SCREEN_FMT);
|
|
|
|
core->setVideoBuffer(core, screen->pixels, screen->pitch / BYTES_PER_PIXEL);
|
|
screens[i] = screen;
|
|
|
|
/* Create texture with appropriate format */
|
|
texture = SDL_CreateTextureFromSurface(st->renderer, screens[0]);
|
|
if (!texture) {
|
|
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
|
|
textures[i] = texture;
|
|
|
|
core->setPeripheral(core, mPERIPH_IMAGE_SOURCE, &gb_img_src[i]);
|
|
|
|
core->reset(core);
|
|
|
|
core->setKeys(core, 0);
|
|
for (j = 0; j < 600; j++) {
|
|
core->runFrame(core);
|
|
}
|
|
core->setKeys(core, 1); /* title screen */
|
|
core->runFrame(core);
|
|
core->setKeys(core, 0);
|
|
for (j = 0; j < 25; j++) {
|
|
core->runFrame(core);
|
|
}
|
|
core->setKeys(core, 1); /* select 'shoot' */
|
|
core->runFrame(core);
|
|
core->setKeys(core, 0);
|
|
for (j = 0; j < 75; j++) {
|
|
core->runFrame(core);
|
|
}
|
|
core->setKeys(core, 1); /* select 'shoot' again */
|
|
core->runFrame(core);
|
|
core->setKeys(core, 0);
|
|
for (j = 0; j < 100; j++) {
|
|
core->runFrame(core);
|
|
}
|
|
|
|
SDL_Log("prepared core %d", i);
|
|
}
|
|
|
|
SDL_SetTextureColorMod(textures[0], 255, 0, 0);
|
|
SDL_SetTextureColorMod(textures[1], 0, 255, 0);
|
|
SDL_SetTextureColorMod(textures[2], 0, 0, 255);
|
|
|
|
return SDL_APP_CONTINUE; /* start the main app loop. */
|
|
}
|
|
|
|
static SDL_AppResult FlipCamera(struct AppState* st) {
|
|
static Uint64 last_flip = 0;
|
|
if ((SDL_GetTicks() - last_flip) < 3000) { /* must wait at least 3 seconds between flips. */
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
if (st->camera) {
|
|
const SDL_CameraID current = SDL_GetCameraID(st->camera);
|
|
SDL_CameraID nextcam = 0;
|
|
if (current == front_camera) {
|
|
nextcam = back_camera;
|
|
} else if (current == back_camera) {
|
|
nextcam = front_camera;
|
|
}
|
|
|
|
if (nextcam) {
|
|
SDL_Log("Flip camera!");
|
|
|
|
SDL_CloseCamera(st->camera);
|
|
|
|
st->camera = SDL_OpenCamera(nextcam, NULL);
|
|
if (!st->camera) {
|
|
SDL_Log("Failed to open camera device: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
|
|
last_flip = SDL_GetTicks();
|
|
}
|
|
}
|
|
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
|
struct AppState* st = (struct AppState*)appstate;
|
|
|
|
if (st->camera == NULL) {
|
|
switch (event->type) {
|
|
case SDL_EVENT_QUIT:
|
|
SDL_Log("Quit!");
|
|
return false;
|
|
|
|
case SDL_EVENT_KEY_DOWN:
|
|
switch (event->key.key) {
|
|
case SDLK_UP: st->cam_idx -= 1; break;
|
|
case SDLK_DOWN: st->cam_idx += 1; break;
|
|
case SDLK_LEFT: st->spec_idx -= 1; break;
|
|
case SDLK_RIGHT: st->spec_idx += 1; break;
|
|
case SDLK_RETURN: {
|
|
SDL_CameraID *devices = SDL_GetCameras(NULL);
|
|
SDL_CameraID device = devices[st->cam_idx];
|
|
SDL_CameraSpec** formats = SDL_GetCameraSupportedFormats(device, NULL);
|
|
st->camera = SDL_OpenCamera(device, formats[st->spec_idx]);
|
|
SDL_free(devices);
|
|
SDL_free(formats);
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
switch (event->type) {
|
|
case SDL_EVENT_KEY_DOWN: {
|
|
const SDL_Keycode sym = event->key.key;
|
|
switch (sym) {
|
|
case SDLK_ESCAPE: case SDLK_AC_BACK: return SDL_APP_SUCCESS;
|
|
case SDLK_SPACE: FlipCamera(st); break;
|
|
case SDLK_UP: keys |= 0x40; break;
|
|
case SDLK_DOWN: keys |= 0x80; break;
|
|
case SDLK_LEFT: keys |= 0x20; break;
|
|
case SDLK_RIGHT: keys |= 0x10; break;
|
|
case SDLK_A: case SDLK_Z: keys |= 1; break;
|
|
case SDLK_B: case SDLK_X: keys |= 2; break;
|
|
case SDLK_RETURN: keys |= 8; break;
|
|
case SDLK_BACKSPACE: keys |= 4; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SDL_EVENT_KEY_UP: {
|
|
const SDL_Keycode sym = event->key.key;
|
|
switch (sym) {
|
|
case SDLK_UP: keys &= ~0x40; break;
|
|
case SDLK_DOWN: keys &= ~0x80; break;
|
|
case SDLK_LEFT: keys &= ~0x20; break;
|
|
case SDLK_RIGHT: keys &= ~0x10; break;
|
|
case SDLK_A: case SDLK_Z: keys &= ~1; break;
|
|
case SDLK_B: case SDLK_X: keys &= ~2; break;
|
|
case SDLK_RETURN: keys &= ~8; break;
|
|
case SDLK_BACKSPACE: keys &= ~4; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
/* !!! FIXME: only flip if clicked in the area of a "flip" icon. */
|
|
return FlipCamera(st);
|
|
|
|
case SDL_EVENT_QUIT:
|
|
SDL_Log("Quit!");
|
|
return SDL_APP_SUCCESS;
|
|
|
|
case SDL_EVENT_CAMERA_DEVICE_APPROVED: {
|
|
SDL_CameraSpec spec;
|
|
SDL_Log("Camera approved!");
|
|
if (!SDL_GetCameraFormat(st->camera, &spec)) {
|
|
SDL_Log("Couldn't get camera spec: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SDL_EVENT_CAMERA_DEVICE_DENIED:
|
|
SDL_Log("Camera denied!");
|
|
return SDL_APP_FAILURE;
|
|
|
|
/*
|
|
case SDL_EVENT_WINDOW_RESIZED: {
|
|
Sint32 winw = event->window.data1, winh = event->window.data2;
|
|
SDL_UpdateWindowSurface(window);
|
|
if (TODO: need to correct aspect ratio?) {
|
|
if (!SDL_SetWindowSize(window, winw/2, winh)) {
|
|
SDL_Log("Failed to resize window to %dx%d: %s", winw, winh, SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
} else {
|
|
SDL_Log("Resized window to %dx%d", winw, winh);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
*/
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
SDL_AppResult SDL_AppIterate(void* appstate) {
|
|
struct AppState* st = (struct AppState*)appstate;
|
|
|
|
if (st->camera == NULL) {
|
|
if (!SelectCamera(st)) {
|
|
SDL_Log("Failed to show camera device menu: %s", SDL_GetError());
|
|
return SDL_APP_FAILURE;
|
|
}
|
|
return SDL_APP_CONTINUE;
|
|
}
|
|
|
|
SDL_Renderer* renderer = st->renderer;
|
|
|
|
int i;
|
|
static int since_last_present = 0;
|
|
|
|
if (scaled != NULL) {
|
|
Uint64 timestampNS = 0;
|
|
SDL_Surface *frame = st->camera ? SDL_AcquireCameraFrame(st->camera, ×tampNS) : NULL;
|
|
|
|
if (frame) {
|
|
struct SDL_Rect srcrect, dstrect;
|
|
SDL_GetSurfaceClipRect(frame, &srcrect);
|
|
SDL_GetSurfaceClipRect(scaled, &dstrect);
|
|
int w = srcrect.h * dstrect.w / dstrect.h;
|
|
|
|
/*SDL_Log("new frame %d x %d %s", frame->w, frame->h, SDL_GetPixelFormatName(frame->format->format));*/
|
|
texture_updated = false;
|
|
|
|
if (w <= srcrect.w) {
|
|
srcrect.x = (srcrect.w - w) / 2;
|
|
srcrect.w = w;
|
|
} else {
|
|
int h = srcrect.w * dstrect.h / dstrect.w;
|
|
srcrect.y = (srcrect.h - h) / 2;
|
|
srcrect.h = h;
|
|
}
|
|
if (!SDL_BlitSurfaceScaled(frame, &srcrect, scaled, &dstrect, SDL_SCALEMODE_NEAREST)) {
|
|
SDL_Log("failed to scale: %s", SDL_GetError());
|
|
}
|
|
|
|
SDL_ReleaseCameraFrame(st->camera, frame);
|
|
|
|
SDL_SetSurfaceColorMod(scaled, 255, 0, 0);
|
|
SDL_BlitSurface(scaled, NULL, filtered[0], NULL);
|
|
SDL_SetSurfaceColorMod(scaled, 0, 255, 0);
|
|
SDL_BlitSurface(scaled, NULL, filtered[1], NULL);
|
|
SDL_SetSurfaceColorMod(scaled, 0, 0, 255);
|
|
SDL_BlitSurface(scaled, NULL, filtered[2], NULL);
|
|
}
|
|
}
|
|
|
|
if (since_last_present < FRAMESKIP_LIMIT) {
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
struct mCore* core = cores[i];
|
|
core->setKeys(core, keys);
|
|
core->runFrame(core);
|
|
}
|
|
/*hack*/
|
|
if (keys && since_last_present < FRAMESKIP_LIMIT - 8) {
|
|
since_last_present = FRAMESKIP_LIMIT - 8;
|
|
}
|
|
}
|
|
|
|
if (!texture_updated) {
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
SDL_RenderClear(renderer);
|
|
|
|
/* Update SDL_Texture with last video frame (only once per new frame) */
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
SDL_Surface* screen = screens[i];
|
|
SDL_Texture* texture = textures[i];
|
|
SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch);
|
|
texture_updated = true;
|
|
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
|
}
|
|
|
|
SDL_RenderPresent(renderer);
|
|
since_last_present = 0;
|
|
} else {
|
|
since_last_present++;
|
|
if (since_last_present >= FRAMESKIP_LIMIT) {
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
SDL_RenderClear(renderer);
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
SDL_Texture *texture = textures[i];
|
|
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
|
}
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
}
|
|
|
|
return SDL_APP_CONTINUE; /* keep iterating. */
|
|
}
|
|
|
|
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
|
|
struct AppState* st = (struct AppState*)appstate;
|
|
int i;
|
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
|
mCoreConfigDeinit(&cores[i]->config);
|
|
cores[i]->deinit(cores[i]);
|
|
SDL_DestroyTexture(textures[i]);
|
|
}
|
|
|
|
SDL_CloseCamera(st->camera);
|
|
SDL_DestroyRenderer(st->renderer);
|
|
SDL_DestroyWindow(st->window);
|
|
free(st);
|
|
}
|