diff --git a/server/src/main/java/com/genymobile/scrcpy/EventController.java b/server/src/main/java/com/genymobile/scrcpy/EventController.java
index 7c40cce0..8787a048 100644
--- a/server/src/main/java/com/genymobile/scrcpy/EventController.java
+++ b/server/src/main/java/com/genymobile/scrcpy/EventController.java
@@ -94,9 +94,13 @@ public class EventController {
}
private boolean injectText(String text) {
+ return injectText(text, true);
+ }
+
+ private boolean injectText(String text, boolean decomposeOnFailure) {
KeyEvent[] events = charMap.getEvents(text.toCharArray());
if (events == null) {
- return false;
+ return decomposeOnFailure ? injectDecomposition(text) : false;
}
for (KeyEvent event : events) {
if (!injectEvent(event)) {
@@ -106,6 +110,16 @@ public class EventController {
return true;
}
+ private boolean injectDecomposition(String text) {
+ for (char c : text.toCharArray()) {
+ String composedText = KeyComposition.decompose(c);
+ if (composedText == null || !injectText(composedText, false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean injectMouse(int action, int buttons, Position position) {
long now = SystemClock.uptimeMillis();
if (action == MotionEvent.ACTION_DOWN) {
diff --git a/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java b/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java
new file mode 100644
index 00000000..af22167d
--- /dev/null
+++ b/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java
@@ -0,0 +1,174 @@
+package com.genymobile.scrcpy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Decompose accented characters.
+ *
+ * For example, {@link #decompose(char) decompose('é')} returns {@code "\u0301e"}.
+ *
+ * This is useful for injecting key events to generate the expected character ({@link android.view.KeyCharacterMap#getEvents(char[])}
+ * KeyCharacterMap.getEvents()} returns {@code null} with input {@code "é"} but works with input {@code "\u0301e"}).
+ *
+ * See diacritical dead key characters.
+ */
+public final class KeyComposition {
+
+ private static final String KEY_DEAD_GRAVE = "\u0300";
+ private static final String KEY_DEAD_ACUTE = "\u0301";
+ private static final String KEY_DEAD_CIRCUMFLEX = "\u0302";
+ private static final String KEY_DEAD_TILDE = "\u0303";
+ private static final String KEY_DEAD_UMLAUT = "\u0308";
+
+ private static final Map COMPOSITION_MAP = createDecompositionMap();
+
+ private KeyComposition() {
+ // not instantiable
+ }
+
+ public static String decompose(char c) {
+ return COMPOSITION_MAP.get(c);
+ }
+
+ private static String grave(char c) {
+ return KEY_DEAD_GRAVE + c;
+ }
+
+ private static String acute(char c) {
+ return KEY_DEAD_ACUTE + c;
+ }
+
+ private static String circumflex(char c) {
+ return KEY_DEAD_CIRCUMFLEX + c;
+ }
+
+ private static String tilde(char c) {
+ return KEY_DEAD_TILDE + c;
+ }
+
+ private static String umlaut(char c) {
+ return KEY_DEAD_UMLAUT + c;
+ }
+
+ private static Map createDecompositionMap() {
+ Map map = new HashMap<>();
+ map.put('À', grave('A'));
+ map.put('È', grave('E'));
+ map.put('Ì', grave('I'));
+ map.put('Ò', grave('O'));
+ map.put('Ù', grave('U'));
+ map.put('à', grave('a'));
+ map.put('è', grave('e'));
+ map.put('ì', grave('i'));
+ map.put('ò', grave('o'));
+ map.put('ù', grave('u'));
+ map.put('Ǹ', grave('N'));
+ map.put('ǹ', grave('n'));
+ map.put('Ẁ', grave('W'));
+ map.put('ẁ', grave('w'));
+ map.put('Ỳ', grave('Y'));
+ map.put('ỳ', grave('y'));
+
+ map.put('Á', acute('A'));
+ map.put('É', acute('E'));
+ map.put('Í', acute('I'));
+ map.put('Ó', acute('O'));
+ map.put('Ú', acute('U'));
+ map.put('Ý', acute('Y'));
+ map.put('á', acute('a'));
+ map.put('é', acute('e'));
+ map.put('í', acute('i'));
+ map.put('ó', acute('o'));
+ map.put('ú', acute('u'));
+ map.put('ý', acute('y'));
+ map.put('Ć', acute('C'));
+ map.put('ć', acute('c'));
+ map.put('Ĺ', acute('L'));
+ map.put('ĺ', acute('l'));
+ map.put('Ń', acute('N'));
+ map.put('ń', acute('n'));
+ map.put('Ŕ', acute('R'));
+ map.put('ŕ', acute('r'));
+ map.put('Ś', acute('S'));
+ map.put('ś', acute('s'));
+ map.put('Ź', acute('Z'));
+ map.put('ź', acute('z'));
+ map.put('Ǵ', acute('G'));
+ map.put('ǵ', acute('g'));
+ map.put('Ḉ', acute('Ç'));
+ map.put('ḉ', acute('ç'));
+ map.put('Ḱ', acute('K'));
+ map.put('ḱ', acute('k'));
+ map.put('Ḿ', acute('M'));
+ map.put('ḿ', acute('m'));
+ map.put('Ṕ', acute('P'));
+ map.put('ṕ', acute('p'));
+ map.put('Ẃ', acute('W'));
+ map.put('ẃ', acute('w'));
+
+ map.put('Â', circumflex('A'));
+ map.put('Ê', circumflex('E'));
+ map.put('Î', circumflex('I'));
+ map.put('Ô', circumflex('O'));
+ map.put('Û', circumflex('U'));
+ map.put('â', circumflex('a'));
+ map.put('ê', circumflex('e'));
+ map.put('î', circumflex('i'));
+ map.put('ô', circumflex('o'));
+ map.put('û', circumflex('u'));
+ map.put('Ĉ', circumflex('C'));
+ map.put('ĉ', circumflex('c'));
+ map.put('Ĝ', circumflex('G'));
+ map.put('ĝ', circumflex('g'));
+ map.put('Ĥ', circumflex('H'));
+ map.put('ĥ', circumflex('h'));
+ map.put('Ĵ', circumflex('J'));
+ map.put('ĵ', circumflex('j'));
+ map.put('Ŝ', circumflex('S'));
+ map.put('ŝ', circumflex('s'));
+ map.put('Ŵ', circumflex('W'));
+ map.put('ŵ', circumflex('w'));
+ map.put('Ŷ', circumflex('Y'));
+ map.put('ŷ', circumflex('y'));
+ map.put('Ẑ', circumflex('Z'));
+ map.put('ẑ', circumflex('z'));
+
+ map.put('Ã', tilde('A'));
+ map.put('Ñ', tilde('N'));
+ map.put('Õ', tilde('O'));
+ map.put('ã', tilde('a'));
+ map.put('ñ', tilde('n'));
+ map.put('õ', tilde('o'));
+ map.put('Ĩ', tilde('I'));
+ map.put('ĩ', tilde('i'));
+ map.put('Ũ', tilde('U'));
+ map.put('ũ', tilde('u'));
+ map.put('Ẽ', tilde('E'));
+ map.put('ẽ', tilde('e'));
+ map.put('Ỹ', tilde('Y'));
+ map.put('ỹ', tilde('y'));
+
+ map.put('Ä', umlaut('A'));
+ map.put('Ë', umlaut('E'));
+ map.put('Ï', umlaut('I'));
+ map.put('Ö', umlaut('O'));
+ map.put('Ü', umlaut('U'));
+ map.put('ä', umlaut('a'));
+ map.put('ë', umlaut('e'));
+ map.put('ï', umlaut('i'));
+ map.put('ö', umlaut('o'));
+ map.put('ü', umlaut('u'));
+ map.put('ÿ', umlaut('y'));
+ map.put('Ÿ', umlaut('Y'));
+ map.put('Ḧ', umlaut('H'));
+ map.put('ḧ', umlaut('h'));
+ map.put('Ẅ', umlaut('W'));
+ map.put('ẅ', umlaut('w'));
+ map.put('Ẍ', umlaut('X'));
+ map.put('ẍ', umlaut('x'));
+ map.put('ẗ', umlaut('t'));
+
+ return map;
+ }
+}