Преглед изворни кода

Basic gamepad digital button support

Karl Zylinski пре 6 месеци
родитељ
комит
7cb32ca1a3
5 измењених фајлова са 193 додато и 20 уклоњено
  1. 4 4
      examples/snake/snake.odin
  2. 41 3
      karl2d.doc.odin
  3. 80 12
      karl2d.odin
  4. 13 1
      window_interface.odin
  5. 55 0
      window_win32.odin

+ 4 - 4
examples/snake/snake.odin

@@ -107,19 +107,19 @@ main :: proc() {
 		total_time := time.duration_seconds(time.diff(started_at, time_now))
 		k2.process_events()
 
-		if k2.key_is_held(.Up) {
+		if k2.key_is_held(.Up) || k2.gamepad_button_is_held(0, .Left_Face_Up) {
 			move_direction = {0, -1}
 		}
 
-		if k2.key_is_held(.Down) {
+		if k2.key_is_held(.Down) || k2.gamepad_button_is_held(0, .Left_Face_Down) {
 			move_direction = {0, 1}
 		}
 
-		if k2.key_is_held(.Left) {
+		if k2.key_is_held(.Left) || k2.gamepad_button_is_held(0, .Left_Face_Left) {
 			move_direction = {-1, 0}
 		}
 
-		if k2.key_is_held(.Right) {
+		if k2.key_is_held(.Right) || k2.gamepad_button_is_held(0, .Left_Face_Right) {
 			move_direction = {1, 0}
 		}
 

+ 41 - 3
karl2d.doc.odin

@@ -93,6 +93,12 @@ get_mouse_wheel_delta :: proc() -> f32
 
 get_mouse_position :: proc() -> Vec2
 
+gamepad_button_went_down :: proc(gamepad: int, button: Gamepad_Button) -> bool
+
+gamepad_button_went_up :: proc(gamepad: int, button: Gamepad_Button) -> bool
+
+gamepad_button_is_held :: proc(gamepad: int, button: Gamepad_Button) -> bool
+
 //---------//
 // DRAWING //
 //---------//
@@ -309,6 +315,8 @@ Handle :: hm.Handle
 Texture_Handle :: distinct Handle
 TEXTURE_NONE :: Texture_Handle {}
 
+MAX_GAMEPADS :: 4
+
 // This keeps track of the internal state of the library. Usually, you do not need to poke at it.
 // It is created and kept as a global variable when 'init' is called. However, 'init' also returns
 // the pointer to it, so you can later use 'set_internal_state' to restore it (after for example hot
@@ -327,14 +335,18 @@ State :: struct {
 	mouse_delta: Vec2,
 	mouse_wheel_delta: f32,
 
-	keys_went_down: #sparse [Keyboard_Key]bool,
-	keys_went_up: #sparse [Keyboard_Key]bool,
-	keys_is_held: #sparse [Keyboard_Key]bool,
+	key_went_down: #sparse [Keyboard_Key]bool,
+	key_went_up: #sparse [Keyboard_Key]bool,
+	key_is_held: #sparse [Keyboard_Key]bool,
 
 	mouse_button_went_down: #sparse [Mouse_Button]bool,
 	mouse_button_went_up: #sparse [Mouse_Button]bool,
 	mouse_button_is_held: #sparse [Mouse_Button]bool,
 
+	gamepad_button_went_down: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+	gamepad_button_went_up: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+	gamepad_button_is_held: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+
 	window: Window_Handle,
 	width: int,
 	height: int,
@@ -477,3 +489,29 @@ Keyboard_Key :: enum {
 	KP_Enter        = 335,
 	KP_Equal        = 336,
 }
+
+Gamepad_Button :: enum {
+	// DPAD buttons
+	Left_Face_Up,
+	Left_Face_Down,
+	Left_Face_Left,
+	Left_Face_Right,
+
+	Right_Face_Up, // XBOX: Y, PS: Triangle
+	Right_Face_Down, // XBOX: A, PS: X
+	Right_Face_Left, // XBOX: X, PS: Square
+	Right_Face_Right, // XBOX: B, PS: Circle
+
+	Left_Shoulder,
+	Left_Trigger,
+
+	Right_Shoulder,
+	Right_Trigger,
+
+	Left_Stick_Press, // Clicking the left analogue stick
+	Right_Stick_Press, // Clicking the right analogue stick
+
+	Middle_Face_Left, // Select / back / options button
+	Middle_Face_Middle, // PS button (not available on XBox)
+	Middle_Face_Right, // Start
+}

+ 80 - 12
karl2d.odin

@@ -105,8 +105,8 @@ present :: proc() {
 //
 // WARNING: Not calling this will make your program impossible to interact with.
 process_events :: proc() {
-	s.keys_went_up = {}
-	s.keys_went_down = {}
+	s.key_went_up = {}
+	s.key_went_down = {}
 	s.mouse_button_went_up = {}
 	s.mouse_button_went_down = {}
 	s.mouse_delta = {}
@@ -122,12 +122,12 @@ process_events :: proc() {
 			s.shutdown_wanted = true
 
 		case Window_Event_Key_Went_Down:
-			s.keys_went_down[e.key] = true
-			s.keys_is_held[e.key] = true
+			s.key_went_down[e.key] = true
+			s.key_is_held[e.key] = true
 
 		case Window_Event_Key_Went_Up:
-			s.keys_went_up[e.key] = true
-			s.keys_is_held[e.key] = false
+			s.key_went_up[e.key] = true
+			s.key_is_held[e.key] = false
 
 		case Window_Event_Mouse_Button_Went_Down:
 			s.mouse_button_went_down[e.button] = true
@@ -145,6 +145,18 @@ process_events :: proc() {
 		case Window_Event_Mouse_Wheel:
 			s.mouse_wheel_delta = e.delta
 
+		case Window_Event_Gamepad_Button_Went_Down:
+			if e.gamepad < MAX_GAMEPADS {
+				s.gamepad_button_went_down[e.gamepad][e.button] = true
+				s.gamepad_button_is_held[e.gamepad][e.button] = true
+			}
+
+		case Window_Event_Gamepad_Button_Went_Up:
+			if e.gamepad < MAX_GAMEPADS {
+				s.gamepad_button_went_up[e.gamepad][e.button] = true
+				s.gamepad_button_is_held[e.gamepad][e.button] = false
+			}
+
 		case Window_Event_Resize:
 			s.width = e.width
 			s.height = e.height
@@ -213,19 +225,19 @@ draw_current_batch :: proc() {
 // Returns true if a keyboard key went down between the current and the previous frame. Set when
 // 'process_events' runs (probably once per frame).
 key_went_down :: proc(key: Keyboard_Key) -> bool {
-	return s.keys_went_down[key]
+	return s.key_went_down[key]
 }
 
 // Returns true if a keyboard key went up (was released) between the current and the previous frame.
 // Set when 'process_events' runs (probably once per frame).
 key_went_up :: proc(key: Keyboard_Key) -> bool {
-	return s.keys_went_up[key]
+	return s.key_went_up[key]
 }
 
 // Returns true if a keyboard is currently being held down. Set when 'process_events' runs (probably
 // once per frame).
 key_is_held :: proc(key: Keyboard_Key) -> bool {
-	return s.keys_is_held[key]
+	return s.key_is_held[key]
 }
 
 mouse_button_went_down :: proc(button: Mouse_Button) -> bool {
@@ -248,6 +260,30 @@ get_mouse_position :: proc() -> Vec2 {
 	return s.mouse_position
 }
 
+gamepad_button_went_down :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
+		return false
+	}
+
+	return s.gamepad_button_went_down[gamepad][button]
+}
+
+gamepad_button_went_up :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
+		return false
+	}
+
+	return s.gamepad_button_went_up[gamepad][button]
+}
+
+gamepad_button_is_held :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
+		return false
+	}
+
+	return s.gamepad_button_is_held[gamepad][button]
+}
+
 //---------//
 // DRAWING //
 //---------//
@@ -931,6 +967,8 @@ Handle :: hm.Handle
 Texture_Handle :: distinct Handle
 TEXTURE_NONE :: Texture_Handle {}
 
+MAX_GAMEPADS :: 4
+
 // This keeps track of the internal state of the library. Usually, you do not need to poke at it.
 // It is created and kept as a global variable when 'init' is called. However, 'init' also returns
 // the pointer to it, so you can later use 'set_internal_state' to restore it (after for example hot
@@ -949,14 +987,18 @@ State :: struct {
 	mouse_delta: Vec2,
 	mouse_wheel_delta: f32,
 
-	keys_went_down: #sparse [Keyboard_Key]bool,
-	keys_went_up: #sparse [Keyboard_Key]bool,
-	keys_is_held: #sparse [Keyboard_Key]bool,
+	key_went_down: #sparse [Keyboard_Key]bool,
+	key_went_up: #sparse [Keyboard_Key]bool,
+	key_is_held: #sparse [Keyboard_Key]bool,
 
 	mouse_button_went_down: #sparse [Mouse_Button]bool,
 	mouse_button_went_up: #sparse [Mouse_Button]bool,
 	mouse_button_is_held: #sparse [Mouse_Button]bool,
 
+	gamepad_button_went_down: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+	gamepad_button_went_up: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+	gamepad_button_is_held: [MAX_GAMEPADS]#sparse [Gamepad_Button]bool,
+
 	window: Window_Handle,
 	width: int,
 	height: int,
@@ -1100,6 +1142,32 @@ Keyboard_Key :: enum {
 	KP_Equal        = 336,
 }
 
+Gamepad_Button :: enum {
+	// DPAD buttons
+	Left_Face_Up,
+	Left_Face_Down,
+	Left_Face_Left,
+	Left_Face_Right,
+
+	Right_Face_Up, // XBOX: Y, PS: Triangle
+	Right_Face_Down, // XBOX: A, PS: X
+	Right_Face_Left, // XBOX: X, PS: Square
+	Right_Face_Right, // XBOX: B, PS: Circle
+
+	Left_Shoulder,
+	Left_Trigger,
+
+	Right_Shoulder,
+	Right_Trigger,
+
+	Left_Stick_Press, // Clicking the left analogue stick
+	Right_Stick_Press, // Clicking the right analogue stick
+
+	Middle_Face_Left, // Select / back / options button
+	Middle_Face_Middle, // PS button (not available on XBox)
+	Middle_Face_Right, // Start
+}
+
 // Used by API builder. Everything after this constant will not be in karl2d.doc.odin
 API_END :: true
 

+ 13 - 1
window_interface.odin

@@ -20,7 +20,7 @@ Window_Interface :: struct {
 
 Window_Handle :: distinct uintptr
 
-Window_Event :: union  {
+Window_Event :: union {
 	Window_Event_Close_Wanted,
 	Window_Event_Key_Went_Down,
 	Window_Event_Key_Went_Up,
@@ -29,6 +29,8 @@ Window_Event :: union  {
 	Window_Event_Resize,
 	Window_Event_Mouse_Button_Went_Down,
 	Window_Event_Mouse_Button_Went_Up,
+	Window_Event_Gamepad_Button_Went_Down,
+	Window_Event_Gamepad_Button_Went_Up,
 }
 
 Window_Event_Key_Went_Down :: struct {
@@ -47,6 +49,16 @@ Window_Event_Mouse_Button_Went_Up :: struct {
 	button: Mouse_Button,
 }
 
+Window_Event_Gamepad_Button_Went_Down :: struct {
+	gamepad: int,
+	button: Gamepad_Button,
+}
+
+Window_Event_Gamepad_Button_Went_Up :: struct {
+	gamepad: int,
+	button: Gamepad_Button,
+}
+
 Window_Event_Close_Wanted :: struct {}
 
 Window_Event_Mouse_Move :: struct {

+ 55 - 0
window_win32.odin

@@ -61,6 +61,8 @@ win32_init :: proc(window_state: rawptr, window_width: int, window_height: int,
 		nil, nil, instance, nil,
 	)
 
+	win32.XInputEnable(true)
+
 	assert(hwnd != nil, "Failed creating window")
 
 	s.hwnd = hwnd
@@ -82,6 +84,59 @@ win32_process_events :: proc() {
 		win32.TranslateMessage(&msg)
 		win32.DispatchMessageW(&msg)
 	}
+
+	for gamepad in 0..<4 {
+		gp_event: win32.XINPUT_KEYSTROKE
+
+		for win32.XInputGetKeystroke(win32.XUSER(gamepad), 0, &gp_event) == .SUCCESS {
+			button: Maybe(Gamepad_Button)
+
+			#partial switch gp_event.VirtualKey {
+			case .DPAD_UP:    button = .Left_Face_Up
+			case .DPAD_DOWN:  button = .Left_Face_Down
+			case .DPAD_LEFT:  button = .Left_Face_Left
+			case .DPAD_RIGHT: button = .Left_Face_Right
+
+			case .Y: button = .Right_Face_Up
+			case .A: button = .Right_Face_Down
+			case .X: button = .Right_Face_Left
+			case .B: button = .Right_Face_Right
+
+			case .LSHOULDER: button = .Left_Shoulder
+			case .LTRIGGER:  button = .Left_Trigger
+
+			case .RSHOULDER: button = .Right_Shoulder
+			case .RTRIGGER:  button = .Right_Trigger
+
+			case .BACK: button = .Middle_Face_Left
+			// Not sure you can get the "middle button" with XInput
+			case .START: button = .Middle_Face_Right
+
+			case .LTHUMB_PRESS: button = .Left_Stick_Press
+			case .RTHUMB_PRESS: button = .Right_Stick_Press
+			}
+
+			b := button.? or_continue
+			evt: Window_Event
+
+			if .KEYDOWN in gp_event.Flags {
+				evt = Window_Event_Gamepad_Button_Went_Down {
+					gamepad = gamepad,
+					button = b,
+				}
+			} else if .KEYUP in gp_event.Flags {
+				evt = Window_Event_Gamepad_Button_Went_Up {
+					gamepad = gamepad,
+					button = b,
+				}
+			}
+
+			if evt != nil {
+				append(&s.events, evt)
+			}
+		}
+	}
+	
 }
 
 win32_get_events :: proc() -> []Window_Event {