diff --git a/src/desktop/backends/glfw2.c b/src/desktop/backends/glfw2.c index 26c706ec..766022a4 100644 --- a/src/desktop/backends/glfw2.c +++ b/src/desktop/backends/glfw2.c @@ -198,9 +198,20 @@ void platformExit(void) { glfwTerminate(); } +static void platformSetCursor(int32_t cursorType) { + // GLFW2 only supports showing/hiding + if (cursorType == -1) { + glfwDisable(GLFW_MOUSE_CURSOR); + } else { + glfwEnable(GLFW_MOUSE_CURSOR); + } +} + void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; + runner->setCursor = platformSetCursor; + runner->currentCursor = 0; // cr_default #ifdef ENABLE_SW_RENDERER if (gfx == SOFTWARE) glfwSetWindowSizeCallback(resizeCallback); diff --git a/src/desktop/backends/glfw3.c b/src/desktop/backends/glfw3.c index 5f978f72..a7042bd1 100644 --- a/src/desktop/backends/glfw3.c +++ b/src/desktop/backends/glfw3.c @@ -251,9 +251,40 @@ void platformExit(void) { glfwTerminate(); } +static void platformSetCursor(int32_t cursorType) { + if (cursorType == -1) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + return; + } + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + int glfwShape; + switch (cursorType) { + case -3: glfwShape = GLFW_CROSSHAIR_CURSOR; break; + case -4: glfwShape = GLFW_IBEAM_CURSOR; break; + case -7: glfwShape = GLFW_VRESIZE_CURSOR; break; + case -9: glfwShape = GLFW_HRESIZE_CURSOR; break; + case -12: glfwShape = GLFW_HAND_CURSOR; break; + case -21: glfwShape = GLFW_HAND_CURSOR; break; + #if (GLFW_VERSION_MINOR >= 4) + case -22: glfwShape = GLFW_RESIZE_ALL_CURSOR; break; + case -8: glfwShape = GLFW_RESIZE_NWSE_CURSOR; break; + case -6: glfwShape = GLFW_RESIZE_NESW_CURSOR; break; + #endif + default: glfwShape = GLFW_ARROW_CURSOR; break; + } + + GLFWcursor* cursor = glfwCreateStandardCursor(glfwShape); + if (cursor) { + glfwSetCursor(window, cursor); + } +} + void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; + runner->setCursor = platformSetCursor; + runner->currentCursor = 0; // cr_default #ifdef ENABLE_SW_RENDERER if (gfx == SOFTWARE) glfwSetWindowSizeCallback(window, resizeCallback); diff --git a/src/desktop/backends/sdl1.c b/src/desktop/backends/sdl1.c index c5785707..0276feaf 100644 --- a/src/desktop/backends/sdl1.c +++ b/src/desktop/backends/sdl1.c @@ -93,9 +93,16 @@ void platformExit(void) { SDL_Quit(); } +static void platformSetCursor(int32_t cursorType) { + // SDL1.2 only supports showing/hiding + SDL_ShowCursor(cursorType == -1 ? SDL_DISABLE : SDL_ENABLE); +} + void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; + runner->setCursor = platformSetCursor; + runner->currentCursor = 0; // cr_default } #ifdef ENABLE_SW_RENDERER diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index c8f48fc5..00bbd3cd 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -127,9 +127,40 @@ void platformExit(void) { SDL_Quit(); } +static void platformSetCursor(int32_t cursorType) { + if (cursorType == -1) { + SDL_ShowCursor(SDL_DISABLE); + return; + } + SDL_ShowCursor(SDL_ENABLE); + + SDL_SystemCursor sdlCursor; + switch (cursorType) { + case -3: sdlCursor = SDL_SYSTEM_CURSOR_CROSSHAIR; break; + case -4: sdlCursor = SDL_SYSTEM_CURSOR_IBEAM; break; + case -6: sdlCursor = SDL_SYSTEM_CURSOR_SIZENESW; break; + case -7: sdlCursor = SDL_SYSTEM_CURSOR_SIZENS; break; + case -8: sdlCursor = SDL_SYSTEM_CURSOR_SIZENWSE; break; + case -9: sdlCursor = SDL_SYSTEM_CURSOR_SIZEWE; break; + case -11: sdlCursor = SDL_SYSTEM_CURSOR_WAIT; break; + case -12: sdlCursor = SDL_SYSTEM_CURSOR_SIZEALL; break; + case -19: sdlCursor = SDL_SYSTEM_CURSOR_WAITARROW; break; + case -21: sdlCursor = SDL_SYSTEM_CURSOR_HAND; break; + case -22: sdlCursor = SDL_SYSTEM_CURSOR_SIZEALL; break; + default: sdlCursor = SDL_SYSTEM_CURSOR_ARROW; break; + } + + SDL_Cursor* cursor = SDL_CreateSystemCursor(sdlCursor); + if (cursor) { + SDL_SetCursor(cursor); + } +} + void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; + runner->setCursor = platformSetCursor; + runner->currentCursor = 0; // cr_default } #ifdef ENABLE_SW_RENDERER diff --git a/src/runner.h b/src/runner.h index bfcda141..50a843d0 100644 --- a/src/runner.h +++ b/src/runner.h @@ -456,6 +456,8 @@ struct Runner { bool (*getWindowSize)(int32_t* outW, int32_t* outH); void (*setWindowSize)(int32_t width, int32_t height); bool (*windowHasFocus)(void); + void (*setCursor)(int32_t cursorType); + int32_t currentCursor; // last value passed to window_set_cursor TileLayerMapEntry* tileLayerMap; // stb_ds hashmap: depth -> tile layer state RuntimeLayer* runtimeLayers; // stb_ds array, index-parallel to currentRoom->layers for parsed entries; dynamic entries appended uint32_t nextLayerId; // counter for IDs of layers/elements created at runtime diff --git a/src/vm_builtins.c b/src/vm_builtins.c index d994d157..e609b9e2 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -6416,6 +6416,24 @@ static RValue builtin_window_has_focus(VMContext* ctx, MAYBE_UNUSED RValue* args return RValue_makeBool(true); } +static RValue builtin_window_set_cursor(VMContext* ctx, RValue* args, int32_t argCount) { + if (argCount < 1) return RValue_makeUndefined(); + Runner* runner = ctx->runner; + if (runner == nullptr) return RValue_makeUndefined(); + int32_t cursorType = RValue_toInt32(args[0]); + runner->currentCursor = cursorType; + if (runner->setCursor != nullptr) { + runner->setCursor(cursorType); + } + return RValue_makeUndefined(); +} + +static RValue builtin_window_get_cursor(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + Runner* runner = ctx->runner; + if (runner == nullptr) return RValue_makeReal(0); + return RValue_makeReal(runner->currentCursor); +} + // ===[ Game State Functions ]=== static RValue builtin_game_restart(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { ctx->runner->pendingRoom = ROOM_RESTARTGAME; @@ -13385,6 +13403,8 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "window_set_size", builtin_window_set_size); VM_registerBuiltin(ctx, "window_center", builtin_window_center); VM_registerBuiltin(ctx, "window_has_focus", builtin_window_has_focus); + VM_registerBuiltin(ctx, "window_set_cursor", builtin_window_set_cursor); + VM_registerBuiltin(ctx, "window_get_cursor", builtin_window_get_cursor); // Game VM_registerBuiltin(ctx, "game_restart", builtin_game_restart);