Merge pull request #45078 from Faless/js/4.x_gamepads

[HTML5] Use internal implementation of the Gamepad API.
This commit is contained in:
Rémi Verschelde 2021-01-18 13:12:54 +01:00 committed by GitHub
commit 869f5b5328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 229 additions and 56 deletions

View File

@ -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,

View File

@ -886,10 +886,10 @@ void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) {
jx.min = p_value.min; jx.min = p_value.min;
jx.value = p_value.value < 0.5 ? 0.6 : 0.4; jx.value = p_value.value < 0.5 ? 0.6 : 0.4;
joy_axis(p_device, p_axis, jx); joy_axis(p_device, p_axis, jx);
} else if (ABS(last) > 0.5 && last * p_value.value < 0) { } else if (ABS(last) > 0.5 && last * p_value.value <= 0) {
JoyAxis jx; JoyAxis jx;
jx.min = p_value.min; jx.min = p_value.min;
jx.value = p_value.value < 0 ? 0.1 : -0.1; jx.value = last > 0 ? 0.1 : -0.1;
joy_axis(p_device, p_axis, jx); joy_axis(p_device, p_axis, jx);
} }

View File

@ -558,56 +558,50 @@ 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 axis = 0; axis < axis_count; axis++) { for (int b = 0; b < s_btns_num; b++) {
float value = s_btns[b];
// 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;
joy_axis.min = 0;
joy_axis.value = value;
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; Input::JoyAxis joy_axis;
joy_axis.min = -1; joy_axis.min = -1;
joy_axis.value = state.axis[axis]; joy_axis.value = s_axes[a];
input->joy_axis(joypad, axis, joy_axis); 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;
@ -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,9 +1015,10 @@ 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 {
return 1; return 1;

View File

@ -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();

View File

@ -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));

View File

@ -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');