| /******************************************************************************* |
| * Copyright (c) 2000, 2011 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.text.tests.performance; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Text; |
| |
| |
| |
| /** |
| * @since 3.1 |
| */ |
| public class KeyboardProbe { |
| |
| private static final boolean TRACE= false; |
| private static final char FAKE_CHAR= '$'; |
| |
| public static void main(String[] args) { |
| new KeyboardProbe().getKeycodes(); |
| } |
| |
| private Display fDisplay; |
| private char[][] fCodes= null; |
| private int fKeyCode; |
| private static final int NONE= 0; |
| private static final int SHIFT= 1; |
| private boolean fKeyContinue; |
| private boolean fTextContinue; |
| private boolean fDisposeDisplay= false; |
| private Shell fShell; |
| |
| /** |
| * Returns the characters being input into a text component by injecting key |
| * events into the system event queue. For each character, the array holds |
| * the resulting character when pressed alone and when pressed together with |
| * shift. |
| * |
| * @return the characters input by pressing the keys. The key code is the |
| * index into the array. |
| */ |
| public synchronized char[][] getKeycodes() { |
| initialize(); |
| return fCodes; |
| } |
| |
| /** |
| * Initializes this keyboard probe. |
| */ |
| public void initialize() { |
| if (fCodes == null) { |
| try { |
| probe(); |
| } finally { |
| if (fDisposeDisplay && fDisplay != null) { |
| fDisplay.dispose(); |
| } |
| fDisplay= null; |
| |
| if (fShell != null && !fShell.isDisposed()) { |
| fShell.dispose(); |
| } |
| fShell= null; |
| } |
| } |
| } |
| |
| /** |
| * Returns the character resulting from pressing the character 'key' with |
| * or without shift being pressed. |
| * |
| * @param key the key to press |
| * @param pressShift whether pressed together with shift |
| * @return the char resulting from the given key combo, or 0 if there is no equivalent |
| */ |
| public char getCharForKeybinding(char key, boolean pressShift) { |
| char[][] keycodes= getKeycodes(); |
| if (key < keycodes.length) |
| return keycodes[key][pressShift ? SHIFT : NONE]; |
| |
| return 0; |
| } |
| |
| /** |
| * Returns a keybinding combo that will produce the wanted character. Note |
| * that there may be more than one combo that can produce the wanted |
| * binding, any one is returned. The last integer in the returned array is |
| * a character that must be set to the Event.character field, any integers |
| * before are SWT constants describing modifier keys that must be pressed |
| * to get the desired result. |
| * |
| * @param expected the char to be input |
| * @return the corresponding key combo, or an empty array if there is no combo |
| */ |
| public int[] getKeybindingForChar(char expected) { |
| char[][] keycodes= getKeycodes(); |
| for (int i= 0; i < keycodes.length; i++) { |
| if (keycodes[i][NONE] == expected) |
| return new int[] {i}; |
| else if (keycodes[i][SHIFT] == expected) |
| return new int[] { SWT.SHIFT, i }; |
| } |
| return new int[0]; |
| } |
| |
| /** |
| * Presses a key combination such that the expected character is input by |
| * simulating key events on the given display. Returns <code>true</code> |
| * if a key combo could be found, <code>false</code> if not. |
| * |
| * @param expected the expected character |
| * @param display the display to simulate the events on |
| * @return <code>true</code> if there was a key combo to press, |
| * <code>false</code> if <code>expected</code> has no combo on |
| * the current keyboard layout. |
| */ |
| public boolean pressChar(char expected, Display display) { |
| return pressChar(expected, new int[0], display); |
| } |
| |
| /** |
| * Presses a key combination such that the expected character is input by |
| * simulating key events on the given display. Returns <code>true</code> |
| * if a key combo could be found, <code>false</code> if not. |
| * |
| * @param expected the expected character |
| * @param modifiers additional modifiers to press |
| * @param display the display to simulate the events on |
| * @return <code>true</code> if there was a key combo to press, |
| * <code>false</code> if <code>expected</code> has no combo on |
| * the current keyboard layout. |
| */ |
| public boolean pressChar(char expected, int[] modifiers, Display display) { |
| int[] charkeys= getKeybindingForChar(expected); |
| int[] combo= new int[charkeys.length + modifiers.length]; |
| System.arraycopy(modifiers, 0, combo, 0, modifiers.length); |
| System.arraycopy(charkeys, 0, combo, modifiers.length, charkeys.length); |
| |
| for (int i= 0; i <= combo.length - 2; i++) { |
| Event event= new Event(); |
| event.type= SWT.KeyDown; |
| event.keyCode= combo[i]; |
| display.post(event); |
| } |
| |
| if (combo.length > 0) { |
| Event event= new Event(); |
| event.type= SWT.KeyDown; |
| event.character= (char) combo[combo.length - 1]; |
| display.post(event); |
| |
| event.type= SWT.KeyUp; |
| display.post(event); |
| } |
| |
| for (int i= combo.length - 2; i >= 0; i--) { |
| Event event= new Event(); |
| event.type= SWT.KeyUp; |
| event.keyCode= combo[i]; |
| display.post(event); |
| } |
| |
| return combo.length > 0; |
| } |
| |
| void probe() { |
| fCodes= new char[128][]; |
| |
| fDisplay= createDisplay(); |
| Text text= createControl(fDisplay); |
| |
| for (fKeyCode= 0; fKeyCode < 128; fKeyCode++) { |
| fCodes[fKeyCode]= new char[2]; |
| |
| postNaturalKeyPress(fKeyCode); |
| char c= getContent(text); |
| if (TRACE) System.out.println("" + fKeyCode + "content[NONE]: " + c); |
| fCodes[fKeyCode][NONE]= c; |
| clearText(text); |
| |
| postShiftKeyPress(fKeyCode); |
| c= getContent(text); |
| if (TRACE) System.out.println("" + fKeyCode + "content[SHIFT]: " + c); |
| fCodes[fKeyCode][SHIFT]= c; |
| clearText(text); |
| |
| } |
| |
| } |
| |
| private char getContent(Text text) { |
| String content= text.getText(); |
| char c; |
| if (content.length() == 2) { |
| c= content.charAt(0); |
| if (TRACE) |
| if (content.charAt(1) != FAKE_CHAR) |
| System.out.println("second char was '" + content.charAt(1) + "'"); |
| } else if (content.length() > 2) { |
| c= content.charAt(0); |
| if (TRACE) |
| System.out.println("rest content was '" + content.substring(1) + "'"); |
| } else if (content.length() == 1) { |
| c= '\0'; |
| if (TRACE) |
| if (content.charAt(0) != FAKE_CHAR) |
| System.out.println("second char was '" + content.charAt(0) + "'"); |
| } else { |
| c= '\0'; |
| if (TRACE) System.out.println("no content"); |
| } |
| return c; |
| } |
| |
| private void clearText(Text text) { |
| fTextContinue= false; |
| text.setText(""); |
| do |
| driveEventQueue(); |
| while (!fTextContinue); |
| } |
| |
| private Display createDisplay() { |
| Display display= Display.getCurrent(); |
| if (display == null) { |
| display= Display.getDefault(); |
| fDisposeDisplay= true; |
| } |
| return display; |
| } |
| |
| private void addListeners(Text control) { |
| control.addListener(SWT.KeyDown, this::onKeyDown); |
| control.addListener(SWT.KeyUp, this::onKeyUp); |
| control.addListener(SWT.Modify, event -> onModify()); |
| } |
| |
| private Text createControl(Display display) { |
| fShell= new Shell(display); |
| fShell.setSize(300, 200); |
| fShell.setText("Keyboard Probe"); //$NON-NLS-1$ |
| fShell.setLayout(new FillLayout()); |
| Text text= new Text(fShell, SWT.MULTI | SWT.LEFT | SWT.WRAP); |
| text.setSize(300, 200); |
| fShell.setVisible(true); |
| fShell.forceActive(); |
| fShell.forceFocus(); |
| addListeners(text); |
| |
| text.setFocus(); |
| return text; |
| } |
| |
| private void postNaturalKeyPress(int i) { |
| |
| fKeyContinue= false; |
| |
| Event event= new Event(); |
| event.type= SWT.KeyDown; |
| event.keyCode= i; |
| event.character= (char) i; |
| fDisplay.post(event); |
| |
| event.type= SWT.KeyUp; |
| fDisplay.post(event); |
| |
| Event event2= new Event(); |
| event2.type= SWT.KeyDown; |
| // event2.keyCode= i; |
| event2.character= FAKE_CHAR; |
| fDisplay.post(event2); |
| |
| event2.type= SWT.KeyUp; |
| fDisplay.post(event2); |
| |
| do |
| driveEventQueue(); |
| while (!fKeyContinue); |
| |
| } |
| |
| private void postShiftKeyPress(int i) { |
| fKeyContinue= false; |
| |
| Event shift= new Event(); |
| shift.type= SWT.KeyDown; |
| shift.keyCode= SWT.SHIFT; |
| fDisplay.post(shift); |
| |
| Event event= new Event(); |
| event.type= SWT.KeyDown; |
| event.character= (char) i; |
| fDisplay.post(event); |
| |
| event.type= SWT.KeyUp; |
| fDisplay.post(event); |
| |
| shift.type= SWT.KeyUp; |
| fDisplay.post(shift); |
| |
| Event event2= new Event(); |
| event2.type= SWT.KeyDown; |
| event2.character= FAKE_CHAR; |
| fDisplay.post(event2); |
| |
| event2.type= SWT.KeyUp; |
| fDisplay.post(event2); |
| |
| do |
| driveEventQueue(); |
| while (!fKeyContinue); |
| } |
| |
| private boolean fShiftPressed= false; |
| |
| private void onKeyDown(Event event) { |
| if (event.keyCode == SWT.SHIFT) |
| fShiftPressed= true; |
| } |
| |
| private void onKeyUp(Event event) { |
| if (!fShiftPressed || event.keyCode == SWT.SHIFT) |
| fKeyContinue= true; |
| fShiftPressed= false; |
| } |
| |
| private void onModify() { |
| fTextContinue= true; |
| } |
| |
| private void driveEventQueue() { |
| while (fDisplay.readAndDispatch()) {} |
| try { |
| Thread.sleep(10); |
| } catch (InterruptedException e) { |
| // continue |
| } |
| } |
| |
| } |