diff --git a/frontend/menu/disp/lakka.c b/frontend/menu/disp/lakka.c new file mode 100644 index 0000000000..01ca070521 --- /dev/null +++ b/frontend/menu/disp/lakka.c @@ -0,0 +1,724 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2014 - Daniel De Matteis + * Copyright (C) 2012-2014 - Michael Lelli + * + * 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 +#include +#include + +#include "../menu_common.h" +#include "../menu_context.h" +#include "../../../file_list.h" +#include "../../../general.h" +#include "../../../gfx/gfx_common.h" +#include "../../../gfx/gl_common.h" +#include "../../../gfx/shader_common.h" +#include "../../../config.def.h" +#include "../../../file.h" +#include "../../../dynamic.h" +#include "../../../compat/posix_string.h" +#include "../../../gfx/shader_parse.h" +#include "../../../performance.h" +#include "../../../input/input_common.h" + +#include "../../../screenshot.h" +#include "../../../gfx/fonts/bitmap.h" + +GLuint texid = 0; +struct texture_image *menu_texture; +static bool textures_inited =false; + +// thanks to https://github.com/DavidEGrayson/ahrs-visualizer/blob/master/png_texture.cpp +static GLuint png_texture_load(const char * file_name, int * width, int * height) +{ + // Generate the OpenGL texture object + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, menu_texture->width, menu_texture->height, 0, GL_RGB, GL_UNSIGNED_BYTE, menu_texture->pixels); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + return texture; +} + +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_HLSL) +#define HAVE_SHADER_MANAGER +#endif + +static uint16_t menu_framebuf[400 * 240]; + +#define LAKKA_TERM_START_X 15 +#define LAKKA_TERM_START_Y 27 +#define LAKKA_TERM_WIDTH (((rgui->width - LAKKA_TERM_START_X - 15) / (FONT_WIDTH_STRIDE))) +#define LAKKA_TERM_HEIGHT (((rgui->height - LAKKA_TERM_START_Y - 15) / (FONT_HEIGHT_STRIDE)) - 1) + +int dim = 192; + +static void lakka_copy_glyph(uint8_t *glyph, const uint8_t *buf) +{ + int y, x; + for (y = 0; y < FONT_HEIGHT; y++) + { + for (x = 0; x < FONT_WIDTH; x++) + { + uint32_t col = + ((uint32_t)buf[3 * (-y * 256 + x) + 0] << 0) | + ((uint32_t)buf[3 * (-y * 256 + x) + 1] << 8) | + ((uint32_t)buf[3 * (-y * 256 + x) + 2] << 16); + + uint8_t rem = 1 << ((x + y * FONT_WIDTH) & 7); + unsigned offset = (x + y * FONT_WIDTH) >> 3; + + if (col != 0xff) + glyph[offset] |= rem; + } + } +} + +static uint16_t gray_filler(unsigned x, unsigned y) +{ + return 0x0008; +} + +static uint16_t green_filler(unsigned x, unsigned y) +{ + return 0xf008; +} + +static void fill_rect(uint16_t *buf, unsigned pitch, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint16_t (*col)(unsigned x, unsigned y)) +{ + unsigned j, i; + for (j = y; j < y + height; j++) + for (i = x; i < x + width; i++) + buf[j * (pitch >> 1) + i] = col(i, j); +} + +static void blit_line(rgui_handle_t *rgui, + int x, int y, const char *message, bool green) +{ + int j, i; + while (*message) + { + for (j = 0; j < FONT_HEIGHT; j++) + { + for (i = 0; i < FONT_WIDTH; i++) + { + uint8_t rem = 1 << ((i + j * FONT_WIDTH) & 7); + int offset = (i + j * FONT_WIDTH) >> 3; + bool col = (rgui->font[FONT_OFFSET((unsigned char)*message) + offset] & rem); + + if (col) + { + rgui->frame_buf[(y + j) * (rgui->frame_buf_pitch >> 1) + (x + i)] = green ? +#ifdef GEKKO + (3 << 0) | (10 << 4) | (3 << 8) | (7 << 12) : 0x7FFF; +#else + (15 << 0) | (7 << 4) | (15 << 8) | (7 << 12) : 0xFFFF; +#endif + } + } + } + + x += FONT_WIDTH_STRIDE; + message++; + } +} + +static void init_font(rgui_handle_t *rgui, const uint8_t *font_bmp_buf) +{ + unsigned i; + uint8_t *font = (uint8_t *) calloc(1, FONT_OFFSET(256)); + rgui->alloc_font = true; + for (i = 0; i < 256; i++) + { + unsigned y = i / 16; + unsigned x = i % 16; + lakka_copy_glyph(&font[FONT_OFFSET(i)], + font_bmp_buf + 54 + 3 * (256 * (255 - 16 * y) + 16 * x)); + } + + rgui->font = font; +} + +static bool rguidisp_init_font(void *data) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + + const uint8_t *font_bmp_buf = NULL; + const uint8_t *font_bin_buf = bitmap_bin; + bool ret = true; + + if (font_bmp_buf) + init_font(rgui, font_bmp_buf); + else if (font_bin_buf) + rgui->font = font_bin_buf; + else + ret = false; + + return ret; +} + +static void lakka_render_background(rgui_handle_t *rgui) +{ + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + 0, 0, rgui->width, rgui->height, gray_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + 5, 5, rgui->width - 10, 5, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + 5, rgui->height - 10, rgui->width - 10, 5, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + 5, 5, 5, rgui->height - 10, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + rgui->width - 10, 5, 5, rgui->height - 10, green_filler); +} + +static void lakka_render_messagebox(void *data, const char *message) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + size_t i; + + if (!message || !*message) + return; + + struct string_list *list = string_split(message, "\n"); + if (!list) + return; + if (list->elems == 0) + { + string_list_free(list); + return; + } + + unsigned width = 0; + unsigned glyphs_width = 0; + for (i = 0; i < list->size; i++) + { + char *msg = list->elems[i].data; + unsigned msglen = strlen(msg); + if (msglen > LAKKA_TERM_WIDTH) + { + msg[LAKKA_TERM_WIDTH - 2] = '.'; + msg[LAKKA_TERM_WIDTH - 1] = '.'; + msg[LAKKA_TERM_WIDTH - 0] = '.'; + msg[LAKKA_TERM_WIDTH + 1] = '\0'; + msglen = LAKKA_TERM_WIDTH; + } + + unsigned line_width = msglen * FONT_WIDTH_STRIDE - 1 + 6 + 10; + width = max(width, line_width); + glyphs_width = max(glyphs_width, msglen); + } + + unsigned height = FONT_HEIGHT_STRIDE * list->size + 6 + 10; + int x = (rgui->width - width) / 2; + int y = (rgui->height - height) / 2; + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + x + 5, y + 5, width - 10, height - 10, gray_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + x, y, width - 5, 5, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + x + width - 5, y, 5, height - 5, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + x + 5, y + height - 5, width - 5, 5, green_filler); + + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, + x, y + 5, 5, height - 5, green_filler); + + for (i = 0; i < list->size; i++) + { + const char *msg = list->elems[i].data; + int offset_x = FONT_WIDTH_STRIDE * (glyphs_width - strlen(msg)) / 2; + int offset_y = FONT_HEIGHT_STRIDE * i; + blit_line(rgui, x + 8 + offset_x, y + 8 + offset_y, msg, false); + } + + string_list_free(list); +} + +const GLfloat background_color[] = { + 0, 200, 200, 0.75, + 0, 200, 200, 0.75, + 0, 200, 200, 0.75, + 0, 200, 200, 0.75, +}; + +void lakka_draw_background(void *data) +{ + gl_t *gl = (gl_t*)data; + + glEnable(GL_BLEND); + + gl->coords.tex_coord = gl->tex_coords; + gl->coords.color = background_color; + glBindTexture(GL_TEXTURE_2D, NULL); + + if (gl->shader) + gl->shader->use(GL_SHADER_STOCK_BLEND); + gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisable(GL_BLEND); + gl->coords.color = gl->white_color_ptr; +} + +void lakka_draw_icon(void *data, GLuint texture, float x, float y, float alpha, float rotation, float scale) +{ + gl_t *gl = (gl_t*)data; + + glViewport(x, gl->win_height - y, dim, dim); + + glEnable(GL_BLEND); + + GLfloat color[] = { + 1.0f, 1.0f, 1.0f, alpha, + 1.0f, 1.0f, 1.0f, alpha, + 1.0f, 1.0f, 1.0f, alpha, + 1.0f, 1.0f, 1.0f, alpha, + }; + + static const GLfloat vertexes_flipped[] = { + 0, 1, + 1, 1, + 0, 0, + 1, 0 + }; + + static const GLfloat vtest[] = { + 0, 0, + 1, 0, + 0, 1, + 1, 1 + }; + + gl->coords.vertex = vtest; + gl->coords.tex_coord = vtest; + gl->coords.color = color; + glBindTexture(GL_TEXTURE_2D, texture); + + if (gl->shader) + gl->shader->use(GL_SHADER_STOCK_BLEND); + + math_matrix mymat; + + math_matrix mrot; + matrix_rotate_z(&mrot, rotation); + matrix_multiply(&mymat, &mrot, &gl->mvp_no_rot); + + math_matrix mscal; + matrix_scale(&mscal, scale, scale, 1); + matrix_multiply(&mymat, &mscal, &mymat); + + gl_shader_set_coords(gl, &gl->coords, &mymat); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisable(GL_BLEND); + + gl->coords.vertex = gl->vertex_ptr; + gl->coords.tex_coord = gl->tex_coords; + gl->coords.color = gl->white_color_ptr; + + gl_set_viewport(gl, gl->win_width, gl->win_height, 0, 0); +} + +static void lakka_render(void *data) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + + gl_t *gl = (gl_t*)driver.video_data; + + gl_set_viewport(gl, gl->win_width, gl->win_height, 0, 0); + + lakka_draw_background(gl); + + lakka_draw_icon(gl, texid, 0, 0, 1, 0, 1); + + gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); + + if (rgui->need_refresh && + (g_extern.lifecycle_state & (1ULL << MODE_MENU)) + && !rgui->msg_force) + return; + + size_t begin = rgui->selection_ptr >= LAKKA_TERM_HEIGHT / 2 ? + rgui->selection_ptr - LAKKA_TERM_HEIGHT / 2 : 0; + size_t end = rgui->selection_ptr + LAKKA_TERM_HEIGHT <= rgui->selection_buf->size ? + rgui->selection_ptr + LAKKA_TERM_HEIGHT : rgui->selection_buf->size; + + // Do not scroll if all items are visible. + if (rgui->selection_buf->size <= LAKKA_TERM_HEIGHT) + begin = 0; + + if (end - begin > LAKKA_TERM_HEIGHT) + end = begin + LAKKA_TERM_HEIGHT; + + lakka_render_background(rgui); + + char title[256]; + const char *dir = NULL; + unsigned menu_type = 0; + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + + if (menu_type == RGUI_SETTINGS_CORE) + snprintf(title, sizeof(title), "CORE SELECTION %s", dir); + else if (menu_type == RGUI_SETTINGS_DEFERRED_CORE) + snprintf(title, sizeof(title), "DETECTED CORES %s", dir); + else if (menu_type == RGUI_SETTINGS_CONFIG) + snprintf(title, sizeof(title), "CONFIG %s", dir); + else if (menu_type == RGUI_SETTINGS_DISK_APPEND) + snprintf(title, sizeof(title), "DISK APPEND %s", dir); + else if (menu_type == RGUI_SETTINGS_VIDEO_OPTIONS) + strlcpy(title, "VIDEO OPTIONS", sizeof(title)); + else if (menu_type == RGUI_SETTINGS_DRIVERS) + strlcpy(title, "DRIVER OPTIONS", sizeof(title)); +#ifdef HAVE_SHADER_MANAGER + else if (menu_type == RGUI_SETTINGS_SHADER_OPTIONS) + strlcpy(title, "SHADER OPTIONS", sizeof(title)); +#endif + else if (menu_type == RGUI_SETTINGS_AUDIO_OPTIONS) + strlcpy(title, "AUDIO OPTIONS", sizeof(title)); + else if (menu_type == RGUI_SETTINGS_DISK_OPTIONS) + strlcpy(title, "DISK OPTIONS", sizeof(title)); + else if (menu_type == RGUI_SETTINGS_CORE_OPTIONS) + strlcpy(title, "CORE OPTIONS", sizeof(title)); +#ifdef HAVE_SHADER_MANAGER + else if (menu_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS) + snprintf(title, sizeof(title), "SHADER %s", dir); +#endif + else if ((menu_type == RGUI_SETTINGS_INPUT_OPTIONS) || + (menu_type == RGUI_SETTINGS_PATH_OPTIONS) || + (menu_type == RGUI_SETTINGS_OPTIONS) || + (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT || menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) || + menu_type == RGUI_SETTINGS_CUSTOM_BIND || + menu_type == RGUI_START_SCREEN || + menu_type == RGUI_SETTINGS) + snprintf(title, sizeof(title), "MENU %s", dir); + else if (menu_type == RGUI_SETTINGS_OPEN_HISTORY) + strlcpy(title, "LOAD HISTORY", sizeof(title)); +#ifdef HAVE_OVERLAY + else if (menu_type == RGUI_SETTINGS_OVERLAY_PRESET) + snprintf(title, sizeof(title), "OVERLAY %s", dir); +#endif + else if (menu_type == RGUI_BROWSER_DIR_PATH) + snprintf(title, sizeof(title), "BROWSER DIR %s", dir); +#ifdef HAVE_SCREENSHOTS + else if (menu_type == RGUI_SCREENSHOT_DIR_PATH) + snprintf(title, sizeof(title), "SCREENSHOT DIR %s", dir); +#endif + else if (menu_type == RGUI_SHADER_DIR_PATH) + snprintf(title, sizeof(title), "SHADER DIR %s", dir); + else if (menu_type == RGUI_SAVESTATE_DIR_PATH) + snprintf(title, sizeof(title), "SAVESTATE DIR %s", dir); +#ifdef HAVE_DYNAMIC + else if (menu_type == RGUI_LIBRETRO_DIR_PATH) + snprintf(title, sizeof(title), "LIBRETRO DIR %s", dir); +#endif + else if (menu_type == RGUI_CONFIG_DIR_PATH) + snprintf(title, sizeof(title), "CONFIG DIR %s", dir); + else if (menu_type == RGUI_SAVEFILE_DIR_PATH) + snprintf(title, sizeof(title), "SAVEFILE DIR %s", dir); +#ifdef HAVE_OVERLAY + else if (menu_type == RGUI_OVERLAY_DIR_PATH) + snprintf(title, sizeof(title), "OVERLAY DIR %s", dir); +#endif + else if (menu_type == RGUI_SYSTEM_DIR_PATH) + snprintf(title, sizeof(title), "SYSTEM DIR %s", dir); + else + { + if (rgui->defer_core) + snprintf(title, sizeof(title), "CONTENT %s", dir); + else + { + const char *core_name = rgui->info.library_name; + if (!core_name) + core_name = g_extern.system.info.library_name; + if (!core_name) + core_name = "No Core"; + snprintf(title, sizeof(title), "CONTENT (%s) %s", core_name, dir); + } + } + + char title_buf[256]; + menu_ticker_line(title_buf, LAKKA_TERM_WIDTH - 3, g_extern.frame_count / 15, title, true); + blit_line(rgui, LAKKA_TERM_START_X + 15, 15, title_buf, true); + + char title_msg[64]; + const char *core_name = rgui->info.library_name; + if (!core_name) + core_name = g_extern.system.info.library_name; + if (!core_name) + core_name = "No Core"; + + const char *core_version = rgui->info.library_version; + if (!core_version) + core_version = g_extern.system.info.library_version; + if (!core_version) + core_version = ""; + + snprintf(title_msg, sizeof(title_msg), "%s - %s %s", PACKAGE_VERSION, core_name, core_version); + blit_line(rgui, LAKKA_TERM_START_X + 15, (LAKKA_TERM_HEIGHT * FONT_HEIGHT_STRIDE) + LAKKA_TERM_START_Y + 2, title_msg, true); + + unsigned x, y; + size_t i; + + x = LAKKA_TERM_START_X; + y = LAKKA_TERM_START_Y; + + for (i = begin; i < end; i++, y += FONT_HEIGHT_STRIDE) + { + const char *path = 0; + unsigned type = 0; + file_list_get_at_offset(rgui->selection_buf, i, &path, &type); + char message[256]; + char type_str[256]; + + unsigned w = 19; + if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS || menu_type == RGUI_SETTINGS_CUSTOM_BIND) + w = 21; + else if (menu_type == RGUI_SETTINGS_PATH_OPTIONS) + w = 24; + +#ifdef HAVE_SHADER_MANAGER + if (type >= RGUI_SETTINGS_SHADER_FILTER && + type <= RGUI_SETTINGS_SHADER_LAST) + { + // HACK. Work around that we're using the menu_type as dir type to propagate state correctly. + if ((menu_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS) + && (menu_type_is(type) == RGUI_SETTINGS_SHADER_OPTIONS)) + { + type = RGUI_FILE_DIRECTORY; + strlcpy(type_str, "(DIR)", sizeof(type_str)); + w = 5; + } + else if (type == RGUI_SETTINGS_SHADER_OPTIONS || type == RGUI_SETTINGS_SHADER_PRESET) + strlcpy(type_str, "...", sizeof(type_str)); + else if (type == RGUI_SETTINGS_SHADER_FILTER) + snprintf(type_str, sizeof(type_str), "%s", + g_settings.video.smooth ? "Linear" : "Nearest"); + else + shader_manager_get_str(&rgui->shader, type_str, sizeof(type_str), type); + } + else +#endif + // Pretty-print libretro cores from menu. + if (menu_type == RGUI_SETTINGS_CORE || menu_type == RGUI_SETTINGS_DEFERRED_CORE) + { + if (type == RGUI_FILE_PLAIN) + { + strlcpy(type_str, "(CORE)", sizeof(type_str)); + file_list_get_alt_at_offset(rgui->selection_buf, i, &path); + w = 6; + } + else + { + strlcpy(type_str, "(DIR)", sizeof(type_str)); + type = RGUI_FILE_DIRECTORY; + w = 5; + } + } + else if (menu_type == RGUI_SETTINGS_CONFIG || +#ifdef HAVE_OVERLAY + menu_type == RGUI_SETTINGS_OVERLAY_PRESET || +#endif + menu_type == RGUI_SETTINGS_DISK_APPEND || + menu_type_is(menu_type) == RGUI_FILE_DIRECTORY) + { + if (type == RGUI_FILE_PLAIN) + { + strlcpy(type_str, "(FILE)", sizeof(type_str)); + w = 6; + } + else if (type == RGUI_FILE_USE_DIRECTORY) + { + *type_str = '\0'; + w = 0; + } + else + { + strlcpy(type_str, "(DIR)", sizeof(type_str)); + type = RGUI_FILE_DIRECTORY; + w = 5; + } + } + else if (menu_type == RGUI_SETTINGS_OPEN_HISTORY) + { + *type_str = '\0'; + w = 0; + } + else if (type >= RGUI_SETTINGS_CORE_OPTION_START) + strlcpy(type_str, + core_option_get_val(g_extern.system.core_options, type - RGUI_SETTINGS_CORE_OPTION_START), + sizeof(type_str)); + else + menu_set_settings_label(type_str, sizeof(type_str), &w, type); + + char entry_title_buf[256]; + char type_str_buf[64]; + bool selected = i == rgui->selection_ptr; + + strlcpy(entry_title_buf, path, sizeof(entry_title_buf)); + strlcpy(type_str_buf, type_str, sizeof(type_str_buf)); + + if ((type == RGUI_FILE_PLAIN || type == RGUI_FILE_DIRECTORY)) + menu_ticker_line(entry_title_buf, LAKKA_TERM_WIDTH - (w + 1 + 2), g_extern.frame_count / 15, path, selected); + else + menu_ticker_line(type_str_buf, w, g_extern.frame_count / 15, type_str, selected); + + snprintf(message, sizeof(message), "%c %-*.*s %-*s", + selected ? '>' : ' ', + LAKKA_TERM_WIDTH - (w + 1 + 2), LAKKA_TERM_WIDTH - (w + 1 + 2), + entry_title_buf, + w, + type_str_buf); + + blit_line(rgui, x, y, message, selected); + } + +#ifdef GEKKO + const char *message_queue; + + if (rgui->msg_force) + { + message_queue = msg_queue_pull(g_extern.msg_queue); + rgui->msg_force = false; + } + else + message_queue = driver.current_msg; + + rgui_render_messagebox(rgui, message_queue); +#endif + + if (rgui->keyboard.display) + { + char msg[1024]; + const char *str = *rgui->keyboard.buffer; + if (!str) + str = ""; + snprintf(msg, sizeof(msg), "%s\n%s", rgui->keyboard.label, str); + lakka_render_messagebox(rgui, msg); + } +} + +static void lakka_set_texture(void *data, bool enable) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + + if (driver.video_poke && driver.video_poke->set_texture_enable) + driver.video_poke->set_texture_frame(driver.video_data, menu_framebuf, + enable, rgui->width, rgui->height, 1.0f); +} + +static void lakka_init_assets(void *data) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + + if (!rgui) + return; + + menu_texture = (struct texture_image*)calloc(1, sizeof(*menu_texture)); + texture_image_load(g_extern.menu_texture_path, menu_texture); + + texid = png_texture_load("/usr/share/retroarch/cavestory.png", &dim, &dim); + + lakka_set_texture(rgui, true); +} + +static void *lakka_init(void) +{ + uint16_t *framebuf = menu_framebuf; + size_t framebuf_pitch; + + rgui_handle_t *rgui = (rgui_handle_t*)calloc(1, sizeof(*rgui)); + + rgui->frame_buf = framebuf; + rgui->width = 320; + rgui->height = 240; + framebuf_pitch = rgui->width * sizeof(uint16_t); + + rgui->frame_buf_pitch = framebuf_pitch; + + bool ret = rguidisp_init_font(rgui); + + if (!ret) + { + RARCH_ERR("No font bitmap or binary, abort"); + /* TODO - should be refactored - perhaps don't do rarch_fail but instead + * exit program */ + g_extern.lifecycle_state &= ~((1ULL << MODE_MENU) | (1ULL << MODE_GAME)); + return NULL; + } + + lakka_init_assets(rgui); + + return rgui; +} + +static void lakka_free_assets(void *data) +{ + texture_image_free(menu_texture); + menu_texture_inited = false; +} + +static void lakka_free(void *data) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + if (rgui->alloc_font) + free((uint8_t*)rgui->font); +} + +int lakka_input_postprocess(void *data, uint64_t old_state) +{ + (void)data; + + int ret = 0; + + if ((rgui->trigger_state & (1ULL << RARCH_MENU_TOGGLE)) && + g_extern.main_is_init && + !g_extern.libretro_dummy) + { + g_extern.lifecycle_state |= (1ULL << MODE_GAME); + ret = -1; + } + + return ret; +} + + +const menu_ctx_driver_t menu_ctx_lakka = { + lakka_set_texture, + lakka_render_messagebox, + lakka_render, + lakka_init, + lakka_free, + lakka_init_assets, + lakka_free_assets, + NULL, + NULL, + "lakka", +}; diff --git a/frontend/menu/menu_context.c b/frontend/menu/menu_context.c index 3718d3610b..e721af0944 100644 --- a/frontend/menu/menu_context.c +++ b/frontend/menu/menu_context.c @@ -31,6 +31,9 @@ static const menu_ctx_driver_t *menu_ctx_drivers[] = { #endif #if defined(HAVE_RGUI) &menu_ctx_rgui, +#endif +#if defined(HAVE_LAKKA) + &menu_ctx_lakka, #endif NULL // zero length array is not valid }; diff --git a/frontend/menu/menu_context.h b/frontend/menu/menu_context.h index 306e42241a..0c8f3a557b 100644 --- a/frontend/menu/menu_context.h +++ b/frontend/menu/menu_context.h @@ -43,6 +43,7 @@ typedef struct menu_ctx_driver extern const menu_ctx_driver_t menu_ctx_rmenu; extern const menu_ctx_driver_t menu_ctx_rmenu_xui; extern const menu_ctx_driver_t menu_ctx_rgui; +extern const menu_ctx_driver_t menu_ctx_lakka; const menu_ctx_driver_t *menu_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize. bool menu_ctx_init_first(const menu_ctx_driver_t **driver, void **handle); // Finds first suitable driver and initializes. diff --git a/griffin/griffin.c b/griffin/griffin.c index ce7d9692f8..d2559174ab 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -574,13 +574,22 @@ MENU #include "../file_list.c" -#if defined(HAVE_RMENU) +#ifdef HAVE_RMENU #include "../frontend/menu/disp/rmenu.c" -#elif defined(HAVE_RGUI) +#endif + +#ifdef HAVE_RGUI #include "../frontend/menu/disp/rgui.c" -#elif defined(HAVE_RMENU_XUI) +#endif + +#ifdef HAVE_RMENU_XUI #include "../frontend/menu/disp/rmenu_xui.cpp" #endif + +#if defined(HAVE_LAKKA) && defined(HAVE_OPENGL) +#include "../frontend/menu/disp/lakka.c" +#endif + #endif #ifdef __cplusplus