kitgit

tirbofish/dropbear · commit

87b14a36c0cde3e5c18c666a59523be84fcb2bfb

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).

Unverified

tk <4tkbytes@pm.me> · 2025-10-20 09:35

view full diff

diff --git a/build.gradle.kts b/build.gradle.kts
index 786e8fa..4757c1c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -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 {
diff --git a/eucalyptus-core/src/input.rs b/eucalyptus-core/src/input.rs
index e4d7017..58c9d4d 100644
--- a/eucalyptus-core/src/input.rs
+++ b/eucalyptus-core/src/input.rs
@@ -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(),
         }
     }
 
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
index 7e1ac66..9752309 100644
--- a/eucalyptus-core/src/scripting/jni.rs
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -3,6 +3,7 @@
 
 pub mod exception;
 mod exports;
+mod utils;
 
 use std::fs;
 use jni::objects::{GlobalRef, JClass, JValue};
diff --git a/eucalyptus-core/src/scripting/jni/exports.rs b/eucalyptus-core/src/scripting/jni/exports.rs
index 97f3a65..df5ef17 100644
--- a/eucalyptus-core/src/scripting/jni/exports.rs
+++ b/eucalyptus-core/src/scripting/jni/exports.rs
@@ -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)
+    }
 }
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting/jni/utils.rs b/eucalyptus-core/src/scripting/jni/utils.rs
new file mode 100644
index 0000000..3fd5e72
--- /dev/null
+++ b/eucalyptus-core/src/scripting/jni/utils.rs
@@ -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,
+    }
+}
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting/native/exports.rs b/eucalyptus-core/src/scripting/native/exports.rs
index 45c5d8a..7ffbbb8 100644
--- a/eucalyptus-core/src/scripting/native/exports.rs
+++ b/eucalyptus-core/src/scripting/native/exports.rs
@@ -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
         }
     }
 }
diff --git a/eucalyptus-editor/src/editor/input.rs b/eucalyptus-editor/src/editor/input.rs
index 8c05b41..e737c23 100644
--- a/eucalyptus-editor/src/editor/input.rs
+++ b/eucalyptus-editor/src/editor/input.rs
@@ -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);
     }
 }
\ No newline at end of file
diff --git a/headers/dropbear.h b/headers/dropbear.h
index 3be9a85..2bb77ec 100644
--- a/headers/dropbear.h
+++ b/headers/dropbear.h
@@ -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);
 
 // ===========================================
 
diff --git a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
index 436a286..7bfccbc 100644
--- a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
@@ -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>
+
+    // -------------------------------------------------------------------
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/input/Gamepad.kt b/src/commonMain/kotlin/com/dropbear/input/Gamepad.kt
new file mode 100644
index 0000000..93ca89f
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/input/Gamepad.kt
@@ -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")
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/input/GamepadButton.kt b/src/commonMain/kotlin/com/dropbear/input/GamepadButton.kt
new file mode 100644
index 0000000..8e5f61b
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/input/GamepadButton.kt
@@ -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,
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/input/InputState.kt b/src/commonMain/kotlin/com/dropbear/input/InputState.kt
index 9560080..865b2c0 100644
--- a/src/commonMain/kotlin/com/dropbear/input/InputState.kt
+++ b/src/commonMain/kotlin/com/dropbear/input/InputState.kt
@@ -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()
+    }
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/input/MouseButton.kt b/src/commonMain/kotlin/com/dropbear/input/MouseButton.kt
new file mode 100644
index 0000000..bbefc3d
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/input/MouseButton.kt
@@ -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()
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/input/MouseButtonCodes.kt b/src/commonMain/kotlin/com/dropbear/input/MouseButtonCodes.kt
new file mode 100644
index 0000000..3514678
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/input/MouseButtonCodes.kt
@@ -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
+}
diff --git a/src/commonMain/kotlin/com/dropbear/math/Vector2.kt b/src/commonMain/kotlin/com/dropbear/math/Vector2.kt
new file mode 100644
index 0000000..78b3f70
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/math/Vector2.kt
@@ -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>
\ No newline at end of file
diff --git a/src/jvmMain/java/com/dropbear/ffi/JNINative.java b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
index 9fe0f59..f36fa40 100644
--- a/src/jvmMain/java/com/dropbear/ffi/JNINative.java
+++ b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
@@ -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);
 }
diff --git a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
index ed7ec46..67b6f9f 100644
--- a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
+++ b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -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())
+    }
 }
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
index 7a070d5..f1a5f98 100644
--- a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
@@ -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
+            }
+        }
+    }
 }
\ No newline at end of file