diff --git a/Backends/HTML5/kha/SystemImpl.hx b/Backends/HTML5/kha/SystemImpl.hx
index eec951ee6..667fd6509 100644
--- a/Backends/HTML5/kha/SystemImpl.hx
+++ b/Backends/HTML5/kha/SystemImpl.hx
@@ -7,6 +7,7 @@ import js.html.ClipboardEvent;
import js.html.DeviceMotionEvent;
import js.html.DeviceOrientationEvent;
import js.html.DragEvent;
+import js.html.FocusEvent;
import js.html.KeyboardEvent;
import js.html.MouseEvent;
import js.html.PointerEvent;
@@ -20,7 +21,9 @@ import kha.graphics4.TextureFormat;
import kha.input.Gamepad;
import kha.input.KeyCode;
import kha.input.Keyboard;
+import kha.input.KeyboardImpl;
import kha.input.Mouse;
+import kha.input.MouseImpl;
import kha.input.Sensor;
import kha.input.Surface;
import kha.js.AudioElementAudio;
@@ -59,6 +62,7 @@ class SystemImpl {
static var firefox: Bool = false;
public static var safari: Bool = false;
public static var ie: Bool = false;
+ public static var macos: Bool = false;
public static var insideInputEvent: Bool = false;
static public var activeMouseEvent: Null;
static public var activeWheelEvent: Null;
@@ -104,6 +108,7 @@ class SystemImpl {
firefox = isFirefox();
safari = isSafari();
ie = isIE();
+ macos = isMacOS();
mobileAudioPlaying = !mobile && !chrome && !firefox;
@@ -197,6 +202,11 @@ class SystemImpl {
return false;
}
+ static function isMacOS(): Bool {
+ var agent = js.Browser.navigator.userAgent;
+ return agent.contains("Mac") || isIOS();
+ }
+
public static function setCanvas(canvas: CanvasElement): Void {
khanvas = canvas;
}
@@ -247,9 +257,9 @@ class SystemImpl {
static function init2(defaultWidth: Int, defaultHeight: Int, ?backbufferFormat: TextureFormat) {
#if !kha_no_keyboard
- keyboard = new Keyboard();
+ keyboard = new KeyboardImpl();
#end
- mouse = new kha.input.MouseImpl();
+ mouse = new MouseImpl();
surface = new Surface();
gamepads = new Array();
gamepadStates = new Array();
@@ -498,7 +508,6 @@ class SystemImpl {
if (keyboard != null) {
canvas.onkeydown = keyDown;
canvas.onkeyup = keyUp;
- canvas.onkeypress = keyPress;
}
canvas.onblur = onBlur;
canvas.onfocus = onFocus;
@@ -991,12 +1000,18 @@ class SystemImpl {
insideInputEvent = false;
}
- static function onBlur() {
+ static function onBlur(event: FocusEvent) {
+ final input = KeyboardImpl.input;
+ if (input != null && event.relatedTarget == input)
+ return;
// System.pause();
System.background();
}
- static function onFocus() {
+ static function onFocus(event: FocusEvent) {
+ final input = KeyboardImpl.input;
+ if (input != null && event.relatedTarget == input)
+ return;
// System.resume();
System.foreground();
}
@@ -1139,10 +1154,17 @@ class SystemImpl {
// prevent key repeat
if (event.repeat) {
event.preventDefault();
+ activeKeyEvent = null;
+ insideInputEvent = false;
return;
}
- var keyCode = fixedKeyCode(event);
+ final keyCode = fixedKeyCode(event);
keyboard.sendDownEvent(keyCode);
+
+ if (isPrintableText(event)) {
+ keyboard.sendPressEvent(event.key);
+ }
+
activeKeyEvent = null;
insideInputEvent = false;
}
@@ -1158,6 +1180,26 @@ class SystemImpl {
}
}
+ static function isPrintableText(event: KeyboardEvent): Bool {
+ if (event.key == null || event.key.length != 1)
+ return false;
+
+ if (event.metaKey)
+ return false;
+ if (macos) {
+ if (event.ctrlKey)
+ return false;
+ }
+ else {
+ // detect ctrl+alt chars
+ if (event.getModifierState("AltGraph"))
+ return true;
+ if (event.ctrlKey || event.altKey)
+ return false;
+ }
+ return true;
+ }
+
static function preventDefaultKeyBehavior(event: KeyboardEvent): Void {
switch (Keyboard.keyBehavior) {
case Default:
@@ -1209,21 +1251,6 @@ class SystemImpl {
insideInputEvent = false;
}
- static function keyPress(event: KeyboardEvent): Void {
- insideInputEvent = true;
- activeKeyEvent = event;
- unlockSound();
-
- if (event.which == 0)
- return; // for Firefox and Safari
- preventDefaultKeyBehavior(event);
- event.stopPropagation();
- keyboard.sendPressEvent(String.fromCharCode(event.which));
-
- activeKeyEvent = null;
- insideInputEvent = false;
- }
-
public static function canSwitchFullscreen(): Bool {
return Syntax.code("'fullscreenElement ' in document ||
'mozFullScreenElement' in document ||
diff --git a/Backends/HTML5/kha/input/KeyboardImpl.hx b/Backends/HTML5/kha/input/KeyboardImpl.hx
new file mode 100644
index 000000000..a577bef61
--- /dev/null
+++ b/Backends/HTML5/kha/input/KeyboardImpl.hx
@@ -0,0 +1,91 @@
+package kha.input;
+
+import js.Browser.document;
+import js.html.InputElement;
+import js.html.InputEvent;
+import js.html.KeyboardEvent;
+
+using StringTools;
+
+class KeyboardImpl extends Keyboard {
+ static var virtualKeyboardId = "kha-virtual-keyboard";
+ public static var input: InputElement;
+
+ public function new() {
+ super();
+ }
+
+ override function show(): Void {
+ input = cast document.getElementById(virtualKeyboardId);
+ input ??= createKeyboardInputElement();
+ document.body.appendChild(input);
+ input.focus();
+ }
+
+ static function createKeyboardInputElement(): InputElement {
+ final input = document.createInputElement();
+ input.id = virtualKeyboardId;
+ input.style.position = "fixed";
+ input.style.left = "0px";
+ input.style.top = "0px";
+ input.style.opacity = "0.0";
+ input.style.pointerEvents = "none";
+ input.type = "text";
+ input.autocomplete = "off";
+ input.spellcheck = false;
+ input.setAttribute("autocorrect", "off");
+ input.setAttribute("autocapitalize", "off");
+
+ input.onkeydown = (e: KeyboardEvent) -> {
+ if (e.repeat) {
+ e.preventDefault();
+ return;
+ }
+ // virtual keyboards do not report pressed chars in most cases
+ // so we only redirect non-printable keys to canvas there
+ if (e.key == null || e.key.length == 1)
+ return;
+ final keyEvent = copyKeyboardEventFrom(e);
+ SystemImpl.khanvas.dispatchEvent(keyEvent);
+ }
+ input.onkeyup = (e: KeyboardEvent) -> {
+ if (e.key == null || e.key.length == 1)
+ return;
+ final keyEvent = copyKeyboardEventFrom(e);
+ SystemImpl.khanvas.dispatchEvent(keyEvent);
+ }
+ input.oninput = (e: InputEvent) -> {
+ // IME-composition, key is not final yet
+ if (e.isComposing)
+ return;
+ final str: String = (e : Dynamic).data ?? return;
+ final kb = Keyboard.get() ?? return;
+ for (code in str) {
+ final char = String.fromCharCode(code);
+ kb.sendPressEvent(char);
+ }
+ input.value = "";
+ }
+ return input;
+ }
+
+ static function copyKeyboardEventFrom(e: KeyboardEvent): KeyboardEvent {
+ return new KeyboardEvent(e.type, {
+ key: e.key,
+ code: e.code,
+ keyCode: e.keyCode,
+ location: e.location,
+ ctrlKey: e.ctrlKey,
+ shiftKey: e.shiftKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ bubbles: true,
+ cancelable: true
+ });
+ }
+
+ override function hide(): Void {
+ SystemImpl.khanvas.focus();
+ input?.remove();
+ }
+}