mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
51d238875e
ever implemented for OpenGL2 driver, lots of code debt, best to instead just keep improving the overlay system instead which is already available for most video drivers
589 lines
15 KiB
C
589 lines
15 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2011-2017 - Higor Euripedes
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gfx/scaler/scaler.h>
|
|
#include <gfx/video_frame.h>
|
|
#include "../../verbosity.h"
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../../menu/menu_driver.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_X11
|
|
#include "../common/x11_common.h"
|
|
#endif
|
|
|
|
#include "SDL.h"
|
|
#include "SDL_syswm.h"
|
|
|
|
#include "../font_driver.h"
|
|
|
|
#include "../../configuration.h"
|
|
#include "../../retroarch.h"
|
|
|
|
typedef struct sdl_menu_frame
|
|
{
|
|
bool active;
|
|
struct scaler_ctx scaler;
|
|
SDL_Surface *frame;
|
|
} sdl_menu_frame_t;
|
|
|
|
typedef struct sdl_video
|
|
{
|
|
bool quitting;
|
|
uint8_t font_r;
|
|
uint8_t font_g;
|
|
uint8_t font_b;
|
|
|
|
struct scaler_ctx scaler;
|
|
sdl_menu_frame_t menu;
|
|
SDL_Surface *screen;
|
|
|
|
void *font;
|
|
const font_renderer_driver_t *font_driver;
|
|
} sdl_video_t;
|
|
|
|
static void sdl_gfx_free(void *data)
|
|
{
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
if (!vid)
|
|
return;
|
|
|
|
if (vid->menu.frame)
|
|
SDL_FreeSurface(vid->menu.frame);
|
|
|
|
if (vid->font)
|
|
vid->font_driver->free(vid->font);
|
|
|
|
scaler_ctx_gen_reset(&vid->scaler);
|
|
scaler_ctx_gen_reset(&vid->menu.scaler);
|
|
|
|
free(vid);
|
|
}
|
|
|
|
static void sdl_init_font(sdl_video_t *vid,
|
|
bool video_font_enable,
|
|
const char *path_font,
|
|
float video_font_size,
|
|
float msg_color_r,
|
|
float msg_color_g,
|
|
float msg_color_b
|
|
)
|
|
{
|
|
int r, g, b;
|
|
|
|
if (!video_font_enable)
|
|
return;
|
|
|
|
if (!font_renderer_create_default(
|
|
&vid->font_driver, &vid->font,
|
|
*path_font ? path_font : NULL,
|
|
video_font_size))
|
|
{
|
|
RARCH_LOG("[SDL]: Could not initialize fonts.\n");
|
|
return;
|
|
}
|
|
|
|
r = msg_color_r * 255;
|
|
g = msg_color_g * 255;
|
|
b = msg_color_b * 255;
|
|
|
|
r = (r < 0) ? 0 : (r > 255 ? 255 : r);
|
|
g = (g < 0) ? 0 : (g > 255 ? 255 : g);
|
|
b = (b < 0) ? 0 : (b > 255 ? 255 : b);
|
|
|
|
vid->font_r = r;
|
|
vid->font_g = g;
|
|
vid->font_b = b;
|
|
}
|
|
|
|
static void sdl_render_msg(
|
|
sdl_video_t *vid,
|
|
SDL_Surface *buffer,
|
|
const char *msg,
|
|
unsigned width,
|
|
unsigned height,
|
|
const SDL_PixelFormat *fmt,
|
|
float msg_pos_x,
|
|
float msg_pos_y
|
|
)
|
|
{
|
|
int x, y, msg_base_x, msg_base_y;
|
|
unsigned rshift, gshift, bshift;
|
|
const struct font_atlas *atlas = NULL;
|
|
|
|
if (!vid->font)
|
|
return;
|
|
|
|
atlas = vid->font_driver->get_atlas(vid->font);
|
|
|
|
msg_base_x = msg_pos_x * width;
|
|
msg_base_y = (1.0f - msg_pos_y) * height;
|
|
|
|
rshift = fmt->Rshift;
|
|
gshift = fmt->Gshift;
|
|
bshift = fmt->Bshift;
|
|
|
|
for (; *msg; msg++)
|
|
{
|
|
int glyph_width, glyph_height;
|
|
int base_x, base_y, max_width, max_height;
|
|
uint32_t *out = NULL;
|
|
const uint8_t *src = NULL;
|
|
const struct font_glyph *glyph = vid->font_driver->get_glyph(vid->font, (uint8_t)*msg);
|
|
if (!glyph)
|
|
continue;
|
|
|
|
glyph_width = glyph->width;
|
|
glyph_height = glyph->height;
|
|
|
|
base_x = msg_base_x + glyph->draw_offset_x;
|
|
base_y = msg_base_y + glyph->draw_offset_y;
|
|
src = atlas->buffer + glyph->atlas_offset_x
|
|
+ glyph->atlas_offset_y * atlas->width;
|
|
|
|
if (base_x < 0)
|
|
{
|
|
src -= base_x;
|
|
glyph_width += base_x;
|
|
base_x = 0;
|
|
}
|
|
|
|
if (base_y < 0)
|
|
{
|
|
src -= base_y * (int)atlas->width;
|
|
glyph_height += base_y;
|
|
base_y = 0;
|
|
}
|
|
|
|
max_width = width - base_x;
|
|
max_height = height - base_y;
|
|
|
|
if (max_width <= 0 || max_height <= 0)
|
|
continue;
|
|
|
|
if (glyph_width > max_width)
|
|
glyph_width = max_width;
|
|
if (glyph_height > max_height)
|
|
glyph_height = max_height;
|
|
|
|
out = (uint32_t*)buffer->pixels + base_y
|
|
* (buffer->pitch >> 2) + base_x;
|
|
|
|
for (y = 0; y < glyph_height; y++, src += atlas->width, out += buffer->pitch >> 2)
|
|
{
|
|
for (x = 0; x < glyph_width; x++)
|
|
{
|
|
unsigned blend = src[x];
|
|
unsigned out_pix = out[x];
|
|
unsigned r = (out_pix >> rshift) & 0xff;
|
|
unsigned g = (out_pix >> gshift) & 0xff;
|
|
unsigned b = (out_pix >> bshift) & 0xff;
|
|
|
|
unsigned out_r = (r * (256 - blend) + vid->font_r * blend) >> 8;
|
|
unsigned out_g = (g * (256 - blend) + vid->font_g * blend) >> 8;
|
|
unsigned out_b = (b * (256 - blend) + vid->font_b * blend) >> 8;
|
|
out[x] = (out_r << rshift) |
|
|
(out_g << gshift) |
|
|
(out_b << bshift);
|
|
}
|
|
}
|
|
|
|
msg_base_x += glyph->advance_x;
|
|
msg_base_y += glyph->advance_y;
|
|
}
|
|
}
|
|
|
|
static void sdl_gfx_set_handles(void)
|
|
{
|
|
/* SysWMinfo headers are broken on OSX. */
|
|
#if defined(_WIN32) || defined(HAVE_X11)
|
|
SDL_SysWMinfo info;
|
|
SDL_VERSION(&info.version);
|
|
|
|
if (SDL_GetWMInfo(&info) != 1)
|
|
return;
|
|
|
|
#if defined(_WIN32)
|
|
video_driver_display_type_set(RARCH_DISPLAY_WIN32);
|
|
video_driver_display_set(0);
|
|
video_driver_window_set((uintptr_t)info.window);
|
|
#elif defined(HAVE_X11)
|
|
video_driver_display_type_set(RARCH_DISPLAY_X11);
|
|
video_driver_display_set((uintptr_t)info.info.x11.display);
|
|
video_driver_window_set((uintptr_t)info.info.x11.window);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void *sdl_gfx_init(const video_info_t *video,
|
|
input_driver_t **input, void **input_data)
|
|
{
|
|
unsigned full_x, full_y;
|
|
const SDL_VideoInfo *video_info = NULL;
|
|
sdl_video_t *vid = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
uint32_t sdl_subsystem_flags = SDL_WasInit(0);
|
|
const char *path_font = settings->paths.path_font;
|
|
float video_font_size = settings->floats.video_font_size;
|
|
bool video_font_enable = settings->bools.video_font_enable;
|
|
float msg_color_r = settings->floats.video_msg_color_r;
|
|
float msg_color_g = settings->floats.video_msg_color_g;
|
|
float msg_color_b = settings->floats.video_msg_color_b;
|
|
|
|
#ifdef HAVE_X11
|
|
XInitThreads();
|
|
#endif
|
|
|
|
/* Initialise graphics subsystem, if required */
|
|
if (sdl_subsystem_flags == 0)
|
|
{
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
return NULL;
|
|
}
|
|
else if ((sdl_subsystem_flags & SDL_INIT_VIDEO) == 0)
|
|
{
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
if (!(vid = (sdl_video_t*)calloc(1, sizeof(*vid))))
|
|
return NULL;
|
|
|
|
video_info = SDL_GetVideoInfo();
|
|
full_x = video_info->current_w;
|
|
full_y = video_info->current_h;
|
|
RARCH_LOG("[SDL]: Detecting desktop resolution %ux%u.\n", full_x, full_y);
|
|
|
|
if (!video->fullscreen)
|
|
RARCH_LOG("[SDL]: Creating window @ %ux%u\n", video->width, video->height);
|
|
|
|
vid->screen = SDL_SetVideoMode(video->width, video->height, 32,
|
|
SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF | (video->fullscreen ? SDL_FULLSCREEN : 0));
|
|
|
|
/* We assume that SDL chooses ARGB8888.
|
|
* Assuming this simplifies the driver *a ton*.
|
|
*/
|
|
|
|
if (!vid->screen)
|
|
{
|
|
RARCH_ERR("[SDL1]: Failed to init SDL surface: %s\n", SDL_GetError());
|
|
goto error;
|
|
}
|
|
|
|
if (video->fullscreen)
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
sdl_gfx_set_handles();
|
|
|
|
if (input && input_data)
|
|
{
|
|
void *sdl_input = input_driver_init_wrap(&input_sdl,
|
|
settings->arrays.input_joypad_driver);
|
|
|
|
if (sdl_input)
|
|
{
|
|
*input = &input_sdl;
|
|
*input_data = sdl_input;
|
|
}
|
|
else
|
|
{
|
|
*input = NULL;
|
|
*input_data = NULL;
|
|
}
|
|
}
|
|
|
|
sdl_init_font(vid,
|
|
video_font_enable,
|
|
path_font, video_font_size,
|
|
msg_color_r,
|
|
msg_color_g,
|
|
msg_color_b);
|
|
|
|
vid->scaler.scaler_type = video->smooth ? SCALER_TYPE_BILINEAR : SCALER_TYPE_POINT;
|
|
vid->scaler.in_fmt = video->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
|
|
vid->scaler.out_fmt = SCALER_FMT_ARGB8888;
|
|
|
|
vid->menu.scaler = vid->scaler;
|
|
vid->menu.scaler.scaler_type = SCALER_TYPE_BILINEAR;
|
|
|
|
vid->menu.frame = SDL_ConvertSurface(
|
|
vid->screen, vid->screen->format, vid->screen->flags | SDL_SRCALPHA);
|
|
|
|
if (!vid->menu.frame)
|
|
{
|
|
RARCH_ERR("[SDL1]: Failed to init menu surface: %s\n", SDL_GetError());
|
|
goto error;
|
|
}
|
|
|
|
return vid;
|
|
|
|
error:
|
|
sdl_gfx_free(vid);
|
|
return NULL;
|
|
}
|
|
|
|
static void sdl_gfx_check_window(sdl_video_t *vid)
|
|
{
|
|
SDL_Event event;
|
|
|
|
SDL_PumpEvents();
|
|
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_QUITMASK))
|
|
{
|
|
if (event.type != SDL_QUIT)
|
|
continue;
|
|
|
|
vid->quitting = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool sdl_gfx_frame(void *data, const void *frame, unsigned width,
|
|
unsigned height, uint64_t frame_count,
|
|
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
|
{
|
|
char title[128];
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
#ifdef HAVE_MENU
|
|
bool menu_is_alive = video_info->menu_is_alive;
|
|
#endif
|
|
|
|
if (!vid)
|
|
return true;
|
|
|
|
title[0] = '\0';
|
|
|
|
video_driver_get_window_title(title, sizeof(title));
|
|
|
|
if (vid->menu.active)
|
|
{
|
|
#ifdef HAVE_MENU
|
|
menu_driver_frame(menu_is_alive, video_info);
|
|
#endif
|
|
SDL_BlitSurface(vid->menu.frame, NULL, vid->screen, NULL);
|
|
}
|
|
else
|
|
{
|
|
if (SDL_MUSTLOCK(vid->screen))
|
|
SDL_LockSurface(vid->screen);
|
|
|
|
video_frame_scale(
|
|
&vid->scaler,
|
|
vid->screen->pixels,
|
|
frame,
|
|
vid->scaler.in_fmt,
|
|
vid->screen->w,
|
|
vid->screen->h,
|
|
vid->screen->pitch,
|
|
width,
|
|
height,
|
|
pitch);
|
|
|
|
|
|
if (SDL_MUSTLOCK(vid->screen))
|
|
SDL_UnlockSurface(vid->screen);
|
|
}
|
|
|
|
if (title[0])
|
|
SDL_WM_SetCaption(title, NULL);
|
|
|
|
SDL_Flip(vid->screen);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void sdl_gfx_set_nonblock_state(void *a, bool b, bool c, unsigned d) { }
|
|
|
|
static bool sdl_gfx_alive(void *data)
|
|
{
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
sdl_gfx_check_window(vid);
|
|
return !vid->quitting;
|
|
}
|
|
|
|
static bool sdl_gfx_focus(void *data)
|
|
{
|
|
return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
|
|
}
|
|
|
|
static bool sdl_gfx_suppress_screensaver(void *data, bool enable)
|
|
{
|
|
#ifdef HAVE_X11
|
|
if (video_driver_display_type_get() == RARCH_DISPLAY_X11)
|
|
{
|
|
x11_suspend_screensaver(video_driver_window_get(), enable);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
/* TODO/FIXME - implement */
|
|
static bool sdl_gfx_has_windowed(void *data) { return true; }
|
|
|
|
static void sdl_gfx_viewport_info(void *data, struct video_viewport *vp)
|
|
{
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
vp->x = 0;
|
|
vp->y = 0;
|
|
vp->width = vp->full_width = vid->screen->w;
|
|
vp->height = vp->full_height = vid->screen->h;
|
|
}
|
|
|
|
static void sdl_set_filtering(void *data, unsigned index, bool smooth, bool ctx_scaling)
|
|
{
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
vid->scaler.scaler_type = smooth ? SCALER_TYPE_BILINEAR : SCALER_TYPE_POINT;
|
|
}
|
|
|
|
static void sdl_apply_state_changes(void *data)
|
|
{
|
|
(void)data;
|
|
}
|
|
|
|
static void sdl_set_texture_frame(void *data, const void *frame, bool rgb32,
|
|
unsigned width, unsigned height, float alpha)
|
|
{
|
|
enum scaler_pix_fmt format = rgb32
|
|
? SCALER_FMT_ARGB8888 : SCALER_FMT_RGBA4444;
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
|
|
video_frame_scale(
|
|
&vid->menu.scaler,
|
|
vid->menu.frame->pixels,
|
|
frame,
|
|
format,
|
|
vid->menu.frame->w,
|
|
vid->menu.frame->h,
|
|
vid->menu.frame->pitch,
|
|
width,
|
|
height,
|
|
width * (rgb32 ? sizeof(uint32_t) : sizeof(uint16_t))
|
|
);
|
|
|
|
SDL_SetAlpha(vid->menu.frame, SDL_SRCALPHA, 255.0 * alpha);
|
|
}
|
|
|
|
static void sdl_set_texture_enable(void *data, bool state, bool full_screen)
|
|
{
|
|
sdl_video_t *vid = (sdl_video_t*)data;
|
|
|
|
(void)full_screen;
|
|
|
|
vid->menu.active = state;
|
|
}
|
|
|
|
static void sdl_show_mouse(void *data, bool state)
|
|
{
|
|
(void)data;
|
|
|
|
SDL_ShowCursor(state);
|
|
}
|
|
|
|
static void sdl_grab_mouse_toggle(void *data)
|
|
{
|
|
const SDL_GrabMode mode = SDL_WM_GrabInput(SDL_GRAB_QUERY);
|
|
|
|
(void)data;
|
|
|
|
SDL_WM_GrabInput(mode == SDL_GRAB_ON ? SDL_GRAB_OFF : SDL_GRAB_ON);
|
|
}
|
|
|
|
static uint32_t sdl_get_flags(void *data)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
|
|
|
|
return flags;
|
|
}
|
|
|
|
static const video_poke_interface_t sdl_poke_interface = {
|
|
sdl_get_flags,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* get_refresh_rate */
|
|
sdl_set_filtering,
|
|
NULL, /* get_video_output_size */
|
|
NULL, /* get_video_output_prev */
|
|
NULL, /* get_video_output_next */
|
|
NULL, /* get_current_framebuffer */
|
|
NULL, /* get_proc_address */
|
|
NULL,
|
|
sdl_apply_state_changes,
|
|
sdl_set_texture_frame,
|
|
sdl_set_texture_enable,
|
|
NULL,
|
|
sdl_show_mouse,
|
|
sdl_grab_mouse_toggle,
|
|
NULL, /* get_current_shader */
|
|
NULL, /* get_current_software_framebuffer */
|
|
NULL, /* get_hw_render_interface */
|
|
NULL, /* set_hdr_max_nits */
|
|
NULL, /* set_hdr_paper_white_nits */
|
|
NULL, /* set_hdr_contrast */
|
|
NULL /* set_hdr_expand_gamut */
|
|
};
|
|
|
|
static void sdl_get_poke_interface(void *data, const video_poke_interface_t **iface)
|
|
{
|
|
(void)data;
|
|
|
|
*iface = &sdl_poke_interface;
|
|
}
|
|
|
|
static bool sdl_gfx_set_shader(void *data,
|
|
enum rarch_shader_type type, const char *path)
|
|
{
|
|
(void)data;
|
|
(void)type;
|
|
(void)path;
|
|
|
|
return false;
|
|
}
|
|
|
|
video_driver_t video_sdl = {
|
|
sdl_gfx_init,
|
|
sdl_gfx_frame,
|
|
sdl_gfx_set_nonblock_state,
|
|
sdl_gfx_alive,
|
|
sdl_gfx_focus,
|
|
sdl_gfx_suppress_screensaver,
|
|
sdl_gfx_has_windowed,
|
|
sdl_gfx_set_shader,
|
|
sdl_gfx_free,
|
|
"sdl",
|
|
NULL,
|
|
NULL, /* set_rotation */
|
|
sdl_gfx_viewport_info,
|
|
NULL, /* read_viewport */
|
|
NULL, /* read_frame_raw */
|
|
#ifdef HAVE_OVERLAY
|
|
NULL,
|
|
#endif
|
|
sdl_get_poke_interface
|
|
};
|