mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-01 06:01:14 +00:00 
			
		
		
		
	[HTML5] Custom Gamepad library to allow remapping.
No longer use emscripten functions for gamepads, implement them as library functions in library_godot_display.js instead. This allows us to do a better job at "guessing" vendorId, productId, OS, etc. thus allowing us to better find the remapping for the controller.
This commit is contained in:
		
							parent
							
								
									a1533f2c44
								
							
						
					
					
						commit
						bab20c6e09
					
				
					 5 changed files with 227 additions and 54 deletions
				
			
		|  | @ -8,13 +8,20 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,back:b5,leftst | ||||||
| Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, | Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, | ||||||
| 
 | 
 | ||||||
| # Javascript | # Javascript | ||||||
| Default HTML5 Gamepad, Default Mapping,leftx:a0,lefty:a1,dpdown:b13,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:b14,lefttrigger:a6,x:b2,dpup:b12,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:b15,righttrigger:a7,b:b1,platform:Javascript, | standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a6,righttrigger:a7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Javascript, | ||||||
| c2a94d6963726f736f66742058626f78,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Javascript, | Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
| 303534632d303563342d576972656c65,PS4 Controller USB/Win,leftx:a0,lefty:a1,dpdown:b15,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,lefttrigger:a3,x:b0,dpup:b14,dpleft:b16,dpright:b17,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,righttrigger:b7,b:b2,platform:Javascript, | Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
| 303534632d303563342d536f6e792043,PS4 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:a7,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,dpleft:a6,lefttrigger:a3,x:b0,dpup:a7,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,dpright:a6,righttrigger:a4,b:b2,platform:Javascript, | Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
| 303534632d303236382d536f6e792050,PS3 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:b6,rightstick:b2,rightshoulder:b11,rightx:a2,start:b3,righty:a3,dpleft:b7,lefttrigger:b8,x:b15,dpup:b4,back:b0,leftstick:b1,leftshoulder:b10,y:b12,a:b14,dpright:b5,righttrigger:b9,b:b13,platform:Javascript, | Linux045e02d1,Microsoft X-Box One pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
| 303435652d303731392d58626f782033,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Javascript, | Linux045e02ea,Microsoft X-Box One S pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
| 303435652d303238652d4d6963726f73,Wired X360 Controller,leftx:a0,lefty:a1,dpdown:a7,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:a6,lefttrigger:a2,x:b2,dpup:a7,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:a6,righttrigger:a5,b:b1,platform:Javascript, | Linux045e0b12,Microsoft X-Box Series X pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript, | ||||||
|  | Linux044fb315,Thrustmaster dual analog 3.2,a:b0,b:b2,y:b3,x:b1,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Javascript, | ||||||
|  | Linux0e8f0003,PS3 Controller,a:b2,b:b1,back:b8,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Javascript, | ||||||
|  | MacOSX24c6581a,PowerA Xbox One Cabled,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Javascript | ||||||
|  | MacOSX045e028e,Xbox 360 Wired Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Javascript | ||||||
|  | MacOSX045e02d1,Xbox One Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Javascript | ||||||
|  | MacOSX045e02ea,Xbox One S Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Javascript | ||||||
|  | MacOSX045e0b12,Xbox Series X Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Javascript | ||||||
| 
 | 
 | ||||||
| # UWP | # UWP | ||||||
| __UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP, | __UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP, | ||||||
|  |  | ||||||
|  | @ -558,57 +558,51 @@ bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Gamepad
 | // Gamepad
 | ||||||
| 
 | void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) { | ||||||
| EM_BOOL DisplayServerJavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) { |  | ||||||
| 	Input *input = Input::get_singleton(); | 	Input *input = Input::get_singleton(); | ||||||
| 	if (p_event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) { | 	if (p_connected) { | ||||||
| 		String guid = ""; | 		input->joy_connection_changed(p_index, true, String::utf8(p_id), String::utf8(p_guid)); | ||||||
| 		if (String::utf8(p_event->mapping) == "standard") |  | ||||||
| 			guid = "Default HTML5 Gamepad"; |  | ||||||
| 		input->joy_connection_changed(p_event->index, true, String::utf8(p_event->id), guid); |  | ||||||
| 	} else { | 	} else { | ||||||
| 		input->joy_connection_changed(p_event->index, false, ""); | 		input->joy_connection_changed(p_index, false, ""); | ||||||
| 	} | 	} | ||||||
| 	return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DisplayServerJavaScript::process_joypads() { | void DisplayServerJavaScript::process_joypads() { | ||||||
| 	int joypad_count = emscripten_get_num_gamepads(); |  | ||||||
| 	Input *input = Input::get_singleton(); | 	Input *input = Input::get_singleton(); | ||||||
| 	for (int joypad = 0; joypad < joypad_count; joypad++) { | 	int32_t pads = godot_js_display_gamepad_sample_count(); | ||||||
| 		EmscriptenGamepadEvent state; | 	int32_t s_btns_num = 0; | ||||||
| 		EMSCRIPTEN_RESULT query_result = emscripten_get_gamepad_status(joypad, &state); | 	int32_t s_axes_num = 0; | ||||||
| 		// Chromium reserves gamepads slots, so NO_DATA is an expected result.
 | 	int32_t s_standard = 0; | ||||||
| 		ERR_CONTINUE(query_result != EMSCRIPTEN_RESULT_SUCCESS && | 	float s_btns[16]; | ||||||
| 					 query_result != EMSCRIPTEN_RESULT_NO_DATA); | 	float s_axes[10]; | ||||||
| 		if (query_result == EMSCRIPTEN_RESULT_SUCCESS && state.connected) { | 	for (int idx = 0; idx < pads; idx++) { | ||||||
| 			int button_count = MIN(state.numButtons, 18); | 		int err = godot_js_display_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard); | ||||||
| 			int axis_count = MIN(state.numAxes, 8); | 		if (err) { | ||||||
| 			for (int button = 0; button < button_count; button++) { | 			continue; | ||||||
| 				float value = state.analogButton[button]; | 		} | ||||||
| 				input->joy_button(joypad, button, value); | 		for (int b = 0; b < s_btns_num; b++) { | ||||||
| 			} | 			float value = s_btns[b]; | ||||||
| 			for (int axis = 0; axis < axis_count; axis++) { | 			// Buttons 6 and 7 in the standard mapping need to be
 | ||||||
|  | 			// axis to be handled as JOY_AXIS_TRIGGER by Godot.
 | ||||||
|  | 			if (s_standard && (b == 6 || b == 7)) { | ||||||
| 				Input::JoyAxis joy_axis; | 				Input::JoyAxis joy_axis; | ||||||
| 				joy_axis.min = -1; | 				joy_axis.min = 0; | ||||||
| 				joy_axis.value = state.axis[axis]; | 				joy_axis.value = value; | ||||||
| 				input->joy_axis(joypad, axis, joy_axis); | 				int a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT; | ||||||
|  | 				input->joy_axis(idx, a, joy_axis); | ||||||
|  | 			} else { | ||||||
|  | 				input->joy_button(idx, b, value); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		for (int a = 0; a < s_axes_num; a++) { | ||||||
|  | 			Input::JoyAxis joy_axis; | ||||||
|  | 			joy_axis.min = -1; | ||||||
|  | 			joy_axis.value = s_axes[a]; | ||||||
|  | 			input->joy_axis(idx, a, joy_axis); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if 0 |  | ||||||
| bool DisplayServerJavaScript::is_joy_known(int p_device) { |  | ||||||
| 	return Input::get_singleton()->is_joy_mapped(p_device); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| String DisplayServerJavaScript::get_joy_guid(int p_device) const { |  | ||||||
| 	return Input::get_singleton()->get_joy_guid_remapped(p_device); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| Vector<String> DisplayServerJavaScript::get_rendering_drivers_func() { | Vector<String> DisplayServerJavaScript::get_rendering_drivers_func() { | ||||||
| 	Vector<String> drivers; | 	Vector<String> drivers; | ||||||
| 	drivers.push_back("dummy"); | 	drivers.push_back("dummy"); | ||||||
|  | @ -766,9 +760,6 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive | ||||||
| #define SET_EM_WINDOW_CALLBACK(ev, cb)                                                         \ | #define SET_EM_WINDOW_CALLBACK(ev, cb)                                                         \ | ||||||
| 	result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &cb); \ | 	result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &cb); \ | ||||||
| 	EM_CHECK(ev) | 	EM_CHECK(ev) | ||||||
| #define SET_EM_CALLBACK_NOTARGET(ev, cb)                         \ |  | ||||||
| 	result = emscripten_set_##ev##_callback(nullptr, true, &cb); \ |  | ||||||
| 	EM_CHECK(ev) |  | ||||||
| 	// These callbacks from Emscripten's html5.h suffice to access most
 | 	// These callbacks from Emscripten's html5.h suffice to access most
 | ||||||
| 	// JavaScript APIs.
 | 	// JavaScript APIs.
 | ||||||
| 	SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback) | 	SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback) | ||||||
|  | @ -783,9 +774,6 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive | ||||||
| 	SET_EM_CALLBACK(canvas_id, keypress, keypress_callback) | 	SET_EM_CALLBACK(canvas_id, keypress, keypress_callback) | ||||||
| 	SET_EM_CALLBACK(canvas_id, keyup, keyup_callback) | 	SET_EM_CALLBACK(canvas_id, keyup, keyup_callback) | ||||||
| 	SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback) | 	SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback) | ||||||
| 	SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback) |  | ||||||
| 	SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback) |  | ||||||
| #undef SET_EM_CALLBACK_NOTARGET |  | ||||||
| #undef SET_EM_CALLBACK | #undef SET_EM_CALLBACK | ||||||
| #undef EM_CHECK | #undef EM_CHECK | ||||||
| 
 | 
 | ||||||
|  | @ -798,6 +786,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive | ||||||
| 			WINDOW_EVENT_FOCUS_OUT); | 			WINDOW_EVENT_FOCUS_OUT); | ||||||
| 	godot_js_display_paste_cb(update_clipboard_callback); | 	godot_js_display_paste_cb(update_clipboard_callback); | ||||||
| 	godot_js_display_drop_files_cb(drop_files_js_callback); | 	godot_js_display_drop_files_cb(drop_files_js_callback); | ||||||
|  | 	godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); | ||||||
| 
 | 
 | ||||||
| 	Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event); | 	Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event); | ||||||
| } | } | ||||||
|  | @ -1026,8 +1015,9 @@ bool DisplayServerJavaScript::can_any_window_draw() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DisplayServerJavaScript::process_events() { | void DisplayServerJavaScript::process_events() { | ||||||
| 	if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) | 	if (godot_js_display_gamepad_sample() == OK) { | ||||||
| 		process_joypads(); | 		process_joypads(); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int DisplayServerJavaScript::get_current_video_driver() const { | int DisplayServerJavaScript::get_current_video_driver() const { | ||||||
|  |  | ||||||
|  | @ -86,7 +86,7 @@ private: | ||||||
| 	static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); | 	static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); | ||||||
| 	static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); | 	static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); | ||||||
| 
 | 
 | ||||||
| 	static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data); | 	static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid); | ||||||
| 	void process_joypads(); | 	void process_joypads(); | ||||||
| 
 | 
 | ||||||
| 	static Vector<String> get_rendering_drivers_func(); | 	static Vector<String> get_rendering_drivers_func(); | ||||||
|  |  | ||||||
|  | @ -76,6 +76,12 @@ extern int godot_js_display_cursor_is_hidden(); | ||||||
| extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const uint8_t *p_ptr, int p_len, int p_hotspot_x, int p_hotspot_y); | extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const uint8_t *p_ptr, int p_len, int p_hotspot_x, int p_hotspot_y); | ||||||
| extern void godot_js_display_cursor_set_visible(int p_visible); | extern void godot_js_display_cursor_set_visible(int p_visible); | ||||||
| 
 | 
 | ||||||
|  | // Display gamepad
 | ||||||
|  | extern char *godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); | ||||||
|  | extern int godot_js_display_gamepad_sample(); | ||||||
|  | extern int godot_js_display_gamepad_sample_count(); | ||||||
|  | extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard); | ||||||
|  | 
 | ||||||
| // Display listeners
 | // Display listeners
 | ||||||
| extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out); | extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out); | ||||||
| extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text)); | extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text)); | ||||||
|  |  | ||||||
|  | @ -269,13 +269,140 @@ const GodotDisplayCursor = { | ||||||
| }; | }; | ||||||
| mergeInto(LibraryManager.library, GodotDisplayCursor); | mergeInto(LibraryManager.library, GodotDisplayCursor); | ||||||
| 
 | 
 | ||||||
|  | /* | ||||||
|  |  * Display Gamepad API helper. | ||||||
|  |  */ | ||||||
|  | const GodotDisplayGamepads = { | ||||||
|  | 	$GodotDisplayGamepads__deps: ['$GodotRuntime', '$GodotDisplayListeners'], | ||||||
|  | 	$GodotDisplayGamepads: { | ||||||
|  | 		samples: [], | ||||||
|  | 
 | ||||||
|  | 		get_pads: function () { | ||||||
|  | 			try { | ||||||
|  | 				// Will throw in iframe when permission is denied.
 | ||||||
|  | 				// Will throw/warn in the future for insecure contexts.
 | ||||||
|  | 				// See https://github.com/w3c/gamepad/pull/120
 | ||||||
|  | 				const pads = navigator.getGamepads(); | ||||||
|  | 				if (pads) { | ||||||
|  | 					return pads; | ||||||
|  | 				} | ||||||
|  | 				return []; | ||||||
|  | 			} catch (e) { | ||||||
|  | 				return []; | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		get_samples: function () { | ||||||
|  | 			return GodotDisplayGamepads.samples; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		get_sample: function (index) { | ||||||
|  | 			const samples = GodotDisplayGamepads.samples; | ||||||
|  | 			return index < samples.length ? samples[index] : null; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		sample: function () { | ||||||
|  | 			const pads = GodotDisplayGamepads.get_pads(); | ||||||
|  | 			const samples = []; | ||||||
|  | 			for (let i = 0; i < pads.length; i++) { | ||||||
|  | 				const pad = pads[i]; | ||||||
|  | 				if (!pad) { | ||||||
|  | 					samples.push(null); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				const s = { | ||||||
|  | 					standard: pad.mapping === 'standard', | ||||||
|  | 					buttons: [], | ||||||
|  | 					axes: [], | ||||||
|  | 					connected: pad.connected, | ||||||
|  | 				}; | ||||||
|  | 				for (let b = 0; b < pad.buttons.length; b++) { | ||||||
|  | 					s.buttons.push(pad.buttons[b].value); | ||||||
|  | 				} | ||||||
|  | 				for (let a = 0; a < pad.axes.length; a++) { | ||||||
|  | 					s.axes.push(pad.axes[a]); | ||||||
|  | 				} | ||||||
|  | 				samples.push(s); | ||||||
|  | 			} | ||||||
|  | 			GodotDisplayGamepads.samples = samples; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		init: function (onchange) { | ||||||
|  | 			GodotDisplayListeners.samples = []; | ||||||
|  | 			function add(pad) { | ||||||
|  | 				const guid = GodotDisplayGamepads.get_guid(pad); | ||||||
|  | 				const c_id = GodotRuntime.allocString(pad.id); | ||||||
|  | 				const c_guid = GodotRuntime.allocString(guid); | ||||||
|  | 				onchange(pad.index, 1, c_id, c_guid); | ||||||
|  | 				GodotRuntime.free(c_id); | ||||||
|  | 				GodotRuntime.free(c_guid); | ||||||
|  | 			} | ||||||
|  | 			const pads = GodotDisplayGamepads.get_pads(); | ||||||
|  | 			for (let i = 0; i < pads.length; i++) { | ||||||
|  | 				// Might be reserved space.
 | ||||||
|  | 				if (pads[i]) { | ||||||
|  | 					add(pads[i]); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			GodotDisplayListeners.add(window, 'gamepadconnected', function (evt) { | ||||||
|  | 				add(evt.gamepad); | ||||||
|  | 			}, false); | ||||||
|  | 			GodotDisplayListeners.add(window, 'gamepaddisconnected', function (evt) { | ||||||
|  | 				onchange(evt.gamepad.index, 0); | ||||||
|  | 			}, false); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		get_guid: function (pad) { | ||||||
|  | 			if (pad.mapping) { | ||||||
|  | 				return pad.mapping; | ||||||
|  | 			} | ||||||
|  | 			const ua = navigator.userAgent; | ||||||
|  | 			let os = 'Unknown'; | ||||||
|  | 			if (ua.indexOf('Android') >= 0) { | ||||||
|  | 				os = 'Android'; | ||||||
|  | 			} else if (ua.indexOf('Linux') >= 0) { | ||||||
|  | 				os = 'Linux'; | ||||||
|  | 			} else if (ua.indexOf('iPhone') >= 0) { | ||||||
|  | 				os = 'iOS'; | ||||||
|  | 			} else if (ua.indexOf('Macintosh') >= 0) { | ||||||
|  | 				// Updated iPads will fall into this category.
 | ||||||
|  | 				os = 'MacOSX'; | ||||||
|  | 			} else if (ua.indexOf('Windows') >= 0) { | ||||||
|  | 				os = 'Windows'; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			const id = pad.id; | ||||||
|  | 			// Chrom* style: NAME (Vendor: xxxx Product: xxxx)
 | ||||||
|  | 			const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i; | ||||||
|  | 			// Firefox/Safari style (safari may remove leading zeores)
 | ||||||
|  | 			const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i; | ||||||
|  | 			let vendor = ''; | ||||||
|  | 			let product = ''; | ||||||
|  | 			if (exp1.test(id)) { | ||||||
|  | 				const match = exp1.exec(id); | ||||||
|  | 				vendor = match[1].padStart(4, '0'); | ||||||
|  | 				product = match[2].padStart(4, '0'); | ||||||
|  | 			} else if (exp2.test(id)) { | ||||||
|  | 				const match = exp2.exec(id); | ||||||
|  | 				vendor = match[1].padStart(4, '0'); | ||||||
|  | 				product = match[2].padStart(4, '0'); | ||||||
|  | 			} | ||||||
|  | 			if (!vendor || !product) { | ||||||
|  | 				return `${os}Unknown`; | ||||||
|  | 			} | ||||||
|  | 			return os + vendor + product; | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  | mergeInto(LibraryManager.library, GodotDisplayGamepads); | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Display server interface. |  * Display server interface. | ||||||
|  * |  * | ||||||
|  * Exposes all the functions needed by DisplayServer implementation. |  * Exposes all the functions needed by DisplayServer implementation. | ||||||
|  */ |  */ | ||||||
| const GodotDisplay = { | const GodotDisplay = { | ||||||
| 	$GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop'], | 	$GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads'], | ||||||
| 	$GodotDisplay: { | 	$GodotDisplay: { | ||||||
| 		window_icon: '', | 		window_icon: '', | ||||||
| 	}, | 	}, | ||||||
|  | @ -491,6 +618,49 @@ const GodotDisplay = { | ||||||
| 		}, false); | 		}, false); | ||||||
| 		GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles)); | 		GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles)); | ||||||
| 	}, | 	}, | ||||||
|  | 
 | ||||||
|  | 	/* | ||||||
|  | 	 * Gamepads | ||||||
|  | 	 */ | ||||||
|  | 	godot_js_display_gamepad_cb__sig: 'vi', | ||||||
|  | 	godot_js_display_gamepad_cb: function (change_cb) { | ||||||
|  | 		const onchange = GodotRuntime.get_func(change_cb); | ||||||
|  | 		GodotDisplayGamepads.init(onchange); | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	godot_js_display_gamepad_sample_count__sig: 'i', | ||||||
|  | 	godot_js_display_gamepad_sample_count: function () { | ||||||
|  | 		return GodotDisplayGamepads.get_samples().length; | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	godot_js_display_gamepad_sample__sig: 'i', | ||||||
|  | 	godot_js_display_gamepad_sample: function () { | ||||||
|  | 		GodotDisplayGamepads.sample(); | ||||||
|  | 		return 0; | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	godot_js_display_gamepad_sample_get__sig: 'iiiiiii', | ||||||
|  | 	godot_js_display_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) { | ||||||
|  | 		const sample = GodotDisplayGamepads.get_sample(p_index); | ||||||
|  | 		if (!sample || !sample.connected) { | ||||||
|  | 			return 1; | ||||||
|  | 		} | ||||||
|  | 		const btns = sample.buttons; | ||||||
|  | 		const btns_len = btns.length < 16 ? btns.length : 16; | ||||||
|  | 		for (let i = 0; i < btns_len; i++) { | ||||||
|  | 			GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float'); | ||||||
|  | 		} | ||||||
|  | 		GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32'); | ||||||
|  | 		const axes = sample.axes; | ||||||
|  | 		const axes_len = axes.length < 10 ? axes.length : 10; | ||||||
|  | 		for (let i = 0; i < axes_len; i++) { | ||||||
|  | 			GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float'); | ||||||
|  | 		} | ||||||
|  | 		GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32'); | ||||||
|  | 		const is_standard = sample.standard ? 1 : 0; | ||||||
|  | 		GodotRuntime.setHeapValue(r_standard, is_standard, 'i32'); | ||||||
|  | 		return 0; | ||||||
|  | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| autoAddDeps(GodotDisplay, '$GodotDisplay'); | autoAddDeps(GodotDisplay, '$GodotDisplay'); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Fabio Alessandrelli
						Fabio Alessandrelli