|
|
|
@ -0,0 +1,369 @@
|
|
|
|
|
#include "SDL_error.h"
|
|
|
|
|
#include "SDL_stdinc.h"
|
|
|
|
|
#include "SDL_surface.h"
|
|
|
|
|
#include "mgba/core/config.h"
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#define SDL_MAIN_USE_CALLBACKS 1
|
|
|
|
|
#include "SDL3/SDL_init.h"
|
|
|
|
|
#include "SDL3/SDL_log.h"
|
|
|
|
|
#include "SDL3/SDL_main.h"
|
|
|
|
|
#include "SDL3/SDL_pixels.h"
|
|
|
|
|
#include "SDL3/SDL_render.h"
|
|
|
|
|
#include "SDL3/SDL_timer.h"
|
|
|
|
|
#include "SDL3/SDL_video.h"
|
|
|
|
|
|
|
|
|
|
#include "mgba/core/core.h"
|
|
|
|
|
#include "mgba/core/interface.h"
|
|
|
|
|
#include "mgba/feature/commandline.h"
|
|
|
|
|
#include "mgba-util/image.h"
|
|
|
|
|
|
|
|
|
|
static SDL_Window* window = NULL;
|
|
|
|
|
static SDL_Renderer* renderer = NULL;
|
|
|
|
|
static SDL_Camera* camera = NULL;
|
|
|
|
|
static SDL_CameraSpec spec;
|
|
|
|
|
static SDL_Texture* texture = NULL;
|
|
|
|
|
static SDL_bool texture_updated = SDL_FALSE;
|
|
|
|
|
static SDL_Surface* frame_current = NULL;
|
|
|
|
|
static SDL_CameraDeviceID front_camera = 0;
|
|
|
|
|
static SDL_CameraDeviceID back_camera = 0;
|
|
|
|
|
static SDL_Surface* scaled = NULL;
|
|
|
|
|
static SDL_Surface* screen = NULL;
|
|
|
|
|
|
|
|
|
|
static struct mCore* core = NULL;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
// does this even make sense?
|
|
|
|
|
case SDL_PIXELFORMAT_INDEX8: return mCOLOR_PAL8;
|
|
|
|
|
// others don't quite match between mgba and SDL
|
|
|
|
|
default: return mCOLOR_ANY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void myStartRequestImage(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
|
|
|
|
SDL_DestroySurface(scaled);
|
|
|
|
|
scaled = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
|
|
|
|
|
|
|
|
SDL_Log("scaled: %d x %d", w, h);
|
|
|
|
|
|
|
|
|
|
// SDL_DestroyTexture(texture);
|
|
|
|
|
// texture = SDL_CreateTextureFromSurface(renderer, scaled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void myStopRequestImage(struct mImageSource* self) {
|
|
|
|
|
SDL_DestroySurface(scaled);
|
|
|
|
|
scaled = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void myRequestImage(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
|
|
|
|
if (scaled != NULL && frame_current != NULL) {
|
|
|
|
|
struct SDL_Rect srcrect = frame_current->clip_rect;
|
|
|
|
|
int w = srcrect.h * scaled->clip_rect.w / scaled->clip_rect.h;
|
|
|
|
|
if (w >= srcrect.w) {
|
|
|
|
|
srcrect.x = (srcrect.w - w) / 2;
|
|
|
|
|
srcrect.w = w;
|
|
|
|
|
} else {
|
|
|
|
|
int h = srcrect.w * scaled->clip_rect.h / scaled->clip_rect.w;
|
|
|
|
|
srcrect.y = (srcrect.h - h) / 2;
|
|
|
|
|
srcrect.h = h;
|
|
|
|
|
}
|
|
|
|
|
int res = SDL_BlitSurfaceScaled(frame_current, &srcrect, scaled, &scaled->clip_rect, SDL_SCALEMODE_NEAREST);
|
|
|
|
|
if (res != 0) {
|
|
|
|
|
SDL_Log("failed to scale: %s", SDL_GetError());
|
|
|
|
|
}
|
|
|
|
|
*fmt = pixfmt_sdl_to_mgba(scaled->format->format);
|
|
|
|
|
*stride = scaled->pitch / (BYTES_PER_PIXEL / scaled->format->bytes_per_pixel);
|
|
|
|
|
*buf = scaled->pixels;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct mImageSource gb_img_src = {
|
|
|
|
|
.requestImage = myRequestImage,
|
|
|
|
|
.startRequestImage = myStartRequestImage,
|
|
|
|
|
.stopRequestImage = myStopRequestImage,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int SDL_AppInit(int argc, char *argv[]) {
|
|
|
|
|
int devcount = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
struct mArguments args = {};
|
|
|
|
|
bool parsed = mArgumentsParse(&args, argc, argv, NULL, 0);
|
|
|
|
|
|
|
|
|
|
/* Enable standard application logging */
|
|
|
|
|
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
|
|
|
|
|
|
|
|
|
if (!parsed) {
|
|
|
|
|
SDL_Log("Couldn't parse arguments");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
core = mCoreFind(args.fname);
|
|
|
|
|
if (!core || !core->init(core)) {
|
|
|
|
|
SDL_Log("Couldn't initialize mgba core!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!mCoreLoadFile(core, args.fname)) {
|
|
|
|
|
SDL_Log("Failed to load ROM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mCoreConfigInit(&core->config, NULL);
|
|
|
|
|
//mCoreConfigLoad(&core->config);
|
|
|
|
|
|
|
|
|
|
mArgumentsApply(&args, NULL, 0, &core->config);
|
|
|
|
|
mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect");
|
|
|
|
|
mCoreConfigSetDefaultValue(&core->config, "sgb.borders", "0");
|
|
|
|
|
|
|
|
|
|
mCoreLoadConfig(core);
|
|
|
|
|
|
|
|
|
|
unsigned w, h;
|
|
|
|
|
core->currentVideoSize(core, &w, &h);
|
|
|
|
|
|
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) != 0) {
|
|
|
|
|
SDL_Log("Couldn't initialize SDL3: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window = SDL_CreateWindow("cgbwebcam", w, h, 0);
|
|
|
|
|
if (!window) {
|
|
|
|
|
SDL_Log("Couldn't create window: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderer = SDL_CreateRenderer(window, NULL, 0);
|
|
|
|
|
if (!renderer) {
|
|
|
|
|
SDL_Log("Couldn't create renderer: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
|
|
|
|
|
|
|
|
|
|
SDL_CameraDeviceID *devices = SDL_GetCameraDevices(&devcount);
|
|
|
|
|
if (!devices) {
|
|
|
|
|
SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Log("Saw %d camera devices.", devcount);
|
|
|
|
|
for (i = 0; i < devcount; i++) {
|
|
|
|
|
const SDL_CameraDeviceID device = devices[i];
|
|
|
|
|
char *name = SDL_GetCameraDeviceName(device);
|
|
|
|
|
const SDL_CameraPosition position = SDL_GetCameraDevicePosition(device);
|
|
|
|
|
const char *posstr = "";
|
|
|
|
|
if (position == SDL_CAMERA_POSITION_FRONT_FACING) {
|
|
|
|
|
front_camera = device;
|
|
|
|
|
posstr = "[front-facing] ";
|
|
|
|
|
} else if (position == SDL_CAMERA_POSITION_BACK_FACING) {
|
|
|
|
|
back_camera = device;
|
|
|
|
|
posstr = "[back-facing] ";
|
|
|
|
|
}
|
|
|
|
|
SDL_Log(" - Camera #%d: %s %s", i, posstr, name);
|
|
|
|
|
SDL_free(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SDL_CameraDeviceID devid = front_camera ? front_camera : devices[0]; /* no front-facing? just take the first one. */
|
|
|
|
|
SDL_free(devices);
|
|
|
|
|
|
|
|
|
|
if (!devid) {
|
|
|
|
|
SDL_Log("No cameras available?");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_CameraSpec spec = {
|
|
|
|
|
.format = SDL_PIXELFORMAT_XBGR1555,
|
|
|
|
|
.width = 320,
|
|
|
|
|
.height = 240,
|
|
|
|
|
.interval_numerator = 1,
|
|
|
|
|
.interval_denominator = 30,
|
|
|
|
|
};
|
|
|
|
|
camera = SDL_OpenCameraDevice(devid, &spec);
|
|
|
|
|
if (!camera) {
|
|
|
|
|
SDL_Log("Failed to open camera device: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if BYTES_PER_PIXEL == 4
|
|
|
|
|
screen = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR8888);
|
|
|
|
|
#elif BYTES_PER_PIXEL == 2
|
|
|
|
|
screen = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
|
|
|
|
#else
|
|
|
|
|
#error "unknown pixel format"
|
|
|
|
|
#endif
|
|
|
|
|
core->setVideoBuffer(core, screen->pixels, screen->pitch / BYTES_PER_PIXEL);
|
|
|
|
|
|
|
|
|
|
/* Create texture with appropriate format */
|
|
|
|
|
texture = SDL_CreateTextureFromSurface(renderer, screen);
|
|
|
|
|
if (!texture) {
|
|
|
|
|
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
core->setPeripheral(core, mPERIPH_IMAGE_SOURCE, (void*)&gb_img_src);
|
|
|
|
|
|
|
|
|
|
core->reset(core);
|
|
|
|
|
|
|
|
|
|
core->setKeys(core, 0);
|
|
|
|
|
for (i = 0; i < 600; i++) {
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
}
|
|
|
|
|
core->setKeys(core, 1); // title screen
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
core->setKeys(core, 0);
|
|
|
|
|
for (i = 0; i < 25; i++) {
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
}
|
|
|
|
|
core->setKeys(core, 1); // select 'shoot'
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
core->setKeys(core, 0);
|
|
|
|
|
for (i = 0; i < 75; i++) {
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
}
|
|
|
|
|
core->setKeys(core, 1); // select 'shoot' again
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
core->setKeys(core, 0);
|
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0; /* start the main app loop. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int FlipCamera(void) {
|
|
|
|
|
static Uint64 last_flip = 0;
|
|
|
|
|
if ((SDL_GetTicks() - last_flip) < 3000) { /* must wait at least 3 seconds between flips. */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (camera) {
|
|
|
|
|
const SDL_CameraDeviceID current = SDL_GetCameraInstanceID(camera);
|
|
|
|
|
SDL_CameraDeviceID nextcam = 0;
|
|
|
|
|
if (current == front_camera) {
|
|
|
|
|
nextcam = back_camera;
|
|
|
|
|
} else if (current == back_camera) {
|
|
|
|
|
nextcam = front_camera;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nextcam) {
|
|
|
|
|
SDL_Log("Flip camera!");
|
|
|
|
|
|
|
|
|
|
if (frame_current) {
|
|
|
|
|
SDL_ReleaseCameraFrame(camera, frame_current);
|
|
|
|
|
frame_current = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_CloseCamera(camera);
|
|
|
|
|
|
|
|
|
|
camera = SDL_OpenCameraDevice(nextcam, NULL);
|
|
|
|
|
if (!camera) {
|
|
|
|
|
SDL_Log("Failed to open camera device: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_flip = SDL_GetTicks();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SDL_AppEvent(const SDL_Event *event) {
|
|
|
|
|
switch (event->type) {
|
|
|
|
|
case SDL_EVENT_KEY_DOWN: {
|
|
|
|
|
const SDL_Keycode sym = event->key.keysym.sym;
|
|
|
|
|
if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) {
|
|
|
|
|
SDL_Log("Key : Escape!");
|
|
|
|
|
return 1;
|
|
|
|
|
} else if (sym == SDLK_SPACE) {
|
|
|
|
|
FlipCamera();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
|
|
|
/* !!! FIXME: only flip if clicked in the area of a "flip" icon. */
|
|
|
|
|
return FlipCamera();
|
|
|
|
|
|
|
|
|
|
case SDL_EVENT_QUIT:
|
|
|
|
|
SDL_Log("Quit!");
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
|
|
|
|
|
SDL_Log("Camera approved!");
|
|
|
|
|
if (SDL_GetCameraFormat(camera, &spec) < 0) {
|
|
|
|
|
SDL_Log("Couldn't get camera spec: %s", SDL_GetError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SDL_EVENT_CAMERA_DEVICE_DENIED:
|
|
|
|
|
SDL_Log("Camera denied!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SDL_AppIterate(void) {
|
|
|
|
|
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
|
|
|
|
SDL_RenderClear(renderer);
|
|
|
|
|
|
|
|
|
|
Uint64 timestampNS = 0;
|
|
|
|
|
SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL;
|
|
|
|
|
|
|
|
|
|
if (frame_next) {
|
|
|
|
|
if (frame_current) {
|
|
|
|
|
if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) {
|
|
|
|
|
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Log("new frame %d x %d %s", frame_next->w, frame_next->h, SDL_GetPixelFormatName(frame_next->format->format));
|
|
|
|
|
|
|
|
|
|
/* It's not needed to keep the frame once updated the texture is updated.
|
|
|
|
|
* But in case of 0-copy, it's needed to have the frame while using the texture.
|
|
|
|
|
*/
|
|
|
|
|
frame_current = frame_next;
|
|
|
|
|
texture_updated = SDL_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
core->runFrame(core);
|
|
|
|
|
/* Update SDL_Texture with last video frame (only once per new frame) */
|
|
|
|
|
if (frame_current && !texture_updated) {
|
|
|
|
|
// if (scaled) SDL_UpdateTexture(texture, NULL, scaled->pixels, scaled->pitch);
|
|
|
|
|
SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch);
|
|
|
|
|
texture_updated = SDL_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
|
|
|
|
SDL_RenderPresent(renderer);
|
|
|
|
|
|
|
|
|
|
return 0; /* keep iterating. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SDL_AppQuit(void) {
|
|
|
|
|
mCoreConfigDeinit(&core->config);
|
|
|
|
|
core->deinit(core);
|
|
|
|
|
|
|
|
|
|
SDL_ReleaseCameraFrame(camera, frame_current);
|
|
|
|
|
SDL_CloseCamera(camera);
|
|
|
|
|
SDL_DestroyTexture(texture);
|
|
|
|
|
SDL_DestroyRenderer(renderer);
|
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
|
}
|