lemur/graphics.c

240 lines
8.4 KiB
C
Raw Normal View History

#include <graphics.h>
2024-10-26 16:46:26 +00:00
#include <stdio.h>
2024-10-28 03:33:17 +00:00
#define SCREEN_WIDTH 384
#define SCREEN_HEIGHT 224
#define MENU_HEIGHT 20
#define WINDOW_WIDTH SCREEN_WIDTH
#define WINDOW_HEIGHT (SCREEN_HEIGHT + MENU_HEIGHT)
static void setColorTable(struct nk_color *table) {
table[NK_COLOR_TEXT] = nk_rgb(80, 80, 80);
table[NK_COLOR_WINDOW] = nk_rgb(255, 255, 255);
table[NK_COLOR_HEADER] = nk_rgb(40, 40, 40);
table[NK_COLOR_BORDER] = nk_rgb(175, 175, 175);
table[NK_COLOR_BUTTON] = nk_rgb(255, 255, 255);
table[NK_COLOR_BUTTON_HOVER] = nk_rgb(215, 215, 215);
table[NK_COLOR_BUTTON_ACTIVE] = nk_rgb(175, 175, 175);
table[NK_COLOR_TOGGLE] = nk_rgb(100, 100, 100);
table[NK_COLOR_TOGGLE_HOVER] = nk_rgb(120, 120, 120);
table[NK_COLOR_TOGGLE_CURSOR] = nk_rgb(45, 45, 45);
table[NK_COLOR_SELECT] = nk_rgb(45, 45, 45);
table[NK_COLOR_SELECT_ACTIVE] = nk_rgb(35, 35, 35);
table[NK_COLOR_SLIDER] = nk_rgb(38, 38, 38);
table[NK_COLOR_SLIDER_CURSOR] = nk_rgb(100, 100, 100);
table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgb(120, 120, 120);
table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgb(150, 150, 150);
table[NK_COLOR_PROPERTY] = nk_rgb(38, 38, 38);
table[NK_COLOR_EDIT] = nk_rgb(38, 38, 38);
table[NK_COLOR_EDIT_CURSOR] = nk_rgb(175, 175, 175);
table[NK_COLOR_COMBO] = nk_rgb(45, 45, 45);
table[NK_COLOR_CHART] = nk_rgb(120, 120, 120);
table[NK_COLOR_CHART_COLOR] = nk_rgb(45, 45, 45);
table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgb(255, 0, 0);
table[NK_COLOR_SCROLLBAR] = nk_rgb(40, 40, 40);
table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgb(100, 100, 100);
table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgb(120, 120, 120);
table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgb(150, 150, 150);
table[NK_COLOR_TAB_HEADER] = nk_rgb(40, 40, 40);
table[NK_COLOR_KNOB] = nk_rgb(38, 38, 38);
table[NK_COLOR_KNOB_CURSOR] = nk_rgb(100, 100, 100);
table[NK_COLOR_KNOB_CURSOR_HOVER] = nk_rgb(120, 120, 120);
table[NK_COLOR_KNOB_CURSOR_ACTIVE] = nk_rgb(150, 150, 150);
}
static void applyStyles(struct nk_context *ctx) {
struct nk_color table[NK_COLOR_COUNT];
setColorTable(table);
nk_style_from_table(ctx, table);
ctx->style.window.padding = nk_vec2(0, 0);
ctx->style.window.spacing = nk_vec2(0, 0);
ctx->style.window.menu_padding = nk_vec2(0, 0);
ctx->style.window.menu_border = 0;
ctx->style.menu_button.hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]);
ctx->style.menu_button.active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]);
ctx->style.contextual_button.padding = nk_vec2(20, 4);
}
2024-10-26 05:20:25 +00:00
static void copyScreenTexture(uint8_t *dst, const uint8_t *src, int pitch) {
int x, y, i;
uint8_t color;
int delta = pitch / 384;
for (y = 0; y < 224; ++y) {
for (x = 0; x < 384; x += 1) {
color = src[(y * 384) + x];
for (i = 0; i < delta; ++i) {
dst[(y * pitch) + (x * delta) + i] = color;
}
}
}
}
int gfxInit(GraphicsContext *gfx) {
2024-10-26 05:20:25 +00:00
float fontScale = 1;
gfx->window = SDL_CreateWindow("Shrooms VB",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
2024-10-26 05:20:25 +00:00
WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
if (!gfx->window) {
fprintf(stderr, "Error creating window: %s\n", SDL_GetError());
return 1;
}
2024-10-23 23:47:28 +00:00
gfx->renderer = SDL_CreateRenderer(gfx->window, -1, 0);
if (!gfx->renderer) {
fprintf(stderr, "Error creating renderer: %s\n", SDL_GetError());
goto cleanup_window;
}
2024-10-26 05:20:25 +00:00
/* scale the renderer output for High-DPI displays */
{
int renderW, renderH;
int windowW, windowH;
float scaleX, scaleY;
SDL_GetRendererOutputSize(gfx->renderer, &renderW, &renderH);
SDL_GetWindowSize(gfx->window, &windowW, &windowH);
scaleX = (float)(renderW) / (float)(windowW);
scaleY = (float)(renderH) / (float)(windowH);
SDL_RenderSetScale(gfx->renderer, scaleX, scaleY);
}
2024-10-26 05:20:25 +00:00
gfx->nk = nk_sdl_init(gfx->window, gfx->renderer);
2024-10-28 03:33:17 +00:00
applyStyles(gfx->nk);
/* tell nuklear the mouse moved somewhere so it doesn't think we're hovering in the top left */
nk_input_motion(gfx->nk, 1024, 1024);
2024-10-26 05:20:25 +00:00
{
struct nk_font_atlas *atlas;
struct nk_font_config config = nk_font_config(0);
nk_sdl_font_stash_begin(&atlas);
2024-10-28 03:33:17 +00:00
gfx->font = nk_font_atlas_add_default(atlas, 13 * fontScale, &config);
2024-10-26 05:20:25 +00:00
nk_sdl_font_stash_end();
2024-10-28 03:33:17 +00:00
gfx->font->handle.height /= fontScale;
nk_style_set_font(gfx->nk, &gfx->font->handle);
2024-10-26 05:20:25 +00:00
}
gfx->leftEye = SDL_CreateTexture(gfx->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 384, 224);
if (!gfx->leftEye) {
fprintf(stderr, "Error creating left eye texture: %s\n", SDL_GetError());
goto cleanup_window;
}
2024-10-19 22:04:15 +00:00
SDL_SetTextureColorMod(gfx->leftEye, 0xff, 0, 0);
gfx->rightEye = SDL_CreateTexture(gfx->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 384, 224);
if (!gfx->rightEye) {
fprintf(stderr, "Error creating left eye texture: %s\n", SDL_GetError());
goto cleanup_left_eye;
}
2024-10-19 22:04:15 +00:00
SDL_SetTextureColorMod(gfx->rightEye, 0, 0xc6, 0xf0);
SDL_SetTextureBlendMode(gfx->rightEye, SDL_BLENDMODE_ADD);
return 0;
cleanup_left_eye:
SDL_DestroyTexture(gfx->leftEye);
cleanup_window:
SDL_DestroyWindow(gfx->window);
return 1;
}
void gfxDestroy(GraphicsContext *gfx) {
SDL_DestroyTexture(gfx->rightEye);
SDL_DestroyTexture(gfx->leftEye);
SDL_DestroyWindow(gfx->window);
}
static void gfxUpdateEye(SDL_Texture *eye, const uint8_t *bytes) {
void *target;
int pitch;
if (SDL_LockTexture(eye, NULL, &target, &pitch)) {
fprintf(stderr, "Error locking buffer for eye: %s\n", SDL_GetError());
return;
}
copyScreenTexture(target, bytes, pitch);
SDL_UnlockTexture(eye);
}
2024-10-28 03:33:17 +00:00
void gfxUpdateEyes(GraphicsContext *gfx, const uint8_t *left, const uint8_t *right) {
gfxUpdateEye(gfx->leftEye, left);
gfxUpdateEye(gfx->rightEye, right);
}
2024-10-26 05:20:25 +00:00
void gfxMenuInputBegin(GraphicsContext *gfx) {
nk_input_begin(gfx->nk);
}
void gfxMenuHandleEvent(GraphicsContext *gfx, SDL_Event *event) {
(void) gfx;
nk_sdl_handle_event(event);
}
void gfxMenuInputEnd(GraphicsContext *gfx) {
nk_sdl_handle_grab();
nk_input_end(gfx->nk);
}
2024-10-28 03:33:17 +00:00
static void pushMenubarSpace(GraphicsContext *gfx, const char *text) {
struct nk_user_font *handle;
int len;
float width;
handle = &gfx->font->handle;
len = nk_strlen(text);
width = handle->width(handle->userdata, handle->height, text, len) + 16;
nk_layout_row_template_push_static(gfx->nk, width);
}
void gfxRender(GraphicsContext *gfx) {
2024-10-28 03:33:17 +00:00
SDL_Rect dst;
dst.x = 0;
dst.y = MENU_HEIGHT;
dst.w = SCREEN_WIDTH;
dst.h = SCREEN_HEIGHT;
SDL_RenderClear(gfx->renderer);
SDL_RenderCopy(gfx->renderer, gfx->leftEye, NULL, &dst);
SDL_RenderCopy(gfx->renderer, gfx->rightEye, NULL, &dst);
2024-10-26 05:20:25 +00:00
/* GUI */
2024-10-28 03:33:17 +00:00
if (nk_begin(gfx->nk, "Game", nk_rect(0, 0, SCREEN_WIDTH, MENU_HEIGHT), NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_BACKGROUND)) {
nk_menubar_begin(gfx->nk);
nk_layout_row_template_begin(gfx->nk, 20);
pushMenubarSpace(gfx, "File");
pushMenubarSpace(gfx, "Emulation");
nk_layout_row_template_end(gfx->nk);
if (nk_menu_begin_label(gfx->nk, "File", NK_TEXT_ALIGN_CENTERED, nk_vec2(120, WINDOW_HEIGHT))) {
nk_layout_row_dynamic(gfx->nk, 22, 1);
if (nk_menu_item_label(gfx->nk, "Open ROM", NK_TEXT_ALIGN_LEFT)) {
printf("Open ROM\n");
}
if (nk_menu_item_label(gfx->nk, "Quit", NK_TEXT_ALIGN_LEFT)) {
SDL_Event QuitEvent;
QuitEvent.type = SDL_QUIT;
QuitEvent.quit.timestamp = SDL_GetTicks();
SDL_PushEvent(&QuitEvent);
}
nk_menu_end(gfx->nk);
2024-10-26 05:20:25 +00:00
}
2024-10-28 03:33:17 +00:00
if (nk_menu_begin_label(gfx->nk, "Emulation", NK_TEXT_ALIGN_CENTERED, nk_vec2(120, WINDOW_HEIGHT))) {
nk_layout_row_dynamic(gfx->nk, 22, 1);
if (nk_menu_item_label(gfx->nk, "Pause", NK_TEXT_ALIGN_LEFT)) {
printf("Pause\n");
}
if (nk_menu_item_label(gfx->nk, "Reset", NK_TEXT_ALIGN_LEFT)) {
printf("Reset\n");
}
nk_menu_end(gfx->nk);
}
nk_menubar_end(gfx->nk);
2024-10-26 05:20:25 +00:00
}
nk_end(gfx->nk);
nk_sdl_render(NK_ANTI_ALIASING_ON);
SDL_RenderPresent(gfx->renderer);
}