Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sokol_app: EMSCRIPTEN: can't write into a textarea while sapp is running #822

Closed
marekmaskarinec opened this issue Apr 26, 2023 · 10 comments

Comments

@marekmaskarinec
Copy link

Hello, I'm trying to run a program using sokol_app compiled to WASM using emscripten alongside a textarea. However I can't enter any input into the the textarea. I can normally select it and even use shortcuts like copy and paste, but entering keys is not possible. Is it possible that sokol_app somehow catches the keyboard input before it can be processed?

I use this shell file: https://git.sr.ht/~mrms/tophat/tree/main/item/playground.html
The compiled result is available here: https://tophat2d.dev/playground/

@floooh
Copy link
Owner

floooh commented Apr 27, 2023

There's a couple of issues in the sokol_app.h Emscripten backend which might interfere:

First, there's a hardwired table of key events that never bubble up, see this comment here: #801 (comment)

(all other events should bubble up though, unless sapp_consume_event() is called on them.

...and then there's a hidden text field here, but this shouldn't be focused unless sapp_show_keyboard() is called:

sokol/sokol_app.h

Lines 4712 to 4730 in 7373dda

EM_JS(void, sapp_js_create_textfield, (void), {
const _sapp_inp = document.createElement("input");
_sapp_inp.type = "text";
_sapp_inp.id = "_sokol_app_input_element";
_sapp_inp.autocapitalize = "none";
_sapp_inp.addEventListener("focusout", function(_sapp_event) {
__sapp_emsc_notify_keyboard_hidden()
});
document.body.append(_sapp_inp);
});
EM_JS(void, sapp_js_focus_textfield, (void), {
document.getElementById("_sokol_app_input_element").focus();
});
EM_JS(void, sapp_js_unfocus_textfield, (void), {
document.getElementById("_sokol_app_input_element").blur();
});
(this whole mobile keyboard support will most likely be removed in the near future, I tinkered around with it a bit more recently but couldn't get it to work reliably across all mobile browsers)

You could try tinkering with the Emscripten keyboard callback here, most importantly, never return 'true' from this callback:

sokol/sokol_app.h

Lines 5339 to 5476 in 7373dda

_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) {
_SOKOL_UNUSED(user_data);
bool retval = true;
if (_sapp_events_enabled()) {
sapp_event_type type;
switch (emsc_type) {
case EMSCRIPTEN_EVENT_KEYDOWN:
type = SAPP_EVENTTYPE_KEY_DOWN;
break;
case EMSCRIPTEN_EVENT_KEYUP:
type = SAPP_EVENTTYPE_KEY_UP;
break;
case EMSCRIPTEN_EVENT_KEYPRESS:
type = SAPP_EVENTTYPE_CHAR;
break;
default:
type = SAPP_EVENTTYPE_INVALID;
break;
}
if (type != SAPP_EVENTTYPE_INVALID) {
bool send_keyup_followup = false;
_sapp_init_event(type);
_sapp.event.key_repeat = emsc_event->repeat;
_sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event);
if (type == SAPP_EVENTTYPE_CHAR) {
// FIXME: this doesn't appear to work on Android Chrome
_sapp.event.char_code = emsc_event->charCode;
/* workaround to make Cmd+V work on Safari */
if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) {
retval = false;
}
}
else {
if (0 != emsc_event->code[0]) {
// This code path is for desktop browsers which send untranslated 'physical' key code strings
// (which is what we actually want for key events)
_sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code);
} else {
// This code path is for mobile browsers which only send localized key code
// strings. Note that the translation will only work for a small subset
// of localization-agnostic keys (like Enter, arrow keys, etc...), but
// regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID)
_sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key);
}
/* Special hack for macOS: if the Super key is pressed, macOS doesn't
send keyUp events. As a workaround, to prevent keys from
"sticking", we'll send a keyup event following a keydown
when the SUPER key is pressed
*/
if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
(_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
(_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
(_sapp.event.modifiers & SAPP_MODIFIER_SUPER))
{
send_keyup_followup = true;
}
// only forward keys to the browser (can further be suppressed by sapp_consume_event())
switch (_sapp.event.key_code) {
case SAPP_KEYCODE_WORLD_1:
case SAPP_KEYCODE_WORLD_2:
case SAPP_KEYCODE_ESCAPE:
case SAPP_KEYCODE_ENTER:
case SAPP_KEYCODE_TAB:
case SAPP_KEYCODE_BACKSPACE:
case SAPP_KEYCODE_INSERT:
case SAPP_KEYCODE_DELETE:
case SAPP_KEYCODE_RIGHT:
case SAPP_KEYCODE_LEFT:
case SAPP_KEYCODE_DOWN:
case SAPP_KEYCODE_UP:
case SAPP_KEYCODE_PAGE_UP:
case SAPP_KEYCODE_PAGE_DOWN:
case SAPP_KEYCODE_HOME:
case SAPP_KEYCODE_END:
case SAPP_KEYCODE_CAPS_LOCK:
case SAPP_KEYCODE_SCROLL_LOCK:
case SAPP_KEYCODE_NUM_LOCK:
case SAPP_KEYCODE_PRINT_SCREEN:
case SAPP_KEYCODE_PAUSE:
case SAPP_KEYCODE_F1:
case SAPP_KEYCODE_F2:
case SAPP_KEYCODE_F3:
case SAPP_KEYCODE_F4:
case SAPP_KEYCODE_F5:
case SAPP_KEYCODE_F6:
case SAPP_KEYCODE_F7:
case SAPP_KEYCODE_F8:
case SAPP_KEYCODE_F9:
case SAPP_KEYCODE_F10:
case SAPP_KEYCODE_F11:
case SAPP_KEYCODE_F12:
case SAPP_KEYCODE_F13:
case SAPP_KEYCODE_F14:
case SAPP_KEYCODE_F15:
case SAPP_KEYCODE_F16:
case SAPP_KEYCODE_F17:
case SAPP_KEYCODE_F18:
case SAPP_KEYCODE_F19:
case SAPP_KEYCODE_F20:
case SAPP_KEYCODE_F21:
case SAPP_KEYCODE_F22:
case SAPP_KEYCODE_F23:
case SAPP_KEYCODE_F24:
case SAPP_KEYCODE_F25:
case SAPP_KEYCODE_LEFT_SHIFT:
case SAPP_KEYCODE_LEFT_CONTROL:
case SAPP_KEYCODE_LEFT_ALT:
case SAPP_KEYCODE_LEFT_SUPER:
case SAPP_KEYCODE_RIGHT_SHIFT:
case SAPP_KEYCODE_RIGHT_CONTROL:
case SAPP_KEYCODE_RIGHT_ALT:
case SAPP_KEYCODE_RIGHT_SUPER:
case SAPP_KEYCODE_MENU:
/* consume the event */
break;
default:
/* forward key to browser */
retval = false;
break;
}
}
if (_sapp_call_event(&_sapp.event)) {
// event was consumed via sapp_consume_event()
retval = true;
}
if (send_keyup_followup) {
_sapp.event.type = SAPP_EVENTTYPE_KEY_UP;
if (_sapp_call_event(&_sapp.event)) {
retval = true;
}
}
}
}
_sapp_emsc_update_keyboard_state();
_sapp_emsc_update_mouse_lock_state();
return retval;
}

Another difference between the key callbacks and other input callbacks is that the key callbacks are attached to the DOM window object, not the canvas:

sokol/sokol_app.h

Lines 5711 to 5713 in 7373dda

emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);
emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb);

If you find a hack that works we can figure out how to solve the problem in a cleaner way.

@marekmaskarinec
Copy link
Author

I've modified _sapp_emsc_key_cb to always return false. This fixed the issue, however it's not clear to me why normal key presses were consumed in the first place. I will use this as a hack until we can fix this issue.

In my opinion the hardcoded list of events that never bubble up should be cusomizable - in some cases it might be useful to let the user refresh etc.

Also this issue seems to be very similar to #800. Maybe consider merging these.

@floooh
Copy link
Owner

floooh commented Apr 28, 2023

Ok good to know. I'll write a separate ticket. I think the hardwired table will definitely need to go, and then leave it to the application to decide what events should be consumed (by calling sapp_consume_event() from the event handler - which was a later addition).

@floooh
Copy link
Owner

floooh commented Jan 2, 2024

This PR removes the hidden text input field in the sokol_app.h Emscripten backend, but I kept the key event bubbling behaviour for now. In my case the change fixes a problem I had in https://marketplace.visualstudio.com/items?itemName=floooh.vscode-kcide with Dear ImGui text input fields because the hidden HTML text input field generated a focus-in/out event which confused Dear ImGui.

I'm not sure if this changes anything for your use case, but it also can't make things worse at least. The PR will be merged shortly.

@marekmaskarinec
Copy link
Author

marekmaskarinec commented Jan 4, 2024

This isn't really relevant for my use case, I'm only concerned with keyboard input.

However now that I'm looking at the code again, I think the main issue here is that all character key presses are automatically eaten. Is this even wanted?

sokol/sokol_app.h

Lines 5363 to 5370 in 7373dda

if (type == SAPP_EVENTTYPE_CHAR) {
// FIXME: this doesn't appear to work on Android Chrome
_sapp.event.char_code = emsc_event->charCode;
/* workaround to make Cmd+V work on Safari */
if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) {
retval = false;
}
}

@vtereshkov
Copy link

Something may have changed after a recent commit to master, as the return value is now false by default:

f6aa461#diff-42747840ac0dd5aaeaa9368919646cc57e72a0bb54c03ad85c7eac18956ea584R5317

However, the commit message says there are no functional changes.

@vtereshkov
Copy link

vtereshkov commented Jan 4, 2024

After some testing, I found that the character keys were no longer eaten. However, all the functional keys still were:

https://github.com/floooh/sokol/blob/master/sokol_app.h#L5426

For example, I could type some text but could not delete it.

@floooh
Copy link
Owner

floooh commented Jan 5, 2024

Yeah, the exception that non-alphanumeric key up/down events are consumed is still in place, but this hasn't changed from before. I need to do a bit more testing to figure out again why I added this hardwired key list in the first place.

One obvious reason for not letting function keys bubble up were things like F7 on Chrome turning on "caret browsing", and probably a couple of other hardwired function key things that don't make sense in many web apps which use a webgl canvas over the whole client area.

Removing that hardwired switch-case would require all applications which actually want that behaviour to call sapp_consume_event() in their event callback, which was a bit of a too drastic compatibility break for my taste.

One potential solution would be to add config booleans to sapp_desc to turn off some of that magic filtering (also for the wheel events), but I need to think about that a bit more.

@marekmaskarinec
Copy link
Author

marekmaskarinec commented Jan 5, 2024 via email

@floooh
Copy link
Owner

floooh commented Jan 27, 2024

This is now sort-of fixed via #975, with some caveats for key events (e.g. key up/down events which generate character events still need to bubble up by default). It's now possible to 'open the flood gates' and let all events bubble and then control bubbling for individual events by calling sapp_consume_event() inside the event callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants