AWT keyboard barely working
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 16 Mar 2015 15:45:46 +0000 (11:45 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 16 Mar 2015 15:45:46 +0000 (11:45 -0400)
build.xml
resources/terminus-ttf-4.39/COPYING [new file with mode: 0644]
resources/terminus-ttf-4.39/TerminusTTF-4.39.ttf [new file with mode: 0644]
resources/terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf [new file with mode: 0644]
resources/terminus-ttf-4.39/TerminusTTF-Italic-4.39.ttf [new file with mode: 0644]
src/jexer/io/AWTScreen.java
src/jexer/io/AWTTerminal.java

index 35aee170952567746104662c5cdc2173b519b39b..f47d0416f26867a228214833504dee768c924982 100644 (file)
--- a/build.xml
+++ b/build.xml
 
 <project name="jexer" basedir="." default="run">
 
-    <property name="src.dir"     value="src"/>
-    <property name="build.dir"   value="build"/>
-    <property name="classes.dir" value="${build.dir}/classes"/>
-    <property name="jar.dir"     value="${build.dir}/jar"/>
+    <property name="src.dir"       value="src"/>
+    <property name="resources.dir" value="resources"/>
+    <property name="build.dir"     value="build"/>
+    <property name="classes.dir"   value="${build.dir}/classes"/>
+    <property name="jar.dir"       value="${build.dir}/jar"/>
 
     <target name="clean">
        <delete dir="${build.dir}"/>
@@ -49,6 +50,7 @@
        <mkdir dir="${jar.dir}"/>
        <jar destfile="${jar.dir}/${ant.project.name}.jar"
             basedir="${classes.dir}">
+         <fileset dir="${resources.dir}"/>
          <manifest>
            <attribute name="Main-Class" value="jexer.demos.Demo1"/>
          </manifest>
diff --git a/resources/terminus-ttf-4.39/COPYING b/resources/terminus-ttf-4.39/COPYING
new file mode 100644 (file)
index 0000000..c964194
--- /dev/null
@@ -0,0 +1,97 @@
+Copyright (c) 2010 Dimitar Toshkov Zhekov,\r
+with Reserved Font Name "Terminus Font".\r
+\r
+Copyright (c) 2011 Tilman Blumenbach,\r
+with Reserved Font Name "Terminus (TTF)".\r
+\r
+This Font Software is licensed under the SIL Open Font License, Version 1.1.\r
+This license is copied below, and is also available with a FAQ at:\r
+http://scripts.sil.org/OFL\r
+\r
+\r
+-----------------------------------------------------------\r
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r
+-----------------------------------------------------------\r
+\r
+PREAMBLE\r
+The goals of the Open Font License (OFL) are to stimulate worldwide\r
+development of collaborative font projects, to support the font creation\r
+efforts of academic and linguistic communities, and to provide a free and\r
+open framework in which fonts may be shared and improved in partnership\r
+with others.\r
+\r
+The OFL allows the licensed fonts to be used, studied, modified and\r
+redistributed freely as long as they are not sold by themselves. The\r
+fonts, including any derivative works, can be bundled, embedded, \r
+redistributed and/or sold with any software provided that any reserved\r
+names are not used by derivative works. The fonts and derivatives,\r
+however, cannot be released under any other type of license. The\r
+requirement for fonts to remain under this license does not apply\r
+to any document created using the fonts or their derivatives.\r
+\r
+DEFINITIONS\r
+"Font Software" refers to the set of files released by the Copyright\r
+Holder(s) under this license and clearly marked as such. This may\r
+include source files, build scripts and documentation.\r
+\r
+"Reserved Font Name" refers to any names specified as such after the\r
+copyright statement(s).\r
+\r
+"Original Version" refers to the collection of Font Software components as\r
+distributed by the Copyright Holder(s).\r
+\r
+"Modified Version" refers to any derivative made by adding to, deleting,\r
+or substituting -- in part or in whole -- any of the components of the\r
+Original Version, by changing formats or by porting the Font Software to a\r
+new environment.\r
+\r
+"Author" refers to any designer, engineer, programmer, technical\r
+writer or other person who contributed to the Font Software.\r
+\r
+PERMISSION & CONDITIONS\r
+Permission is hereby granted, free of charge, to any person obtaining\r
+a copy of the Font Software, to use, study, copy, merge, embed, modify,\r
+redistribute, and sell modified and unmodified copies of the Font\r
+Software, subject to the following conditions:\r
+\r
+1) Neither the Font Software nor any of its individual components,\r
+in Original or Modified Versions, may be sold by itself.\r
+\r
+2) Original or Modified Versions of the Font Software may be bundled,\r
+redistributed and/or sold with any software, provided that each copy\r
+contains the above copyright notice and this license. These can be\r
+included either as stand-alone text files, human-readable headers or\r
+in the appropriate machine-readable metadata fields within text or\r
+binary files as long as those fields can be easily viewed by the user.\r
+\r
+3) No Modified Version of the Font Software may use the Reserved Font\r
+Name(s) unless explicit written permission is granted by the corresponding\r
+Copyright Holder. This restriction only applies to the primary font name as\r
+presented to the users.\r
+\r
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r
+Software shall not be used to promote, endorse or advertise any\r
+Modified Version, except to acknowledge the contribution(s) of the\r
+Copyright Holder(s) and the Author(s) or with their explicit written\r
+permission.\r
+\r
+5) The Font Software, modified or unmodified, in part or in whole,\r
+must be distributed entirely under this license, and must not be\r
+distributed under any other license. The requirement for fonts to\r
+remain under this license does not apply to any document created\r
+using the Font Software.\r
+\r
+TERMINATION\r
+This license becomes null and void if any of the above conditions are\r
+not met.\r
+\r
+DISCLAIMER\r
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r
+OTHER DEALINGS IN THE FONT SOFTWARE.\r
diff --git a/resources/terminus-ttf-4.39/TerminusTTF-4.39.ttf b/resources/terminus-ttf-4.39/TerminusTTF-4.39.ttf
new file mode 100644 (file)
index 0000000..f4bb6b2
Binary files /dev/null and b/resources/terminus-ttf-4.39/TerminusTTF-4.39.ttf differ
diff --git a/resources/terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf b/resources/terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf
new file mode 100644 (file)
index 0000000..06700de
Binary files /dev/null and b/resources/terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf differ
diff --git a/resources/terminus-ttf-4.39/TerminusTTF-Italic-4.39.ttf b/resources/terminus-ttf-4.39/TerminusTTF-Italic-4.39.ttf
new file mode 100644 (file)
index 0000000..e80d0c0
Binary files /dev/null and b/resources/terminus-ttf-4.39/TerminusTTF-Italic-4.39.ttf differ
index e10e0ec67579af69a65c77dd37a74da7cb7b8ab8..2b913f1d41c9cf0621b3c85ff79c032465e87346 100644 (file)
@@ -39,18 +39,72 @@ import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Frame;
 import java.awt.Graphics;
+import java.awt.Insets;
 import java.awt.geom.Rectangle2D;
+import java.io.InputStream;
 
 /**
  * This Screen implementation draws to a Java AWT Frame.
  */
 public final class AWTScreen extends Screen {
 
+    private static Color MYBLACK;
+    private static Color MYRED;
+    private static Color MYGREEN;
+    private static Color MYYELLOW;
+    private static Color MYBLUE;
+    private static Color MYMAGENTA;
+    private static Color MYCYAN;
+    private static Color MYWHITE;
+
+    private static Color MYBOLD_BLACK;
+    private static Color MYBOLD_RED;
+    private static Color MYBOLD_GREEN;
+    private static Color MYBOLD_YELLOW;
+    private static Color MYBOLD_BLUE;
+    private static Color MYBOLD_MAGENTA;
+    private static Color MYBOLD_CYAN;
+    private static Color MYBOLD_WHITE;
+
+    private static boolean dosColors = false;
+
+    /**
+     * Setup AWT colors to match DOS color palette.
+     */
+    private static void setDOSColors() {
+        if (dosColors) {
+            return;
+        }
+        MYBLACK        = new Color(0x00, 0x00, 0x00);
+        MYRED          = new Color(0xa8, 0x00, 0x00);
+        MYGREEN        = new Color(0x00, 0xa8, 0x00);
+        MYYELLOW       = new Color(0xa8, 0x54, 0x00);
+        MYBLUE         = new Color(0x00, 0x00, 0xa8);
+        MYMAGENTA      = new Color(0xa8, 0x00, 0xa8);
+        MYCYAN         = new Color(0x00, 0xa8, 0xa8);
+        MYWHITE        = new Color(0xa8, 0xa8, 0xa8);
+        MYBOLD_BLACK   = new Color(0x54, 0x54, 0x54);
+        MYBOLD_RED     = new Color(0xfc, 0x54, 0x54);
+        MYBOLD_GREEN   = new Color(0x54, 0xfc, 0x54);
+        MYBOLD_YELLOW  = new Color(0xfc, 0xfc, 0x54);
+        MYBOLD_BLUE    = new Color(0x54, 0x54, 0xfc);
+        MYBOLD_MAGENTA = new Color(0xfc, 0x54, 0xfc);
+        MYBOLD_CYAN    = new Color(0x54, 0xfc, 0xfc);
+        MYBOLD_WHITE   = new Color(0xfc, 0xfc, 0xfc);
+
+        dosColors = true;
+    }
+
     /**
      * AWTFrame is our top-level hook into the AWT system.
      */
     class AWTFrame extends Frame {
 
+        /**
+         * The terminus font resource filename.
+         */
+        private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
+
         /**
          * The TUI Screen data.
          */
@@ -66,41 +120,165 @@ public final class AWTScreen extends Screen {
          */
         private int textHeight = 1;
 
+        /**
+         * Descent of a character cell.
+         */
+        private int maxDescent = 0;
+
         /**
          * Top pixel value.
          */
         private int top = 30;
-        
+
         /**
          * Left pixel value.
          */
         private int left = 30;
-         
+
+        /**
+         * Convert a CellAttributes foreground color to an AWT Color.
+         *
+         * @param attr the text attributes
+         * @return the AWT Color
+         */
+        private Color attrToForegroundColor(final CellAttributes attr) {
+            /*
+             * TODO:
+             *   reverse
+             *   blink
+             *   underline
+             */
+            if (attr.getBold()) {
+                if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
+                    return MYBOLD_BLACK;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
+                    return MYBOLD_RED;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
+                    return MYBOLD_BLUE;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
+                    return MYBOLD_GREEN;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
+                    return MYBOLD_YELLOW;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
+                    return MYBOLD_CYAN;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
+                    return MYBOLD_MAGENTA;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
+                    return MYBOLD_WHITE;
+                }
+            } else {
+                if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
+                    return MYBLACK;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
+                    return MYRED;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
+                    return MYBLUE;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
+                    return MYGREEN;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
+                    return MYYELLOW;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
+                    return MYCYAN;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
+                    return MYMAGENTA;
+                } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
+                    return MYWHITE;
+                }
+            }
+            throw new IllegalArgumentException("Invalid color: " + attr.getForeColor().getValue());
+        }
+
+        /**
+         * Convert a CellAttributes background color to an AWT Color.
+         *
+         * @param attr the text attributes
+         * @return the AWT Color
+         */
+        private Color attrToBackgroundColor(final CellAttributes attr) {
+            /*
+             * TODO:
+             *   reverse
+             *   blink
+             *   underline
+             */
+            if (attr.getBackColor().equals(jexer.bits.Color.BLACK)) {
+                return MYBLACK;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.RED)) {
+                return MYRED;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.BLUE)) {
+                return MYBLUE;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.GREEN)) {
+                return MYGREEN;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.YELLOW)) {
+                return MYYELLOW;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.CYAN)) {
+                return MYCYAN;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
+                return MYMAGENTA;
+            } else if (attr.getBackColor().equals(jexer.bits.Color.WHITE)) {
+                return MYWHITE;
+            }
+            throw new IllegalArgumentException("Invalid color: " + attr.getBackColor().getValue());
+        }
+
         /**
          * Public constructor.
+         *
+         * @param screen the Screen that Backend talks to
          */
-        public AWTFrame() {
+        public AWTFrame(final AWTScreen screen) {
+            this.screen = screen;
+            setDOSColors();
+
             setTitle("Jexer Application");
             setBackground(java.awt.Color.black);
             setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
-            setFont(new Font("Liberation Mono", Font.BOLD, 16));
+            // setFont(new Font("Liberation Mono", Font.BOLD, 16));
             // setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16));
-            setSize(100, 100);
+
+            try {
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                InputStream in = loader.getResourceAsStream(FONTFILE);
+                Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
+                Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22);
+                setFont(terminus);
+            } catch (Exception e) {
+                e.printStackTrace();
+                // setFont(new Font("Liberation Mono", Font.PLAIN, 24));
+                setFont(new Font(Font.MONOSPACED, Font.PLAIN, 24));
+            }
             setVisible(true);
+            resizeToScreen();
         }
 
         /**
          * Resize to font dimensions.
          */
         public void resizeToScreen() {
-            Graphics gr        = getGraphics();
-            FontMetrics fm     = gr.getFontMetrics();
-            textWidth          = fm.charWidth('m');
-            textHeight         = fm.getHeight();
-            setSize((textWidth + 1) * screen.width + (2 * left),
-                (textHeight + 1) * screen.height + (2 * top));
-
-            System.err.printf("W: %d H: %d\n", textWidth, textHeight);
+            Graphics gr = getGraphics();
+            FontMetrics fm = gr.getFontMetrics();
+            maxDescent = fm.getMaxDescent();
+            Rectangle2D bounds = fm.getMaxCharBounds(gr);
+            int leading = fm.getLeading();
+            textWidth = (int)Math.round(bounds.getWidth());
+            textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
+            // This also produces the same number, but works better for ugly
+            // monospace.
+            textHeight = fm.getMaxAscent() + maxDescent - leading;
+
+            // Figure out the thickness of borders and use that to set the
+            // final size.
+            Insets insets = getInsets();
+            left = insets.left;
+            top = insets.top;
+
+            setSize(textWidth * screen.width + insets.left + insets.right,
+                textHeight * screen.height + insets.top + insets.bottom);
+
+            /*
+            System.err.printf("W: %d H: %d MD: %d L: %d\n", textWidth,
+                textHeight, maxDescent, leading);
+             */
         }
 
         /**
@@ -109,60 +287,27 @@ public final class AWTScreen extends Screen {
          * @param gr the AWT Graphics context
          */
         @Override
-        public void paint(Graphics gr) {
+        public void paint(final Graphics gr) {
 
             for (int y = 0; y < screen.height; y++) {
                 for (int x = 0; x < screen.width; x++) {
                     Cell lCell = screen.logical[x][y];
                     Cell pCell = screen.physical[x][y];
 
-                    int xPixel = x * (textWidth + 1) + left;
-                    int yPixel = y * (textHeight + 1) + top - y;
+                    int xPixel = x * textWidth + left;
+                    int yPixel = y * textHeight + top;
 
                     if (!lCell.equals(pCell)) {
+
                         // Draw the background rectangle, then the foreground
                         // character.
-                        if (lCell.getBackColor().equals(jexer.bits.Color.BLACK)) {
-                            gr.setColor(Color.black);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.RED)) {
-                            gr.setColor(Color.red);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.BLUE)) {
-                            gr.setColor(Color.blue);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.GREEN)) {
-                            gr.setColor(Color.green);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.YELLOW)) {
-                            gr.setColor(Color.yellow);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.CYAN)) {
-                            gr.setColor(Color.cyan);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
-                            gr.setColor(Color.magenta);
-                        } else if (lCell.getBackColor().equals(jexer.bits.Color.WHITE)) {
-                            gr.setColor(Color.white);
-                        }
-                        gr.fillRect(xPixel, yPixel, textWidth + 1,
-                            textHeight + 2);
-
-                        if (lCell.getForeColor().equals(jexer.bits.Color.BLACK)) {
-                            gr.setColor(Color.black);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.RED)) {
-                            gr.setColor(Color.red);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.BLUE)) {
-                            gr.setColor(Color.blue);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.GREEN)) {
-                            gr.setColor(Color.green);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.YELLOW)) {
-                            gr.setColor(Color.yellow);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.CYAN)) {
-                            gr.setColor(Color.cyan);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
-                            gr.setColor(Color.magenta);
-                        } else if (lCell.getForeColor().equals(jexer.bits.Color.WHITE)) {
-                            gr.setColor(Color.white);
-                        }
+                        gr.setColor(attrToBackgroundColor(lCell));
+                        gr.fillRect(xPixel, yPixel, textWidth, textHeight);
+                        gr.setColor(attrToForegroundColor(lCell));
                         char [] chars = new char[1];
                         chars[0] = lCell.getChar();
                         gr.drawChars(chars, 0, 1, xPixel,
-                            yPixel + textHeight - 2);
+                            yPixel + textHeight - maxDescent);
 
                         // Physical is always updated
                         physical[x][y].setTo(lCell);
@@ -173,17 +318,15 @@ public final class AWTScreen extends Screen {
     }
 
     /**
-     * The raw AWT Frame.
+     * The raw AWT Frame.  Note package private access.
      */
-    private AWTFrame frame;
+    AWTFrame frame;
 
     /**
      * Public constructor.
      */
     public AWTScreen() {
-        frame = new AWTFrame();
-        frame.screen = this;
-        frame.resizeToScreen();
+        frame = new AWTFrame(this);
     }
 
     /**
index c9ddc13f0772eb116dc50327356a424460d84e0b..fd03487a61d7f092950d31fb7d7ba4dbbc0ce6fa 100644 (file)
  */
 package jexer.io;
 
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowListener;
 import java.util.List;
 import java.util.LinkedList;
 
@@ -46,7 +50,12 @@ import static jexer.TKeypress.*;
 /**
  * This class reads keystrokes and mouse events from an AWT Frame.
  */
-public final class AWTTerminal {
+public final class AWTTerminal implements KeyListener {
+
+    /**
+     * The backend Screen.
+     */
+    private AWTScreen screen;
 
     /**
      * The session information.
@@ -106,15 +115,18 @@ public final class AWTTerminal {
     /**
      * Constructor sets up state for getEvent().
      *
-     * @param screen the top-level AWT frame 
+     * @param screen the top-level AWT frame
      */
     public AWTTerminal(final AWTScreen screen) {
+        this.screen      = screen;
         mouse1           = false;
         mouse2           = false;
         mouse3           = false;
         stopReaderThread = false;
         sessionInfo      = new TSessionInfo();
         eventQueue       = new LinkedList<TInputEvent>();
+
+        screen.frame.addKeyListener(this);
     }
 
     /**
@@ -122,6 +134,7 @@ public final class AWTTerminal {
      */
     public void shutdown() {
         // System.err.println("=== shutdown() ==="); System.err.flush();
+        screen.frame.dispose();
     }
 
     /**
@@ -160,4 +173,213 @@ public final class AWTTerminal {
         }
     }
 
+    /**
+     * Pass AWT keystrokes into the event queue.
+     *
+     * @param key keystroke received
+     */
+    @Override
+    public void keyReleased(final KeyEvent key) {
+        // Ignore release events
+    }
+
+    /**
+     * Pass AWT keystrokes into the event queue.
+     *
+     * @param key keystroke received
+     */
+    @Override
+    public void keyTyped(final KeyEvent key) {
+        // Ignore typed events
+    }
+
+    /**
+     * Pass AWT keystrokes into the event queue.
+     *
+     * @param key keystroke received
+     */
+    @Override
+    public void keyPressed(final KeyEvent key) {
+        boolean alt = false;
+        boolean shift = false;
+        boolean ctrl = false;
+        char ch = ' ';
+        boolean isKey = false;
+        int fnKey = 0;
+        if (key.isActionKey()) {
+            isKey = true;
+        } else {
+            ch = key.getKeyChar();
+        }
+        alt = key.isAltDown();
+        ctrl = key.isControlDown();
+        shift = key.isShiftDown();
+
+        /*
+        System.err.printf("AWT Key: %s\n", key);
+        System.err.printf("   isKey: %s\n", isKey);
+        System.err.printf("   alt: %s\n", alt);
+        System.err.printf("   ctrl: %s\n", ctrl);
+        System.err.printf("   shift: %s\n", shift);
+        System.err.printf("   ch: %s\n", ch);
+         */
+
+        // Special case: not return the bare modifier presses
+        switch (key.getKeyCode()) {
+        case KeyEvent.VK_ALT:
+            return;
+        case KeyEvent.VK_ALT_GRAPH:
+            return;
+        case KeyEvent.VK_CONTROL:
+            return;
+        case KeyEvent.VK_SHIFT:
+            return;
+        case KeyEvent.VK_META:
+            return;
+        default:
+            break;
+        }
+
+        TKeypress keypress = null;
+        if (isKey) {
+            switch (key.getKeyCode()) {
+            case KeyEvent.VK_F1:
+                keypress = new TKeypress(true, TKeypress.F1, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F2:
+                keypress = new TKeypress(true, TKeypress.F2, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F3:
+                keypress = new TKeypress(true, TKeypress.F3, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F4:
+                keypress = new TKeypress(true, TKeypress.F4, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F5:
+                keypress = new TKeypress(true, TKeypress.F5, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F6:
+                keypress = new TKeypress(true, TKeypress.F6, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F7:
+                keypress = new TKeypress(true, TKeypress.F7, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F8:
+                keypress = new TKeypress(true, TKeypress.F8, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F9:
+                keypress = new TKeypress(true, TKeypress.F9, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F10:
+                keypress = new TKeypress(true, TKeypress.F10, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F11:
+                keypress = new TKeypress(true, TKeypress.F11, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_F12:
+                keypress = new TKeypress(true, TKeypress.F12, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_HOME:
+                keypress = new TKeypress(true, TKeypress.HOME, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_END:
+                keypress = new TKeypress(true, TKeypress.END, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_PAGE_UP:
+                keypress = new TKeypress(true, TKeypress.PGUP, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_PAGE_DOWN:
+                keypress = new TKeypress(true, TKeypress.PGDN, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_INSERT:
+                keypress = new TKeypress(true, TKeypress.INS, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_DELETE:
+                keypress = new TKeypress(true, TKeypress.F1, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_RIGHT:
+                keypress = new TKeypress(true, TKeypress.RIGHT, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_LEFT:
+                keypress = new TKeypress(true, TKeypress.LEFT, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_UP:
+                keypress = new TKeypress(true, TKeypress.UP, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_DOWN:
+                keypress = new TKeypress(true, TKeypress.DOWN, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_TAB:
+                // Special case: distinguish TAB vs BTAB
+                if (shift) {
+                    keypress = kbShiftTab;
+                } else {
+                    keypress = kbTab;
+                }
+                break;
+            case KeyEvent.VK_ENTER:
+                keypress = new TKeypress(true, TKeypress.ENTER, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_ESCAPE:
+                keypress = new TKeypress(true, TKeypress.ESC, ' ',
+                    alt, ctrl, shift);
+                break;
+            case KeyEvent.VK_BACK_SPACE:
+                // Special case: return it as kbBackspace (Ctrl-H)
+                keypress = new TKeypress(false, 0, 'H', false, true, false);
+                break;
+            default:
+                // Unsupported, ignore
+                return;
+            }
+        }
+
+        if (keypress == null) {
+            switch (ch) {
+            case 0x08:
+                keypress = kbBackspace;
+                break;
+            case 0x0A:
+                keypress = kbEnter;
+                break;
+            case 0x0D:
+                keypress = kbEnter;
+                break;
+            default:
+                if (!alt && ctrl && !shift) {
+                    ch = key.getKeyText(key.getKeyCode()).charAt(0);
+                }
+                // Not a special key, put it together
+                keypress = new TKeypress(false, 0, ch, alt, ctrl, shift);
+            }
+        }
+
+        // Save it and we are done.
+        synchronized (eventQueue) {
+            eventQueue.add(new TKeypressEvent(keypress));
+        }
+    }
 }