My emscripten app uses keyboard input, however, I want to run the app on mobile. Hence, I just want to add a few html buttons that when clicked on, simulate a key press. This task seems pretty trivial but I can't make it work. I tried dispatching keypress events from document (document.dispatchEvent(keyboardEvent);), nothing seems to happen. Am I not emitting the right event? Or does emscripten not use event listeners? Should I call a function directly or something?
Help will be greatly appreciated.
Thanks
Search for simulateKeyEvent in tests/*.py, we have similar code in the test suite that could be helpful.
I had to do something similar recently, and here is what I did for mouse events:
// eventType: "mousemove", "mousedown" or "mouseup".
// x and y: Normalized coordinate in the range [0,1] where to inject the event.
// button: which button was clicked. 0 = mouse left button. If eventType="mousemove", pass 0.
function simulateMouseEvent(eventType, x, y, button) {
// Remap from [0,1] to canvas CSS pixel size.
x *= Module['canvas'].clientWidth;
y *= Module['canvas'].clientHeight;
var rect = Module['canvas'].getBoundingClientRect();
// Offset the injected coordinate from top-left of the client area to the top-left of the canvas.
x = Math.round(rect.left + x);
y = Math.round(rect.top + y);
var e = document.createEvent("MouseEvents");
e.initMouseEvent(eventType, true, true, window,
eventType == 'mousemove' ? 0 : 1, x, y, x, y,
0, 0, 0, 0,
button, null);
// To dispatch directly to Emscripten's html5.h API (use this if the Emscripten page uses emscripten/html5.h event handlers):
if (typeof JSEvents !== 'undefined' && JSEvents.eventHandlers && JSEvents.eventHandlers.length > 0) {
for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {
if ((JSEvents.eventHandlers[i].target == Module['canvas'] || JSEvents.eventHandlers[i].target == window)
&& JSEvents.eventHandlers[i].eventTypeString == eventType) {
JSEvents.eventHandlers[i].handlerFunc(e);
}
}
} else {
// Alternatively, to dispatch directly to browser (use this if the Emscripten page uses SDL or something else to grab mouse input):
Module['canvas'].dispatchEvent(e);
}
}
and for keyboard events:
function simulateKeyEvent(eventType, keyCode, charCode) {
var e = document.createEventObject ? document.createEventObject() : document.createEvent("Events");
if (e.initEvent) e.initEvent(eventType, true, true);
e.keyCode = keyCode;
e.which = keyCode;
e.charCode = charCode;
// Dispatch directly to Emscripten's html5.h API (use this if page uses emscripten/html5.h event handling):
if (typeof JSEvents !== 'undefined' && JSEvents.eventHandlers && JSEvents.eventHandlers.length > 0) {
for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {
if ((JSEvents.eventHandlers[i].target == Module['canvas'] || JSEvents.eventHandlers[i].target == window)
&& JSEvents.eventHandlers[i].eventTypeString == eventType) {
JSEvents.eventHandlers[i].handlerFunc(e);
}
}
} else {
// Dispatch to browser for real (use this if page uses SDL or something else for event handling):
Module['canvas'].dispatchEvent ? Module['canvas'].dispatchEvent(e) : Module['canvas'].fireEvent("on" + eventType, e);
}
}
I was bit by a Chrome issue with the KeyboardEvent object, see http://stackoverflow.com/questions/8942678/keyboardevent-in-chrome-keycode-is-0/12522752#12522752
Hopefully that is useful!
This issue has been automatically marked as stale because there has been no activity in the past 2 years. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.
I documented this since it was kinda tricky here:
https://ryanpcmcquen.org/code/2020/11/26/porting-a-c-game-to-webassembly.html
Here's the JavaScript I used:
// Thanks to @juj for some hints on this issue as to how to get this working:
// https://github.com/emscripten-core/emscripten/issues/3614#issuecomment-142032269
var create_and_fire_event = function (event, type) {
var control_event = new Event(type, { bubbles: true });
control_event.code = event.target.dataset.key;
control_event.key = event.target.dataset.key;
control_event.keyCode = event.target.dataset.which;
control_event.which = event.target.dataset.which;
Module.canvas.dispatchEvent(control_event);
};
Module.postRun.push(function () {
var transform_multiplier = 0.75;
var minimum_transform_multiplier = 0.6;
while ((Module.canvas.width * transform_multiplier) > window.innerWidth && transform_multiplier > minimum_transform_multiplier) {
transform_multiplier = transform_multiplier - 0.05;
}
Module.canvas.parentNode.style.transform = `scale(${transform_multiplier})`;
var margin_removal = (window.innerWidth * (transform_multiplier > minimum_transform_multiplier ? transform_multiplier - 0.1 : transform_multiplier)) / 2;
Module.canvas.parentNode.style.margin = `-${margin_removal * 0.15}px -${margin_removal}px`;
Array.from(document.querySelectorAll('.controls')).forEach(
function (control) {
if ('ontouchstart' in document.documentElement) {
// Mobile:
control.addEventListener('touchstart', function (
event
) {
create_and_fire_event(event, 'keydown');
});
control.addEventListener('touchend', function (
event
) {
create_and_fire_event(event, 'keyup');
});
} else {
// Desktop:
control.addEventListener('click', function (event) {
create_and_fire_event(event, 'keydown');
window.setTimeout(function () {
create_and_fire_event(event, 'keyup');
}, 100);
});
}
}
);
});
Most helpful comment
I had to do something similar recently, and here is what I did for mouse events:
and for keyboard events:
I was bit by a Chrome issue with the KeyboardEvent object, see http://stackoverflow.com/questions/8942678/keyboardevent-in-chrome-keycode-is-0/12522752#12522752
Hopefully that is useful!