#include "assets.h" #include "nuklear.h" #include "window.h" #define MENU_HEIGHT 20 #define SCREEN_WIDTH 384 #define SCREEN_HEIGHT 224 static void setColorTable(struct nk_color *table) { table[NK_COLOR_TEXT] = nk_rgb(40, 40, 40); 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, float scaleX, float scaleY) { 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.menu_button.padding = nk_vec2(2 * scaleX, 2 * scaleY); ctx->style.contextual_button.padding = nk_vec2(20 * scaleX, 4 * scaleY); } /* scale the window for High-DPI displays */ static void scaleWindow(WindowContext *win) { int renderW, renderH; int oldWindowX, oldWindowY; int newWindowX, newWindowY; int oldWindowW, oldWindowH; int newWindowW, newWindowH; float scaleX, scaleY; float hdpi, vdpi; SDL_GetRendererOutputSize(win->renderer, &renderW, &renderH); SDL_GetWindowPosition(win->window, &oldWindowX, &oldWindowY); SDL_GetWindowSize(win->window, &oldWindowW, &oldWindowH); SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(win->window), NULL, &hdpi, &vdpi); scaleX = (float)(renderW) / (float)(oldWindowW); scaleY = (float)(renderH) / (float)(oldWindowH); win->screenScaleX = (hdpi / 96) * scaleX; win->screenScaleY = (vdpi / 96) * scaleY; newWindowW = SCREEN_WIDTH * win->screenSizeMultiplier * (hdpi / 96) / scaleX; newWindowH = (SCREEN_HEIGHT * win->screenSizeMultiplier + MENU_HEIGHT) * (vdpi / 96) / scaleY; newWindowX = oldWindowX - (newWindowW - oldWindowW) / 2; newWindowY = oldWindowY - (newWindowH - oldWindowH) / 2; if (newWindowX < 0) newWindowX = 0; if (newWindowY < 0) newWindowY = 0; SDL_SetWindowSize(win->window, newWindowW, newWindowH); SDL_SetWindowPosition(win->window, newWindowX, newWindowY); } int windowInit(WindowContext *win, const char *title) { win->screenSizeMultiplier = 1.0f; win->window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT + MENU_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); if (!win->window) { fprintf(stderr, "Error creating window: %s\n", SDL_GetError()); return -1; } win->renderer = SDL_CreateRenderer(win->window, -1, 0); if (!win->renderer) { fprintf(stderr, "Error creating renderer: %s\n", SDL_GetError()); goto cleanup_window; } win->leftEye = SDL_CreateTexture(win->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 384, 224); if (!win->leftEye) { fprintf(stderr, "Error creating left eye texture: %s\n", SDL_GetError()); goto cleanup_renderer; } SDL_SetTextureColorMod(win->leftEye, 0xff, 0, 0); win->rightEye = SDL_CreateTexture(win->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 384, 224); if (!win->rightEye) { fprintf(stderr, "Error creating right eye texture: %s\n", SDL_GetError()); goto cleanup_left_eye; } SDL_SetTextureColorMod(win->rightEye, 0, 0xc6, 0xf0); SDL_SetTextureBlendMode(win->rightEye, SDL_BLENDMODE_ADD); scaleWindow(win); win->nk = nk_sdl_init(win->window, win->renderer); applyStyles(win->nk, win->screenScaleX, win->screenScaleY); /* tell nuklear the mouse moved somewhere so it doesn't think we're hovering in the top left */ nk_input_motion(win->nk, 1024, 1024); { struct nk_font_atlas *atlas; struct nk_font_config config = nk_font_config(0); config.pixel_snap = 1; config.oversample_h = 8; config.oversample_v = 8; nk_sdl_font_stash_begin(&atlas); win->font = nk_font_atlas_add_from_memory(atlas, (void*) SELAWIK, SELAWIK_LEN, 13 * win->screenScaleY, &config); nk_sdl_font_stash_end(); nk_style_set_font(win->nk, &win->font->handle); } return 0; cleanup_left_eye: SDL_DestroyTexture(win->leftEye); cleanup_renderer: SDL_DestroyRenderer(win->renderer); cleanup_window: SDL_DestroyWindow(win->window); return -1; } void windowDestroy(WindowContext *win) { SDL_DestroyTexture(win->rightEye); SDL_DestroyTexture(win->leftEye); SDL_DestroyRenderer(win->renderer); SDL_DestroyWindow(win->window); } float windowGetScreenSizeMultiplier(WindowContext *win) { return win->screenSizeMultiplier; } void windowSetScreenSizeMultiplier(WindowContext *win, float multiplier) { win->screenSizeMultiplier = multiplier; scaleWindow(win); applyStyles(win->nk, win->screenScaleX, win->screenScaleY); } 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; } } } } static void updateEye(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 windowUpdate(WindowContext *win, const uint8_t *left, const uint8_t *right) { updateEye(win->leftEye, left); updateEye(win->rightEye, right); } int windowScaleX(WindowContext *win, int x) { return x * win->screenScaleX; } int windowScaleY(WindowContext *win, int y) { return y * win->screenScaleY; } int windowGetMenuHeight(WindowContext *win) { return (MENU_HEIGHT + 2) * win->screenScaleY; } int windowGetScreenHeight(WindowContext *win) { return SCREEN_HEIGHT * win->screenSizeMultiplier * win->screenScaleY; } void windowDisplayBegin(WindowContext *win) { SDL_Rect dst; dst.x = 0; dst.y = MENU_HEIGHT * win->screenScaleY; dst.w = SCREEN_WIDTH * win->screenSizeMultiplier * win->screenScaleX; dst.h = SCREEN_HEIGHT * win->screenSizeMultiplier * win->screenScaleY; SDL_RenderClear(win->renderer); SDL_RenderCopy(win->renderer, win->leftEye, NULL, &dst); SDL_RenderCopy(win->renderer, win->rightEye, NULL, &dst); } void windowDisplayEnd(WindowContext *win) { nk_sdl_render(NK_ANTI_ALIASING_ON); SDL_RenderPresent(win->renderer); } bool windowGuiBegin(WindowContext *win, const char *title) { return nk_begin(win->nk, title, nk_rect(0, 0, SCREEN_WIDTH * win->screenSizeMultiplier * win->screenScaleX, MENU_HEIGHT * win->screenScaleY), NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_BACKGROUND); } void windowGuiEnd(WindowContext *win) { nk_end(win->nk); } void windowMenubarBegin(WindowContext *win, const char **items) { const char **item; nk_menubar_begin(win->nk); nk_layout_row_template_begin(win->nk, MENU_HEIGHT * win->screenScaleY); for (item = items; *item != NULL; item++) { struct nk_user_font *handle; int len; float width; handle = &win->font->handle; len = nk_strlen(*item); width = handle->width(handle->userdata, handle->height, *item, len) + (16 * win->screenScaleX); nk_layout_row_template_push_static(win->nk, width); } nk_layout_row_template_end(win->nk); } void windowMenubarEnd(WindowContext *win) { nk_menubar_end(win->nk); }