tirbofish/dropbear · diff
added more functions for mouse input detection :) i believe input state is dealt with, but controller support doesn't exist yet. ill do that later (not my biggest priority rn).
Signature present but could not be verified.
Unverified
@@ -33,6 +33,7 @@ val libPathProvider = provider { ) candidates.firstOrNull { it.exists() }?.absolutePath + ?: println("No Rust library exists") } kotlin { @@ -50,33 +51,13 @@ kotlin { } val nativeLibPath = libPathProvider.get() - if (nativeLibPath != null) { - val nativeLibDir = file(nativeLibPath).parentFile.absolutePath - val nativeLibFileName = file(nativeLibPath).name - val nativeLibNameForLinking = when { - isMacOs -> nativeLibFileName.removePrefix("lib").removeSuffix(".dylib") - isLinux -> nativeLibFileName.removePrefix("lib").removeSuffix(".so") - isMingwX64 -> nativeLibFileName.removeSuffix(".dll") - else -> throw GradleException("Unsupported OS for library name derivation.") - } - - nativeTarget.apply { - binaries { - sharedLib { - baseName = "dropbear" - - if (isLinux || isMacOs) { - linkerOpts("-L$nativeLibDir", "-l$nativeLibNameForLinking", "-Wl,-rpath,\\\$ORIGIN") - } else if (isMingwX64) { - val importLibName = "$nativeLibNameForLinking.lib" - val importLibPath = file("$nativeLibDir/$importLibName").absolutePath - linkerOpts( - importLibPath - ) - } - } - } - } + val nativeLibDir = file(nativeLibPath).parentFile.absolutePath + val nativeLibFileName = file(nativeLibPath).name + val nativeLibNameForLinking = when { + isMacOs -> nativeLibFileName.removePrefix("lib").removeSuffix(".dylib") + isLinux -> nativeLibFileName.removePrefix("lib").removeSuffix(".so") + isMingwX64 -> nativeLibFileName.removeSuffix(".dll") + else -> throw GradleException("Unsupported OS for library name derivation.") } nativeTarget.apply { @@ -88,6 +69,21 @@ kotlin { } } } + binaries { + sharedLib { + baseName = "dropbear" + + if (isLinux || isMacOs) { + linkerOpts("-L$nativeLibDir", "-l$nativeLibNameForLinking", "-Wl,-rpath,\\\$ORIGIN") + } else if (isMingwX64) { + val importLibName = "$nativeLibNameForLinking.dll.lib" + val importLibPath = file("$nativeLibDir/$importLibName").absolutePath + linkerOpts( + importLibPath + ) + } + } + } } sourceSets { @@ -11,6 +11,7 @@ pub struct InputState { pub last_key_press_times: HashMap<KeyCode, Instant>, #[allow(dead_code)] pub double_press_threshold: Duration, + pub mouse_pos: (f64, f64), pub mouse_button: HashSet<MouseButton>, pub pressed_keys: HashSet<KeyCode>, @@ -22,8 +23,6 @@ pub struct InputState { pub pressed_buttons: HashMap<GamepadId, HashSet<Button>>, pub left_stick_position: HashMap<GamepadId, (f32, f32)>, pub right_stick_position: HashMap<GamepadId, (f32, f32)>, - pub left_trigger: HashMap<GamepadId, f32>, - pub right_trigger: HashMap<GamepadId, f32>, } impl Default for InputState { @@ -47,8 +46,6 @@ impl InputState { pressed_buttons: Default::default(), left_stick_position: Default::default(), right_stick_position: Default::default(), - left_trigger: Default::default(), - right_trigger: Default::default(), } } @@ -3,6 +3,7 @@ pub mod exception; mod exports; +mod utils; use std::fs; use jni::objects::{GlobalRef, JClass, JValue}; @@ -1,21 +1,19 @@ +use crate::ptr::InputStatePtr; +use crate::scripting::jni::utils::{java_button_to_rust, new_float_array}; +use crate::utils::keycode_from_ordinal; +use dropbear_engine::entity::{AdoptedEntity, Transform}; use glam::{DQuat, DVec3}; use hecs::World; +use jni::objects::{JClass, JObject, JString}; +use jni::sys::{jboolean, jclass, jfloatArray, jint, jlong}; use jni::JNIEnv; -use jni::objects::{JObject, JString}; -use jni::sys::{jboolean, jclass, jint, jlong}; -use winit::keyboard::{KeyCode, PhysicalKey}; -use winit::platform::scancode::PhysicalKeyExtScancode; -use dropbear_engine::entity::{AdoptedEntity, Transform}; -use crate::ptr::InputStatePtr; -use num_enum::FromPrimitive; -use crate::utils::keycode_from_ordinal; // JNIEXPORT jlong JNICALL Java_com_dropbear_ffi_JNINative_getEntity // (JNIEnv *, jclass, jlong, jstring); #[unsafe(no_mangle)] pub fn Java_com_dropbear_ffi_JNINative_getEntity( mut env: JNIEnv, - _obj: jclass, + _obj: JClass, world_handle: jlong, label: JString, ) -> jlong { @@ -116,7 +114,7 @@ pub fn Java_com_dropbear_ffi_JNINative_getTransform( #[unsafe(no_mangle)] pub fn Java_com_dropbear_ffi_JNINative_setTransform( mut env: JNIEnv, - _class: jclass, + _class: JClass, world_handle: jlong, entity_id: jlong, transform_obj: JObject, @@ -213,7 +211,7 @@ pub fn Java_com_dropbear_ffi_JNINative_setTransform( #[unsafe(no_mangle)] pub fn Java_com_dropbear_ffi_JNINative_printInputState( _env: JNIEnv, - _class: jclass, + _class: JClass, input_handle: jlong, ) { let input = input_handle as InputStatePtr; @@ -232,7 +230,7 @@ pub fn Java_com_dropbear_ffi_JNINative_printInputState( #[unsafe(no_mangle)] pub fn Java_com_dropbear_ffi_JNINative_isKeyPressed( _env: JNIEnv, - _class: jclass, + _class: JClass, input_handle: jlong, key: jint, ) -> jboolean { @@ -259,4 +257,135 @@ pub fn Java_com_dropbear_ffi_JNINative_isKeyPressed( false.into() } } +} + +// JNIEXPORT jfloatArray JNICALL Java_com_dropbear_ffi_JNINative_getMousePosition +// (JNIEnv *, jclass, jlong); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_getMousePosition( + mut env: JNIEnv, + _class: JClass, + input_handle: jlong, +) -> jfloatArray { + let input = input_handle as InputStatePtr; + if input.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_getMousePosition] [ERROR] Input state pointer is null"); + return new_float_array(&mut env, -1.0, -1.0); + } + + let input = unsafe { &*input }; + + new_float_array(&mut env, input.mouse_pos.0 as f32, input.mouse_pos.1 as f32) +} + +// JNIEXPORT jboolean JNICALL Java_com_dropbear_ffi_JNINative_isMouseButtonPressed +// (JNIEnv *, jclass, jlong, jint); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_isMouseButtonPressed( + _env: JNIEnv, + _class: JClass, + input_handle: jlong, + button: jint, +) -> jboolean { + let input_ptr = input_handle as InputStatePtr; + + if input_ptr.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_isMouseButtonPressed] [ERROR] Input state pointer is null"); + return false as jboolean; + } + + let input = unsafe { &*input_ptr }; + + if let Some(rust_button) = java_button_to_rust(button) { + let is_pressed = input.mouse_button.contains(&rust_button); + is_pressed as jboolean + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_isMouseButtonPressed] [ERROR] Invalid button code: {}", button); + false as jboolean + } +} + +// JNIEXPORT jfloatArray JNICALL Java_com_dropbear_ffi_JNINative_getMouseDelta +// (JNIEnv *, jclass, jlong); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_getMouseDelta( + mut env: JNIEnv, + _class: JClass, + input_handle: jlong, +) -> jfloatArray { + let input = input_handle as InputStatePtr; + if input.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_getMouseDelta] [ERROR] Input state pointer is null"); + return new_float_array(&mut env, -1.0, -1.0); + } + + let input = unsafe { &*input }; + + if let Some(pos) = input.mouse_delta { + new_float_array(&mut env, pos.0 as f32, pos.1 as f32) + } else { + new_float_array(&mut env, 0.0, 0.0) + } +} + +// JNIEXPORT jboolean JNICALL Java_com_dropbear_ffi_JNINative_isCursorLocked +// (JNIEnv *, jclass, jlong); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_isCursorLocked( + _env: JNIEnv, + _class: JClass, + input_handle: jlong, +) -> jboolean { + let input = input_handle as InputStatePtr; + if input.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_isCursorLocked] [ERROR] Input state pointer is null"); + return false as jboolean; + } + + let input = unsafe { &*input }; + + input.is_cursor_locked as jboolean +} + +// JNIEXPORT void JNICALL Java_com_dropbear_ffi_JNINative_setCursorLocked +// (JNIEnv *, jclass, jlong, jboolean); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_setCursorLocked( + _env: JNIEnv, + _class: JClass, + input_handle: jlong, + locked: jboolean, +) { + let input = input_handle as InputStatePtr; + + if input.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_isCursorLocked] [ERROR] Input state pointer is null"); + return; + } + + let input = unsafe { &mut *input }; + + input.is_cursor_locked = locked != 0; +} + +// JNIEXPORT jfloatArray JNICALL Java_com_dropbear_ffi_JNINative_getLastMousePos +// (JNIEnv *, jclass, jlong); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_getLastMousePos( + mut env: JNIEnv, + _class: JClass, + input_handle: jlong, +) -> jfloatArray { + let input = input_handle as InputStatePtr; + if input.is_null() { + println!("[Java_com_dropbear_ffi_JNINative_getLastMousePos] [ERROR] Input state pointer is null"); + return new_float_array(&mut env, -1.0, -1.0); + } + + let input = unsafe { &*input }; + if let Some(pos) = input.last_mouse_pos { + new_float_array(&mut env, pos.0 as f32, pos.1 as f32) + } else { + new_float_array(&mut env, 0.0, 0.0) + } } @@ -0,0 +1,42 @@ +use jni::JNIEnv; +use jni::objects::JFloatArray; +use jni::sys::{jfloatArray, jint}; + +pub fn new_float_array(env: &mut JNIEnv, x: f32, y: f32) -> jfloatArray { + let java_array: JFloatArray = match env.new_float_array(2) { + Ok(v) => v, + Err(e) => { + eprintln!("[ERROR] Failed to create float array: {}", e); + return std::ptr::null_mut(); + } + }; + let elements: [f32; 2] = [x, y]; + match env.set_float_array_region(&java_array, 0, &elements) { + Ok(()) => { + java_array.into_raw() + }, + Err(e) => { + eprintln!("[ERROR] Error setting float array region: {}", e); + env.throw_new("java/lang/RuntimeException", "Failed to set float array region").unwrap(); + std::ptr::null_mut() + } + } +} + +const JAVA_MOUSE_BUTTON_LEFT: jint = 0; +const JAVA_MOUSE_BUTTON_RIGHT: jint = 1; +const JAVA_MOUSE_BUTTON_MIDDLE: jint = 2; +const JAVA_MOUSE_BUTTON_BACK: jint = 3; +const JAVA_MOUSE_BUTTON_FORWARD: jint = 4; + +pub fn java_button_to_rust(button_code: jint) -> Option<winit::event::MouseButton> { + match button_code { + JAVA_MOUSE_BUTTON_LEFT => Some(winit::event::MouseButton::Left), + JAVA_MOUSE_BUTTON_RIGHT => Some(winit::event::MouseButton::Right), + JAVA_MOUSE_BUTTON_MIDDLE => Some(winit::event::MouseButton::Middle), + JAVA_MOUSE_BUTTON_BACK => Some(winit::event::MouseButton::Back), + JAVA_MOUSE_BUTTON_FORWARD => Some(winit::event::MouseButton::Forward), + other if other >= 0 => Some(winit::event::MouseButton::Other(other as u16)), // Assuming Other uses the int directly + _ => None, + } +} @@ -1,6 +1,4 @@ use std::ffi::{c_char, CStr}; -use winit::keyboard::{KeyCode, PhysicalKey}; -use winit::platform::scancode::PhysicalKeyExtScancode; use dropbear_engine::entity::{AdoptedEntity, Transform}; use crate::ptr::InputStatePtr; use crate::scripting::native::DropbearNativeError; @@ -129,11 +127,11 @@ pub unsafe extern "C" fn dropbear_print_input_state( pub unsafe extern "C" fn dropbear_is_key_pressed( input_state_ptr: InputStatePtr, key: i32, - out_is_pressed: *mut bool, + out_is_pressed: *mut i32, ) -> i32 { if input_state_ptr.is_null() { eprintln!("[dropbear_is_key_pressed] [ERROR] Input state pointer is null"); - unsafe { *out_is_pressed = false }; + unsafe { *out_is_pressed = 0 }; //false return DropbearNativeError::NullPointer as i32; } @@ -143,14 +141,14 @@ pub unsafe extern "C" fn dropbear_is_key_pressed( Some(k) => { println!("[dropbear_is_key_pressed] [DEBUG] Keycode: {:?}", k); if input.pressed_keys.contains(&k) { - true.into() + 1 } else { - false.into() + 0 } } None => { println!("[dropbear_is_key_pressed] [WARN] Ordinal keycode is invalid"); - false.into() + 0 } } } @@ -258,6 +258,7 @@ impl Mouse for Editor { && let Some((camera, _)) = q.get() { camera.track_mouse_delta(dx, dy); + self.input_state.mouse_delta = Some((dx, dy)); } } self.input_state.last_mouse_pos = Some((position.x, position.y)); @@ -290,6 +291,7 @@ impl Mouse for Editor { && let Some((camera, _)) = q.get() { camera.track_mouse_delta(dx, dy); + self.input_state.mouse_delta = Some((dx, dy)); } let _ = window.set_cursor_position(center); @@ -344,7 +346,5 @@ impl Controller for Editor { self.input_state.pressed_buttons.remove(&id); self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); - self.input_state.left_trigger.remove(&id); - self.input_state.right_trigger.remove(&id); } } @@ -47,8 +47,13 @@ int dropbear_set_transform( ); void dropbear_print_input_state(const InputState* input_state_ptr); - int dropbear_is_key_pressed(const InputState* input_state_ptr, int keycode, int* out_value); // out_value is a boolean 0 or 1 +int dropbear_get_mouse_position(const InputState* input_state_ptr, float* out_x, float* out_y); +int dropbear_is_mouse_button_pressed(const InputState* input_state_ptr, int button_code, int* out_pressed); +int dropbear_get_mouse_delta(const InputState* input_state_ptr, float* out_delta_x, float* out_delta_y); +int dropbear_is_cursor_locked(const InputState* input_state_ptr, int* out_locked); +int dropbear_set_cursor_locked(const InputState* input_state_ptr, int locked); +int dropbear_get_last_mouse_pos(const InputState* input_state_ptr, float* out_x, float* out_y); // =========================================== @@ -1,8 +1,11 @@ package com.dropbear.ffi import com.dropbear.EntityId +import com.dropbear.input.Gamepad import com.dropbear.input.KeyCode +import com.dropbear.input.MouseButton import com.dropbear.math.Transform +import com.dropbear.math.Vector2D expect class NativeEngine { /** @@ -20,13 +23,23 @@ expect class NativeEngine { */ fun setTransform(entityId: EntityId, transform: Transform) + // --------------------------- INPUT STATE --------------------------- + /** * Prints the input state, typically used for debugging. */ fun printInputState() - /** * Checks if a Key is pressed by its KeyCode */ fun isKeyPressed(key: KeyCode): Boolean + fun getMousePosition(): Vector2D? + fun isMouseButtonPressed(button: MouseButton): Boolean + fun getMouseDelta(): Vector2D? + fun isCursorLocked(): Boolean + fun setCursorLocked(locked: Boolean) + fun getLastMousePos(): Vector2D? +// fun getConnectedGamepads(): List<Gamepad> + + // ------------------------------------------------------------------- } @@ -0,0 +1,13 @@ +package com.dropbear.input + +import com.dropbear.math.Vector2D + +class Gamepad( + val id: Int, + val leftStickPosition: Vector2D, + val rightStickPosition: Vector2D, +) { + fun isButtonPressed(button: GamepadButton): Boolean { + TODO("Not yet implemented") + } +} @@ -0,0 +1,32 @@ +package com.dropbear.input + +/** + * Enum representing the different gamepad buttons. + * + * Enum taken from the `gilrs` crate + * + * See the [Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg) image + * for more information + */ +enum class GamepadButton { + Unknown, + South, + East, + North, + West, + C, + Z, + LeftTrigger, + RightTrigger, + LeftTrigger2, + RightTrigger2, + Select, + Start, + Mode, + LeftThumb, + RightThumb, + DPadUp, + DPadDown, + DPadLeft, + DPadRight, +} @@ -1,6 +1,7 @@ package com.dropbear.input import com.dropbear.DropbearEngine +import com.dropbear.math.Vector2D class InputState(private val engine: DropbearEngine) { @@ -11,4 +12,33 @@ class InputState(private val engine: DropbearEngine) { fun isKeyPressed(key: KeyCode): Boolean { return engine.native.isKeyPressed(key) } + + fun getMousePosition(): Vector2D { + return engine.native.getMousePosition() ?: Vector2D(0.0, 0.0) + } + + fun isMouseButtonPressed(button: MouseButton): Boolean { + return engine.native.isMouseButtonPressed(button) + } + + fun getMouseDelta(): Vector2D { + return engine.native.getMouseDelta() ?: Vector2D(0.0, 0.0) + } + + fun isCursorLocked(): Boolean { + return engine.native.isCursorLocked() + } + + fun setCursorLocked(locked: Boolean) { + return engine.native.setCursorLocked(locked) + } + + fun getLastMousePos(): Vector2D { + return engine.native.getLastMousePos() ?: Vector2D(0.0, 0.0) + } + + fun getConnectedGamepads(): List<Gamepad> { + TODO("Not yet implemented") +// return engine.native.getConnectedGamepads() + } } @@ -0,0 +1,10 @@ +package com.dropbear.input + +sealed class MouseButton { + object Left : MouseButton() + object Right : MouseButton() + object Middle : MouseButton() + object Back : MouseButton() + object Forward : MouseButton() + data class Other(val value: Int) : MouseButton() +} @@ -0,0 +1,9 @@ +package com.dropbear.input + +object MouseButtonCodes { + const val LEFT = 0 + const val RIGHT = 1 + const val MIDDLE = 2 + const val BACK = 3 + const val FORWARD = 4 +} @@ -0,0 +1,33 @@ +package com.dropbear.math + +import kotlin.jvm.JvmField + +/** + * A class for holding a vector of `2` values of the same type. + */ +class Vector2<T: Number>( + @JvmField var x: T, + @JvmField var y: T, +) { + companion object { + /** + * Creates a new [com.dropbear.math.Vector2] of type `T` with one value. + */ + fun <T: Number> uniform(value: T): Vector2<T> { + return Vector2(value, value) + } + + /** + * Creates a [com.dropbear.math.Vector2] of type [Double] filled with only zeroes + */ + fun zero(): Vector2<Double> { + return Vector2(0.0, 0.0) + } + } + + fun asDoubleVector(): Vector2<Double> { + return Vector2(this.x.toDouble(), this.y.toDouble()) + } +} + +public typealias Vector2D = Vector2<Double> @@ -17,4 +17,10 @@ public class JNINative { // input public static native void printInputState(long inputHandle); public static native boolean isKeyPressed(long inputHandle, int ordinal); + public static native float[] getMousePosition(long inputHandle); + public static native boolean isMouseButtonPressed(long inputHandle, int ordinal); + public static native float[] getMouseDelta(long inputHandle); + public static native boolean isCursorLocked(long inputHandle); + public static native void setCursorLocked(long inputHandle, boolean locked); + public static native float[] getLastMousePos(long inputHandle); } @@ -2,7 +2,10 @@ package com.dropbear.ffi import com.dropbear.EntityId import com.dropbear.input.KeyCode +import com.dropbear.input.MouseButton +import com.dropbear.input.MouseButtonCodes import com.dropbear.math.Transform +import com.dropbear.math.Vector2D actual class NativeEngine { private var worldHandle: Long = 0L @@ -37,4 +40,40 @@ actual class NativeEngine { actual fun isKeyPressed(key: KeyCode): Boolean { return JNINative.isKeyPressed(inputHandle, key.ordinal) } + + actual fun getMousePosition(): Vector2D? { + val result = JNINative.getMousePosition(inputHandle); + return Vector2D(result[0].toDouble(), result[1].toDouble()) + } + + actual fun isMouseButtonPressed(button: MouseButton): Boolean { + val buttonCode: Int = when (button) { + MouseButton.Left -> MouseButtonCodes.LEFT + MouseButton.Right -> MouseButtonCodes.RIGHT + MouseButton.Middle -> MouseButtonCodes.MIDDLE + MouseButton.Back -> MouseButtonCodes.BACK + MouseButton.Forward -> MouseButtonCodes.FORWARD + is MouseButton.Other -> button.value + } + + return JNINative.isMouseButtonPressed(inputHandle, buttonCode) + } + + actual fun getMouseDelta(): Vector2D? { + val result = JNINative.getMouseDelta(inputHandle); + return Vector2D(result[0].toDouble(), result[1].toDouble()) + } + + actual fun isCursorLocked(): Boolean { + return JNINative.isCursorLocked(inputHandle) + } + + actual fun setCursorLocked(locked: Boolean) { + JNINative.setCursorLocked(inputHandle, locked) + } + + actual fun getLastMousePos(): Vector2D? { + val result = JNINative.getLastMousePos(inputHandle); + return Vector2D(result[0].toDouble(), result[1].toDouble()) + } } @@ -6,8 +6,11 @@ package com.dropbear.ffi import com.dropbear.EntityId import com.dropbear.ffi.generated.* import com.dropbear.input.KeyCode +import com.dropbear.input.MouseButton +import com.dropbear.input.MouseButtonCodes import com.dropbear.logging.Logger import com.dropbear.math.Transform +import com.dropbear.math.Vector2D import kotlinx.cinterop.* import kotlin.experimental.ExperimentalNativeApi @@ -117,4 +120,129 @@ actual class NativeEngine { return out.value != 0 } } + + actual fun getMousePosition(): Vector2D? { + memScoped { + val xVar = alloc<FloatVar>() + val yVar = alloc<FloatVar>() + + val result = dropbear_get_mouse_position( + inputHandle.reinterpret(), + xVar.ptr, + yVar.ptr + ) + + if (result == 0) { + val x = xVar.value.toDouble() + val y = yVar.value.toDouble() + return Vector2D(x, y) + } else { + println("getMousePosition failed with code: $result") + return null + } + } + } + + actual fun isMouseButtonPressed(button: MouseButton): Boolean { + val buttonCode: Int = when (button) { + MouseButton.Left -> MouseButtonCodes.LEFT + MouseButton.Right -> MouseButtonCodes.RIGHT + MouseButton.Middle -> MouseButtonCodes.MIDDLE + MouseButton.Back -> MouseButtonCodes.BACK + MouseButton.Forward -> MouseButtonCodes.FORWARD + is MouseButton.Other -> button.value + } + + memScoped { + val pressedVar = alloc<IntVar>() + + val result = dropbear_is_mouse_button_pressed( + inputHandle.reinterpret(), + buttonCode, + pressedVar.ptr + ) + + if (result == 0) { + return pressedVar.value != 0 + } else { + println("isMouseButtonPressed failed with code: $result") + return false + } + } + } + + actual fun getMouseDelta(): Vector2D? { + memScoped { + val deltaXVar = alloc<FloatVar>() + val deltaYVar = alloc<FloatVar>() + + val result = dropbear_get_mouse_delta( + inputHandle.reinterpret(), + deltaXVar.ptr, + deltaYVar.ptr + ) + + if (result == 0) { + val deltaX = deltaXVar.value.toDouble() + val deltaY = deltaYVar.value.toDouble() + return Vector2D(deltaX, deltaY) + } else { + println("getMouseDelta failed with code: $result") + return null + } + } + } + + actual fun isCursorLocked(): Boolean { + memScoped { + val lockedVar = alloc<IntVar>() + + val result = dropbear_is_cursor_locked( + inputHandle.reinterpret(), + lockedVar.ptr + ) + + if (result == 0) { + return lockedVar.value != 0 + } else { + println("isCursorLocked failed with code: $result") + return false + } + } + } + + actual fun setCursorLocked(locked: Boolean) { + val lockedInt = if (locked) 1 else 0 + + val result = dropbear_set_cursor_locked( + inputHandle.reinterpret(), + lockedInt + ) + + if (result != 0) { + println("setCursorLocked failed with code: $result") + } + } + + actual fun getLastMousePos(): Vector2D? { + memScoped { + val xVar = alloc<FloatVar>() + val yVar = alloc<FloatVar>() + + val result = dropbear_get_last_mouse_pos( + inputHandle.reinterpret(), + xVar.ptr, + yVar.ptr + ) + + if (result == 0) { + val x = xVar.value.toDouble() + val y = yVar.value.toDouble() + return Vector2D(x, y) + } else { + println("getLastMousePos failed with code: $result") + return null + } + } + } }