#include "nuklear.h" #include "window.h" #define SCREEN_WIDTH 384 #define SCREEN_HEIGHT 224 #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); } int windowInit(WindowContext *win, const char *title) { float fontScale; win->window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_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); /* scale the renderer output for High-DPI displays */ { int renderW, renderH; int windowW, windowH; float scaleX, scaleY; SDL_GetRendererOutputSize(win->renderer, &renderW, &renderH); SDL_GetWindowSize(win->window, &windowW, &windowH); scaleX = (float)(renderW) / (float)(windowW); scaleY = (float)(renderH) / (float)(windowH); SDL_RenderSetScale(win->renderer, scaleX, scaleY); fontScale = scaleY; } win->nk = nk_sdl_init(win->window, win->renderer); applyStyles(win->nk); /* 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); nk_sdl_font_stash_begin(&atlas); win->font = nk_font_atlas_add_default(atlas, 13 * fontScale, &config); nk_sdl_font_stash_end(); win->font->handle.height /= fontScale; 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); } 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 windowGetScreenHeight(WindowContext *win) { (void) win; return SCREEN_HEIGHT; } void windowDisplayBegin(WindowContext *win) { SDL_Rect dst; dst.x = 0; dst.y = MENU_HEIGHT; dst.w = SCREEN_WIDTH; dst.h = SCREEN_HEIGHT; 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, MENU_HEIGHT), 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); 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; 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); }