Browse Source

Add gamepad support

gingerBill 1 year ago
parent
commit
95721fe296
3 changed files with 142 additions and 32 deletions
  1. 41 19
      core/sys/wasm/js/events.odin
  2. 1 2
      core/sys/wasm/js/events_all_targets.odin
  3. 100 11
      core/sys/wasm/js/odin.js

+ 41 - 19
core/sys/wasm/js/events.odin

@@ -192,11 +192,8 @@ KEYBOARD_MAX_CODE_SIZE :: 16
 GAMEPAD_MAX_ID_SIZE      :: 64
 GAMEPAD_MAX_MAPPING_SIZE :: 64
 
-Gamepad_Button :: struct {
-	pressed: bool,
-	touched: bool,
-	value:   f64,
-}
+GAMEPAD_MAX_BUTTONS :: 64
+GAMEPAD_MAX_AXES    :: 16
 
 Event_Target_Kind :: enum u32 {
 	Element  = 0,
@@ -218,6 +215,30 @@ Event_Option :: enum u8 {
 }
 Event_Options :: distinct bit_set[Event_Option; u8]
 
+Gamepad_Button :: struct {
+	value:   f64,
+	pressed: bool,
+	touched: bool,
+}
+
+Gamepad_State :: struct {
+	id:           string,
+	mapping:      string,
+	index:        int,
+	connected:    bool,
+	timestamp:    f64,
+
+	button_count: int,
+	axis_count:   int,
+	buttons: [GAMEPAD_MAX_BUTTONS]Gamepad_Button `fmt:"v,button_count"`,
+	axes:    [GAMEPAD_MAX_AXES]f64               `fmt:"v,axes_count"`,
+
+	_id_len:      int `fmt:"-"`,
+	_mapping_len: int `fmt:"-"`,
+	_id_buf:      [GAMEPAD_MAX_ID_SIZE]byte      `fmt:"-"`,
+	_mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`,
+}
+
 Event :: struct {
 	kind:                 Event_Kind,
 	target_kind:          Event_Target_Kind,
@@ -276,20 +297,7 @@ Event :: struct {
 			buttons: bit_set[0..<16; u16],
 		},
 
-		gamepad: struct {
-			id:           string,
-			mapping:      string,
-			index:        int,
-			connected:    bool,
-			timestamp:    f64,
-			button_count: int,
-			axes_count:   int,
-
-			_id_len:      int `fmt:"-"`,
-			_mapping_len: int `fmt:"-"`,
-			_id_buf:      [GAMEPAD_MAX_ID_SIZE]byte      `fmt:"-"`,
-			_mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`,
-		},
+		gamepad: Gamepad_State,
 	},
 
 
@@ -366,6 +374,20 @@ remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr
 	return _remove_event_listener(id, name, user_data, callback)
 }
 
+get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool {
+	@(default_calling_convention="contextless")
+	foreign dom_lib {
+		@(link_name="get_gamepad_state")
+		_get_gamepad_state :: proc(index: int, s: ^Gamepad_State) -> bool ---
+	}
+
+	if s == nil {
+		return false
+	}
+	return _get_gamepad_state(index, s)
+}
+
+
 @(export, link_name="odin_dom_do_event_callback")
 do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) {
 	@(default_calling_convention="contextless")

+ 1 - 2
core/sys/wasm/js/events_all_targets.odin

@@ -284,5 +284,4 @@ add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, c
 }
 remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
 	panic("vendor:wasm/js not supported on non JS targets")
-}
-
+}

+ 100 - 11
core/sys/wasm/js/odin.js

@@ -1533,28 +1533,47 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
 
 					wmi.storeU8(off(1), !!e.repeat);
 
-					wmi.storeInt(off(W), e.key.length)
-					wmi.storeInt(off(W), e.code.length)
+					wmi.storeInt(off(W, W), e.key.length)
+					wmi.storeInt(off(W, W), e.code.length)
 					wmi.storeString(off(16, 1), e.key);
 					wmi.storeString(off(16, 1), e.code);
 				} else if (e.type === 'scroll') {
-					wmi.storeF64(off(8), window.scrollX);
-					wmi.storeF64(off(8), window.scrollY);
+					wmi.storeF64(off(8, 8), window.scrollX);
+					wmi.storeF64(off(8, 8), window.scrollY);
 				} else if (e.type === 'visibilitychange') {
 					wmi.storeU8(off(1), !document.hidden);
 				} else if (e instanceof GamepadEvent) {
 					const idPtr      = off(W*2, W);
 					const mappingPtr = off(W*2, W);
 
-					wmi.storeI32(off(W), e.gamepad.index);
+					wmi.storeI32(off(W, W), e.gamepad.index);
 					wmi.storeU8(off(1), !!e.gamepad.connected);
-					wmi.storeF64(off(8), e.gamepad.timestamp);
-
-					wmi.storeInt(off(W), e.gamepad.buttons.length);
-					wmi.storeInt(off(W), e.gamepad.axes.length);
+					wmi.storeF64(off(8, 8), e.gamepad.timestamp);
+
+					wmi.storeInt(off(W, W), e.gamepad.buttons.length);
+					wmi.storeInt(off(W, W), e.gamepad.axes.length);
+
+					for (let i = 0; i < 64; i++) {
+						if (i < e.gamepad.buttons.length) {
+							let b = e.gamepad.buttons[i];
+							wmi.storeF64(off(8, 8), b.value);
+							wmi.storeU8(off(1),  !!b.pressed);
+							wmi.storeU8(off(1),  !!b.touched);
+						} else {
+							off(16, 8);
+						}
+					}
+					for (let i = 0; i < 16; i++) {
+						if (i < e.gamepad.axes.length) {
+							let a = e.gamepad.axes[i];
+							wmi.storeF64(off(8, 8), a);
+						} else {
+							off(8, 8);
+						}
+					}
 
-					wmi.storeInt(off(W), e.gamepad.id.length)
-					wmi.storeInt(off(W), e.gamepad.mapping.length)
+					wmi.storeInt(off(W, W), e.gamepad.id.length)
+					wmi.storeInt(off(W, W), e.gamepad.mapping.length)
 					wmi.storeString(off(64, 1), e.gamepad.id);
 					wmi.storeString(off(64, 1), e.gamepad.mapping);
 				}
@@ -1661,6 +1680,76 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
 				return false;
 			},
 
+			get_gamepad_state: (gamepad_id, ep) => {
+				let index = gamepad_id;
+				let gps = navigator.getGamepads();
+				if (0 <= index && index < gps.length) {
+					let gamepad = gps[index];
+					if (!gamepad) {
+						return false;
+					}
+
+					const W = wasmMemoryInterface.intSize;
+					let offset = ep;
+					let off = (amount, alignment) => {
+						if (alignment === undefined) {
+							alignment = Math.min(amount, W);
+						}
+						if (offset % alignment != 0) {
+							offset += alignment - (offset%alignment);
+						}
+						let x = offset;
+						offset += amount;
+						return x;
+					};
+
+					let align = (alignment) => {
+						const modulo = offset & (alignment-1);
+						if (modulo != 0) {
+							offset += alignment - modulo
+						}
+					};
+
+					let wmi = wasmMemoryInterface;
+
+					const idPtr      = off(W*2, W);
+					const mappingPtr = off(W*2, W);
+
+					wmi.storeI32(off(W), gamepad.index);
+					wmi.storeU8(off(1), !!gamepad.connected);
+					wmi.storeF64(off(8), gamepad.timestamp);
+
+					wmi.storeInt(off(W), gamepad.buttons.length);
+					wmi.storeInt(off(W), gamepad.axes.length);
+
+					for (let i = 0; i < 64; i++) {
+						if (i < gamepad.buttons.length) {
+							let b = gamepad.buttons[i];
+							wmi.storeF64(off(8, 8), b.value);
+							wmi.storeU8(off(1),  !!b.pressed);
+							wmi.storeU8(off(1),  !!b.touched);
+						} else {
+							off(16, 8);
+						}
+					}
+					for (let i = 0; i < 16; i++) {
+						if (i < gamepad.axes.length) {
+							wmi.storeF64(off(8, 8), gamepad.axes[i]);
+						} else {
+							off(8, 8);
+						}
+					}
+
+					wmi.storeInt(off(W, W), gamepad.id.length)
+					wmi.storeInt(off(W, W), gamepad.mapping.length)
+					wmi.storeString(off(64, 1), gamepad.id);
+					wmi.storeString(off(64, 1), gamepad.mapping);
+
+					return true;
+				}
+				return false;
+			},
+
 			get_element_value_f64: (id_ptr, id_len) => {
 				let id = wasmMemoryInterface.loadString(id_ptr, id_len);
 				let element = getElement(id);