mirror of https://github.com/inolen/redream.git
initial ui work
This commit is contained in:
parent
47b600e2da
commit
ce86c18abf
|
@ -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})
|
||||
|
|
|
@ -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.
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 281 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -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 :=
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
309
src/imgui.cc
309
src/imgui.cc
|
@ -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
|
||||
}
|
||||
|
|
33
src/imgui.h
33
src/imgui.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue