291 lines
10 KiB
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;
|
|
} |