#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_N3DS
#include "../SDL_sysvideo.h"
#include "SDL_n3dsevents_c.h"
#include "SDL_n3dsframebuffer_c.h"
#include "SDL_n3dsswkb.h"
#include "SDL_n3dstouch.h"
#include "SDL_n3dsvideo.h"
#define N3DSVID_DRIVER_NAME "n3ds"
static SDL_DisplayID AddN3DSDisplay(gfxScreen_t screen);
static bool N3DS_VideoInit(SDL_VideoDevice *_this);
static void N3DS_VideoQuit(SDL_VideoDevice *_this);
static bool N3DS_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
static bool N3DS_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
static bool N3DS_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
static bool N3DS_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
static void N3DS_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
struct SDL_DisplayData
{
gfxScreen_t screen;
};
struct SDL_DisplayModeData
{
GSPGPU_FramebufferFormat fmt;
};
static const struct
{
SDL_PixelFormat pixfmt;
GSPGPU_FramebufferFormat gspfmt;
} format_map[] = {
{ SDL_PIXELFORMAT_RGBA8888, GSP_RGBA8_OES },
{ SDL_PIXELFORMAT_BGR24, GSP_BGR8_OES },
{ SDL_PIXELFORMAT_RGB565, GSP_RGB565_OES },
{ SDL_PIXELFORMAT_RGBA5551, GSP_RGB5_A1_OES },
{ SDL_PIXELFORMAT_RGBA4444, GSP_RGBA4_OES }
};
static void N3DS_DeleteDevice(SDL_VideoDevice *device)
{
SDL_free(device->internal);
SDL_free(device);
}
static SDL_VideoDevice *N3DS_CreateDevice(void)
{
SDL_VideoDevice *device;
SDL_VideoData *phdata;
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
return NULL;
}
phdata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
if (!phdata) {
SDL_free(device);
return NULL;
}
device->internal = phdata;
device->VideoInit = N3DS_VideoInit;
device->VideoQuit = N3DS_VideoQuit;
device->GetDisplayModes = N3DS_GetDisplayModes;
device->SetDisplayMode = N3DS_SetDisplayMode;
device->GetDisplayBounds = N3DS_GetDisplayBounds;
device->CreateSDLWindow = N3DS_CreateWindow;
device->DestroyWindow = N3DS_DestroyWindow;
device->HasScreenKeyboardSupport = N3DS_HasScreenKeyboardSupport;
device->StartTextInput = N3DS_StartTextInput;
device->StopTextInput = N3DS_StopTextInput;
device->PumpEvents = N3DS_PumpEvents;
device->CreateWindowFramebuffer = SDL_N3DS_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = SDL_N3DS_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = SDL_N3DS_DestroyWindowFramebuffer;
device->free = N3DS_DeleteDevice;
device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY;
return device;
}
VideoBootStrap N3DS_bootstrap = { N3DSVID_DRIVER_NAME, "N3DS Video Driver", N3DS_CreateDevice, NULL, false };
static bool N3DS_VideoInit(SDL_VideoDevice *_this)
{
SDL_VideoData *internal = (SDL_VideoData *)_this->internal;
gfxInit(GSP_RGBA8_OES, GSP_RGBA8_OES, false);
hidInit();
internal->top_display = AddN3DSDisplay(GFX_TOP);
internal->touch_display = AddN3DSDisplay(GFX_BOTTOM);
N3DS_InitTouch();
N3DS_SwkbInit();
return true;
}
static SDL_DisplayID AddN3DSDisplay(gfxScreen_t screen)
{
SDL_DisplayMode mode;
SDL_DisplayModeData *modedata;
SDL_VideoDisplay display;
SDL_DisplayData *display_driver_data = SDL_calloc(1, sizeof(SDL_DisplayData));
if (!display_driver_data) {
return 0;
}
SDL_zero(mode);
SDL_zero(display);
display_driver_data->screen = screen;
modedata = SDL_malloc(sizeof(SDL_DisplayModeData));
if (!modedata) {
return 0;
}
mode.w = (screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM;
mode.h = GSP_SCREEN_WIDTH;
mode.refresh_rate = 60.0f;
mode.format = SDL_PIXELFORMAT_RGBA8888;
mode.internal = modedata;
modedata->fmt = GSP_RGBA8_OES;
display.name = (screen == GFX_TOP) ? "N3DS top screen" : "N3DS bottom screen";
display.desktop_mode = mode;
display.internal = display_driver_data;
return SDL_AddVideoDisplay(&display, false);
}
static void N3DS_VideoQuit(SDL_VideoDevice *_this)
{
N3DS_SwkbQuit();
N3DS_QuitTouch();
hidExit();
gfxExit();
}
static bool N3DS_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
{
SDL_DisplayData *displaydata = display->internal;
SDL_DisplayModeData *modedata;
SDL_DisplayMode mode;
int i;
for (i = 0; i < SDL_arraysize(format_map); i++) {
modedata = SDL_malloc(sizeof(SDL_DisplayModeData));
if (!modedata)
continue;
SDL_zero(mode);
mode.w = (displaydata->screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM;
mode.h = GSP_SCREEN_WIDTH;
mode.refresh_rate = 60.0f;
mode.format = format_map[i].pixfmt;
mode.internal = modedata;
modedata->fmt = format_map[i].gspfmt;
if (!SDL_AddFullscreenDisplayMode(display, &mode)) {
SDL_free(modedata);
}
}
return true;
}
static bool N3DS_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
{
SDL_DisplayData *displaydata = display->internal;
SDL_DisplayModeData *modedata = mode->internal;
gfxSetScreenFormat(displaydata->screen, modedata->fmt);
return true;
}
static bool N3DS_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
SDL_DisplayData *driver_data = display->internal;
if (!driver_data) {
return false;
}
rect->x = 0;
rect->y = (driver_data->screen == GFX_TOP) ? 0 : GSP_SCREEN_WIDTH;
rect->w = display->current_mode->w;
rect->h = display->current_mode->h;
return true;
}
static bool N3DS_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
{
SDL_DisplayData *display_data;
SDL_WindowData *window_data = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
if (!window_data) {
return false;
}
display_data = SDL_GetDisplayDriverDataForWindow(window);
window_data->screen = display_data->screen;
window->internal = window_data;
SDL_SetKeyboardFocus(window);
return true;
}
static void N3DS_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
if (!window) {
return;
}
SDL_free(window->internal);
}
#endif