mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2025-09-24 09:03:32 +02:00
513 lines
16 KiB
C
513 lines
16 KiB
C
#include "screen.h"
|
|
|
|
nn_screen *nn_newScreen(nn_Context *context, int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors) {
|
|
nn_Alloc *alloc = &context->allocator;
|
|
// TODO: handle OOMs
|
|
nn_screen *screen = nn_alloc(alloc, sizeof(nn_screen));
|
|
screen->ctx = *context;
|
|
screen->buffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight);
|
|
screen->lock = nn_newGuard(context);
|
|
screen->refc = 1;
|
|
screen->width = maxWidth;
|
|
screen->height = maxHeight;
|
|
screen->viewportWidth = maxWidth;
|
|
screen->viewportHeight = maxHeight;
|
|
screen->maxWidth = maxWidth;
|
|
screen->maxHeight = maxHeight;
|
|
screen->maxDepth = maxDepth;
|
|
screen->depth = maxDepth;
|
|
screen->editableColors = editableColors;
|
|
screen->paletteColors = paletteColors;
|
|
screen->palette = nn_alloc(alloc, sizeof(int) * screen->paletteColors);
|
|
nn_memset(screen->palette, 0, sizeof(int) * screen->paletteColors);
|
|
screen->aspectRatioWidth = 1;
|
|
screen->aspectRatioHeight = 1;
|
|
screen->isOn = true;
|
|
screen->isTouchModeInverted = true;
|
|
screen->isPrecise = true;
|
|
screen->isDirty = true;
|
|
screen->keyboardCount = 0;
|
|
return screen;
|
|
}
|
|
|
|
void nn_retainScreen(nn_screen *screen) {
|
|
nn_incRef(&screen->refc);
|
|
}
|
|
|
|
void nn_destroyScreen(nn_screen *screen) {
|
|
if(!nn_decRef(&screen->refc)) return;
|
|
nn_Alloc a = screen->ctx.allocator;
|
|
nn_deleteGuard(&screen->ctx, screen->lock);
|
|
nn_dealloc(&a, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight);
|
|
nn_dealloc(&a, screen->palette, sizeof(int) * screen->paletteColors);
|
|
nn_dealloc(&a, screen, sizeof(nn_screen));
|
|
}
|
|
|
|
void nn_lockScreen(nn_screen *screen) {
|
|
nn_lock(&screen->ctx, screen->lock);
|
|
}
|
|
|
|
void nn_unlockScreen(nn_screen *screen) {
|
|
nn_unlock(&screen->ctx, screen->lock);
|
|
}
|
|
|
|
void nn_getResolution(nn_screen *screen, int *width, int *height) {
|
|
*width = screen->width;
|
|
*height = screen->height;
|
|
}
|
|
|
|
void nn_maxResolution(nn_screen *screen, int *width, int *height) {
|
|
*width = screen->maxWidth;
|
|
*height = screen->maxHeight;
|
|
}
|
|
|
|
void nn_setResolution(nn_screen *screen, int width, int height) {
|
|
screen->width = width;
|
|
screen->height = height;
|
|
}
|
|
|
|
nn_bool_t nn_unsafeReallocateScreenBuffer(nn_screen *screen, int maxWidth, int maxHeight) {
|
|
nn_Alloc *alloc = &screen->ctx.allocator;
|
|
|
|
nn_scrchr_t *newBuffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight);
|
|
if(newBuffer == NULL) {
|
|
return false;
|
|
}
|
|
|
|
for(nn_size_t y = 0; y < maxHeight; y++) {
|
|
for(nn_size_t x = 0; x < maxWidth; x++) {
|
|
nn_size_t destIdx = x + y * maxWidth;
|
|
|
|
newBuffer[destIdx] = nn_getPixel(screen, x, y);
|
|
}
|
|
}
|
|
|
|
nn_dealloc(alloc, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight);
|
|
|
|
screen->buffer = newBuffer;
|
|
screen->maxWidth = maxWidth;
|
|
screen->maxHeight = maxHeight;
|
|
return true;
|
|
}
|
|
|
|
void nn_getViewport(nn_screen *screen, int *width, int *height) {
|
|
*width = screen->viewportWidth;
|
|
*height = screen->viewportHeight;
|
|
}
|
|
|
|
void nn_setViewport(nn_screen *screen, int width, int height) {
|
|
screen->viewportWidth = width;
|
|
screen->viewportHeight = height;
|
|
}
|
|
|
|
void nn_getAspectRatio(nn_screen *screen, int *width, int *height) {
|
|
*width = screen->aspectRatioWidth;
|
|
*height = screen->aspectRatioHeight;
|
|
}
|
|
|
|
void nn_setAspectRatio(nn_screen *screen, int width, int height) {
|
|
screen->aspectRatioWidth = width;
|
|
screen->aspectRatioHeight = height;
|
|
}
|
|
|
|
void nn_addKeyboard(nn_screen *screen, nn_address address) {
|
|
if(screen->keyboardCount == NN_MAX_SCREEN_KEYBOARDS) return;
|
|
char *kb = nn_strdup(&screen->ctx.allocator, address);
|
|
if(kb == NULL) return;
|
|
screen->keyboards[screen->keyboardCount++] = kb;
|
|
}
|
|
|
|
void nn_removeKeyboard(nn_screen *screen, nn_address address) {
|
|
nn_size_t j = 0;
|
|
for(nn_size_t i = 0; i < screen->keyboardCount; i++) {
|
|
if(nn_strcmp(screen->keyboards[i], address) == 0) {
|
|
nn_deallocStr(&screen->ctx.allocator, screen->keyboards[i]);
|
|
} else {
|
|
screen->keyboards[j] = screen->keyboards[i];
|
|
j++;
|
|
}
|
|
}
|
|
screen->keyboardCount = j;
|
|
}
|
|
|
|
nn_address nn_getKeyboard(nn_screen *screen, nn_size_t idx) {
|
|
if(idx >= screen->keyboardCount) return NULL;
|
|
return screen->keyboards[idx];
|
|
}
|
|
|
|
nn_size_t nn_getKeyboardCount(nn_screen *screen) {
|
|
return screen->keyboardCount;
|
|
}
|
|
|
|
void nn_setEditableColors(nn_screen *screen, int count) {
|
|
screen->editableColors = count;
|
|
}
|
|
|
|
int nn_getEditableColors(nn_screen *screen) {
|
|
return screen->editableColors;
|
|
}
|
|
|
|
void nn_setPaletteColor(nn_screen *screen, int idx, int color) {
|
|
if(idx >= screen->paletteColors) return;
|
|
screen->palette[idx] = color;
|
|
}
|
|
|
|
int nn_getPaletteColor(nn_screen *screen, int idx) {
|
|
if(idx >= screen->paletteColors) return 0;
|
|
return screen->palette[idx];
|
|
}
|
|
|
|
int nn_getPaletteCount(nn_screen *screen) {
|
|
return screen->paletteColors;
|
|
}
|
|
|
|
int nn_maxDepth(nn_screen *screen) {
|
|
return screen->maxDepth;
|
|
}
|
|
|
|
int nn_getDepth(nn_screen *screen) {
|
|
return screen->depth;
|
|
}
|
|
|
|
void nn_setDepth(nn_screen *screen, int depth) {
|
|
if(depth > screen->maxDepth) depth = screen->maxDepth;
|
|
screen->depth = depth;
|
|
}
|
|
|
|
void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel) {
|
|
if(x < 0) return;
|
|
if(y < 0) return;
|
|
if(x >= screen->width) return;
|
|
if(y >= screen->height) return;
|
|
screen->buffer[x + y * screen->maxWidth] = pixel;
|
|
screen->isDirty = true; // stuff changed
|
|
}
|
|
|
|
nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y) {
|
|
nn_scrchr_t blank = {
|
|
.codepoint = ' ',
|
|
.fg = 0xFFFFFF,
|
|
.bg = 0x000000,
|
|
.isFgPalette = false,
|
|
.isBgPalette = false,
|
|
};
|
|
if(x < 0) return blank;
|
|
if(y < 0) return blank;
|
|
if(x >= screen->width) return blank;
|
|
if(y >= screen->height) return blank;
|
|
return screen->buffer[x + y * screen->maxWidth];
|
|
}
|
|
|
|
nn_bool_t nn_isDirty(nn_screen *screen) {
|
|
return screen->isDirty;
|
|
}
|
|
|
|
void nn_setDirty(nn_screen *screen, nn_bool_t dirty) {
|
|
screen->isDirty = dirty;
|
|
}
|
|
|
|
nn_bool_t nn_isPrecise(nn_screen *screen) {
|
|
return screen->isPrecise;
|
|
}
|
|
|
|
void nn_setPrecise(nn_screen *screen, nn_bool_t precise) {
|
|
screen->isPrecise = precise;
|
|
}
|
|
|
|
nn_bool_t nn_isTouchModeInverted(nn_screen *screen) {
|
|
return screen->isTouchModeInverted;
|
|
}
|
|
|
|
void nn_setTouchModeInverted(nn_screen *screen, nn_bool_t touchModeInverted) {
|
|
screen->isTouchModeInverted = touchModeInverted;
|
|
}
|
|
|
|
nn_bool_t nn_isOn(nn_screen *buffer) {
|
|
return buffer->isOn;
|
|
}
|
|
|
|
void nn_setOn(nn_screen *buffer, nn_bool_t on) {
|
|
buffer->isOn = on;
|
|
}
|
|
|
|
void nn_screenComp_destroy(void *_, nn_component *component, nn_screen *screen) {
|
|
nn_destroyScreen(screen);
|
|
}
|
|
|
|
void nn_screenComp_getKeyboards(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
nn_value arr = nn_values_array(&screen->ctx.allocator, nn_getKeyboardCount(screen));
|
|
|
|
nn_size_t len = arr.array->len;
|
|
for(nn_size_t i = 0; i < len; i++) {
|
|
nn_size_t addrlen = nn_strlen(nn_getKeyboard(screen, i));
|
|
nn_value addr = nn_values_string(&screen->ctx.allocator, nn_getKeyboard(screen, i), addrlen);
|
|
nn_values_set(arr, i, addr);
|
|
}
|
|
|
|
nn_unlockScreen(screen);
|
|
nn_return(computer, arr);
|
|
}
|
|
|
|
void nn_screenComp_getAspectRatio(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
int w, h;
|
|
nn_getAspectRatio(screen, &w, &h);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_integer(computer, w);
|
|
nn_return_integer(computer, h);
|
|
}
|
|
|
|
void nn_screenComp_isOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
nn_bool_t isOn = nn_isOn(screen);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isOn);
|
|
}
|
|
|
|
void nn_screenComp_turnOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
nn_bool_t isOff = !nn_isOn(screen);
|
|
nn_setOn(screen, true);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isOff);
|
|
nn_return_boolean(computer, true);
|
|
}
|
|
|
|
void nn_screenComp_turnOff(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
nn_bool_t isOn = nn_isOn(screen);
|
|
nn_setOn(screen, false);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isOn);
|
|
nn_return_boolean(computer, false);
|
|
}
|
|
|
|
void nn_screenComp_setPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_bool_t isPrecise = nn_toBooleanOr(nn_getArgument(computer, 0), true);
|
|
|
|
nn_lockScreen(screen);
|
|
|
|
nn_setPrecise(screen, isPrecise);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isPrecise);
|
|
}
|
|
|
|
void nn_screenComp_isPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
nn_bool_t isPrecise = nn_isPrecise(screen);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isPrecise);
|
|
}
|
|
|
|
void nn_screenComp_setTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_bool_t isTouchModeInverted = nn_toBooleanOr(nn_getArgument(computer, 0), true);
|
|
|
|
nn_lockScreen(screen);
|
|
|
|
nn_setTouchModeInverted(screen, isTouchModeInverted);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isTouchModeInverted);
|
|
}
|
|
|
|
void nn_screenComp_isTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
|
|
nn_lockScreen(screen);
|
|
|
|
nn_bool_t isTouchModeInverted = nn_isTouchModeInverted(screen);
|
|
|
|
nn_unlockScreen(screen);
|
|
|
|
nn_return_boolean(computer, isTouchModeInverted);
|
|
}
|
|
|
|
void nn_loadScreenTable(nn_universe *universe) {
|
|
nn_componentTable *screenTable = nn_newComponentTable(nn_getAllocator(universe), "screen", NULL, NULL, (nn_componentDestructor *)nn_screenComp_destroy);
|
|
nn_storeUserdata(universe, "NN:SCREEN", screenTable);
|
|
|
|
nn_defineMethod(screenTable, "getKeyboards", (nn_componentMethod *)nn_screenComp_getKeyboards, "getKeyboards(): string[] - Returns the keyboards registered to this screen.");
|
|
nn_defineMethod(screenTable, "getAspectRatio", (nn_componentMethod *)nn_screenComp_getAspectRatio, "getAspectRatio(): integer, integer - Returns the dimensions, in blocks, of the screen.");
|
|
nn_defineMethod(screenTable, "isOn", (nn_componentMethod *)nn_screenComp_isOn, "isOn(): boolean - Returns whether the screen is on.");
|
|
nn_defineMethod(screenTable, "turnOn", (nn_componentMethod *)nn_screenComp_turnOn, "turnOn(): boolean, boolean - Turns the screen on. Returns whether the screen was off and the new power state.");
|
|
nn_defineMethod(screenTable, "turnOff", (nn_componentMethod *)nn_screenComp_turnOff, "turnOff(): boolean, boolean - Turns the screen off. Returns whether the screen was on and the new power state.");
|
|
nn_defineMethod(screenTable, "setPrecise", (nn_componentMethod *)nn_screenComp_setPrecise, "setPrecise(precise: boolean) - Sets whether precise (sub-pixel) mouse events are enabled");
|
|
nn_defineMethod(screenTable, "isPrecise", (nn_componentMethod *)nn_screenComp_isPrecise, "isPrecise(): boolean - Checks whether precise mouse events are enabled");
|
|
nn_defineMethod(screenTable, "setTouchModeInverted", (nn_componentMethod *)nn_screenComp_setTouchModeInverted, "setTouchModeInverted(mode: boolean) - Sets whether inverted touch mode is enabled");
|
|
nn_defineMethod(screenTable, "isTouchModeInverted", (nn_componentMethod *)nn_screenComp_isTouchModeInverted, "isTouchModeInverted(): boolean - Checks whether inverted touch mode is enabled");
|
|
}
|
|
|
|
nn_componentTable *nn_getScreenTable(nn_universe *universe) {
|
|
return nn_queryUserdata(universe, "NN:SCREEN");
|
|
}
|
|
|
|
nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen) {
|
|
nn_componentTable *screenTable = nn_queryUserdata(nn_getUniverse(computer), "NN:SCREEN");
|
|
return nn_newComponent(computer, address, slot, screenTable, screen);
|
|
}
|
|
|
|
static const int nni_mcBlack = 0x000000;
|
|
static const int nni_mcWhite = 0xFFFFFF;
|
|
|
|
void nn_getStd4BitPalette(int color[16]) {
|
|
color[0] = nni_mcWhite; // white
|
|
color[1] = 0xF9801D; // orange
|
|
color[2] = 0xC74EBD; // magenta
|
|
color[3] = 0x3AB3DA; // lightblue
|
|
color[4] = 0xFED83D; // yellow
|
|
color[5] = 0x80C71F; // lime
|
|
color[6] = 0xF38BAA; // pink
|
|
color[7] = 0x474F52; // gray
|
|
color[8] = 0x9D9D97; // silver
|
|
color[9] = 0x169C9C; // cyan
|
|
color[10] = 0x8932B8; // purple
|
|
color[11] = 0x3C44AA; // blue
|
|
color[12] = 0x835432; // brown
|
|
color[13] = 0x5E7C16; // green
|
|
color[14] = 0xB02E26; // red
|
|
color[15] = nni_mcBlack; // black
|
|
}
|
|
|
|
void nn_getLegacy4BitPalette(int color[16]) {
|
|
// taken from https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/scala/li/cil/oc/util/PackedColor.scala
|
|
|
|
color[0] = 0xFFFFFF;
|
|
color[1] = 0xFFCC33;
|
|
color[2] = 0xCC66CC;
|
|
color[3] = 0x6699FF;
|
|
color[4] = 0xFFFF33;
|
|
color[5] = 0x33CC33;
|
|
color[6] = 0xFF6699;
|
|
color[7] = 0x333333;
|
|
color[8] = 0xCCCCCC;
|
|
color[9] = 0x336699;
|
|
color[10] = 0x9933CC;
|
|
color[11] = 0x333399;
|
|
color[12] = 0x663300;
|
|
color[13] = 0x336600;
|
|
color[14] = 0xFF3333;
|
|
color[15] = 0x000000;
|
|
}
|
|
|
|
void nn_getStd8BitPalette(int color[256]) {
|
|
// source: https://ocdoc.cil.li/component:gpu
|
|
int reds[6] = {0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF};
|
|
int greens[8] = {0x00, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF};
|
|
int blues[5] = {0x00, 0x40, 0x80, 0xC0, 0xFF};
|
|
|
|
for(int r = 0; r < 6; r++) {
|
|
for(int g = 0; g < 8; g++) {
|
|
for(int b = 0; b < 5; b++) {
|
|
int i = r * 8 * 5 + g * 5 + b;
|
|
color[i] = (reds[r] << 16) | (greens[g] << 8) | (blues[b]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: turn into an algorithm
|
|
color[240] = 0x0F0F0F;
|
|
color[241] = 0x1E1E1E;
|
|
color[242] = 0x2D2D2D;
|
|
color[243] = 0x3C3C3C;
|
|
color[244] = 0x4B4B4B;
|
|
color[245] = 0x5A5A5A;
|
|
color[246] = 0x696969;
|
|
color[247] = 0x787878;
|
|
color[248] = 0x878787;
|
|
color[249] = 0x969696;
|
|
color[250] = 0xA5A5A5;
|
|
color[251] = 0xB4B4B4;
|
|
color[252] = 0xC3C3C3;
|
|
color[253] = 0xD2D2D2;
|
|
color[254] = 0xE1E1E1;
|
|
color[255] = 0xF0F0F0;
|
|
}
|
|
|
|
static int nni_4bit_colors[16];
|
|
static nn_bool_t nni_4bit_did = false;
|
|
static int nni_8bit_colors[256];
|
|
static nn_bool_t nni_8bit_did = false;
|
|
|
|
static int nni_4bitl_colors[16];
|
|
static nn_bool_t nni_4bitl_did = false;
|
|
|
|
const char *nn_depthName(int depth) {
|
|
if(depth == 1) return "OneBit";
|
|
if(depth == 2) return "TwoBit";
|
|
if(depth == 3) return "ThreeBit";
|
|
if(depth == 4) return "FourBit";
|
|
if(depth == 8) return "EightBit";
|
|
if(depth == 16) return "SixteenBit";
|
|
if(depth == 24) return "TwentyFourBit";
|
|
return NULL;
|
|
}
|
|
|
|
int nn_mapDepth(int color, int depth, nn_bool_t legacy) {
|
|
if(depth == 1) {
|
|
if(color == 0) return nni_mcBlack;
|
|
return nni_mcWhite;
|
|
}
|
|
if(depth == 2) {
|
|
int palette[4] = {
|
|
0x000000,
|
|
0x444444,
|
|
0x999999,
|
|
0xFFFFFF,
|
|
};
|
|
return nn_mapColor(color, palette, 4);
|
|
}
|
|
if(depth == 3) {
|
|
int palette[8] = {
|
|
0x000000,
|
|
0xFF0000,
|
|
0x00FF00,
|
|
0xFFFF00,
|
|
0x0000FF,
|
|
0xFF00FF,
|
|
0x00FFFF,
|
|
0xFFFFFF,
|
|
};
|
|
return nn_mapColor(color, palette, 8);
|
|
}
|
|
if(depth == 4) {
|
|
if(legacy) {
|
|
if(!nni_4bitl_did) {
|
|
nni_4bitl_did = true;
|
|
nn_getLegacy4BitPalette(nni_4bitl_colors);
|
|
}
|
|
return nn_mapColor(color, nni_4bitl_colors, 16);
|
|
} else {
|
|
if(!nni_4bit_did) {
|
|
nni_4bit_did = true;
|
|
nn_getStd4BitPalette(nni_4bit_colors);
|
|
}
|
|
return nn_mapColor(color, nni_4bit_colors, 16);
|
|
}
|
|
}
|
|
if(depth == 8) {
|
|
if(!nni_8bit_did) {
|
|
nni_8bit_did = true;
|
|
nn_getStd8BitPalette(nni_8bit_colors);
|
|
}
|
|
return nn_mapColor(color, nni_8bit_colors, 256);
|
|
}
|
|
return color;
|
|
}
|