initial ui work

This commit is contained in:
Anthony Pesch 2017-10-05 22:27:44 -04:00
parent 47b600e2da
commit ce86c18abf
24 changed files with 44543 additions and 108 deletions

View File

@ -119,6 +119,9 @@ if(ARCH_X64)
list(APPEND RELIB_INCLUDES deps/xbyak)
endif()
# zlib, reused from libchdr
list(APPEND RELIB_INCLUDES deps/chdr/deps/zlib-1.2.11)
#--------------------------------------------------
# optional libs
#--------------------------------------------------
@ -245,7 +248,6 @@ set(RELIB_SOURCES
src/jit/jit.c
src/jit/pass_stats.c
src/render/gl_backend.c
src/imgui.cc
src/options.c
src/stats.c)
@ -345,14 +347,21 @@ endif()
#--------------------------------------------------
if(BUILD_LIBRETRO)
set(REDREAM_SOURCES ${RELIB_SOURCES} src/host/retro_host.c src/emulator.c)
set(REDREAM_SOURCES ${RELIB_SOURCES}
src/host/retro_host.c
src/emulator.c)
set(REDREAM_INCLUDES ${RELIB_INCLUDES} deps/libretro/include)
set(REDREAM_LIBS ${RELIB_LIBS})
set(REDREAM_DEFS ${RELIB_DEFS})
set(REDREAM_FLAGS ${RELIB_FLAGS})
else()
set(REDREAM_SOURCES ${RELIB_SOURCES} src/host/sdl_host.c src/emulator.c src/tracer.c)
set(REDREAM_INCLUDES ${RELIB_INCLUDES})
set(REDREAM_SOURCES ${RELIB_SOURCES}
src/host/sdl_host.c
src/emulator.c
src/imgui.cc
src/tracer.c
src/ui.c)
set(REDREAM_INCLUDES ${RELIB_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR})
set(REDREAM_LIBS ${RELIB_LIBS} ${IMGUI_LIBS} ${SDL_LIBS})
set(REDREAM_DEFS ${RELIB_DEFS} HAVE_GDBSERVER HAVE_IMGUI)
set(REDREAM_FLAGS ${RELIB_FLAGS})

18
assets/README.md Normal file
View File

@ -0,0 +1,18 @@
All of the assets here are compressed with zlib and #include'd as code in the project.
For png files, the data is first converted into raw rgb / rgba data with GraphicMagick:
```
gm convert image.png image.rgb
```
The data is then compressed with:
```
openssl zlib -e < image.rgb > image.gz
```
The data is finally transformed into a C-style array with:
```
xxd -i image.gz
```
Note, the width, height and uncompressed sizes are all filled in manually alongside the xxd output.

19548
assets/clouds.inc Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/clouds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

1222
assets/disc.inc Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/disc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

9491
assets/opensans-regular.inc Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/opensans-regular.ttf Normal file

Binary file not shown.

3916
assets/oswald-medium.inc Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/oswald-medium.ttf Normal file

Binary file not shown.

View File

@ -84,7 +84,7 @@ SOURCES_C := $(CORE_DIR)/src/core/assert.c \
$(CORE_DIR)/src/options.c \
$(CORE_DIR)/src/stats.c
SOURCES_CXX := $(CORE_DIR)/src/imgui.cc
SOURCES_CXX :=
SOURCES_CPP :=

View File

@ -33,9 +33,11 @@ const char *fs_appdir();
void fs_set_appdir(const char *path);
int fs_userdir(char *userdir, size_t size);
int fs_mediadirs(char *dirs, int num, size_t size);
void fs_dirname(const char *path, char *dir, size_t size);
void fs_basename(const char *path, char *base, size_t size);
void fs_realpath(const char *path, char *resolved, size_t size);
int fs_exists(const char *path);
int fs_isdir(const char *path);

View File

@ -4,8 +4,111 @@
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "core/core.h"
#include "core/filesystem.h"
int fs_mkdir(const char *path) {
int res = mkdir(path, 0755);
return res == 0 || errno == EEXIST;
}
int fs_isfile(const char *path) {
struct stat buffer;
if (stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFREG) == S_IFREG;
}
int fs_isdir(const char *path) {
struct stat buffer;
if (stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFDIR) == S_IFDIR;
}
int fs_exists(const char *path) {
struct stat buffer;
return stat(path, &buffer) == 0;
}
void fs_realpath(const char *path, char *resolved, size_t size) {
char tmp[PATH_MAX];
if (realpath(path, tmp)) {
strncpy(resolved, tmp, size);
} else {
strncpy(resolved, path, size);
}
}
int fs_mediadirs(char *dirs, int num, size_t size) {
char *ptr = dirs;
char *end = dirs + size * num;
/* search in the home directory */
static const char *home_search[] = {"Desktop", "Documents", "Downloads",
"Music", "Pictures", "Videos"};
char home[PATH_MAX];
int res = fs_userdir(home, sizeof(home));
if (res) {
for (int i = 0; i < ARRAY_SIZE(home_search); i++) {
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s" PATH_SEPARATOR "%s", home,
home_search[i]);
if (!fs_isdir(path)) {
continue;
}
if (ptr < end) {
strncpy(ptr, path, size);
ptr += size;
}
}
}
/* search for additional mounts */
const char *mnt_search[] = {
#if PLATFORM_DARWIN
"/Volumes",
#else
"/media",
"/mnt"
#endif
};
for (int i = 0; i < ARRAY_SIZE(mnt_search); i++) {
const char *path = mnt_search[i];
DIR *dir = opendir(path);
if (!dir) {
continue;
}
struct dirent *ent = NULL;
while ((ent = readdir(dir)) != NULL) {
const char *dname = ent->d_name;
/* ignore special directories */
if (!strcmp(dname, "..") || !strcmp(dname, ".")) {
continue;
}
if (ptr < end) {
snprintf(ptr, size, "%s" PATH_SEPARATOR "%s", path, dname);
ptr += size;
}
}
closedir(dir);
}
return (ptr - dirs) / size;
}
int fs_userdir(char *userdir, size_t size) {
const char *home = getenv("HOME");
@ -22,29 +125,3 @@ int fs_userdir(char *userdir, size_t size) {
return 0;
}
int fs_exists(const char *path) {
struct stat buffer;
return stat(path, &buffer) == 0;
}
int fs_isdir(const char *path) {
struct stat buffer;
if (stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFDIR) == S_IFDIR;
}
int fs_isfile(const char *path) {
struct stat buffer;
if (stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFREG) == S_IFREG;
}
int fs_mkdir(const char *path) {
int res = mkdir(path, 0755);
return res == 0 || errno == EEXIST;
}

View File

@ -1,3 +1,4 @@
#include <Windows.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@ -5,6 +6,61 @@
#include <userenv.h>
#include "core/filesystem.h"
int fs_mkdir(const char *path) {
int res = _mkdir(path);
return res == 0 || errno == EEXIST;
}
int fs_isfile(const char *path) {
struct _stat buffer;
if (_stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFREG) == S_IFREG;
}
int fs_isdir(const char *path) {
struct _stat buffer;
if (_stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFDIR) == S_IFDIR;
}
int fs_exists(const char *path) {
struct _stat buffer;
return _stat(path, &buffer) == 0;
}
void fs_realpath(const char *path, char *resolved, size_t size) {
if (!_fullpath(resolved, path, size)) {
strncpy(resolved, path, size);
}
}
int fs_mediadirs(char *dirs, int num, size_t size) {
char *ptr = dirs;
char *end = dirs + size * num;
char path[PATH_MAX];
DWORD drives = GetLogicalDrives();
int max_drives = (int)sizeof(drives) * 8;
for (int i = 0; i < max_drives; i++) {
if (!(drives & (1 << i))) {
continue;
}
if (ptr < end) {
snprintf(path, sizeof(path), "%c:" PATH_SEPARATOR, 'A' + i);
strncpy(ptr, path, size);
ptr += size;
}
}
return 1;
}
int fs_userdir(char *userdir, size_t size) {
HANDLE accessToken = NULL;
HANDLE processHandle = GetCurrentProcess();
@ -20,29 +76,3 @@ int fs_userdir(char *userdir, size_t size) {
CloseHandle(accessToken);
return 1;
}
int fs_exists(const char *path) {
struct _stat buffer;
return _stat(path, &buffer) == 0;
}
int fs_isdir(const char *path) {
struct _stat buffer;
if (_stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFDIR) == S_IFDIR;
}
int fs_isfile(const char *path) {
struct _stat buffer;
if (_stat(path, &buffer) != 0) {
return 0;
}
return (buffer.st_mode & S_IFREG) == S_IFREG;
}
int fs_mkdir(const char *path) {
int res = _mkdir(path);
return res == 0 || errno == EEXIST;
}

View File

@ -11,5 +11,12 @@ void audio_push(struct host *host, const int16_t *data, int frames);
/* video */
/* input */
int input_max_controllers(struct host *host);
const char *input_controller_name(struct host *host, int port);
/* ui */
int ui_load_game(struct host *host, const char *path);
void ui_opened(struct host *host);
void ui_closed(struct host *host);
#endif

View File

@ -12,12 +12,13 @@
#include "options.h"
#include "render/render_backend.h"
#include "tracer.h"
#include "ui.h"
/*
* sdl host implementation
*/
#define AUDIO_FREQ 44100
#define VIDEO_DEFAULT_WIDTH 640
#define VIDEO_DEFAULT_WIDTH 853
#define VIDEO_DEFAULT_HEIGHT 480
#define INPUT_MAX_CONTROLLERS 4
@ -31,6 +32,7 @@ struct host {
struct SDL_Window *win;
int closed;
struct ui *ui;
struct emu *emu;
struct tracer *tracer;
struct imgui *imgui;
@ -284,6 +286,10 @@ static void video_shutdown(struct host *host) {
emu_vid_destroyed(host->emu);
}
if (host->ui) {
ui_vid_destroyed(host->ui);
}
r_destroy(host->video.r);
video_destroy_context(host, host->video.ctx);
}
@ -292,6 +298,10 @@ static int video_init(struct host *host) {
host->video.ctx = video_create_context(host);
host->video.r = r_create(host->video.width, host->video.height);
if (host->ui) {
ui_vid_created(host->ui, host->video.r);
}
if (host->emu) {
emu_vid_created(host->emu, host->video.r);
}
@ -505,6 +515,10 @@ static void input_keydown(struct host *host, int port, int key,
break;
}
if (host->ui && ui_keydown(host->ui, key, value)) {
continue;
}
if (host->emu && emu_keydown(host->emu, port, key, value)) {
continue;
}
@ -576,6 +590,28 @@ static int input_init(struct host *host) {
return 1;
}
int input_max_controllers(struct host *host) {
return INPUT_MAX_CONTROLLERS;
}
const char *input_controller_name(struct host *host, int port) {
CHECK(port >= 0 && port < INPUT_MAX_CONTROLLERS);
SDL_GameController *ctrl = host->input.controllers[port];
return SDL_GameControllerName(ctrl);
}
/*
* ui
*/
void ui_closed(struct host *host) {}
void ui_opened(struct host *host) {}
int ui_load_game(struct host *host, const char *path) {
return emu_load(host->emu, path);
}
/*
* internal
*/
@ -874,6 +910,7 @@ int main(int argc, char **argv) {
if (load && strstr(load, ".trace")) {
host->tracer = tracer_create(host);
} else {
host->ui = ui_create(host);
host->emu = emu_create(host);
}
@ -898,41 +935,56 @@ int main(int argc, char **argv) {
}
}
} else if (host->emu) {
if (emu_load(host->emu, load)) {
while (!host->closed) {
/* even though the emulator itself will poll for events when updating
controller input, the main loop needs to also poll to ensure the
close event is received */
host_poll_events(host);
int success = 0;
/* only step the emulator if the available audio is running low. this
syncs the emulation speed with the host audio clock. note however,
if audio is disabled, the emulator will run unthrottled */
if (!audio_buffer_low(host)) {
continue;
}
if (load || OPTION_bios) {
success = emu_load(host->emu, load);
}
/* reset vertex buffers */
imgui_begin_frame(host->imgui);
if (!success) {
/* if nothing is loaded, open the game select ui */
ui_set_page(host->ui, UI_PAGE_GAMES);
}
/* render emulator output and build up imgui buffers */
emu_render_frame(host->emu);
while (!host->closed) {
/* even though the emulator itself will poll for events when updating
controller input, the main loop needs to also poll to ensure the
close event is received */
host_poll_events(host);
/* overlay imgui */
imgui_end_frame(host->imgui);
/* flip profiler at end of frame */
int64_t now = time_nanoseconds();
prof_flip(time_nanoseconds());
host_swap_window(host);
/* only step the emulator if the available audio is running low. this
syncs the emulation speed with the host audio clock. note however,
if audio is disabled, the emulator will run unthrottled */
if (!audio_buffer_low(host)) {
continue;
}
/* reset vertex buffers */
imgui_begin_frame(host->imgui);
/* render emulator output and build up imgui buffers */
emu_render_frame(host->emu);
ui_build_menus(host->ui);
/* overlay imgui */
imgui_end_frame(host->imgui);
/* flip profiler at end of frame */
int64_t now = time_nanoseconds();
prof_flip(time_nanoseconds());
host_swap_window(host);
}
}
}
host_shutdown(host);
if (host->ui) {
ui_destroy(host->ui);
host->ui = NULL;
}
if (host->emu) {
emu_destroy(host->emu);
}

View File

@ -1,12 +1,11 @@
#ifdef HAVE_IMGUI
#define IMGUI_IMPLEMENTATION
#define IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_PLACEMENT_NEW
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#endif
extern "C" {
#include <zlib.h>
#include "core/core.h"
#include "core/time.h"
#include "host/keycode.h"
@ -14,8 +13,11 @@ extern "C" {
#include "render/render_backend.h"
}
#define IMFONT_MAX_HEIGHT 128
struct imgui {
struct render_backend *r;
struct ImFont *fonts[IMFONT_NUM_FONTS][IMFONT_MAX_HEIGHT];
int64_t time;
int alt[2];
int ctrl[2];
@ -23,8 +25,210 @@ struct imgui {
uint16_t keys[K_NUM_KEYS];
};
/* maintain global pointer for ig* functions */
static struct imgui *g_imgui;
#include "assets/fontawesome-webfont.inc"
#include "assets/opensans-regular.inc"
#include "assets/oswald-medium.inc"
static ImFont *imgui_get_font(struct imgui *imgui, int id, int font_height);
/*
* imgui extensions
*/
using namespace ImGui;
namespace ImGui {
static ImRect ButtonBox(const ImVec2 &label_size, const ImVec2 &req_size) {
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
ImGuiWindow *window = GetCurrentWindow();
ImVec2 pos = window->DC.CursorPos;
ImVec2 size =
CalcItemSize(req_size, label_size.x + style.FramePadding.x * 2.0f,
label_size.y + style.FramePadding.y * 2.0f);
return ImRect(pos, pos + size);
}
static ImU32 SelectableColor(bool selected, bool hovered, bool held) {
if (selected || (hovered && held)) {
return ImGui::GetColorU32(ImGuiCol_ButtonActive);
}
if (hovered) {
return ImGui::GetColorU32(ImGuiCol_ButtonHovered);
}
return ImGui::GetColorU32(ImGuiCol_Button);
}
static void RenderCircularNavHighlight(const ImRect &bb, ImGuiID id) {
ImGuiContext &g = *GImGui;
if (id != g.NavId || g.NavDisableHighlight) {
return;
}
ImGuiWindow *window = GetCurrentWindow();
const float THICKNESS = 2.0f;
const float DISTANCE = 3.0f + THICKNESS * 0.5f;
ImRect display_rect(bb.Min - ImVec2(DISTANCE, DISTANCE),
bb.Max + ImVec2(DISTANCE, DISTANCE));
if (!window->ClipRect.Contains(display_rect)) {
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
}
const ImRect draw_rect(
display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f),
display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f));
const ImVec2 center = draw_rect.GetCenter();
float radius = draw_rect.GetWidth() / 2.0f;
window->DrawList->AddCircle(
center, radius, GetColorU32(ImGuiCol_NavHighlight), 48, THICKNESS);
if (!window->ClipRect.Contains(display_rect)) {
window->DrawList->PopClipRect();
}
}
}
int igDiscButton(ImTextureID user_texture_id, float item_diameter,
float draw_diameter, const struct ImVec2 uv0,
const struct ImVec2 uv1) {
ImGuiWindow *window = GetCurrentWindow();
if (window->SkipItems) {
return 0;
}
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
ImGui::PushID((void *)user_texture_id);
const ImGuiID id = window->GetID("#image");
ImGui::PopID();
const ImVec2 item_size = {item_diameter, item_diameter};
const ImVec2 item_pos = window->DC.CursorPos;
const ImRect item_bb(item_pos, item_pos + item_size);
const ImVec2 draw_size = {draw_diameter, draw_diameter};
const ImVec2 draw_pos = {item_pos.x - (draw_diameter - item_diameter) / 2.0f,
item_pos.y - (draw_diameter - item_diameter) / 2.0f};
const ImRect draw_bb(draw_pos, draw_pos + draw_size);
ImGui::ItemSize(item_bb);
if (!ImGui::ItemAdd(item_bb, id)) {
return 0;
}
bool hovered, held;
bool pressed = ButtonBehavior(item_bb, id, &hovered, &held);
ImGui::RenderCircularNavHighlight(draw_bb, id);
window->DrawList->AddImage(user_texture_id, draw_bb.Min, draw_bb.Max, uv0,
uv1, 0xffffffff);
return (int)pressed;
}
int igOptionString(const char *label, const char *value, struct ImVec2 size) {
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems) {
return 0;
}
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const ImVec2 value_size = ImGui::CalcTextSize(value, NULL, true);
const ImVec2 total_size(label_size.x + value_size.x,
MAX(label_size.y, value_size.y));
const ImRect bb = ImGui::ButtonBox(total_size, size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id)) {
return 0;
}
ImGuiButtonFlags flags = 0;
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) {
flags |= ImGuiButtonFlags_Repeat;
}
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
const ImU32 col = SelectableColor(false, hovered, held);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
ImGui::RenderTextClipped(bb.Min + style.FramePadding,
bb.Max - style.FramePadding, label, NULL,
&label_size, ImVec2(0.0f, 0.5f), &bb);
ImGui::RenderTextClipped(bb.Min + style.FramePadding,
bb.Max - style.FramePadding, value, NULL,
&value_size, ImVec2(1.0f, 0.5f), &bb);
return (int)pressed;
}
int igOptionInt(const char *label, int value, struct ImVec2 size) {
char value_str[128];
snprintf(value_str, sizeof(value_str), "%d", value);
return igOptionString(label, value_str, size);
}
int igTab(const char *label, struct ImVec2 size, int selected) {
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems) {
return 0;
}
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const ImRect bb = ImGui::ButtonBox(label_size, size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id)) {
return 0;
}
ImGuiButtonFlags flags = 0;
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) {
flags |= ImGuiButtonFlags_Repeat;
}
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
const ImU32 col = SelectableColor(selected, hovered, held);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
ImGui::RenderTextClipped(bb.Min + style.FramePadding,
bb.Max - style.FramePadding, label, NULL,
&label_size, style.ButtonTextAlign, &bb);
return (int)pressed;
}
void igPushFontEx(int id, int font_height) {
ImGuiIO &io = ImGui::GetIO();
ImFont *font = imgui_get_font(g_imgui, id, font_height);
ImGui::PushFont(font);
}
/*
* imgui implementation
*/
static void imgui_update_font_tex(struct imgui *imgui) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
/* destroy old texture first */
@ -48,11 +252,74 @@ static void imgui_update_font_tex(struct imgui *imgui) {
font_tex = r_create_texture(imgui->r, PXL_RGBA, FILTER_BILINEAR, WRAP_REPEAT,
WRAP_REPEAT, 0, width, height, pixels);
io.Fonts->TexID = (void *)(intptr_t)font_tex;
#endif
}
static ImFont *imgui_get_font(struct imgui *imgui, int id, int font_height) {
CHECK(id >= 0 && id < IMFONT_NUM_FONTS);
CHECK(font_height >= 0 && font_height < IMFONT_MAX_HEIGHT);
ImFont **font = &g_imgui->fonts[id][font_height];
if (*font) {
return *font;
}
int font_len = 0;
int font_gz_len = 0;
const uint8_t *font_gz = NULL;
switch (id) {
case IMFONT_OSWALD_MEDIUM:
font_len = oswald_medium_len;
font_gz_len = oswald_medium_gz_len;
font_gz = oswald_medium_gz;
break;
case IMFONT_OPENSANS_REGULAR:
font_len = opensans_regular_len;
font_gz_len = opensans_regular_len;
font_gz = opensans_regular_gz;
break;
default:
LOG_FATAL("igPushFontEx unsupported font %d", id);
break;
}
ImGuiIO &io = ImGui::GetIO();
/* load base font. note, AddFontFromMemoryTTF takes ownership of font_data, so
it doesn't need to be freed */
{
unsigned long tmp_len = font_len;
uint8_t *font_data = (uint8_t *)malloc(tmp_len);
int res = uncompress(font_data, &tmp_len, font_gz, font_gz_len);
CHECK_EQ(res, Z_OK);
*font = io.Fonts->AddFontFromMemoryTTF(font_data, tmp_len,
(float)font_height, NULL, NULL);
CHECK_NOTNULL(*font);
}
/* merge fontawesome icons */
{
static const ImWchar fa_ranges[] = {IMICON_RANGES, 0};
ImFontConfig config;
config.MergeMode = true;
unsigned long tmp_len = fontawesome_webfont_len;
uint8_t *font_data = (uint8_t *)malloc(tmp_len);
int res = uncompress(font_data, &tmp_len, fontawesome_webfont_gz,
fontawesome_webfont_gz_len);
CHECK_EQ(res, Z_OK);
*font = io.Fonts->AddFontFromMemoryTTF(
font_data, tmp_len, (float)font_height, &config, fa_ranges);
CHECK_NOTNULL(*font);
}
imgui_update_font_tex(imgui);
return *font;
}
void imgui_end_frame(struct imgui *imgui) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
int width = (int)io.DisplaySize.x;
@ -103,7 +370,6 @@ void imgui_end_frame(struct imgui *imgui) {
r_end_ui_surfaces(imgui->r);
}
#endif
}
void imgui_begin_frame(struct imgui *imgui) {
@ -111,7 +377,6 @@ void imgui_begin_frame(struct imgui *imgui) {
int64_t delta_time = now - imgui->time;
imgui->time = now;
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
int width = r_width(imgui->r);
@ -121,12 +386,22 @@ void imgui_begin_frame(struct imgui *imgui) {
io.MouseWheel = 0.0;
io.DisplaySize = ImVec2((float)width, (float)height);
/* update navigation inputs */
io.NavInputs[ImGuiNavInput_PadActivate] = imgui->keys[K_CONT_A];
io.NavInputs[ImGuiNavInput_PadCancel] = imgui->keys[K_CONT_B];
io.NavInputs[ImGuiNavInput_PadUp] =
imgui->keys[K_CONT_DPAD_UP] || imgui->keys[K_CONT_JOYY_NEG];
io.NavInputs[ImGuiNavInput_PadDown] =
imgui->keys[K_CONT_DPAD_DOWN] || imgui->keys[K_CONT_JOYY_POS];
io.NavInputs[ImGuiNavInput_PadLeft] =
imgui->keys[K_CONT_DPAD_LEFT] || imgui->keys[K_CONT_JOYX_NEG];
io.NavInputs[ImGuiNavInput_PadRight] =
imgui->keys[K_CONT_DPAD_RIGHT] || imgui->keys[K_CONT_JOYX_POS];
ImGui::NewFrame();
#endif
}
int imgui_keydown(struct imgui *imgui, int key, uint16_t value) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
if (key == K_MWHEELUP) {
@ -152,40 +427,33 @@ int imgui_keydown(struct imgui *imgui, int key, uint16_t value) {
imgui->keys[key] = value;
io.KeysDown[key] = (bool)value;
}
#endif
return 0;
}
void imgui_mousemove(struct imgui *imgui, int x, int y) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
io.MousePos = ImVec2((float)x, (float)y);
#endif
}
void imgui_destroy(struct imgui *imgui) {
#ifdef HAVE_IMGUI
ImGui::Shutdown();
free(imgui);
#endif
}
void imgui_vid_destroyed(struct imgui *imgui) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
/* free up cached font data */
io.Fonts->Clear();
memset(imgui->fonts, 0, sizeof(imgui->fonts));
imgui_update_font_tex(imgui);
imgui->r = NULL;
#endif
}
void imgui_vid_created(struct imgui *imgui, struct render_backend *r) {
#ifdef HAVE_IMGUI
ImGuiIO &io = ImGui::GetIO();
imgui->r = r;
@ -193,11 +461,9 @@ void imgui_vid_created(struct imgui *imgui, struct render_backend *r) {
/* register default font */
io.Fonts->AddFontDefault();
imgui_update_font_tex(imgui);
#endif
}
struct imgui *imgui_create() {
#ifdef HAVE_IMGUI
struct imgui *imgui =
reinterpret_cast<struct imgui *>(calloc(1, sizeof(struct imgui)));
@ -212,8 +478,7 @@ struct imgui *imgui_create() {
io.SetClipboardTextFn = nullptr;
io.GetClipboardTextFn = nullptr;
g_imgui = imgui;
return imgui;
#else
return NULL;
#endif
}

View File

@ -1,18 +1,43 @@
#ifndef IMGUI_H
#define IMGUI_H
#include "host/keycode.h"
#ifdef HAVE_IMGUI
#ifndef IMGUI_IMPLEMENTATION
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include <cimgui/cimgui.h>
#endif
#endif
#include "host/keycode.h"
struct imgui;
struct render_backend;
/* imgui extensions */
enum {
IMFONT_OSWALD_MEDIUM,
IMFONT_OPENSANS_REGULAR,
IMFONT_NUM_FONTS,
};
#define IMICON_RANGES \
0xf00d, 0xf00d, 0xf028, 0xf028, 0xf067, 0xf067, 0xf07c, 0xf07c, 0xf0a0, \
0xf0a0, 0xf108, 0xf108, 0xf11b, 0xf11b
#define IMICON_TIMES u8"\uf00d"
#define IMICON_VOLUME_UP u8"\uf028"
#define IMICON_PLUS u8"\uf067"
#define IMICON_FOLDER_OPEN u8"\uf07c"
#define IMICON_HDD u8"\uf0a0"
#define IMICON_DESKTOP u8"\uf108"
#define IMICON_GAMEPAD u8"\uf11b"
void igPushFontEx(int id, int font_height);
int igTab(const char *label, struct ImVec2 size, int selected);
int igOptionInt(const char *label, int value, struct ImVec2 size);
int igOptionString(const char *label, const char *value, struct ImVec2 size);
int igDiscButton(ImTextureID user_texture_id, float item_diameter,
float draw_diameter, const struct ImVec2 uv0,
const struct ImVec2 uv1);
/* imgui implementation */
struct imgui *imgui_create();
void imgui_destroy(struct imgui *imgui);

View File

@ -37,6 +37,7 @@ struct button_map BUTTONS[] = {
const int NUM_BUTTONS = ARRAY_SIZE(BUTTONS);
/* host */
DEFINE_OPTION_INT(bios, 0, "Boot to bios");
DEFINE_OPTION_INT(audio, 1, "Enable audio");
DEFINE_PERSISTENT_OPTION_INT(latency, 50, "Preferred audio latency in ms");
DEFINE_PERSISTENT_OPTION_INT(fullscreen, 0, "Start window fullscreen");

View File

@ -18,6 +18,7 @@ extern struct button_map BUTTONS[];
extern const int NUM_BUTTONS;
/* host */
DECLARE_OPTION_INT(bios);
DECLARE_OPTION_INT(audio);
DECLARE_OPTION_INT(latency);
DECLARE_OPTION_INT(fullscreen);

1551
src/ui.c Normal file

File diff suppressed because it is too large Load Diff

34
src/ui.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef UI_H
#define UI_H
#include "host/keycode.h"
struct ui;
struct host;
struct render_backend;
enum {
UI_PAGE_NONE = -1,
UI_PAGE_GAMES = 0,
UI_PAGE_OPTIONS,
UI_PAGE_LIBRARY,
UI_PAGE_AUDIO,
UI_PAGE_VIDEO,
UI_PAGE_INPUT,
UI_PAGE_CONTROLLERS,
UI_PAGE_KEYBOARD,
UI_NUM_PAGES,
};
struct ui *ui_create(struct host *host);
void ui_destroy(struct ui *ui);
void ui_vid_created(struct ui *ui, struct render_backend *r);
void ui_vid_destroyed(struct ui *ui);
void ui_mousemove(struct ui *ui, int x, int y);
int ui_keydown(struct ui *ui, int key, int16_t value);
void ui_build_menus(struct ui *ui);
void ui_set_page(struct ui *ui, int page_index);
#endif