/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.input; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; /** * A KeyboardInterpreter translate scancodes into KeyboardEvent's. * * @author epr * @author Martin Husted Hartvig */ public abstract class AbstractKeyboardInterpreter implements KeyboardInterpreter { private int flags; protected int lastDeadVK = -1; protected final Keys keys; // FIXME this is an evil hack to mitigate the performance issues that // result from the interpretExtendedScanCode's inappropriate use of exceptions. private final UnsupportedKeyException unsupportedKeyException = new UnsupportedKeyException(); private final DeadKeyException deadKeyException = new DeadKeyException(); private boolean extendedMode; private int capsLock = 0; private final char enter = '\n'; private final char divide = '/'; public AbstractKeyboardInterpreter() { keys = new Keys(); initKeys(keys); } /* (non-Javadoc) * @see org.jnode.driver.input.KeyboardInterpreter#interpretScancode(int) */ public final KeyboardEvent interpretScancode(int scancode) { final boolean extendedMode = this.extendedMode; if (scancode == XT_EXTENDED) { this.extendedMode = true; return null; } else { this.extendedMode = false; } final boolean released = ((scancode & XT_RELEASE) != 0); final long time = System.currentTimeMillis(); scancode &= 0x7f; int vk = deriveKeyCode(scancode, extendedMode); // debug output to find new keycodes // System.out.println("[" + (extendedMode ? "E" : "N") + scancode + "," + vk + "] " /*+ KeyEvent.getKeyText(vk)*/); if (!extendedMode) { if ((flags & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { vk = keys.getKey(scancode).getAltGrVirtuelKey(); } else if ((flags & InputEvent.SHIFT_DOWN_MASK) != 0) { vk = keys.getKey(scancode).getUpperVirtuelKey(); } else { vk = keys.getKey(scancode).getLowerVirtuelKey(); } } adjustFlags(vk, released); // debug output to find new keycodes // System.out.println("[" + (extendedMode ? "E" : "N") + scancode + "," + vk + "] " /*+ KeyEvent.getKeyText(vk)*/); try { final char ch; ch = interpretExtendedScanCode(scancode, vk, released, extendedMode); return new KeyboardEvent(released ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED, time, flags, vk, ch); } catch (UnsupportedKeyException e) { final char ch; if ((flags & InputEvent.CTRL_DOWN_MASK) != 0) { ch = keys.getKey(scancode).getControlChar(); } else if ((flags & InputEvent.SHIFT_DOWN_MASK) != 0) { ch = keys.getKey(scancode).getUpperChar(); if (!extendedMode) vk = keys.getKey(scancode).getUpperVirtuelKey(); } else if ((flags & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { ch = keys.getKey(scancode).getAltGrChar(); if (!extendedMode) vk = keys.getKey(scancode).getAltGrVirtuelKey(); } else { ch = keys.getKey(scancode).getLowerChar(); } return new KeyboardEvent(released ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED, time, flags, vk, ch); } catch (DeadKeyException e) { return null; } } private void adjustFlags(int vk, boolean released) { final int mask; switch (vk) { case KeyEvent.VK_ALT: mask = InputEvent.ALT_DOWN_MASK; break; case KeyEvent.VK_ALT_GRAPH: mask = InputEvent.ALT_GRAPH_DOWN_MASK; break; case KeyEvent.VK_CONTROL: mask = InputEvent.CTRL_DOWN_MASK; break; case KeyEvent.VK_SHIFT: mask = InputEvent.SHIFT_DOWN_MASK; break; case KeyEvent.VK_CAPS_LOCK: // FIXME ... can someone explain why 'capslock' isn't a boolean?? if (capsLock == 0 || capsLock == 3) mask = InputEvent.SHIFT_DOWN_MASK; else mask = 0; capsLock++; capsLock %= 4; break; default: mask = 0; } if (mask != 0) { if (released) { this.flags &= ~mask; } else { this.flags |= mask; } } } protected int deriveKeyCode(int scancode, boolean extended) { int vk = 0; if (extended) { switch (scancode) { case 82: vk = KeyEvent.VK_INSERT; break; case 71: vk = KeyEvent.VK_HOME; break; case 73: vk = KeyEvent.VK_PAGE_UP; break; case 83: vk = KeyEvent.VK_DELETE; break; case 79: vk = KeyEvent.VK_END; break; case 81: vk = KeyEvent.VK_PAGE_DOWN; break; case 72: vk = KeyEvent.VK_UP; break; case 75: vk = KeyEvent.VK_LEFT; break; case 80: vk = KeyEvent.VK_DOWN; break; case 77: vk = KeyEvent.VK_RIGHT; break; case 28: vk = KeyEvent.VK_ENTER; break; case 55: vk = KeyEvent.VK_PRINTSCREEN; break; case 56: vk = KeyEvent.VK_ALT_GRAPH; break; case 29: vk = KeyEvent.VK_CONTROL; break; case 93: vk = KeyEvent.VK_PROPS; break; case 53: vk = KeyEvent.VK_DIVIDE; break; default: vk = 0; } } return vk; } /** * Method interpretExtendedScanCode this method sould be used to handle the dead keys and other special keys * * @param scancode an int * @param vk an int * @param released a boolean * @return the char to use or throws an Exception * @throws UnsupportedKeyException is thrown if the current key is not handled by this method * @throws DeadKeyException is thrown if the current key is a dead key * @since 0.15 */ protected char interpretExtendedScanCode(int scancode, int vk, boolean released, boolean extended) throws UnsupportedKeyException, DeadKeyException { boolean deadKey = false; switch (vk) { case KeyEvent.VK_DEAD_ABOVEDOT: case KeyEvent.VK_DEAD_ABOVERING: case KeyEvent.VK_DEAD_ACUTE: case KeyEvent.VK_DEAD_BREVE: case KeyEvent.VK_DEAD_CARON: case KeyEvent.VK_DEAD_CEDILLA: case KeyEvent.VK_DEAD_CIRCUMFLEX: case KeyEvent.VK_DEAD_DIAERESIS: case KeyEvent.VK_DEAD_DOUBLEACUTE: case KeyEvent.VK_DEAD_GRAVE: case KeyEvent.VK_DEAD_IOTA: case KeyEvent.VK_DEAD_MACRON: case KeyEvent.VK_DEAD_OGONEK: case KeyEvent.VK_DEAD_SEMIVOICED_SOUND: case KeyEvent.VK_DEAD_TILDE: case KeyEvent.VK_DEAD_VOICED_SOUND: lastDeadVK = vk; deadKey = true; break; case KeyEvent.VK_INSERT: case KeyEvent.VK_HOME: case KeyEvent.VK_PAGE_UP: case KeyEvent.VK_DELETE: case KeyEvent.VK_END: case KeyEvent.VK_PAGE_DOWN: case KeyEvent.VK_UP: case KeyEvent.VK_LEFT: case KeyEvent.VK_DOWN: case KeyEvent.VK_RIGHT: case KeyEvent.VK_PRINTSCREEN: case KeyEvent.VK_PROPS: if (extended) return KeyEvent.CHAR_UNDEFINED; break; case KeyEvent.VK_SHIFT: case KeyEvent.VK_CONTROL: case KeyEvent.VK_ALT: case KeyEvent.VK_ALT_GRAPH: case KeyEvent.VK_CAPS_LOCK: return KeyEvent.CHAR_UNDEFINED; case KeyEvent.VK_ENTER: if (extended) return enter; break; case KeyEvent.VK_DIVIDE: if (extended) return divide; break; } if (deadKey) { throw deadKeyException; } else if (lastDeadVK != -1) { try { Key key = keys.getKey(scancode); char[] deadChars = key.getDeadKeyChar(lastDeadVK); if (flags == InputEvent.SHIFT_DOWN_MASK) { if (deadChars.length > 1) { return deadChars[1]; } else throw unsupportedKeyException; } else if (deadChars.length > 0) return deadChars[0]; else return KeyEvent.CHAR_UNDEFINED; } finally { if (!released) { lastDeadVK = -1; } } } throw unsupportedKeyException; } /* (non-Javadoc) * @see org.jnode.driver.input.KeyboardInterpreter#interpretKeycode(int) */ public KeyboardEvent interpretKeycode(int keycode) { final int scancode = keys.getScanCode(keycode); if (scancode < 0) return null; return interpretScancode(scancode); } /** * @return Returns the flags. */ protected final int getFlags() { return this.flags; } protected abstract void initKeys(Keys keys); }