#ifdef TCOD_SDL2
#include <sys.h>
#include <stdio.h>
#include <string.h>
#include <console.h>
#include <libtcod_int.h>
#include <libtcod_utility.h>
static SDL_Surface* scale_screen=NULL;
static bool clear_screen=false;
static TCOD_console_data_t *root_console_cache;
void TCOD_sys_set_clear_screen(void) {
clear_screen=true;
}
static void get_closest_mode(int *w, int *h) {
SDL_DisplayMode wantedmode, closestmode;
wantedmode.w = *w;
wantedmode.h = *h;
wantedmode.format = 0;
wantedmode.refresh_rate = 0;
wantedmode.driverdata = 0;
if (SDL_GetClosestDisplayMode(window?SDL_GetWindowDisplayIndex(window):0, &wantedmode, &closestmode) == &closestmode) {
*w=closestmode.w;
*h=closestmode.h;
}
}
static void actual_rendering(void) {
SDL_Rect srcRect, dstRect;
SDL_Texture *texture;
if (scale_data.min_scale_factor - 1e-3f > scale_factor) {
srcRect.x=0; srcRect.y=0; srcRect.w=scale_screen->w; srcRect.h=scale_screen->h;
if (TCOD_ctx.fullscreen) {
dstRect.x=TCOD_ctx.fullscreen_offsetx; dstRect.y=TCOD_ctx.fullscreen_offsety;
} else {
dstRect.x=0; dstRect.y=0;
}
dstRect.w=scale_screen->w; dstRect.h=scale_screen->h;
} else {
srcRect.x=scale_data.src_x0; srcRect.y=scale_data.src_y0; srcRect.w=scale_data.src_copy_width; srcRect.h=scale_data.src_copy_height;
dstRect.x=scale_data.dst_offset_x; dstRect.y=scale_data.dst_offset_y;
dstRect.w=scale_data.dst_display_width; dstRect.h=scale_data.dst_display_height;
}
if ( TCOD_ctx.sdl_cbk ) {
TCOD_ctx.sdl_cbk((void *)scale_screen);
}
texture = SDL_CreateTextureFromSurface(renderer, scale_screen);
SDL_RenderCopy(renderer, texture, &srcRect, &dstRect);
SDL_DestroyTexture(texture);
}
static TCOD_console_data_t *ensure_cache(TCOD_console_data_t* root) {
if (!root_console_cache ||
root_console_cache->w != root->w ||
root_console_cache->h != root->h) {
if (root_console_cache) { TCOD_console_delete(root_console_cache); }
root_console_cache = TCOD_console_new(root->w, root->h);
}
return root_console_cache;
}
static void render(TCOD_SDL_driver_t *sdl, void *vbitmap, TCOD_console_data_t *console) {
if ( TCOD_ctx.renderer == TCOD_RENDERER_SDL ) {
int console_width_p = console->w * TCOD_ctx.font_width;
int console_height_p = console->h * TCOD_ctx.font_height;
if (scale_screen == NULL) {
int bpp;
uint32_t rmask, gmask, bmask, amask;
if (SDL_PixelFormatEnumToMasks(SDL_GetWindowPixelFormat(window), &bpp, &rmask, &gmask, &bmask, &amask) == SDL_FALSE) {
TCOD_fatal("SDL : failed to create scaling surface : indeterminate window pixel format");
return;
}
scale_screen=SDL_CreateRGBSurface(SDL_SWSURFACE,console_width_p,console_height_p,bpp,rmask,gmask,bmask,amask);
if (scale_screen == NULL) {
TCOD_fatal("SDL : failed to create scaling surface");
return;
}
} else if (clear_screen) {
clear_screen=false;
SDL_FillRect(scale_screen,0,0);
TCOD_console_set_dirty(0, 0, console->w, console->h);
}
TCOD_sys_console_to_bitmap(scale_screen, console, ensure_cache(console));
if (scale_data.last_scale_factor != scale_factor || scale_data.last_scale_xc != sdl->scale_xc || scale_data.last_scale_yc != sdl->scale_yc ||
scale_data.last_fullscreen != TCOD_ctx.fullscreen || scale_data.force_recalc) {
scale_data.last_scale_factor = scale_factor;
scale_data.last_scale_xc = sdl->scale_xc;
scale_data.last_scale_yc = sdl->scale_yc;
scale_data.last_fullscreen = TCOD_ctx.fullscreen;
scale_data.force_recalc = 0;
if (scale_data.last_fullscreen) {
scale_data.surface_width = TCOD_ctx.actual_fullscreen_width;
scale_data.surface_height = TCOD_ctx.actual_fullscreen_height;
} else {
scale_data.surface_width = console_width_p;
scale_data.surface_height = console_height_p;
}
scale_data.min_scale_factor = MAX((float)console_width_p/scale_data.surface_width, (float)console_height_p/scale_data.surface_height);
if (scale_data.min_scale_factor > 1.0f)
scale_data.min_scale_factor = 1.0f;
scale_data.dst_height_width_ratio = (float)scale_data.surface_height/scale_data.surface_width;
scale_data.src_proportionate_width = (int)(console_width_p / scale_factor);
scale_data.src_proportionate_height = (int)((console_width_p * scale_data.dst_height_width_ratio) / scale_factor);
scale_data.src_x0 = (int)((sdl->scale_xc * console_width_p) - (0.5f * scale_data.src_proportionate_width));
if (scale_data.src_x0 + scale_data.src_proportionate_width > console_width_p)
scale_data.src_x0 = console_width_p - scale_data.src_proportionate_width;
if (scale_data.src_x0 < 0)
scale_data.src_x0 = 0;
scale_data.src_copy_width = scale_data.src_proportionate_width;
if (scale_data.src_x0 + scale_data.src_copy_width > console_width_p)
scale_data.src_copy_width = console_width_p - scale_data.src_x0;
scale_data.src_y0 = (int)((sdl->scale_yc * console_height_p) - (0.5f * scale_data.src_proportionate_height));
if (scale_data.src_y0 + scale_data.src_proportionate_height > console_height_p)
scale_data.src_y0 = console_height_p - scale_data.src_proportionate_height;
if (scale_data.src_y0 < 0)
scale_data.src_y0 = 0;
scale_data.src_copy_height = scale_data.src_proportionate_height;
if (scale_data.src_y0 + scale_data.src_copy_height > console_height_p)
scale_data.src_copy_height = console_height_p - scale_data.src_y0;
scale_data.dst_display_width = (scale_data.src_copy_width * scale_data.surface_width) / scale_data.src_proportionate_width;
scale_data.dst_display_height = (scale_data.src_copy_height * scale_data.surface_height) / scale_data.src_proportionate_height;
scale_data.dst_offset_x = (scale_data.surface_width - scale_data.dst_display_width)/2;
scale_data.dst_offset_y = (scale_data.surface_height - scale_data.dst_display_height)/2;
}
SDL_RenderClear(renderer);
actual_rendering();
SDL_RenderPresent(renderer);
}
#ifndef NO_OPENGL
else {
TCOD_opengl_render(oldFade, NULL, console, ensure_cache(console));
TCOD_opengl_swap();
}
#endif
oldFade=(int)TCOD_console_get_fade();
}
static TCOD_console_data_t *get_root_console_cache(void){
return root_console_cache;
}
static SDL_Surface *create_surface(int width, int height, bool with_alpha) {
uint32_t rmask,gmask,bmask,amask;
SDL_Surface *bitmap;
int flags=SDL_SWSURFACE;
if ( with_alpha ) {
if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
rmask=0x000000FF;
gmask=0x0000FF00;
bmask=0x00FF0000;
amask=0xFF000000;
} else {
rmask=0xFF000000;
gmask=0x00FF0000;
bmask=0x0000FF00;
amask=0x000000FF;
}
} else {
if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
rmask=0x0000FF;
gmask=0x00FF00;
bmask=0xFF0000;
} else {
rmask=0xFF0000;
gmask=0x00FF00;
bmask=0x0000FF;
}
amask=0;
}
bitmap=SDL_CreateRGBSurface(flags,width,height,
with_alpha ? 32:24,
rmask,gmask,bmask,amask);
if ( with_alpha ) {
SDL_SetSurfaceAlphaMod(bitmap, 255);
}
return (void *)bitmap;
}
static void create_window(int w, int h, bool fullscreen) {
uint32_t winflags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
#if defined(TCOD_ANDROID)
TCOD_ctx.fullscreen = fullscreen = true;
#endif
if ( fullscreen ) {
find_resolution();
#ifndef NO_OPENGL
if (TCOD_ctx.renderer != TCOD_RENDERER_SDL ) {
TCOD_opengl_init_attributes();
winflags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL;
# if defined(TCOD_ANDROID) && defined(FUTURE_SUPPORT)
winflags |= SDL_WINDOW_RESIZABLE;
# endif
window = SDL_CreateWindow(TCOD_ctx.window_title,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,TCOD_ctx.actual_fullscreen_width,TCOD_ctx.actual_fullscreen_height,winflags);
if ( window && TCOD_opengl_init_state(w, h, charmap) && TCOD_opengl_init_shaders() ) {
TCOD_LOG(("Using %s renderer...\n",TCOD_ctx.renderer == TCOD_RENDERER_GLSL ? "GLSL" : "OPENGL"));
} else {
TCOD_LOG(("Fallback to SDL renderer...\n"));
TCOD_ctx.renderer = TCOD_RENDERER_SDL;
}
}
#endif
if (TCOD_ctx.renderer == TCOD_RENDERER_SDL ) {
winflags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
# if defined(TCOD_ANDROID) && defined(FUTURE_SUPPORT)
winflags |= SDL_WINDOW_RESIZABLE;
# endif
window = SDL_CreateWindow(TCOD_ctx.window_title,SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, TCOD_ctx.actual_fullscreen_width,TCOD_ctx.actual_fullscreen_height,winflags);
if ( window == NULL ) TCOD_fatal_nopar("SDL : cannot set fullscreen video mode");
}
SDL_ShowCursor(0);
SDL_GetWindowSize(window,&TCOD_ctx.actual_fullscreen_width,&TCOD_ctx.actual_fullscreen_height);
TCOD_sys_init_screen_offset();
} else {
#ifndef NO_OPENGL
if (TCOD_ctx.renderer != TCOD_RENDERER_SDL ) {
TCOD_opengl_init_attributes();
winflags |= SDL_WINDOW_OPENGL;
window = SDL_CreateWindow(TCOD_ctx.window_title,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,w*TCOD_ctx.font_width,h*TCOD_ctx.font_height,winflags);
if ( window && TCOD_opengl_init_state(w, h, charmap) && TCOD_opengl_init_shaders() ) {
TCOD_LOG(("Using %s renderer...\n",TCOD_ctx.renderer == TCOD_RENDERER_GLSL ? "GLSL" : "OPENGL"));
} else {
TCOD_LOG(("Fallback to SDL renderer...\n"));
TCOD_ctx.renderer = TCOD_RENDERER_SDL;
}
}
#endif
if (TCOD_ctx.renderer == TCOD_RENDERER_SDL ) {
window = SDL_CreateWindow(TCOD_ctx.window_title,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,w*TCOD_ctx.font_width,h*TCOD_ctx.font_height,winflags);
SDL_PumpEvents();
SDL_SetWindowSize(window,w*TCOD_ctx.font_width,h*TCOD_ctx.font_height);
TCOD_LOG(("Using SDL renderer...\n"));
}
if ( window == NULL ) TCOD_fatal_nopar("SDL : cannot create window");
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if ( renderer == NULL ) TCOD_fatal_nopar("SDL : cannot create renderer");
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
static void destroy_window(void) {
#ifndef NO_OPENGL
if (TCOD_ctx.renderer == TCOD_RENDERER_OPENGL || TCOD_ctx.renderer == TCOD_RENDERER_GLSL) {
TCOD_opengl_uninit_state();
}
#endif
if (scale_screen) {
SDL_FreeSurface(scale_screen);
scale_screen = NULL;
}
if (renderer) {
SDL_DestroyRenderer(renderer);
renderer = NULL;
}
if (window) {
SDL_DestroyWindow(window);
window = NULL;
}
}
static void set_fullscreen(bool fullscreen) {
bool mouseOn=SDL_ShowCursor(-1);
if ( fullscreen ) {
find_resolution();
SDL_SetWindowFullscreen(window, fullscreen);
SDL_ShowCursor(mouseOn ? 1:0);
SDL_GetWindowSize(window,&TCOD_ctx.actual_fullscreen_width,&TCOD_ctx.actual_fullscreen_height);
TCOD_sys_init_screen_offset();
} else {
SDL_SetWindowFullscreen(window, fullscreen);
SDL_ShowCursor(mouseOn ? 1:0);
TCOD_ctx.fullscreen_offsetx=0;
TCOD_ctx.fullscreen_offsety=0;
}
TCOD_ctx.fullscreen=fullscreen;
oldFade=-1;
}
static void set_window_title(const char *title) {
SDL_SetWindowTitle(window, title);
}
static void save_screenshot(const char *filename) {
if ( TCOD_ctx.renderer == TCOD_RENDERER_SDL ) {
SDL_Rect rect;
uint32_t format;
SDL_Texture *texture;
SDL_RenderGetViewport(renderer, &rect);
format = SDL_GetWindowPixelFormat(window);
texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, rect.w, rect.h);
if (0 != texture) {
if (SDL_SetRenderTarget(renderer, texture)) {
void *pixels;
int pitch, access;
actual_rendering();
SDL_SetRenderTarget(renderer, NULL);
rect.x = rect.y = rect.w = rect.h = 0;
if (-1 != SDL_QueryTexture(texture, &format, &access, &rect.w, &rect.h) &&
-1 != SDL_LockTexture(texture, NULL, &pixels, &pitch)) {
int depth;
uint32_t rmask, gmask, bmask, amask;
if (SDL_TRUE == SDL_PixelFormatEnumToMasks(format, &depth, &rmask, &gmask, &bmask, &amask)) {
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels, rect.w, rect.h, depth, pitch, rmask, gmask, bmask, amask);
TCOD_sys_save_bitmap((void *)surface,filename);
SDL_FreeSurface(surface);
} else
TCOD_LOG(("TCOD_sys_save_screenshot - failed call to SDL_PixelFormatEnumToMasks"));
SDL_UnlockTexture(texture);
} else
TCOD_LOG(("TCOD_sys_save_screenshot - failed call to SDL_QueryTexture or SDL_LockTexture"));
} else
TCOD_LOG(("TCOD_sys_save_screenshot - failed call to SDL_SetRenderTarget"));
SDL_DestroyTexture(texture);
} else
TCOD_LOG(("TCOD_sys_save_screenshot - failed call to SDL_CreateTexture"));
#ifndef NO_OPENGL
} else {
SDL_Surface *screenshot=(SDL_Surface *)TCOD_opengl_get_screen();
TCOD_sys_save_bitmap((void *)screenshot,filename);
SDL_FreeSurface(screenshot);
#endif
}
}
static void get_current_resolution(int *w, int *h) {
int displayidx;
SDL_Rect rect = { 0, 0, 0, 0 };
if (window) {
TCOD_IFNOT(window) return;
displayidx = SDL_GetWindowDisplayIndex(window);
TCOD_IFNOT(displayidx >= 0) return;
} else {
TCOD_IFNOT(SDL_GetNumVideoDisplays() > 0) return;
displayidx = 0;
}
TCOD_IFNOT(SDL_GetDisplayBounds(displayidx, &rect) == 0) return;
*w=rect.w;
*h=rect.h;
}
static void set_mouse_position(int x, int y) {
SDL_WarpMouseInWindow(window, (uint16_t)x,(uint16_t)y);
}
static char *get_clipboard_text(void) {
#ifdef TCOD_LINUX
if (!window)
return "";
#endif
if (last_clipboard_text) {
SDL_free(last_clipboard_text);
last_clipboard_text = NULL;
}
last_clipboard_text = SDL_GetClipboardText();
return last_clipboard_text;
}
static bool set_clipboard_text(const char *text) {
#ifdef TCOD_LINUX
if (!window)
return false;
#endif
return SDL_SetClipboardText(text) == 0;
}
static bool file_read(const char *filename, unsigned char **buf, size_t *size) {
int64_t filesize;
SDL_RWops *rwops= SDL_RWFromFile(filename,"rb");
if (!rwops) return false;
SDL_RWseek(rwops,0,RW_SEEK_END);
filesize=SDL_RWtell(rwops);
SDL_RWseek(rwops,0,RW_SEEK_SET);
*buf = (unsigned char *)malloc(sizeof(unsigned char)*filesize);
if (SDL_RWread(rwops,*buf,sizeof(unsigned char),filesize) != filesize) {
SDL_RWclose(rwops);
free(*buf);
return false;
}
SDL_RWclose(rwops);
*size=filesize;
return true;
}
static bool file_exists(const char * filename) {
SDL_RWops *rwops;
rwops = SDL_RWFromFile(filename,"rb");
if (rwops) {
SDL_RWclose(rwops);
return true;
}
return false;
}
static bool file_write(const char *filename, unsigned char *buf, uint32_t size) {
SDL_RWops *rwops= SDL_RWFromFile(filename,"wb");
if (!rwops) return false;
SDL_RWwrite(rwops,buf,sizeof(unsigned char),size);
SDL_RWclose(rwops);
return true;
}
static void shutdown(void) {
if (last_clipboard_text) {
SDL_free(last_clipboard_text);
last_clipboard_text = NULL;
}
if (root_console_cache) {
TCOD_console_delete(root_console_cache);
root_console_cache = NULL;
}
}
TCOD_SDL_driver_t *SDL_implementation_factory(void) {
TCOD_SDL_driver_t *ret=(TCOD_SDL_driver_t *)calloc(1,sizeof(TCOD_SDL_driver_t));
ret->scale_xc = 0.5f;
ret->scale_yc = 0.5f;
ret->get_closest_mode = get_closest_mode;
ret->render = render;
ret->create_surface = create_surface;
ret->create_window = create_window;
ret->destroy_window = destroy_window;
ret->set_fullscreen = set_fullscreen;
ret->set_window_title = set_window_title;
ret->save_screenshot = save_screenshot;
ret->get_current_resolution = get_current_resolution;
ret->set_mouse_position = set_mouse_position;
ret->get_clipboard_text = get_clipboard_text;
ret->set_clipboard_text = set_clipboard_text;
ret->file_read = file_read;
ret->file_exists = file_exists;
ret->file_write = file_write;
ret->shutdown = shutdown;
ret->get_root_console_cache = get_root_console_cache;
return ret;
}
#endif