diff --git a/src/backends/appkit.m b/src/backends/appkit.m index 5c72fa6e7..818d93fb9 100644 --- a/src/backends/appkit.m +++ b/src/backends/appkit.m @@ -333,6 +333,33 @@ void platformSetWindowSize(int32_t width, int32_t height) { [window setFrame:newFrame display:YES animate:NO]; } +static bool platformGetWindowPosition(int32_t *outX, int32_t *outY) { + if (!outX || !outY) return false; + NSRect frame = [window frame]; + CGFloat screenHeight = [NSScreen mainScreen].frame.size.height; + *outX = (int32_t)round(frame.origin.x); + *outY = (int32_t)round(screenHeight - (frame.origin.y + frame.size.height)); + return true; +} + +static void platformSetWindowPosition(int32_t x, int32_t y) { + if (!window) return; + NSScreen *screen = [window screen]; + if (!screen) { + screen = [NSScreen mainScreen]; + } + + CGFloat screenHeight = [screen frame].size.height; + NSRect frame = [window frame]; + CGFloat windowHeight = frame.size.height; + + CGFloat appKitX = (CGFloat)x; + CGFloat appKitY = screenHeight - (CGFloat)y - windowHeight; + + NSPoint newOrigin = NSMakePoint(appKitX, appKitY); + [window setFrameOrigin:newOrigin]; +} + void platformGetMousePos(double *xPos, double *yPos) { NSPoint mouseLocation = [window mouseLocationOutsideOfEventStream]; *xPos = mouseLocation.x; @@ -641,6 +668,8 @@ void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = windowIsFocused; runner->setCursor = platformSetCursor; + runner->getWindowPosition = platformGetWindowPosition; + runner->setWindowPosition = platformSetWindowPosition; runner->currentCursor = GML_CR_ARROW; } diff --git a/src/backends/glfw2.c b/src/backends/glfw2.c index 3f07ef37e..684248df4 100644 --- a/src/backends/glfw2.c +++ b/src/backends/glfw2.c @@ -82,6 +82,11 @@ void platformSetWindowSize(int32_t width, int32_t height) { glfwSetWindowSize(width, height); } +static void platformSetWindowPosition(int32_t x, int32_t y) { + if (!window) return; + glfwSetWindowPos(x, y); +} + void platformGetMousePos(double *xPos, double *yPos) { if (!xPos || !yPos) return; int mx = 0, my = 0; @@ -244,6 +249,7 @@ void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; runner->setCursor = platformSetCursor; + runner->setWindowPosition = platformSetWindowPosition; runner->currentCursor = GML_CR_DEFAULT; #ifdef ENABLE_SW_RENDERER if (gfx == SOFTWARE) diff --git a/src/backends/glfw3.c b/src/backends/glfw3.c index 23af551ee..5a9476816 100644 --- a/src/backends/glfw3.c +++ b/src/backends/glfw3.c @@ -117,6 +117,26 @@ void platformSetWindowSize(int32_t width, int32_t height) { glfwSetWindowSize(window, logicalW, logicalH); } +static bool platformGetWindowPosition(int32_t *outX, int32_t *outY) { + if (!outX || !outY) return false; + int rawX, rawY; + float xscale, yscale; + glfwGetWindowContentScale(window, &xscale, &yscale); + glfwGetWindowPos(window, &rawX, &rawY); + *outX = (int32_t)((float)rawX * xscale + 0.5f); + *outY = (int32_t)((float)rawY * yscale + 0.5f); + return true; +} + +static void platformSetWindowPosition(int32_t x, int32_t y) { + if (!window) return; + float xscale, yscale; + glfwGetWindowContentScale(window, &xscale, &yscale); + int targetX = (int)(x / xscale + 0.5f); + int targetY = (int)(y / yscale + 0.5f); + glfwSetWindowPos(window, targetX, targetY); +} + void platformGetMousePos(double *xPos, double *yPos) { if (!xPos || !yPos) return; glfwGetCursorPos(window, xPos, yPos); @@ -318,6 +338,8 @@ void platformInitFunctions(Runner *runner) { runner->windowHasFocus = platformGetWindowFocus; runner->setCursor = platformSetCursor; runner->currentCursor = GML_CR_DEFAULT; + runner->getWindowPosition = platformGetWindowPosition; + runner->setWindowPosition = platformSetWindowPosition; #ifdef ENABLE_SW_RENDERER if (gfx == SOFTWARE) glfwSetWindowSizeCallback(window, resizeCallback); diff --git a/src/backends/noop.c b/src/backends/noop.c index b71767fc3..52cd937a2 100644 --- a/src/backends/noop.c +++ b/src/backends/noop.c @@ -27,11 +27,6 @@ void platformExit(void) { g_initialized = false; } -void platformInitFunctions(Runner *runner) { - g_runner = runner; - runner->setCursor = NULL; - runner->currentCursor = GML_CR_DEFAULT; -} bool platformGetWindowSize(int32_t *outW, int32_t *outH) { if (!outW || !outH) return false; @@ -50,6 +45,12 @@ void platformSetWindowSize(int32_t width, int32_t height) { if (height > 0) g_height = height; } +void platformInitFunctions(Runner *runner) { + g_runner = runner; + runner->setCursor = NULL; + runner->currentCursor = GML_CR_DEFAULT; +} + void platformSetWindowTitle(const char *title) { (void)title; } diff --git a/src/backends/sdl2.c b/src/backends/sdl2.c index a1e6db3ce..95839de0b 100644 --- a/src/backends/sdl2.c +++ b/src/backends/sdl2.c @@ -140,6 +140,21 @@ static float platformGetWindowScale(void) { return (logical_h > 0) ? (float)draw_h / logical_h : 1.0f; } +static void platformSetWindowPosition(int32_t x, int32_t y) { + float scale = platformGetWindowScale(); + SDL_SetWindowPosition(window, (int)(x / scale + 0.5f), (int)(y / scale + 0.5f)); +} + +static bool platformGetWindowPosition(int32_t* outX, int32_t* outY) { + if (!outX || !outY) return false; + int32_t rawX, rawY; + float scale = platformGetWindowScale(); + SDL_GetWindowPosition(window, &rawX, &rawY); + *outX = (int32_t)(rawX * scale + 0.5f); + *outY = (int32_t)(rawY * scale + 0.5f); + return true; +} + void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; @@ -284,6 +299,8 @@ void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; runner->setCursor = platformSetCursor; + runner->getWindowPosition = platformGetWindowPosition; + runner->setWindowPosition = platformSetWindowPosition; runner->currentCursor = GML_CR_DEFAULT; } diff --git a/src/backends/sdl3.c b/src/backends/sdl3.c index 7c9f6bee2..768a62c3e 100644 --- a/src/backends/sdl3.c +++ b/src/backends/sdl3.c @@ -8,6 +8,7 @@ #include "platformdefs.h" #include "gettime.h" #include +#include #include "runner_mouse.h" static Runner *g_runner; @@ -134,6 +135,15 @@ void platformSetWindowSize(int32_t width, int32_t height) { scr = SDL_GetWindowSurface(window); } +static bool platformGetWindowPosition(int32_t* outX, int32_t* outY) { + if (!outX || !outY) return false; + return SDL_GetWindowPosition(window, outX, outY); +} + +static void platformSetWindowPosition(int32_t x, int32_t y) { + SDL_SetWindowPosition(window, x, y); +} + void platformGetMousePos(double *xPos, double *yPos) { if (!xPos || !yPos) return; float mx = 0, my = 0; @@ -230,6 +240,8 @@ void platformInitFunctions(Runner *runner) { g_runner = runner; runner->windowHasFocus = platformGetWindowFocus; runner->setCursor = platformSetCursor; + runner->getWindowPosition = platformGetWindowPosition; + runner->setWindowPosition = platformSetWindowPosition; runner->currentCursor = GML_CR_DEFAULT; } diff --git a/src/backends/web.c b/src/backends/web.c index 6a3301501..a9f346b68 100644 --- a/src/backends/web.c +++ b/src/backends/web.c @@ -194,12 +194,6 @@ void platformExit(void) { emscripten_webgl_destroy_context(gWebGLContextHandle); } -void platformInitFunctions(Runner *runner) { - gRunner = runner; - runner->setCursor = nullptr; - runner->currentCursor = GML_CR_DEFAULT; -} - bool platformGetWindowSize(int32_t *outW, int32_t *outH) { if (!outW || !outH) return false; if (!gInitialized) return false; @@ -208,6 +202,12 @@ bool platformGetWindowSize(int32_t *outW, int32_t *outH) { return true; } +void platformInitFunctions(Runner *runner) { + gRunner = runner; + runner->setCursor = nullptr; + runner->currentCursor = GML_CR_DEFAULT; +} + bool platformGetScaledWindowSize(int32_t *outW, int32_t *outH) { return platformGetWindowSize(outW, outH); } diff --git a/src/runner.h b/src/runner.h index c5b9cbe32..da0eab4ea 100644 --- a/src/runner.h +++ b/src/runner.h @@ -746,6 +746,8 @@ struct Runner { void (*setWindowTitle)(const char* title); bool (*getWindowSize)(int32_t* outW, int32_t* outH); void (*setWindowSize)(int32_t width, int32_t height); + bool (*getWindowPosition)(int32_t* outX, int32_t* outY); + void (*setWindowPosition)(int32_t x, int32_t y); bool (*windowHasFocus)(void); void (*setCursor)(int32_t cursorType); int32_t currentCursor; // last value passed to window_set_cursor diff --git a/src/vm_builtins.c b/src/vm_builtins.c index f00536dfb..cbcb3e3f0 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -9173,6 +9173,42 @@ static RValue builtin_window_get_height(VMContext* ctx, MAYBE_UNUSED RValue* arg return RValue_makeReal((GMLReal) ctx->dataWin->gen8.defaultWindowHeight); } +static RValue builtin_window_get_x(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + Runner* runner = ctx->runner; + if (runner != nullptr && runner->getWindowPosition != nullptr) { + int32_t x = 0; + int32_t y = 0; + if (runner->getWindowPosition(&x, &y)) { + return RValue_makeReal((GMLReal) x); + } + } + return RValue_makeReal((GMLReal)0); +} + +static RValue builtin_window_get_y(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + Runner* runner = ctx->runner; + if (runner != nullptr && runner->getWindowPosition != nullptr) { + int32_t x = 0; + int32_t y = 0; + if (runner->getWindowPosition(&x, &y)) { + return RValue_makeReal((GMLReal) y); + } + } + return RValue_makeReal((GMLReal)0); +} + +static RValue builtin_window_set_position(VMContext* ctx, RValue* args, MAYBE_UNUSED int32_t argCount) { + REQUIRE_ARGC_AT_LEAST("window_set_position", 2, RValue_makeUndefined()); + int32_t x = RValue_toInt32(args[0]); + int32_t y = RValue_toInt32(args[1]); + + Runner* runner = ctx->runner; + if (runner != nullptr && runner->setWindowPosition != nullptr) { + runner->setWindowPosition(x, y); + } + return RValue_makeUndefined(); +} + static RValue builtin_window_set_size(VMContext* ctx, RValue* args, MAYBE_UNUSED int32_t argCount) { REQUIRE_ARGC_AT_LEAST("window_set_size", 2, RValue_makeUndefined()); @@ -23122,6 +23158,9 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "window_get_caption", builtin_window_get_caption); VM_registerBuiltin(ctx, "window_get_width", builtin_window_get_width); VM_registerBuiltin(ctx, "window_get_height", builtin_window_get_height); + VM_registerBuiltin(ctx, "window_get_x", builtin_window_get_x); + VM_registerBuiltin(ctx, "window_get_y", builtin_window_get_y); + VM_registerBuiltin(ctx, "window_set_position", builtin_window_set_position); 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);