#35 fix demo
[fanfix.git] / src / jexer / backend / SwingTerminal.java
index f588679ddf8888671ef01103559f80b736238f35..134eceda63772cec5c776b9d0dedf323f4559bf3 100644 (file)
@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (C) 2017 Kevin Lamonte
+ * Copyright (C) 2019 Kevin Lamonte
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -51,8 +51,8 @@ import java.awt.event.WindowListener;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import javax.swing.JComponent;
@@ -98,7 +98,7 @@ public class SwingTerminal extends LogicalScreen
     /**
      * The terminus font resource filename.
      */
-    private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
+    public static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
 
     /**
      * Cursor style to draw.
@@ -164,11 +164,6 @@ public class SwingTerminal extends LogicalScreen
      */
     private Map<Cell, BufferedImage> glyphCache;
 
-    /**
-     * If true, we were successful getting Terminus.
-     */
-    private boolean gotTerminus = false;
-
     /**
      * If true, we were successful at getting the font dimensions.
      */
@@ -187,12 +182,22 @@ public class SwingTerminal extends LogicalScreen
     /**
      * Width of a character cell in pixels.
      */
-    private int textWidth = 1;
+    private int textWidth = 16;
 
     /**
      * Height of a character cell in pixels.
      */
-    private int textHeight = 1;
+    private int textHeight = 20;
+
+    /**
+     * Width of a character cell in pixels, as reported by font.
+     */
+    private int fontTextWidth = 1;
+
+    /**
+     * Height of a character cell in pixels, as reported by font.
+     */
+    private int fontTextHeight = 1;
 
     /**
      * Descent of a character cell in pixels.
@@ -209,6 +214,16 @@ public class SwingTerminal extends LogicalScreen
      */
     private int textAdjustX = 0;
 
+    /**
+     * System-dependent height adjustment for text in the character cell.
+     */
+    private int textAdjustHeight = 0;
+
+    /**
+     * System-dependent width adjustment for text in the character cell.
+     */
+    private int textAdjustWidth = 0;
+
     /**
      * Top pixel absolute location.
      */
@@ -286,6 +301,13 @@ public class SwingTerminal extends LogicalScreen
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Static constructor.
+     */
+    static {
+        setDOSColors();
+    }
+
     /**
      * Public constructor creates a new JFrame to render to.
      *
@@ -301,27 +323,7 @@ public class SwingTerminal extends LogicalScreen
 
         this.fontSize = fontSize;
 
-        setDOSColors();
-
-        // Figure out my cursor style.
-        String cursorStyleString = System.getProperty(
-            "jexer.Swing.cursorStyle", "underline").toLowerCase();
-        if (cursorStyleString.equals("underline")) {
-            cursorStyle = CursorStyle.UNDERLINE;
-        } else if (cursorStyleString.equals("outline")) {
-            cursorStyle = CursorStyle.OUTLINE;
-        } else if (cursorStyleString.equals("block")) {
-            cursorStyle = CursorStyle.BLOCK;
-        }
-
-        // Pull the system property for triple buffering.
-        if (System.getProperty("jexer.Swing.tripleBuffer") != null) {
-            if (System.getProperty("jexer.Swing.tripleBuffer").equals("true")) {
-                SwingComponent.tripleBuffer = true;
-            } else {
-                SwingComponent.tripleBuffer = false;
-            }
-        }
+        reloadOptions();
 
         try {
             SwingUtilities.invokeAndWait(new Runnable() {
@@ -388,7 +390,7 @@ public class SwingTerminal extends LogicalScreen
                     SwingTerminal.this.top = insets.top;
 
                     // Load the font so that we can set sessionInfo.
-                    getDefaultFont();
+                    setDefaultFont();
 
                     // Get the default cols x rows and set component size
                     // accordingly.
@@ -398,14 +400,16 @@ public class SwingTerminal extends LogicalScreen
                             SwingTerminal.this.textHeight,
                             windowWidth, windowHeight);
 
-                    SwingTerminal.this.setDimensions(sessionInfo.getWindowWidth(),
-                        sessionInfo.getWindowHeight());
+                    SwingTerminal.this.setDimensions(sessionInfo.
+                        getWindowWidth(), sessionInfo.getWindowHeight());
 
-                    SwingTerminal.this.resizeToScreen();
+                    SwingTerminal.this.resizeToScreen(true);
                     SwingTerminal.this.swing.setVisible(true);
                 }
             });
-        } catch (Exception e) {
+        } catch (java.lang.reflect.InvocationTargetException e) {
+            e.printStackTrace();
+        } catch (InterruptedException e) {
             e.printStackTrace();
         }
 
@@ -413,7 +417,7 @@ public class SwingTerminal extends LogicalScreen
         mouse1           = false;
         mouse2           = false;
         mouse3           = false;
-        eventQueue       = new LinkedList<TInputEvent>();
+        eventQueue       = new ArrayList<TInputEvent>();
 
         // Add listeners to Swing.
         swing.addKeyListener(this);
@@ -440,18 +444,7 @@ public class SwingTerminal extends LogicalScreen
 
         this.fontSize = fontSize;
 
-        setDOSColors();
-
-        // Figure out my cursor style.
-        String cursorStyleString = System.getProperty(
-            "jexer.Swing.cursorStyle", "underline").toLowerCase();
-        if (cursorStyleString.equals("underline")) {
-            cursorStyle = CursorStyle.UNDERLINE;
-        } else if (cursorStyleString.equals("outline")) {
-            cursorStyle = CursorStyle.OUTLINE;
-        } else if (cursorStyleString.equals("block")) {
-            cursorStyle = CursorStyle.BLOCK;
-        }
+        reloadOptions();
 
         try {
             SwingUtilities.invokeAndWait(new Runnable() {
@@ -517,7 +510,7 @@ public class SwingTerminal extends LogicalScreen
                     SwingTerminal.this.top = insets.top;
 
                     // Load the font so that we can set sessionInfo.
-                    getDefaultFont();
+                    setDefaultFont();
 
                     // Get the default cols x rows and set component size
                     // accordingly.
@@ -527,7 +520,9 @@ public class SwingTerminal extends LogicalScreen
                             SwingTerminal.this.textHeight);
                 }
             });
-        } catch (Exception e) {
+        } catch (java.lang.reflect.InvocationTargetException e) {
+            e.printStackTrace();
+        } catch (InterruptedException e) {
             e.printStackTrace();
         }
 
@@ -535,7 +530,7 @@ public class SwingTerminal extends LogicalScreen
         mouse1           = false;
         mouse2           = false;
         mouse3           = false;
-        eventQueue       = new LinkedList<TInputEvent>();
+        eventQueue       = new ArrayList<TInputEvent>();
 
         // Add listeners to Swing.
         swing.addKeyListener(this);
@@ -578,6 +573,7 @@ public class SwingTerminal extends LogicalScreen
         ) {
             do {
                 do {
+                    clearPhysical();
                     drawToSwing();
                 } while (swing.getBufferStrategy().contentsRestored());
 
@@ -639,10 +635,53 @@ public class SwingTerminal extends LogicalScreen
         this.listener = listener;
     }
 
+    /**
+     * Reload options from System properties.
+     */
+    public void reloadOptions() {
+        // Figure out my cursor style.
+        String cursorStyleString = System.getProperty(
+            "jexer.Swing.cursorStyle", "underline").toLowerCase();
+        if (cursorStyleString.equals("underline")) {
+            cursorStyle = CursorStyle.UNDERLINE;
+        } else if (cursorStyleString.equals("outline")) {
+            cursorStyle = CursorStyle.OUTLINE;
+        } else if (cursorStyleString.equals("block")) {
+            cursorStyle = CursorStyle.BLOCK;
+        }
+
+        // Pull the system property for triple buffering.
+        if (System.getProperty("jexer.Swing.tripleBuffer",
+                "true").equals("true")
+        ) {
+            SwingComponent.tripleBuffer = true;
+        } else {
+            SwingComponent.tripleBuffer = false;
+        }
+    }
+
     // ------------------------------------------------------------------------
     // SwingTerminal ----------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Get the width of a character cell in pixels.
+     *
+     * @return the width in pixels of a character cell
+     */
+    public int getTextWidth() {
+        return textWidth;
+    }
+
+    /**
+     * Get the height of a character cell in pixels.
+     *
+     * @return the height in pixels of a character cell
+     */
+    public int getTextHeight() {
+        return textHeight;
+    }
+
     /**
      * Setup Swing colors to match DOS color palette.
      */
@@ -681,6 +720,15 @@ public class SwingTerminal extends LogicalScreen
         return blinkMillis;
     }
 
+    /**
+     * Get the current status of the blink flag.
+     *
+     * @return true if the cursor and blinking text should be visible
+     */
+    public boolean getCursorBlinkVisible() {
+        return cursorBlinkVisible;
+    }
+
     /**
      * Get the font size in points.
      *
@@ -707,27 +755,40 @@ public class SwingTerminal extends LogicalScreen
      * @param font the new font
      */
     public void setFont(final Font font) {
-        this.font = font;
-        getFontDimensions();
-        swing.setFont(font);
-        glyphCacheBlink = new HashMap<Cell, BufferedImage>();
-        glyphCache = new HashMap<Cell, BufferedImage>();
-        resizeToScreen();
+        synchronized (this) {
+            this.font = font;
+            getFontDimensions();
+            swing.setFont(font);
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            resizeToScreen(true);
+        }
+    }
+
+    /**
+     * Get the font this screen was last set to.
+     *
+     * @return the font
+     */
+    public Font getFont() {
+        return font;
     }
 
     /**
      * Set the font to Terminus, the best all-around font for both CP437 and
      * ISO8859-1.
      */
-    public void getDefaultFont() {
+    public void setDefaultFont() {
         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, fontSize);
-            gotTerminus = true;
             font = terminus;
-        } catch (Exception e) {
+        } catch (java.awt.FontFormatException e) {
+            e.printStackTrace();
+            font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
+        } catch (java.io.IOException e) {
             e.printStackTrace();
             font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
         }
@@ -735,13 +796,107 @@ public class SwingTerminal extends LogicalScreen
         setFont(font);
     }
 
+    /**
+     * Get the X text adjustment.
+     *
+     * @return X text adjustment
+     */
+    public int getTextAdjustX() {
+        return textAdjustX;
+    }
+
+    /**
+     * Set the X text adjustment.
+     *
+     * @param textAdjustX the X text adjustment
+     */
+    public void setTextAdjustX(final int textAdjustX) {
+        synchronized (this) {
+            this.textAdjustX = textAdjustX;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the Y text adjustment.
+     *
+     * @return Y text adjustment
+     */
+    public int getTextAdjustY() {
+        return textAdjustY;
+    }
+
+    /**
+     * Set the Y text adjustment.
+     *
+     * @param textAdjustY the Y text adjustment
+     */
+    public void setTextAdjustY(final int textAdjustY) {
+        synchronized (this) {
+            this.textAdjustY = textAdjustY;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the height text adjustment.
+     *
+     * @return height text adjustment
+     */
+    public int getTextAdjustHeight() {
+        return textAdjustHeight;
+    }
+
+    /**
+     * Set the height text adjustment.
+     *
+     * @param textAdjustHeight the height text adjustment
+     */
+    public void setTextAdjustHeight(final int textAdjustHeight) {
+        synchronized (this) {
+            this.textAdjustHeight = textAdjustHeight;
+            textHeight = fontTextHeight + textAdjustHeight;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the width text adjustment.
+     *
+     * @return width text adjustment
+     */
+    public int getTextAdjustWidth() {
+        return textAdjustWidth;
+    }
+
+    /**
+     * Set the width text adjustment.
+     *
+     * @param textAdjustWidth the width text adjustment
+     */
+    public void setTextAdjustWidth(final int textAdjustWidth) {
+        synchronized (this) {
+            this.textAdjustWidth = textAdjustWidth;
+            textWidth = fontTextWidth + textAdjustWidth;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
     /**
      * Convert a CellAttributes foreground color to an Swing Color.
      *
      * @param attr the text attributes
      * @return the Swing Color
      */
-    private Color attrToForegroundColor(final CellAttributes attr) {
+    public static Color attrToForegroundColor(final CellAttributes attr) {
         int rgb = attr.getForeColorRGB();
         if (rgb >= 0) {
             int red     = (rgb >> 16) & 0xFF;
@@ -798,7 +953,7 @@ public class SwingTerminal extends LogicalScreen
      * @param attr the text attributes
      * @return the Swing Color
      */
-    private Color attrToBackgroundColor(final CellAttributes attr) {
+    public static Color attrToBackgroundColor(final CellAttributes attr) {
         int rgb = attr.getBackColorRGB();
         if (rgb >= 0) {
             int red     = (rgb >> 16) & 0xFF;
@@ -830,13 +985,11 @@ public class SwingTerminal extends LogicalScreen
     }
 
     /**
-     * Figure out what textAdjustX and textAdjustY should be, based on the
-     * location of a vertical bar (to find textAdjustY) and a horizontal bar
-     * (to find textAdjustX).
-     *
-     * @return true if textAdjustX and textAdjustY were guessed at correctly
+     * Figure out what textAdjustX, textAdjustY, textAdjustHeight, and
+     * textAdjustWidth should be, based on the location of a vertical bar and
+     * a horizontal bar.
      */
-    private boolean getFontAdjustments() {
+    private void getFontAdjustments() {
         BufferedImage image = null;
 
         // What SHOULD happen is that the topmost/leftmost white pixel is at
@@ -846,66 +999,73 @@ public class SwingTerminal extends LogicalScreen
         Graphics2D gr2 = null;
         int gr2x = 3;
         int gr2y = 3;
-        image = new BufferedImage(textWidth * 2, textHeight * 2,
+        image = new BufferedImage(fontTextWidth * 2, fontTextHeight * 2,
             BufferedImage.TYPE_INT_ARGB);
 
         gr2 = image.createGraphics();
         gr2.setFont(swing.getFont());
         gr2.setColor(java.awt.Color.BLACK);
-        gr2.fillRect(0, 0, textWidth * 2, textHeight * 2);
+        gr2.fillRect(0, 0, fontTextWidth * 2, fontTextHeight * 2);
         gr2.setColor(java.awt.Color.WHITE);
         char [] chars = new char[1];
+        chars[0] = jexer.bits.GraphicsChars.SINGLE_BAR;
+        gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
         chars[0] = jexer.bits.GraphicsChars.VERTICAL_BAR;
-        gr2.drawChars(chars, 0, 1, gr2x, gr2y + textHeight - maxDescent);
+        gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
         gr2.dispose();
 
-        for (int x = 0; x < textWidth; x++) {
-            for (int y = 0; y < textHeight; y++) {
-
-                /*
-                System.err.println("X: " + x + " Y: " + y + " " +
-                    image.getRGB(x, y));
-                 */
-
-                if ((image.getRGB(x, y) & 0xFFFFFF) != 0) {
-                    textAdjustY = (gr2y - y);
-
-                    // System.err.println("textAdjustY: " + textAdjustY);
-                    x = textWidth;
-                    break;
-                }
-            }
-        }
-
-        gr2 = image.createGraphics();
-        gr2.setFont(swing.getFont());
-        gr2.setColor(java.awt.Color.BLACK);
-        gr2.fillRect(0, 0, textWidth * 2, textHeight * 2);
-        gr2.setColor(java.awt.Color.WHITE);
-        chars[0] = jexer.bits.GraphicsChars.SINGLE_BAR;
-        gr2.drawChars(chars, 0, 1, gr2x, gr2y + textHeight - maxDescent);
-        gr2.dispose();
+        int top = fontTextHeight * 2;
+        int bottom = -1;
+        int left = fontTextWidth * 2;
+        int right = -1;
+        textAdjustX = 0;
+        textAdjustY = 0;
+        textAdjustHeight = 0;
+        textAdjustWidth = 0;
 
-        for (int x = 0; x < textWidth; x++) {
-            for (int y = 0; y < textHeight; y++) {
+        for (int x = 0; x < fontTextWidth * 2; x++) {
+            for (int y = 0; y < fontTextHeight * 2; y++) {
 
                 /*
-                System.err.println("X: " + x + " Y: " + y + " " +
+                System.err.println("X: " + x + " Y: " + y + " " +
                     image.getRGB(x, y));
-                 */
+                */
 
                 if ((image.getRGB(x, y) & 0xFFFFFF) != 0) {
-                    textAdjustX = (gr2x - x);
-
-                    // System.err.println("textAdjustX: " + textAdjustX);
-                    return true;
+                    // Pixel is present.
+                    if (y < top) {
+                        top = y;
+                    }
+                    if (y > bottom) {
+                        bottom = y;
+                    }
+                    if (x < left) {
+                        left = x;
+                    }
+                    if (x > right) {
+                        right = x;
+                    }
                 }
             }
         }
+        if (left < right) {
+            textAdjustX = (gr2x - left);
+            textAdjustWidth = fontTextWidth - (right - left + 1);
+        }
+        if (top < bottom) {
+            textAdjustY = (gr2y - top);
+            textAdjustHeight = fontTextHeight - (bottom - top + 1);
+        }
+        // System.err.println("top " + top + " bottom " + bottom);
+        // System.err.println("left " + left + " right " + right);
 
-        // Something weird happened, don't rely on this function.
-        // System.err.println("getFontAdjustments: false");
-        return false;
+        // Special case: do not believe fonts that claim to be wider than
+        // they are tall.
+        if (fontTextWidth >= fontTextHeight) {
+            textAdjustX = 0;
+            textAdjustWidth = 0;
+            fontTextWidth = fontTextHeight / 2;
+        }
     }
 
     /**
@@ -933,26 +1093,16 @@ public class SwingTerminal extends LogicalScreen
         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;
+        fontTextWidth = (int)Math.round(bounds.getWidth());
+        // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
 
         // This produces the same number, but works better for ugly
         // monospace.
-        textHeight = fm.getMaxAscent() + maxDescent - leading;
+        fontTextHeight = fm.getMaxAscent() + maxDescent - leading;
 
-        if (gotTerminus == true) {
-            textHeight++;
-        }
-
-        if (getFontAdjustments() == false) {
-            // We were unable to programmatically determine textAdjustX and
-            // textAdjustY, so try some guesses based on VM vendor.
-            String runtime = System.getProperty("java.runtime.name");
-            if ((runtime != null) && (runtime.contains("Java(TM)"))) {
-                textAdjustY = -1;
-                textAdjustX = 0;
-            }
-        }
+        getFontAdjustments();
+        textHeight = fontTextHeight + textAdjustHeight;
+        textWidth = fontTextWidth + textAdjustWidth;
 
         if (sessionInfo != null) {
             sessionInfo.setTextCellDimensions(textWidth, textHeight);
@@ -961,10 +1111,57 @@ public class SwingTerminal extends LogicalScreen
     }
 
     /**
-     * Resize to font dimensions.
+     * Resize the physical screen to match the logical screen dimensions.
+     *
+     * @param resizeComponent if true, resize the Swing component
+     */
+    private void resizeToScreen(final boolean resizeComponent) {
+        if (resizeComponent) {
+            swing.setDimensions(textWidth * width, textHeight * height);
+        }
+        clearPhysical();
+    }
+
+    /**
+     * Resize the physical screen to match the logical screen dimensions.
      */
+    @Override
     public void resizeToScreen() {
-        swing.setDimensions(textWidth * width, textHeight * height);
+        resizeToScreen(false);
+    }
+
+    /**
+     * Draw one cell's image to the screen.
+     *
+     * @param gr the Swing Graphics context
+     * @param cell the Cell to draw
+     * @param xPixel the x-coordinate to render to.  0 means the
+     * left-most pixel column.
+     * @param yPixel the y-coordinate to render to.  0 means the top-most
+     * pixel row.
+     */
+    private void drawImage(final Graphics gr, final Cell cell,
+        final int xPixel, final int yPixel) {
+
+        /*
+        System.err.println("drawImage(): " + xPixel + " " + yPixel +
+            " " + cell);
+        */
+
+        // Draw the background rectangle, then the foreground character.
+        assert (cell.isImage());
+        gr.setColor(cell.getBackground());
+        gr.fillRect(xPixel, yPixel, textWidth, textHeight);
+
+        BufferedImage image = cell.getImage();
+        if (image != null) {
+            if (swing.getFrame() != null) {
+                gr.drawImage(image, xPixel, yPixel, swing.getFrame());
+            } else {
+                gr.drawImage(image, xPixel, yPixel, swing.getComponent());
+            }
+            return;
+        }
     }
 
     /**
@@ -1015,8 +1212,7 @@ public class SwingTerminal extends LogicalScreen
             gr2 = (Graphics2D) gr;
         }
 
-        Cell cellColor = new Cell();
-        cellColor.setTo(cell);
+        Cell cellColor = new Cell(cell);
 
         // Check for reverse
         if (cell.isReverse()) {
@@ -1048,8 +1244,7 @@ public class SwingTerminal extends LogicalScreen
 
             // We need a new key that will not be mutated by
             // invertCell().
-            Cell key = new Cell();
-            key.setTo(cell);
+            Cell key = new Cell(cell);
             if (cell.isBlink() && !cursorBlinkVisible) {
                 glyphCacheBlink.put(key, image);
             } else {
@@ -1082,18 +1277,31 @@ public class SwingTerminal extends LogicalScreen
             int xPixel = cursorX * textWidth + left;
             int yPixel = cursorY * textHeight + top;
             Cell lCell = logical[cursorX][cursorY];
+            int cursorWidth = textWidth;
+            switch (lCell.getWidth()) {
+            case SINGLE:
+                // NOP
+                break;
+            case LEFT:
+                cursorWidth *= 2;
+                break;
+            case RIGHT:
+                cursorWidth *= 2;
+                xPixel -= textWidth;
+                break;
+            }
             gr.setColor(attrToForegroundColor(lCell));
             switch (cursorStyle) {
             default:
                 // Fall through...
             case UNDERLINE:
-                gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2);
+                gr.fillRect(xPixel, yPixel + textHeight - 2, cursorWidth, 2);
                 break;
             case BLOCK:
-                gr.fillRect(xPixel, yPixel, textWidth, textHeight);
+                gr.fillRect(xPixel, yPixel, cursorWidth, textHeight);
                 break;
             case OUTLINE:
-                gr.drawRect(xPixel, yPixel, textWidth - 1, textHeight - 1);
+                gr.drawRect(xPixel, yPixel, cursorWidth - 1, textHeight - 1);
                 break;
             }
         }
@@ -1190,7 +1398,11 @@ public class SwingTerminal extends LogicalScreen
                         || reallyCleared
                         || (swing.getFrame() == null)) {
 
-                        drawGlyph(gr, lCell, xPixel, yPixel);
+                        if (lCell.isImage()) {
+                            drawImage(gr, lCell, xPixel, yPixel);
+                        } else {
+                            drawGlyph(gr, lCell, xPixel, yPixel);
+                        }
 
                         // Physical is always updated
                         physical[x][y].setTo(lCell);
@@ -1260,7 +1472,11 @@ public class SwingTerminal extends LogicalScreen
                                 && cursorVisible)
                             || (lCell.isBlink())
                         ) {
-                            drawGlyph(gr, lCell, xPixel, yPixel);
+                            if (lCell.isImage()) {
+                                drawImage(gr, lCell, xPixel, yPixel);
+                            } else {
+                                drawGlyph(gr, lCell, xPixel, yPixel);
+                            }
                             physical[x][y].setTo(lCell);
                         }
                     }
@@ -1387,6 +1603,15 @@ public class SwingTerminal extends LogicalScreen
         return sessionInfo;
     }
 
+    /**
+     * Getter for the underlying Swing component.
+     *
+     * @return the SwingComponent
+     */
+    public SwingComponent getSwingComponent() {
+        return swing;
+    }
+
     // ------------------------------------------------------------------------
     // KeyListener ------------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -1654,9 +1879,9 @@ public class SwingTerminal extends LogicalScreen
      * @param event window event received
      */
     public void windowClosing(final WindowEvent event) {
-        // Drop a cmAbort and walk away
+        // Drop a cmBackendDisconnect and walk away
         synchronized (eventQueue) {
-            eventQueue.add(new TCommandEvent(cmAbort));
+            eventQueue.add(new TCommandEvent(cmBackendDisconnect));
             resetBlinkTimer();
         }
         if (listener != null) {
@@ -1746,6 +1971,12 @@ public class SwingTerminal extends LogicalScreen
             return;
         }
 
+        if (sessionInfo == null) {
+            // This is the initial component resize in construction, bail
+            // out.
+            return;
+        }
+
         // Drop a new TResizeEvent into the queue
         sessionInfo.queryWindowSize();
         synchronized (eventQueue) {