From ab27b7c85fd99f6b37bae1a20b2789d77315e09e Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 12 Jul 2018 16:55:08 -0400 Subject: [PATCH] initial SIXEL video driver --- Makefile.common | 12 + gfx/common/sixel_common.h | 34 ++ gfx/drivers/caca_gfx.c | 1 + gfx/drivers/sixel_gfx.c | 668 ++++++++++++++++++++++++ gfx/drivers_context/sixel_ctx.c | 202 +++++++ gfx/drivers_font/sixel_font.c | 149 ++++++ gfx/font_driver.c | 36 ++ gfx/font_driver.h | 1 + gfx/video_defines.h | 1 + gfx/video_driver.c | 12 +- gfx/video_driver.h | 3 + menu/drivers_display/menu_display_gdi.c | 12 - menu/menu_driver.c | 13 +- menu/menu_driver.h | 2 + qb/config.libs.sh | 3 +- qb/config.params.sh | 1 + 16 files changed, 1131 insertions(+), 19 deletions(-) create mode 100644 gfx/common/sixel_common.h create mode 100644 gfx/drivers/sixel_gfx.c create mode 100644 gfx/drivers_context/sixel_ctx.c create mode 100644 gfx/drivers_font/sixel_font.c diff --git a/Makefile.common b/Makefile.common index ad3109b9c9..2aca4eb425 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1044,6 +1044,18 @@ ifeq ($(HAVE_CACA), 1) endif endif +ifeq ($(HAVE_SIXEL), 1) + DEFINES += -DHAVE_SIXEL + CFLAGS += -I/usr/include/sixel + OBJ += gfx/drivers/sixel_gfx.o gfx/drivers_font/sixel_font.o \ + gfx/drivers_context/sixel_ctx.o + LIBS += -lsixel + + ifeq ($(HAVE_MENU_COMMON), 1) + OBJ += menu/drivers_display/menu_display_sixel.o + endif +endif + ifeq ($(HAVE_PLAIN_DRM), 1) OBJ += gfx/drivers/drm_gfx.o CFLAGS += -I/usr/include/libdrm diff --git a/gfx/common/sixel_common.h b/gfx/common/sixel_common.h new file mode 100644 index 0000000000..5d6e40ced1 --- /dev/null +++ b/gfx/common/sixel_common.h @@ -0,0 +1,34 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * copyright (c) 2011-2017 - Daniel De Matteis + * copyright (c) 2016-2017 - Brad Parker + * + * 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 . + */ + +#ifndef __SIXEL_COMMON_H +#define __SIXEL_COMMON_H + +#include + +#define SIXEL_COLORS 256 + +typedef struct sixel +{ + SIXELSTATUS sixel_status; + unsigned video_width; + unsigned video_height; + unsigned screen_width; + unsigned screen_height; +} sixel_t; + +#endif diff --git a/gfx/drivers/caca_gfx.c b/gfx/drivers/caca_gfx.c index a9405a6be1..b245502cb7 100644 --- a/gfx/drivers/caca_gfx.c +++ b/gfx/drivers/caca_gfx.c @@ -363,4 +363,5 @@ video_driver_t video_caca = { NULL, /* overlay_interface */ #endif caca_gfx_get_poke_interface, + NULL /* wrap_type_to_enum */ }; diff --git a/gfx/drivers/sixel_gfx.c b/gfx/drivers/sixel_gfx.c new file mode 100644 index 0000000000..4777587688 --- /dev/null +++ b/gfx/drivers/sixel_gfx.c @@ -0,0 +1,668 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * 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 . + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#include "../font_driver.h" + +#include "../../driver.h" +#include "../../configuration.h" +#include "../../verbosity.h" +#include "../../frontend/frontend_driver.h" +#include "../common/sixel_common.h" + +#ifndef _WIN32 +#define HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#define HAVE_SYS_SELECT_H + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#ifndef SIXEL_PIXELFORMAT_BGRA8888 +#error "Old version of libsixel detected, please upgrade to at least 1.6.0." +#endif + +static unsigned char *sixel_menu_frame = NULL; +static unsigned sixel_menu_width = 0; +static unsigned sixel_menu_height = 0; +static unsigned sixel_menu_pitch = 0; +static unsigned sixel_video_width = 0; +static unsigned sixel_video_height = 0; +static unsigned sixel_video_pitch = 0; +static unsigned sixel_video_bits = 0; +static unsigned sixel_menu_bits = 0; +static double sixel_video_scale = 1; +static bool sixel_rgb32 = false; +static bool sixel_menu_rgb32 = false; +static unsigned *sixel_temp_buf = NULL; + +static int sixel_write(char *data, int size, void *priv) +{ + return fwrite(data, 1, size, (FILE*)priv); +} + +static SIXELSTATUS output_sixel(unsigned char *pixbuf, int width, int height, + int ncolors, int pixelformat) +{ + sixel_output_t *context; + sixel_dither_t *dither; + SIXELSTATUS status; + + context = sixel_output_create(sixel_write, stdout); + dither = sixel_dither_create(ncolors); + status = sixel_dither_initialize(dither, pixbuf, + width, height, + pixelformat, + SIXEL_LARGE_AUTO, + SIXEL_REP_AUTO, + SIXEL_QUALITY_AUTO); + if (SIXEL_FAILED(status)) + return status; + status = sixel_encode(pixbuf, width, height, + pixelformat, dither, context); + if (SIXEL_FAILED(status)) + return status; + sixel_output_unref(context); + sixel_dither_unref(dither); + + return status; +} + +#ifdef HAVE_SYS_IOCTL_H +# ifdef HAVE_TERMIOS_H +static int wait_stdin(int usec) +{ +#ifdef HAVE_SYS_SELECT_H + fd_set rfds; + struct timeval tv; +#endif /* HAVE_SYS_SELECT_H */ + int ret = 0; + +#ifdef HAVE_SYS_SELECT_H + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv); +#else + (void) usec; +#endif /* HAVE_SYS_SELECT_H */ + + return ret; +} +# endif +#endif + +static void scroll_on_demand(int pixelheight) +{ +#ifdef HAVE_SYS_IOCTL_H + struct winsize size = {0, 0, 0, 0}; +#endif +#ifdef HAVE_TERMIOS_H + struct termios old_termios; + struct termios new_termios; +#endif + int row = 0; + int col = 0; + int cellheight; + int scroll; + +#ifdef HAVE_SYS_IOCTL_H + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + if (size.ws_ypixel <= 0) + { + printf("\033[H\0337"); + return; + } +# ifdef HAVE_TERMIOS_H + /* set the terminal to cbreak mode */ + tcgetattr(STDIN_FILENO, &old_termios); + memcpy(&new_termios, &old_termios, sizeof(old_termios)); + new_termios.c_lflag &= ~(ECHO | ICANON); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios); + + /* request cursor position report */ + printf("\033[6n"); + + if (wait_stdin(1000 * 1000) != (-1)) + { + /* wait 1 sec */ + if (scanf("\033[%d;%dR", &row, &col) == 2) + { + cellheight = pixelheight * size.ws_row / size.ws_ypixel + 1; + scroll = cellheight + row - size.ws_row + 1; + printf("\033[%dS\033[%dA", scroll, scroll); + printf("\0337"); + } + else + { + printf("\033[H\0337"); + } + } + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios); +# else + printf("\033[H\0337"); +# endif /* HAVE_TERMIOS_H */ +#else + printf("\033[H\0337"); +#endif /* HAVE_SYS_IOCTL_H */ +} + +static void *sixel_gfx_init(const video_info_t *video, + const input_driver_t **input, void **input_data) +{ + settings_t *settings = config_get_ptr(); + sixel_t *sixel = (sixel_t*)calloc(1, sizeof(*sixel)); + gfx_ctx_input_t inp; + const gfx_ctx_driver_t *ctx_driver = NULL; + const char *scale_str = NULL; + + *input = NULL; + *input_data = NULL; + + sixel_rgb32 = video->rgb32; + sixel_video_bits = video->rgb32 ? 32 : 16; + + if (video->rgb32) + sixel_video_pitch = video->width * 4; + else + sixel_video_pitch = video->width * 2; + + scale_str = getenv("SIXEL_SCALE"); + + if (scale_str) + { + sixel_video_scale = atof(scale_str); + + /* just in case the conversion fails, pick something sane */ + if (!sixel_video_scale) + sixel_video_scale = 1.0; + } + + ctx_driver = video_context_driver_init_first(sixel, + settings->arrays.video_context_driver, + GFX_CTX_SIXEL_API, 1, 0, false); + + if (!ctx_driver) + goto error; + + video_context_driver_set((const gfx_ctx_driver_t*)ctx_driver); + + RARCH_LOG("[SIXEL]: Found SIXEL context: %s\n", ctx_driver->ident); + + inp.input = input; + inp.input_data = input_data; + + video_context_driver_input_driver(&inp); + + if (settings->bools.video_font_enable) + font_driver_init_osd(sixel, false, + video->is_threaded, + FONT_DRIVER_RENDER_SIXEL); + + RARCH_LOG("[SIXEL]: Init complete.\n"); + + return sixel; + +error: + video_context_driver_destroy(); + if (sixel) + free(sixel); + return NULL; +} + +static bool sixel_gfx_frame(void *data, const void *frame, + unsigned frame_width, unsigned frame_height, uint64_t frame_count, + unsigned pitch, const char *msg, video_frame_info_t *video_info) +{ + gfx_ctx_mode_t mode; + const void *frame_to_copy = frame; + unsigned width = 0; + unsigned height = 0; + unsigned bits = sixel_video_bits; + unsigned pixfmt = SIXEL_PIXELFORMAT_RGB565; + bool draw = true; + sixel_t *sixel = (sixel_t*)data; + + if (!frame || !frame_width || !frame_height) + return true; + +#ifdef HAVE_MENU + menu_driver_frame(video_info); +#endif + + if (sixel_video_width != frame_width || sixel_video_height != frame_height || sixel_video_pitch != pitch) + { + if (frame_width > 4 && frame_height > 4) + { + sixel_video_width = frame_width; + sixel_video_height = frame_height; + sixel_video_pitch = pitch; + sixel->screen_width = sixel_video_width * sixel_video_scale; + sixel->screen_height = sixel_video_height * sixel_video_scale; + } + } + + if (sixel_menu_frame && video_info->menu_is_alive) + { + frame_to_copy = sixel_menu_frame; + width = sixel_menu_width; + height = sixel_menu_height; + pitch = sixel_menu_pitch; + bits = sixel_menu_bits; + } + else + { + width = sixel_video_width; + height = sixel_video_height; + pitch = sixel_video_pitch; + + if (frame_width == 4 && frame_height == 4 && (frame_width < width && frame_height < height)) + draw = false; + + if (video_info->menu_is_alive) + draw = false; + } + + if (sixel->video_width != width || sixel->video_height != height) + { + scroll_on_demand(sixel->screen_height); + + sixel->video_width = width; + sixel->video_height = height; + + if (sixel_temp_buf) + { + free(sixel_temp_buf); + } + + sixel_temp_buf = (unsigned*)malloc(sixel->screen_width * sixel->screen_height * sizeof(unsigned)); + } + + if (bits == 16) + { + if (sixel_temp_buf) + { + if (frame_to_copy == sixel_menu_frame) + { + /* Scale and convert 16-bit RGBX4444 image to 32-bit RGBX8888. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned short pixel = ((unsigned short*)frame_to_copy)[width * scaled_y + scaled_x]; + + /* convert RGBX4444 to RGBX8888 */ + unsigned r = ((pixel & 0xF000) << 8) | ((pixel & 0xF000) << 4); + unsigned g = ((pixel & 0x0F00) << 4) | ((pixel & 0x0F00) << 0); + unsigned b = ((pixel & 0x00F0) << 0) | ((pixel & 0x00F0) >> 4); + + sixel_temp_buf[sixel->screen_width * y + x] = 0xFF000000 | b | g | r; + } + } + + pixfmt = SIXEL_PIXELFORMAT_RGBA8888; + frame_to_copy = sixel_temp_buf; + } + else + { + /* Scale and convert 16-bit RGB565 image to 32-bit RGBX8888. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned short pixel = ((unsigned short*)frame_to_copy)[(pitch / (bits / 8)) * scaled_y + scaled_x]; + + /* convert RGB565 to RGBX8888 */ + unsigned r = ((pixel & 0x001F) << 3) | ((pixel & 0x001C) >> 2); + unsigned g = ((pixel & 0x07E0) << 5) | ((pixel & 0x0600) >> 1); + unsigned b = ((pixel & 0xF800) << 8) | ((pixel & 0xE000) << 3); + + sixel_temp_buf[sixel->screen_width * y + x] = 0xFF000000 | b | g | r; + } + } + + pixfmt = SIXEL_PIXELFORMAT_BGRA8888; + frame_to_copy = sixel_temp_buf; + } + } + else + { + /* no temp buffer available yet */ + } + } + else + { + /* Scale 32-bit RGBX8888 image to output geometry. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned pixel = ((unsigned*)frame_to_copy)[(pitch / (bits / 8)) * scaled_y + scaled_x]; + + sixel_temp_buf[sixel->screen_width * y + x] = pixel; + } + } + + pixfmt = SIXEL_PIXELFORMAT_BGRA8888; + frame_to_copy = sixel_temp_buf; + } + + if (draw && sixel->screen_width > 0 && sixel->screen_height > 0) + { + printf("\0338"); + + sixel->sixel_status = output_sixel((unsigned char*)frame_to_copy, sixel->screen_width, sixel->screen_height, + SIXEL_COLORS, pixfmt); + + if (SIXEL_FAILED(sixel->sixel_status)) + { + fprintf(stderr, "%s\n%s\n", + sixel_helper_format_error(sixel->sixel_status), + sixel_helper_get_additional_message()); + } + } + + if (msg) + font_driver_render_msg(video_info, NULL, msg, NULL); + + return true; +} + +static void sixel_gfx_set_nonblock_state(void *data, bool toggle) +{ + (void)data; + (void)toggle; +} + +static bool sixel_gfx_alive(void *data) +{ + gfx_ctx_size_t size_data; + unsigned temp_width = 0; + unsigned temp_height = 0; + bool quit = false; + bool resize = false; + + /* Needed because some context drivers don't track their sizes */ + video_driver_get_size(&temp_width, &temp_height); + + size_data.quit = &quit; + size_data.resize = &resize; + size_data.width = &temp_width; + size_data.height = &temp_height; + + video_context_driver_check_window(&size_data); + + if (temp_width != 0 && temp_height != 0) + video_driver_set_size(&temp_width, &temp_height); + + return true; +} + +static bool sixel_gfx_focus(void *data) +{ + (void)data; + return true; +} + +static bool sixel_gfx_suppress_screensaver(void *data, bool enable) +{ + (void)data; + (void)enable; + return false; +} + +static bool sixel_gfx_has_windowed(void *data) +{ + (void)data; + return true; +} + +static void sixel_gfx_free(void *data) +{ + sixel_t *sixel = (sixel_t*)data; + + printf("\033\\"); + + if (sixel_menu_frame) + { + free(sixel_menu_frame); + sixel_menu_frame = NULL; + } + + if (sixel_temp_buf) + { + free(sixel_temp_buf); + sixel_temp_buf = NULL; + } + + font_driver_free_osd(); + + if (sixel) + free(sixel); +} + +static bool sixel_gfx_set_shader(void *data, + enum rarch_shader_type type, const char *path) +{ + (void)data; + (void)type; + (void)path; + + return false; +} + +static void sixel_gfx_set_rotation(void *data, + unsigned rotation) +{ + (void)data; + (void)rotation; +} + +static void sixel_gfx_viewport_info(void *data, + struct video_viewport *vp) +{ + (void)data; + (void)vp; +} + +static bool sixel_gfx_read_viewport(void *data, uint8_t *buffer, bool is_idle) +{ + (void)data; + (void)buffer; + + return true; +} + +static void sixel_set_texture_frame(void *data, + const void *frame, bool rgb32, unsigned width, unsigned height, + float alpha) +{ + unsigned pitch = width * 2; + + if (rgb32) + pitch = width * 4; + + if (sixel_menu_frame) + { + free(sixel_menu_frame); + sixel_menu_frame = NULL; + } + + if (!sixel_menu_frame || sixel_menu_width != width || sixel_menu_height != height || sixel_menu_pitch != pitch) + if (pitch && height) + sixel_menu_frame = (unsigned char*)malloc(pitch * height); + + if (sixel_menu_frame && frame && pitch && height) + { + memcpy(sixel_menu_frame, frame, pitch * height); + sixel_menu_width = width; + sixel_menu_height = height; + sixel_menu_pitch = pitch; + sixel_menu_bits = rgb32 ? 32 : 16; + } +} + +static void sixel_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + font_driver_render_msg(video_info, font, msg, params); +} + +static void sixel_get_video_output_size(void *data, + unsigned *width, unsigned *height) +{ + gfx_ctx_size_t size_data; + size_data.width = width; + size_data.height = height; + video_context_driver_get_video_output_size(&size_data); +} + +static void sixel_get_video_output_prev(void *data) +{ + video_context_driver_get_video_output_prev(); +} + +static void sixel_get_video_output_next(void *data) +{ + video_context_driver_get_video_output_next(); +} + +static void sixel_set_video_mode(void *data, unsigned width, unsigned height, + bool fullscreen) +{ + gfx_ctx_mode_t mode; + + mode.width = width; + mode.height = height; + mode.fullscreen = fullscreen; + + video_context_driver_set_video_mode(&mode); +} + +static const video_poke_interface_t sixel_poke_interface = { + NULL, + NULL, + NULL, + NULL, + NULL, + sixel_set_video_mode, + NULL, + NULL, + sixel_get_video_output_size, + sixel_get_video_output_prev, + sixel_get_video_output_next, + NULL, + NULL, + NULL, + NULL, +#if defined(HAVE_MENU) + sixel_set_texture_frame, + NULL, + sixel_set_osd_msg, + NULL, +#else + NULL, + NULL, + NULL, + NULL, +#endif + NULL, + NULL, + NULL, + NULL, +}; + +static void sixel_gfx_get_poke_interface(void *data, + const video_poke_interface_t **iface) +{ + (void)data; + *iface = &sixel_poke_interface; +} + +static void sixel_gfx_set_viewport(void *data, unsigned viewport_width, + unsigned viewport_height, bool force_full, bool allow_rotate) +{ +} + +bool sixel_has_menu_frame(void) +{ + return (sixel_menu_frame != NULL); +} + +video_driver_t video_sixel = { + sixel_gfx_init, + sixel_gfx_frame, + sixel_gfx_set_nonblock_state, + sixel_gfx_alive, + sixel_gfx_focus, + sixel_gfx_suppress_screensaver, + sixel_gfx_has_windowed, + sixel_gfx_set_shader, + sixel_gfx_free, + "sixel", + sixel_gfx_set_viewport, + sixel_gfx_set_rotation, + sixel_gfx_viewport_info, + sixel_gfx_read_viewport, + NULL, /* read_frame_raw */ +#ifdef HAVE_OVERLAY + NULL, /* overlay_interface */ +#endif + sixel_gfx_get_poke_interface, + NULL /* wrap_type_to_enum */ +}; diff --git a/gfx/drivers_context/sixel_ctx.c b/gfx/drivers_context/sixel_ctx.c new file mode 100644 index 0000000000..39926c5f06 --- /dev/null +++ b/gfx/drivers_context/sixel_ctx.c @@ -0,0 +1,202 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * 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 . + */ + +/* SIXEL context. */ + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../../configuration.h" +#include "../../dynamic.h" +#include "../../verbosity.h" +#include "../../ui/ui_companion_driver.h" +#include "../video_driver.h" + +#if defined(_WIN32) && !defined(_XBOX) +#include "../common/win32_common.h" +#endif + +static void gfx_ctx_sixel_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height, bool is_shutdown) +{ +} + +static bool gfx_ctx_sixel_set_resize(void *data, + unsigned width, unsigned height) +{ + (void)data; + (void)width; + (void)height; + + return false; +} + +static void gfx_ctx_sixel_update_window_title(void *data, void *data2) +{ +#if defined(_WIN32) && !defined(_XBOX) + const ui_window_t *window = ui_companion_driver_get_window_ptr(); + char title[128]; + + title[0] = '\0'; + + video_driver_get_window_title(title, sizeof(title)); + + if (window && title[0]) + window->set_title(&main_window, title); +#endif +} + +static void gfx_ctx_sixel_get_video_size(void *data, + unsigned *width, unsigned *height) +{ + (void)data; +} + +static void *gfx_ctx_sixel_init( + video_frame_info_t *video_info, void *video_driver) +{ + (void)video_driver; + + return (void*)"sixel"; +} + +static void gfx_ctx_sixel_destroy(void *data) +{ + (void)data; +} + +static bool gfx_ctx_sixel_set_video_mode(void *data, + video_frame_info_t *video_info, + unsigned width, unsigned height, + bool fullscreen) +{ + return true; +} + + +static void gfx_ctx_sixel_input_driver(void *data, + const char *joypad_name, + const input_driver_t **input, void **input_data) +{ + (void)data; + +#ifdef HAVE_UDEV + *input_data = input_udev.init(joypad_name); + + if (*input_data) + { + *input = &input_udev; + return; + } +#endif + *input = NULL; + *input_data = NULL; +} + +static bool gfx_ctx_sixel_has_focus(void *data) +{ + return true; +} + +static bool gfx_ctx_sixel_suppress_screensaver(void *data, bool enable) +{ + return true; +} + +static bool gfx_ctx_sixel_has_windowed(void *data) +{ + (void)data; + + return true; +} + +static bool gfx_ctx_sixel_get_metrics(void *data, + enum display_metric_types type, float *value) +{ + return false; +} + +static bool gfx_ctx_sixel_bind_api(void *data, + enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + (void)data; + + return true; +} + +static void gfx_ctx_sixel_show_mouse(void *data, bool state) +{ + (void)data; +} + +static void gfx_ctx_sixel_swap_interval(void *data, unsigned interval) +{ + (void)data; + (void)interval; +} + +static void gfx_ctx_sixel_set_flags(void *data, uint32_t flags) +{ + (void)data; + (void)flags; +} + +static uint32_t gfx_ctx_sixel_get_flags(void *data) +{ + uint32_t flags = 0; + BIT32_SET(flags, GFX_CTX_FLAGS_NONE); + return flags; +} + +static void gfx_ctx_sixel_swap_buffers(void *data, void *data2) +{ + (void)data; +} + +const gfx_ctx_driver_t gfx_ctx_sixel = { + gfx_ctx_sixel_init, + gfx_ctx_sixel_destroy, + gfx_ctx_sixel_bind_api, + gfx_ctx_sixel_swap_interval, + gfx_ctx_sixel_set_video_mode, + gfx_ctx_sixel_get_video_size, + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + gfx_ctx_sixel_get_metrics, + NULL, + gfx_ctx_sixel_update_window_title, + gfx_ctx_sixel_check_window, + gfx_ctx_sixel_set_resize, + gfx_ctx_sixel_has_focus, + gfx_ctx_sixel_suppress_screensaver, + gfx_ctx_sixel_has_windowed, + gfx_ctx_sixel_swap_buffers, + gfx_ctx_sixel_input_driver, + NULL, + NULL, + NULL, + gfx_ctx_sixel_show_mouse, + "sixel", + gfx_ctx_sixel_get_flags, + gfx_ctx_sixel_set_flags, + NULL, + NULL, + NULL +}; + diff --git a/gfx/drivers_font/sixel_font.c b/gfx/drivers_font/sixel_font.c new file mode 100644 index 0000000000..f97b732ca5 --- /dev/null +++ b/gfx/drivers_font/sixel_font.c @@ -0,0 +1,149 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * 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 . + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../font_driver.h" +#include "../../verbosity.h" +#include "../common/sixel_common.h" + +typedef struct +{ + const font_renderer_driver_t *sixel_font_driver; + void *sixel_font_data; + sixel_t *sixel; +} sixel_raster_t; + +static void *sixel_init_font(void *data, + const char *font_path, float font_size, + bool is_threaded) +{ + sixel_raster_t *font = (sixel_raster_t*)calloc(1, sizeof(*font)); + + if (!font) + return NULL; + + font->sixel = (sixel_t*)data; + + if (!font_renderer_create_default((const void**)&font->sixel_font_driver, + &font->sixel_font_data, font_path, font_size)) + { + RARCH_WARN("Couldn't initialize font renderer.\n"); + return NULL; + } + + return font; +} + +static void sixel_render_free_font(void *data, bool is_threaded) +{ + (void)data; + (void)is_threaded; +} + +static int sixel_get_message_width(void *data, const char *msg, + unsigned msg_len, float scale) +{ + return 0; +} + +static const struct font_glyph *sixel_font_get_glyph( + void *data, uint32_t code) +{ + return NULL; +} + +static void sixel_render_msg(video_frame_info_t *video_info, + void *data, const char *msg, + const void *userdata) +{ + float x, y, scale; + unsigned width, height; + unsigned newX, newY; + unsigned align; + sixel_raster_t *font = (sixel_raster_t*)data; + const struct font_params *params = (const struct font_params*)userdata; + + if (!font || string_is_empty(msg)) + return; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + align = params->text_align; + } + else + { + x = video_info->font_msg_pos_x; + y = video_info->font_msg_pos_y; + scale = 1.0f; + align = TEXT_ALIGN_LEFT; + } + + if (!font->sixel) + return; + + width = font->sixel->screen_width; + height = font->sixel->screen_height; + newY = height - (y * height * scale); + + switch (align) + { + case TEXT_ALIGN_RIGHT: + newX = (x * width * scale) - strlen(msg); + break; + case TEXT_ALIGN_CENTER: + newX = (x * width * scale) - (strlen(msg) / 2); + break; + case TEXT_ALIGN_LEFT: + default: + newX = x * width * scale; + break; + } + + /* FIXME: add text drawing support */ +} + +static void sixel_font_flush_block(unsigned width, unsigned height, + void* data) +{ + (void)data; +} + +static void sixel_font_bind_block(void* data, void* userdata) +{ + (void)data; +} + +font_renderer_t sixel_font = { + sixel_init_font, + sixel_render_free_font, + sixel_render_msg, + "sixel font", + sixel_font_get_glyph, /* get_glyph */ + sixel_font_bind_block, /* bind_block */ + sixel_font_flush_block, /* flush */ + sixel_get_message_width /* get_message_width */ +}; diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 4d57e0162b..d9ed697afd 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -217,6 +217,37 @@ static bool caca_font_init_first( } #endif +#ifdef HAVE_SIXEL +static const font_renderer_t *sixel_font_backends[] = { + &sixel_font, + NULL, +}; + +static bool sixel_font_init_first( + const void **font_driver, void **font_handle, + void *video_data, const char *font_path, + float font_size, bool is_threaded) +{ + unsigned i; + + for (i = 0; sixel_font_backends[i]; i++) + { + void *data = sixel_font_backends[i]->init( + video_data, font_path, font_size, + is_threaded); + + if (!data) + continue; + + *font_driver = sixel_font_backends[i]; + *font_handle = data; + return true; + } + + return false; +} +#endif + #ifdef DJGPP static const font_renderer_t *vga_font_backends[] = { &vga_font, @@ -595,6 +626,11 @@ static bool font_init_first( return caca_font_init_first(font_driver, font_handle, video_data, font_path, font_size, is_threaded); #endif +#ifdef HAVE_SIXEL + case FONT_DRIVER_RENDER_SIXEL: + return sixel_font_init_first(font_driver, font_handle, + video_data, font_path, font_size, is_threaded); +#endif #if defined(_WIN32) && !defined(_XBOX) case FONT_DRIVER_RENDER_GDI: return gdi_font_init_first(font_driver, font_handle, diff --git a/gfx/font_driver.h b/gfx/font_driver.h index a843b2709f..54876a0e87 100644 --- a/gfx/font_driver.h +++ b/gfx/font_driver.h @@ -170,6 +170,7 @@ extern font_renderer_t d3d12_font; extern font_renderer_t caca_font; extern font_renderer_t gdi_font; extern font_renderer_t vga_font; +extern font_renderer_t sixel_font; extern font_renderer_driver_t stb_font_renderer; extern font_renderer_driver_t stb_unicode_font_renderer; diff --git a/gfx/video_defines.h b/gfx/video_defines.h index 473c72b68f..3ef84d919e 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -93,6 +93,7 @@ enum font_driver_render_api FONT_DRIVER_RENDER_VULKAN_API, FONT_DRIVER_RENDER_METAL_API, FONT_DRIVER_RENDER_CACA, + FONT_DRIVER_RENDER_SIXEL, FONT_DRIVER_RENDER_GDI, FONT_DRIVER_RENDER_VGA }; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 5d0d50452d..908e25aedb 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -339,11 +339,14 @@ static const video_driver_t *video_drivers[] = { #if defined(_WIN32) && !defined(_XBOX) &video_gdi, #endif -#ifdef HAVE_CACA - &video_caca, -#endif #ifdef DJGPP &video_vga, +#endif +#ifdef HAVE_SIXEL + &video_sixel, +#endif +#ifdef HAVE_CACA + &video_caca, #endif &video_null, NULL, @@ -408,6 +411,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #endif #if defined(_WIN32) && !defined(_XBOX) &gfx_ctx_gdi, +#endif +#ifdef HAVE_SIXEL + &gfx_ctx_sixel, #endif &gfx_ctx_null, NULL diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 2077ec383e..ca3dca38ad 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -95,6 +95,7 @@ enum gfx_ctx_api GFX_CTX_DIRECT3D12_API, GFX_CTX_OPENVG_API, GFX_CTX_VULKAN_API, + GFX_CTX_SIXEL_API, GFX_CTX_METAL_API, GFX_CTX_GDI_API, GFX_CTX_GX_API, @@ -1273,6 +1274,7 @@ extern video_driver_t video_xshm; extern video_driver_t video_caca; extern video_driver_t video_gdi; extern video_driver_t video_vga; +extern video_driver_t video_sixel; extern video_driver_t video_null; extern const gfx_ctx_driver_t gfx_ctx_osmesa; @@ -1294,6 +1296,7 @@ extern const gfx_ctx_driver_t gfx_ctx_emscripten; extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev; extern const gfx_ctx_driver_t gfx_ctx_khr_display; extern const gfx_ctx_driver_t gfx_ctx_gdi; +extern const gfx_ctx_driver_t gfx_ctx_sixel; extern const gfx_ctx_driver_t gfx_ctx_null; diff --git a/menu/drivers_display/menu_display_gdi.c b/menu/drivers_display/menu_display_gdi.c index e3f0c2160c..3cea3ee2e9 100644 --- a/menu/drivers_display/menu_display_gdi.c +++ b/menu/drivers_display/menu_display_gdi.c @@ -59,18 +59,6 @@ static void menu_display_gdi_viewport(menu_display_ctx_draw_t *draw, static void menu_display_gdi_restore_clear_color(void) { - /*HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); - RECT rect; - HWND hwnd = win32_get_window(); - HDC hdc = GetDC(hwnd); - - GetClientRect(hwnd, &rect); - - FillRect(hdc, &rect, brush); - - DeleteObject(brush); - - ReleaseDC(hwnd, hdc);*/ } static void menu_display_gdi_clear_color( diff --git a/menu/menu_driver.c b/menu/menu_driver.c index fdb7025b4a..4bc0a32ec1 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -130,14 +130,17 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = { #ifdef WIIU &menu_display_ctx_wiiu, #endif -#ifdef HAVE_CACA - &menu_display_ctx_caca, -#endif #if defined(_WIN32) && !defined(_XBOX) &menu_display_ctx_gdi, #endif #ifdef DJGPP &menu_display_ctx_vga, +#endif +#ifdef HAVE_SIXEL + &menu_display_ctx_sixel, +#endif +#ifdef HAVE_CACA + &menu_display_ctx_caca, #endif &menu_display_ctx_null, NULL, @@ -299,6 +302,10 @@ static bool menu_display_check_compatibility( if (string_is_equal(video_driver, "gx2")) return true; break; + case MENU_VIDEO_DRIVER_SIXEL: + if (string_is_equal(video_driver, "sixel")) + return true; + break; case MENU_VIDEO_DRIVER_CACA: if (string_is_equal(video_driver, "caca")) return true; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 205197328f..b3c363afb7 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -315,6 +315,7 @@ enum menu_display_driver_type MENU_VIDEO_DRIVER_CTR, MENU_VIDEO_DRIVER_WIIU, MENU_VIDEO_DRIVER_CACA, + MENU_VIDEO_DRIVER_SIXEL, MENU_VIDEO_DRIVER_GDI, MENU_VIDEO_DRIVER_VGA }; @@ -814,6 +815,7 @@ extern menu_display_ctx_driver_t menu_display_ctx_wiiu; extern menu_display_ctx_driver_t menu_display_ctx_caca; extern menu_display_ctx_driver_t menu_display_ctx_gdi; extern menu_display_ctx_driver_t menu_display_ctx_vga; +extern menu_display_ctx_driver_t menu_display_ctx_sixel; extern menu_display_ctx_driver_t menu_display_ctx_null; extern menu_ctx_driver_t menu_ctx_xui; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 93c2a34434..b6d29c9b6e 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -233,6 +233,7 @@ fi check_pkgconf ALSA alsa check_val '' ALSA -lasound alsa check_lib '' CACA -lcaca +check_lib '' SIXEL -lsixel if [ "$HAVE_OSS" != 'no' ]; then check_header OSS sys/soundcard.h @@ -516,7 +517,7 @@ if [ "$HAVE_MATERIALUI" != 'no' ] || [ "$HAVE_XMB" != 'no' ] || [ "$HAVE_ZARCH" HAVE_SHADERPIPELINE=no HAVE_VULKAN=no die : 'Notice: Hardware rendering context not available.' - elif [ "$HAVE_CACA" = 'yes' ]; then + elif [ "$HAVE_CACA" = 'yes' ] || [ "$HAVE_SIXEL" = 'yes' ]; then die : 'Notice: Hardware rendering context not available.' else HAVE_MATERIALUI=no diff --git a/qb/config.params.sh b/qb/config.params.sh index cd3e06d9dc..db514977d7 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -5,6 +5,7 @@ HAVE_MAN_DIR= # Manpage install directory (Deprecated) HAVE_OPENGLES_LIBS= # Link flags for custom GLES library HAVE_OPENGLES_CFLAGS= # C-flags for custom GLES library HAVE_CACA=no # Libcaca support +HAVE_SIXEL=no # Libsixel support HAVE_LIBRETRODB=yes # Libretrodb support HAVE_RGUI=yes # RGUI menu HAVE_MATERIALUI=auto # MaterialUI menu