shrooms-vb-native/window.c

291 lines
10 KiB
C

#include "assets.h"
#include "nuklear.h"
#include <string.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);
}
bool windowMenuBegin(WindowContext *win, const char *label, int width) {
if (!nk_menu_begin_label(win->nk, label, NK_TEXT_ALIGN_CENTERED, nk_vec2(windowScaleX(win, width), windowGetScreenHeight(win)))) {
return false;
}
nk_layout_row_dynamic(win->nk, windowGetMenuHeight(win), 1);
return true;
}
void windowMenuEnd(WindowContext *win) {
nk_menu_end(win->nk);
}
bool windowMenuItemLabel(WindowContext *win, const char *label) {
return nk_menu_item_label(win->nk, label, NK_TEXT_ALIGN_LEFT);
}
bool windowMenuItemLabelChecked(WindowContext *win, const char *label, bool checked) {
char buffer[80];
bool result;
if (!checked) {
return windowMenuItemLabel(win, label);
}
strcpy(buffer, " * ");
strncpy(buffer + 5, label, 74);
buffer[79] = '\0';
nk_style_push_vec2(win->nk, &win->nk->style.contextual_button.padding, nk_vec2(4 * win->screenScaleX, 4 * win->screenScaleY));
result = nk_menu_item_label(win->nk, buffer, NK_TEXT_ALIGN_LEFT);
nk_style_pop_vec2(win->nk);
return result;
}