blob: 92de537379e2ff153b04dae9c381c1261d3675c7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.screenshare.job;
import static java.lang.Boolean.TRUE;
import static java.lang.Character.isUpperCase;
import static java.lang.Character.toUpperCase;
import static javax.swing.KeyStroke.getKeyStroke;
import static org.apache.openmeetings.screenshare.util.Util.getInt;
import static org.slf4j.LoggerFactory.getLogger;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.KeyStroke;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
public class OmKeyEvent {
private static final Logger log = getLogger(OmKeyEvent.class);
private static final Map<Integer, Integer> KEY_MAP = new HashMap<>();
private static final Map<Character, Integer> CHAR_MAP = new HashMap<>();
private static final List<Character> UMLAUT_LIST = List.of('ß', 'ö', 'Ö', 'ä', 'Ä', 'ü', 'Ü');
private static final Set<Character> UMLAUTS = Collections.unmodifiableSet(UMLAUT_LIST.stream().collect(Collectors.toSet()));
private static final Set<Character> UNPRINTABLE = Collections.unmodifiableSet(Stream.concat(UMLAUT_LIST.stream(), Stream.of('§')).collect(Collectors.toSet()));
static {
KEY_MAP.put(13, KeyEvent.VK_ENTER);
KEY_MAP.put(16, 0);
KEY_MAP.put(20, KeyEvent.VK_CAPS_LOCK);
KEY_MAP.put(43, KeyEvent.VK_ADD); //normal + -> numpad + ????
KEY_MAP.put(46, KeyEvent.VK_DELETE);
KEY_MAP.put(110, KeyEvent.VK_DECIMAL);
KEY_MAP.put(186, KeyEvent.VK_SEMICOLON);
KEY_MAP.put(187, KeyEvent.VK_EQUALS);
KEY_MAP.put(188, KeyEvent.VK_COMMA);
KEY_MAP.put(189, KeyEvent.VK_MINUS);
KEY_MAP.put(190, KeyEvent.VK_PERIOD);
KEY_MAP.put(191, KeyEvent.VK_SLASH);
KEY_MAP.put(219, KeyEvent.VK_OPEN_BRACKET);
KEY_MAP.put(220, KeyEvent.VK_BACK_SLASH);
KEY_MAP.put(221, KeyEvent.VK_CLOSE_BRACKET);
KEY_MAP.put(222, KeyEvent.VK_QUOTE);
CHAR_MAP.put(Character.valueOf('#'), KeyEvent.VK_NUMBER_SIGN);
CHAR_MAP.put(Character.valueOf('<'), KeyEvent.VK_LESS);
CHAR_MAP.put(Character.valueOf('.'), KeyEvent.VK_PERIOD);
CHAR_MAP.put(Character.valueOf(','), KeyEvent.VK_COMMA);
CHAR_MAP.put(Character.valueOf('-'), KeyEvent.VK_MINUS);
CHAR_MAP.put(Character.valueOf('='), KeyEvent.VK_EQUALS);
CHAR_MAP.put(Character.valueOf('['), KeyEvent.VK_OPEN_BRACKET);
CHAR_MAP.put(Character.valueOf(']'), KeyEvent.VK_CLOSE_BRACKET);
CHAR_MAP.put(Character.valueOf(';'), KeyEvent.VK_SEMICOLON);
CHAR_MAP.put(Character.valueOf('\''), KeyEvent.VK_QUOTE);
CHAR_MAP.put(Character.valueOf('\\'), KeyEvent.VK_BACK_SLASH);
CHAR_MAP.put(Character.valueOf('`'), KeyEvent.VK_BACK_QUOTE);
CHAR_MAP.put(Character.valueOf('/'), KeyEvent.VK_SLASH);
}
private boolean alt = false;
private boolean ctrl = false;
private boolean shift = false;
private int inKey = 0;
private int key = 0;
private char ch = 0;
public OmKeyEvent(Map<String, Object> obj) {
alt = TRUE.equals(obj.get("alt"));
ctrl = TRUE.equals(obj.get("ctrl"));
shift = TRUE.equals(obj.get("shift")) || isUpperCase(ch);
ch = (char)getInt(obj, "char");
key = inKey = getInt(obj, "key");
Integer _key = null;
if (CharUtils.isAsciiPrintable(ch)) {
boolean alpha = Character.isAlphabetic(ch);
if (alpha) { // can't be combined due to different types
key = getKeyStroke(toUpperCase(ch), 0).getKeyCode();
} else {
key = getKeyStroke(Character.valueOf(ch), 0).getKeyCode();
}
if (key == 0) {
_key = CHAR_MAP.get(ch);
if (_key == null) {
// fallback
key = inKey;
}
}
if (!alpha && _key == null) {
_key = KEY_MAP.get(key);
}
} else {
_key = KEY_MAP.get(key);
}
this.key = _key == null ? key : _key;
log.debug("sequence:: shift {}, ch {}, orig {} -> key {}({}), map {}", shift, ch == 0 ? ' ' : ch, inKey, key, Integer.toHexString(key), _key);
}
private static int getVowel(char ch) {
int vowel = ch;
switch(toUpperCase(ch)) {
case 'Ö':
vowel = KeyEvent.VK_O;
break;
case 'Ä':
vowel = KeyEvent.VK_A;
break;
case 'Ü':
vowel = KeyEvent.VK_U;
break;
}
return vowel;
}
public void press(RemoteJob r) {
List<Integer> list = new ArrayList<>();
if (UNPRINTABLE.contains(ch)) {
if (SystemUtils.IS_OS_LINUX) {
r.press(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_U);
String hex = Integer.toHexString(ch);
log.debug("sequence:: hex {}", hex);
for (int i = 0; i < hex.length(); ++i) {
r.press(KeyStroke.getKeyStroke(toUpperCase(hex.charAt(i)), 0).getKeyCode());
}
r.press(KeyEvent.VK_ENTER);
} else if (SystemUtils.IS_OS_MAC) {
if (ch == 'ß') {
r.press(KeyEvent.VK_ALT, KeyEvent.VK_S);
} else {
if (UMLAUTS.contains(ch)) {
r.press(KeyEvent.VK_ALT, KeyEvent.VK_U);
if (shift) {
list.add(KeyEvent.VK_SHIFT);
}
list.add(getVowel(ch));
r.press(list);
}
}
} else if (SystemUtils.IS_OS_WINDOWS && UMLAUTS.contains(ch)) {
list.add(KeyEvent.VK_ALT);
list.add(KeyEvent.VK_ADD);
String code = String.format("%04d", (int)ch);
for (int i = 0; i < code.length(); ++i) {
list.add(KeyEvent.VK_NUMPAD0 + code.charAt(i));
}
r.press(list);
}
} else {
if (shift) {
list.add(KeyEvent.VK_SHIFT);
}
if (alt) {
list.add(KeyEvent.VK_ALT);
}
if (ctrl) {
list.add(KeyEvent.VK_CONTROL);
}
if (key != 0) {
list.add(key);
}
r.press(list);
}
}
}