From 2439c3d7b931f47709c1a038e51b0ae13a10224a Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 5 Nov 2022 17:54:45 +0100 Subject: [PATCH] Keyboard event-based input is working for keydown and keyup --- assets/scripts/init.toy | 31 ++++++----- assets/scripts/render.toy | 56 +++++++++++++++++-- core/engine.c | 111 +++++++++++++++++++++++++++++++------- core/engine.h | 10 ++++ core/engine_node.c | 27 +++++++--- core/engine_node.h | 8 +-- core/lib_engine.c | 4 +- core/lib_input.c | 67 +++++++++++++++++++++-- core/lib_node.c | 6 +-- 9 files changed, 266 insertions(+), 54 deletions(-) diff --git a/assets/scripts/init.toy b/assets/scripts/init.toy index 04239e8..c3434c5 100644 --- a/assets/scripts/init.toy +++ b/assets/scripts/init.toy @@ -1,20 +1,27 @@ import engine; -//import input; +import input; + -/* //input settings, mapping SDL2's virtual keys to event names -mapInputEventToKey("character_up", "w", 0); //event, keysym, keymod -mapInputEventToKey("character_left", "a", 0); //event, keysym, keymod -mapInputEventToKey("character_down", "s", 0); //event, keysym, keymod -mapInputEventToKey("character_right", "d", 0); //event, keysym, keymod +mapInputEventToKeyDown("character_up", "w"); //event, keysym +mapInputEventToKeyDown("character_left", "a"); //event, keysym +mapInputEventToKeyDown("character_down", "s"); //event, keysym +mapInputEventToKeyDown("character_right", "d"); //event, keysym -mapInputEventToSpecial("character_up", "arrow_up"); //event, special -mapInputEventToSpecial("character_left", "arrow_left"); //event, special -mapInputEventToSpecial("character_down", "arrow_down"); //event, special -mapInputEventToSpecial("character_right", "arrow_right"); //event, special +mapInputEventToKeyUp("character_up", "w"); //event, keysym +mapInputEventToKeyUp("character_left", "a"); //event, keysym +mapInputEventToKeyUp("character_down", "s"); //event, keysym +mapInputEventToKeyUp("character_right", "d"); //event, keysym -mapInputEventToKey("character_jump", " ", 0); //event, keysym, keymod -*/ +mapInputEventToKeyDown("character_up", "up"); //event, keysym +mapInputEventToKeyDown("character_left", "left"); //event, keysym +mapInputEventToKeyDown("character_down", "down"); //event, keysym +mapInputEventToKeyDown("character_right", "right"); //event, keysym + +mapInputEventToKeyUp("character_up", "up"); //event, keysym +mapInputEventToKeyUp("character_left", "left"); //event, keysym +mapInputEventToKeyUp("character_down", "down"); //event, keysym +mapInputEventToKeyUp("character_right", "right"); //event, keysym //this function must always be called, or the engine won't run initWindow("Airport Game", 800, 600, false); diff --git a/assets/scripts/render.toy b/assets/scripts/render.toy index 446bbff..5973eb1 100644 --- a/assets/scripts/render.toy +++ b/assets/scripts/render.toy @@ -1,13 +1,18 @@ import node; +//constants +var SPEED: int const = 10; + //variables var parent: opaque = null; var x: int = 50; var y: int = 50; +var xspeed: int = 0; +var yspeed: int = 0; + //accessors fn getX(node: opaque) { - print "Called getX"; return x; } @@ -26,8 +31,53 @@ fn onInit(node: opaque) { print "accessed parent"; } +fn onKeyDown(node: opaque, event: string) { + if (event == "character_up") { + yspeed -= SPEED; + return; + } + + if (event == "character_down") { + yspeed += SPEED; + return; + } + + if (event == "character_left") { + xspeed -= SPEED; + return; + } + + if (event == "character_right") { + xspeed += SPEED; + return; + } +} + +fn onKeyUp(node: opaque, event: string) { + if (event == "character_up" && yspeed < 0) { + yspeed = 0; + return; + } + + if (event == "character_down" && yspeed > 0) { + yspeed = 0; + return; + } + + if (event == "character_left" && xspeed < 0) { + xspeed = 0; + return; + } + + if (event == "character_right" && xspeed > 0) { + xspeed = 0; + return; + } +} + fn onStep(node: opaque) { - print "render.toy:onStep()"; + x += xspeed; + y += yspeed; } fn onFree(node: opaque) { @@ -37,7 +87,7 @@ fn onFree(node: opaque) { } fn onDraw(node: opaque) { - print "render.toy:onDraw() called"; +// print "render.toy:onDraw() called"; var px = parent.callNode("getX"); var py = parent.callNode("getY"); diff --git a/core/engine.c b/core/engine.c index 9840425..50b9c76 100644 --- a/core/engine.c +++ b/core/engine.c @@ -11,6 +11,8 @@ #include "parser.h" #include "compiler.h" #include "interpreter.h" +#include "literal_array.h" +#include "literal_dictionary.h" #include "console_colors.h" @@ -40,6 +42,12 @@ void initEngine() { fatalError("Failed to initialize SDL2"); } + //init events + initLiteralArray(&engine.keyDownEvents); + initLiteralDictionary(&engine.symKeyDownEvents); + initLiteralArray(&engine.keyUpEvents); + initLiteralDictionary(&engine.symKeyUpEvents); + //init Toy initInterpreter(&engine.interpreter); injectNativeHook(&engine.interpreter, "engine", hookEngine); @@ -56,13 +64,9 @@ void initEngine() { } void freeEngine() { - SDL_DestroyRenderer(engine.renderer); - SDL_DestroyWindow(engine.window); - SDL_Quit(); - //clear existing root node if (engine.rootNode != NULL) { - callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onFree"); + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onFree", NULL); freeEngineNode(engine.rootNode); @@ -71,14 +75,31 @@ void freeEngine() { freeInterpreter(&engine.interpreter); + //free events + freeLiteralArray(&engine.keyDownEvents); + freeLiteralDictionary(&engine.symKeyDownEvents); + freeLiteralArray(&engine.keyUpEvents); + freeLiteralDictionary(&engine.symKeyUpEvents); + + //free SDL + SDL_DestroyRenderer(engine.renderer); + SDL_DestroyWindow(engine.window); + SDL_Quit(); + engine.renderer = NULL; engine.window = NULL; } -static void execStep() { - //call onStep - if (engine.rootNode != NULL) { - callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onStep"); +static void execEvents() { + //clear event lists + if (engine.keyDownEvents.count > 0) { + freeLiteralArray(&engine.keyDownEvents); + //NOTE: this is likely memory intensive - a more bespoke linked list designed for this task would be better + //NOTE: alternatively - manual memory-wipes, skipping the free step could be better + } + + if (engine.keyUpEvents.count > 0) { + freeLiteralArray(&engine.keyUpEvents); } //poll events @@ -103,9 +124,66 @@ static void execStep() { } break; - //TODO: input + //input + case SDL_KEYDOWN: { + //determine the given keycode + Literal keycodeLiteral = TO_INTEGER_LITERAL( (int)(event.key.keysym.sym) ); + if (!existsLiteralDictionary(&engine.symKeyDownEvents, keycodeLiteral)) { + break; + } + + //get the event name + Literal eventLiteral = getLiteralDictionary(&engine.symKeyDownEvents, keycodeLiteral); + + //push to the event list + pushLiteralArray(&engine.keyDownEvents, eventLiteral); + } + break; + + case SDL_KEYUP: { + //determine the given keycode + Literal keycodeLiteral = TO_INTEGER_LITERAL( (int)(event.key.keysym.sym) ); + if (!existsLiteralDictionary(&engine.symKeyUpEvents, keycodeLiteral)) { + break; + } + + //get the event name + Literal eventLiteral = getLiteralDictionary(&engine.symKeyUpEvents, keycodeLiteral); + + //push to the event list + pushLiteralArray(&engine.keyUpEvents, eventLiteral); + } + break; } } + + //callbacks + if (engine.rootNode != NULL) { + //key down events + for (int i = 0; i < engine.keyDownEvents.count; i++) { + LiteralArray args; + initLiteralArray(&args); + pushLiteralArray(&args, engine.keyDownEvents.literals[i]); + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onKeyDown", &args); + freeLiteralArray(&args); + } + + //key up events + for (int i = 0; i < engine.keyUpEvents.count; i++) { + LiteralArray args; + initLiteralArray(&args); + pushLiteralArray(&args, engine.keyUpEvents.literals[i]); + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onKeyUp", &args); + freeLiteralArray(&args); + } + } +} + +void execStep() { + if (engine.rootNode != NULL) { + //steps + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onStep", NULL); + } } //the heart of the engine @@ -120,18 +198,11 @@ void execEngine() { struct timeval delta = { .tv_sec = 0, .tv_usec = 1000 * 1000 / 60 }; //60 frames per second while (engine.running) { + execEvents(); + //calc the time passed gettimeofday(&engine.realTime, NULL); - // printf("real time: %ld.%ld sim time: %ld.%ld + (delta: %ld.%ld)\n", - // engine.realTime.tv_sec, - // engine.realTime.tv_usec, - // engine.simTime.tv_sec, - // engine.simTime.tv_usec, - // delta.tv_sec, - // delta.tv_usec - // ); - //if not enough time has passed if (timercmp(&engine.simTime, &engine.realTime, <)) { //while not enough time has passed @@ -151,7 +222,7 @@ void execEngine() { SDL_SetRenderDrawColor(engine.renderer, 0, 0, 0, 255); //NOTE: This line can be disabled later SDL_RenderClear(engine.renderer); //NOTE: This line can be disabled later - callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onDraw"); + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onDraw", NULL); SDL_RenderPresent(engine.renderer); } diff --git a/core/engine.h b/core/engine.h index c0b7a78..50a7a2f 100644 --- a/core/engine.h +++ b/core/engine.h @@ -4,6 +4,9 @@ #include "engine_node.h" #include "interpreter.h" +#include "literal_array.h" +#include "literal_dictionary.h" + #include #include @@ -24,6 +27,13 @@ typedef struct _engine { SDL_Renderer* renderer; int screenWidth; int screenHeight; + + //input syms mapped to events + LiteralArray keyDownEvents; //list of events that occurred this frame + LiteralDictionary symKeyDownEvents; //keysym -> event names + + LiteralArray keyUpEvents; //list of events that occurred this frame + LiteralDictionary symKeyUpEvents; //keysym -> event names } Engine; //extern singleton diff --git a/core/engine_node.c b/core/engine_node.c index 566e81f..920f0b0 100644 --- a/core/engine_node.c +++ b/core/engine_node.c @@ -101,7 +101,7 @@ void freeEngineNode(EngineNode* node) { node->freeMemory(node); } -Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key) { +Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key, LiteralArray* args) { Literal ret = TO_NULL_LITERAL; //if this fn exists @@ -116,6 +116,12 @@ Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Litera pushLiteralArray(&arguments, n); + if (args) { + for (int i = 0; i < args->count; i++) { + pushLiteralArray(&arguments, args->literals[i]); + } + } + callLiteralFn(interpreter, fn, &arguments, &returns); ret = popLiteralArray(&returns); @@ -130,18 +136,18 @@ Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Litera return ret; } -Literal callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName) { +Literal callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName, LiteralArray* args) { //call "fnName" on this node, and all children, if it exists Literal key = TO_IDENTIFIER_LITERAL(copyString(fnName, strlen(fnName)), strlen(fnName)); - Literal ret = callEngineNodeLiteral(node, interpreter, key); + Literal ret = callEngineNodeLiteral(node, interpreter, key, args); freeLiteral(key); return ret; } -void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key) { +void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key, LiteralArray* args) { //if this fn exists if (existsLiteralDictionary(node->functions, key)) { Literal fn = getLiteralDictionary(node->functions, key); @@ -152,6 +158,13 @@ void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, initLiteralArray(&arguments); initLiteralArray(&returns); + //feed the arguments in backwards! + if (args) { + for (int i = args->count -1; i >= 0; i--) { + pushLiteralArray(&arguments, args->literals[i]); + } + } + pushLiteralArray(&arguments, n); callLiteralFn(interpreter, fn, &arguments, &returns); @@ -166,16 +179,16 @@ void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, //recurse to the (non-tombstone) children for (int i = 0; i < node->count; i++) { if (node->children[i] != NULL) { - callRecursiveEngineNodeLiteral(node->children[i], interpreter, key); + callRecursiveEngineNodeLiteral(node->children[i], interpreter, key, args); } } } -void callRecursiveEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName) { +void callRecursiveEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName, LiteralArray* args) { //call "fnName" on this node, and all children, if it exists Literal key = TO_IDENTIFIER_LITERAL(copyString(fnName, strlen(fnName)), strlen(fnName)); - callRecursiveEngineNodeLiteral(node, interpreter, key); + callRecursiveEngineNodeLiteral(node, interpreter, key, args); freeLiteral(key); } diff --git a/core/engine_node.h b/core/engine_node.h index 708c69c..07b1c16 100644 --- a/core/engine_node.h +++ b/core/engine_node.h @@ -44,11 +44,11 @@ CORE_API void initEngineNode(EngineNode* node, Interpreter* interpreter, void* t CORE_API void pushEngineNode(EngineNode* node, EngineNode* child); //push to the array (prune tombstones when expanding/copying) CORE_API void freeEngineNode(EngineNode* node); //free and tombstone this node -CORE_API Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key); -CORE_API Literal callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName); //call "fnName" on this node, and only this node, if it exists +CORE_API Literal callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key, LiteralArray* args); +CORE_API Literal callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName, LiteralArray* args); //call "fnName" on this node, and only this node, if it exists -CORE_API void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key); -CORE_API void callRecursiveEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName); //call "fnName" on this node, and all children, if it exists +CORE_API void callRecursiveEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key, LiteralArray* args); +CORE_API void callRecursiveEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName, LiteralArray* args); //call "fnName" on this node, and all children, if it exists CORE_API int loadTextureEngineNode(EngineNode* node, char* fname); CORE_API void freeTextureEngineNode(EngineNode* node); diff --git a/core/lib_engine.c b/core/lib_engine.c index b1b7f05..89a7f1a 100644 --- a/core/lib_engine.c +++ b/core/lib_engine.c @@ -98,7 +98,7 @@ static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) //clear existing root node if (engine.rootNode != NULL) { - callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onFree"); + callRecursiveEngineNode(engine.rootNode, &engine.interpreter, "onFree", NULL); freeEngineNode(engine.rootNode); FREE(EngineNode, engine.rootNode); @@ -137,7 +137,7 @@ static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) initEngineNode(engine.rootNode, &inner, tb, size); //init the new node (and ONLY this node) - callEngineNode(engine.rootNode, &engine.interpreter, "onInit"); + callEngineNode(engine.rootNode, &engine.interpreter, "onInit", NULL); //cleanup freeLiteralArray(&inner.stack); diff --git a/core/lib_input.c b/core/lib_input.c index 7e9971b..2dbc6f1 100644 --- a/core/lib_input.c +++ b/core/lib_input.c @@ -2,7 +2,68 @@ #include "memory.h" -//TODO: native input calls +#include "engine.h" + +#include + +static int nativeMapInputEventToKey(Interpreter* interpreter, LiteralArray* arguments, LiteralDictionary* symKeyEventsPtr, char* fnName) { + //checks + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments passed to "); + interpreter->errorOutput(fnName); + interpreter->errorOutput("\n"); + return -1; + } + + Literal symLiteral = popLiteralArray(arguments); + Literal evtLiteral = popLiteralArray(arguments); + + Literal evtLiteralIdn = evtLiteral; + if (IS_IDENTIFIER(evtLiteral) && parseIdentifierToValue(interpreter, &evtLiteral)) { + freeLiteral(evtLiteralIdn); + } + + Literal symLiteralIdn = symLiteral; + if (IS_IDENTIFIER(symLiteral) && parseIdentifierToValue(interpreter, &symLiteral)) { + freeLiteral(symLiteralIdn); + } + + if (!IS_STRING(symLiteral) || !IS_STRING(evtLiteral)) { + interpreter->errorOutput("Incorrect type of arguments passed to mapInputEventToKey\n"); + return -1; + } + + //use the keycode for faster lookups + SDL_Keycode keycode = SDL_GetKeyFromName( AS_STRING(symLiteral) ); + + if (keycode == SDLK_UNKNOWN) { + interpreter->errorOutput("Unknown key found: "); + interpreter->errorOutput(SDL_GetError()); + interpreter->errorOutput("\n"); + return -1; + } + + Literal keycodeLiteral = TO_INTEGER_LITERAL( (int)keycode ); + + //save the sym-event pair + setLiteralDictionary(symKeyEventsPtr, keycodeLiteral, evtLiteral); //I could possibly map multiple events to one sym + + //cleanup + freeLiteral(symLiteral); + freeLiteral(evtLiteral); + freeLiteral(keycodeLiteral); + + return 0; +} + +//dry wrappers +static int nativeMapInputEventToKeyDown(Interpreter* interpreter, LiteralArray* arguments) { + return nativeMapInputEventToKey(interpreter, arguments, &engine.symKeyDownEvents, "mapInputEventToKeyDown"); +} + +static int nativeMapInputEventToKeyUp(Interpreter* interpreter, LiteralArray* arguments) { + return nativeMapInputEventToKey(interpreter, arguments, &engine.symKeyUpEvents, "mapInputEventToKeyUp"); +} //call the hook typedef struct Natives { @@ -13,9 +74,9 @@ typedef struct Natives { int hookInput(Interpreter* interpreter, Literal identifier, Literal alias) { //build the natives list Natives natives[] = { - // {"mapInputEventToKey", nativeMapInputEventToKey}, + {"mapInputEventToKeyDown", nativeMapInputEventToKeyDown}, + {"mapInputEventToKeyUp", nativeMapInputEventToKeyUp}, // {"mapInputEventToMouse", nativeMapInputEventToMouse}, - // {"mapInputEventToSpecial", nativeMapInputEventToSpecial}, {NULL, NULL} }; diff --git a/core/lib_node.c b/core/lib_node.c index 02305f5..03d033a 100644 --- a/core/lib_node.c +++ b/core/lib_node.c @@ -96,7 +96,7 @@ static int nativeInitNode(Interpreter* interpreter, LiteralArray* arguments) { EngineNode* engineNode = AS_OPAQUE(node); //init the new node (and ONLY this node) - callEngineNode(engineNode, &engine.interpreter, "onInit"); + callEngineNode(engineNode, &engine.interpreter, "onInit", NULL); //cleanup freeLiteral(node); @@ -140,7 +140,7 @@ static int nativeFreeChildNode(Interpreter* interpreter, LiteralArray* arguments //free the node if (childNode != NULL) { - callRecursiveEngineNode(childNode, &engine.interpreter, "onFree"); + callRecursiveEngineNode(childNode, &engine.interpreter, "onFree", NULL); freeEngineNode(childNode); } @@ -563,7 +563,7 @@ static int nativeCallNode(Interpreter* interpreter, LiteralArray* arguments) { Literal fnNameIdentifier = TO_IDENTIFIER_LITERAL(copyString(strptr, strlen(strptr)), strlen(strptr)); //call the function - Literal result = callEngineNodeLiteral(AS_OPAQUE(nodeLiteral), interpreter, fnNameIdentifier); + Literal result = callEngineNodeLiteral(AS_OPAQUE(nodeLiteral), interpreter, fnNameIdentifier, NULL); pushLiteralArray(&interpreter->stack, result);