#include #include #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); } 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) { float fontScale = 1; gfx->window = SDL_CreateWindow("Shrooms VB", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 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; } gfx->renderer = SDL_CreateRenderer(gfx->window, -1, 0); if (!gfx->renderer) { fprintf(stderr, "Error creating renderer: %s\n", SDL_GetError()); goto cleanup_window; } /* 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); } gfx->nk = nk_sdl_init(gfx->window, gfx->renderer); 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); { struct nk_font_atlas *atlas; struct nk_font_config config = nk_font_config(0); nk_sdl_font_stash_begin(&atlas); gfx->font = nk_font_atlas_add_default(atlas, 13 * fontScale, &config); nk_sdl_font_stash_end(); gfx->font->handle.height /= fontScale; nk_style_set_font(gfx->nk, &gfx->font->handle); } 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; } 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; } 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); } void gfxUpdateEyes(GraphicsContext *gfx, const uint8_t *left, const uint8_t *right) { gfxUpdateEye(gfx->leftEye, left); gfxUpdateEye(gfx->rightEye, right); } 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); } 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) { 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); /* GUI */ 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); } 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); } nk_end(gfx->nk); nk_sdl_render(NK_ANTI_ALIASING_ON); SDL_RenderPresent(gfx->renderer); }