X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fbackend%2FSwingTerminal.java;h=0727efc894d5832dc515fa1342d622a2eac885df;hb=505be508ae7d3fb48122be548b310a238cfb91eb;hp=3f5b58e3577707dd5f0474fde7f6819390909a4d;hpb=a2855f1dd3ca42b61ac4c47a18e2253ea32dd56e;p=fanfix.git diff --git a/src/jexer/backend/SwingTerminal.java b/src/jexer/backend/SwingTerminal.java index 3f5b58e..0727efc 100644 --- a/src/jexer/backend/SwingTerminal.java +++ b/src/jexer/backend/SwingTerminal.java @@ -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"), @@ -36,6 +36,7 @@ import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; @@ -51,8 +52,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 +99,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. @@ -117,7 +118,12 @@ public class SwingTerminal extends LogicalScreen /** * Use an outlined block for the cursor. */ - OUTLINE + OUTLINE, + + /** + * Use a vertical bar for the cursor. + */ + VERTICAL_BAR, } // ------------------------------------------------------------------------ @@ -164,11 +170,6 @@ public class SwingTerminal extends LogicalScreen */ private Map glyphCache; - /** - * If true, we were successful getting Terminus. - */ - private boolean gotTerminus = false; - /** * If true, we were successful at getting the font dimensions. */ @@ -187,12 +188,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 +220,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 +307,13 @@ public class SwingTerminal extends LogicalScreen // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Static constructor. + */ + static { + setDOSColors(); + } + /** * Public constructor creates a new JFrame to render to. * @@ -301,27 +329,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 +396,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 +406,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 +423,7 @@ public class SwingTerminal extends LogicalScreen mouse1 = false; mouse2 = false; mouse3 = false; - eventQueue = new LinkedList(); + eventQueue = new ArrayList(); // Add listeners to Swing. swing.addKeyListener(this); @@ -440,18 +450,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 +516,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 +526,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 +536,7 @@ public class SwingTerminal extends LogicalScreen mouse1 = false; mouse2 = false; mouse3 = false; - eventQueue = new LinkedList(); + eventQueue = new ArrayList(); // Add listeners to Swing. swing.addKeyListener(this); @@ -584,7 +585,6 @@ public class SwingTerminal extends LogicalScreen swing.getBufferStrategy().show(); Toolkit.getDefaultToolkit().sync(); } while (swing.getBufferStrategy().contentsLost()); - } else { // Non-triple-buffered, call drawToSwing() once drawToSwing(); @@ -639,10 +639,58 @@ 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; + } else if (cursorStyleString.equals("verticalbar")) { + cursorStyle = CursorStyle.VERTICAL_BAR; + } + + // Pull the system property for triple buffering. + if (System.getProperty("jexer.Swing.tripleBuffer", + "true").equals("true") + ) { + SwingComponent.tripleBuffer = true; + } else { + SwingComponent.tripleBuffer = false; + } + + // Set custom colors + setCustomSystemColors(); + } + // ------------------------------------------------------------------------ // 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. */ @@ -670,6 +718,62 @@ public class SwingTerminal extends LogicalScreen dosColors = true; } + /** + * Setup Swing colors to match those provided in system properties. + */ + private static void setCustomSystemColors() { + synchronized (SwingTerminal.class) { + MYBLACK = getCustomColor("jexer.Swing.color0", MYBLACK); + MYRED = getCustomColor("jexer.Swing.color1", MYRED); + MYGREEN = getCustomColor("jexer.Swing.color2", MYGREEN); + MYYELLOW = getCustomColor("jexer.Swing.color3", MYYELLOW); + MYBLUE = getCustomColor("jexer.Swing.color4", MYBLUE); + MYMAGENTA = getCustomColor("jexer.Swing.color5", MYMAGENTA); + MYCYAN = getCustomColor("jexer.Swing.color6", MYCYAN); + MYWHITE = getCustomColor("jexer.Swing.color7", MYWHITE); + MYBOLD_BLACK = getCustomColor("jexer.Swing.color8", MYBOLD_BLACK); + MYBOLD_RED = getCustomColor("jexer.Swing.color9", MYBOLD_RED); + MYBOLD_GREEN = getCustomColor("jexer.Swing.color10", MYBOLD_GREEN); + MYBOLD_YELLOW = getCustomColor("jexer.Swing.color11", MYBOLD_YELLOW); + MYBOLD_BLUE = getCustomColor("jexer.Swing.color12", MYBOLD_BLUE); + MYBOLD_MAGENTA = getCustomColor("jexer.Swing.color13", MYBOLD_MAGENTA); + MYBOLD_CYAN = getCustomColor("jexer.Swing.color14", MYBOLD_CYAN); + MYBOLD_WHITE = getCustomColor("jexer.Swing.color15", MYBOLD_WHITE); + } + } + + /** + * Setup one Swing color to match the RGB value provided in system + * properties. + * + * @param key the system property key + * @param defaultColor the default color to return if key is not set, or + * incorrect + * @return a color from the RGB string, or defaultColor + */ + private static Color getCustomColor(final String key, + final Color defaultColor) { + + String rgb = System.getProperty(key); + if (rgb == null) { + return defaultColor; + } + if (rgb.startsWith("#")) { + rgb = rgb.substring(1); + } + int rgbInt = 0; + try { + rgbInt = Integer.parseInt(rgb, 16); + } catch (NumberFormatException e) { + return defaultColor; + } + Color color = new Color((rgbInt & 0xFF0000) >>> 16, + (rgbInt & 0x00FF00) >>> 8, + (rgbInt & 0x0000FF)); + + return color; + } + /** * Get the number of millis to wait before switching the blink from * visible to invisible. @@ -681,6 +785,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 +820,62 @@ 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(); - glyphCache = new HashMap(); - resizeToScreen(); + if (!SwingUtilities.isEventDispatchThread()) { + // Not in the Swing thread: force this inside the Swing thread. + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + synchronized (this) { + SwingTerminal.this.font = font; + getFontDimensions(); + swing.setFont(font); + glyphCacheBlink = new HashMap(); + glyphCache = new HashMap(); + resizeToScreen(true); + } + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (java.lang.reflect.InvocationTargetException e) { + e.printStackTrace(); + } + } else { + synchronized (this) { + SwingTerminal.this.font = font; + getFontDimensions(); + swing.setFont(font); + glyphCacheBlink = new HashMap(); + glyphCache = new HashMap(); + 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 +883,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(); + glyphCache = new HashMap(); + 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(); + glyphCache = new HashMap(); + 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(); + glyphCache = new HashMap(); + 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(); + glyphCache = new HashMap(); + 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 +1040,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 +1072,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 +1086,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("H 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 +1180,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 +1198,68 @@ 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()); + + // Enable anti-aliasing + if (gr instanceof Graphics2D) { + ((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + ((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + } + + 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, getTextWidth(), + getTextHeight(), swing.getFrame()); + } else { + gr.drawImage(image, xPixel, yPixel, getTextWidth(), + getTextHeight(),swing.getComponent()); + } + return; + } } /** @@ -983,7 +1278,7 @@ public class SwingTerminal extends LogicalScreen /* System.err.println("drawGlyph(): " + xPixel + " " + yPixel + " " + cell); - */ + */ BufferedImage image = null; if (cell.isBlink() && !cursorBlinkVisible) { @@ -1015,8 +1310,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()) { @@ -1024,6 +1318,17 @@ public class SwingTerminal extends LogicalScreen cellColor.setBackColor(cell.getForeColor()); } + // Enable anti-aliasing + if ((gr instanceof Graphics2D) && (swing.getFrame() != null)) { + // Anti-aliasing on JComponent makes the hash character disappear + // for Terminus font, and also kills performance. Only enable it + // for JFrame. + ((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + ((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + } + // Draw the background rectangle, then the foreground character. gr2.setColor(attrToBackgroundColor(cellColor)); gr2.fillRect(gr2x, gr2y, textWidth, textHeight); @@ -1033,9 +1338,8 @@ public class SwingTerminal extends LogicalScreen || (cell.isBlink() && cursorBlinkVisible) ) { gr2.setColor(attrToForegroundColor(cellColor)); - char [] chars = new char[1]; - chars[0] = cell.getChar(); - gr2.drawChars(chars, 0, 1, gr2x + textAdjustX, + char [] chars = Character.toChars(cell.getChar()); + gr2.drawChars(chars, 0, chars.length, gr2x + textAdjustX, gr2y + textHeight - maxDescent + textAdjustY); if (cell.isUnderline()) { @@ -1048,8 +1352,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 +1385,34 @@ 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; + case VERTICAL_BAR: + gr.fillRect(xPixel, yPixel, 2, textHeight); break; } } @@ -1141,7 +1460,7 @@ public class SwingTerminal extends LogicalScreen if (bounds != null) { // Only update what is in the bounds xCellMin = textColumn(bounds.x); - xCellMax = textColumn(bounds.x + bounds.width); + xCellMax = textColumn(bounds.x + bounds.width) + 1; if (xCellMax > width) { xCellMax = width; } @@ -1152,7 +1471,7 @@ public class SwingTerminal extends LogicalScreen xCellMin = 0; } yCellMin = textRow(bounds.y); - yCellMax = textRow(bounds.y + bounds.height); + yCellMax = textRow(bounds.y + bounds.height) + 1; if (yCellMax > height) { yCellMax = height; } @@ -1174,7 +1493,7 @@ public class SwingTerminal extends LogicalScreen /* System.err.printf("bounds %s X %d %d Y %d %d\n", bounds, xCellMin, xCellMax, yCellMin, yCellMax); - */ + */ for (int y = yCellMin; y < yCellMax; y++) { for (int x = xCellMin; x < xCellMax; x++) { @@ -1190,7 +1509,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 +1583,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); } } @@ -1434,13 +1761,16 @@ public class SwingTerminal extends LogicalScreen } else { ch = key.getKeyChar(); } - alt = key.isAltDown(); + // Both meta and alt count as alt, thanks to Mac using alt for + // "symbols" so meta ("command") is the only other modifier left. + alt = key.isAltDown() | key.isMetaDown(); ctrl = key.isControlDown(); shift = key.isShiftDown(); /* System.err.printf("Swing Key: %s\n", key); System.err.printf(" isKey: %s\n", isKey); + System.err.printf(" meta: %s\n", key.isMetaDown()); System.err.printf(" alt: %s\n", alt); System.err.printf(" ctrl: %s\n", ctrl); System.err.printf(" shift: %s\n", shift); @@ -1613,6 +1943,7 @@ public class SwingTerminal extends LogicalScreen break; default: if (!alt && ctrl && !shift) { + // Control character, replace ch with 'A', 'B', etc. ch = KeyEvent.getKeyText(key.getKeyCode()).charAt(0); } // Not a special key, put it together @@ -1663,9 +1994,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) { @@ -1755,6 +2086,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) { @@ -1788,6 +2125,10 @@ public class SwingTerminal extends LogicalScreen boolean eventMouse1 = false; boolean eventMouse2 = false; boolean eventMouse3 = false; + boolean eventAlt = false; + boolean eventCtrl = false; + boolean eventShift = false; + if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { eventMouse1 = true; } @@ -1797,6 +2138,16 @@ public class SwingTerminal extends LogicalScreen if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { eventMouse3 = true; } + if ((modifiers & MouseEvent.ALT_DOWN_MASK) != 0) { + eventAlt = true; + } + if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { + eventCtrl = true; + } + if ((modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0) { + eventShift = true; + } + mouse1 = eventMouse1; mouse2 = eventMouse2; mouse3 = eventMouse3; @@ -1804,7 +2155,8 @@ public class SwingTerminal extends LogicalScreen int y = textRow(mouse.getY()); TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION, - x, y, x, y, mouse1, mouse2, mouse3, false, false); + x, y, x, y, mouse1, mouse2, mouse3, false, false, + eventAlt, eventCtrl, eventShift); synchronized (eventQueue) { eventQueue.add(mouseEvent); @@ -1832,8 +2184,24 @@ public class SwingTerminal extends LogicalScreen oldMouseX = x; oldMouseY = y; + boolean eventAlt = false; + boolean eventCtrl = false; + boolean eventShift = false; + + int modifiers = mouse.getModifiersEx(); + if ((modifiers & MouseEvent.ALT_DOWN_MASK) != 0) { + eventAlt = true; + } + if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { + eventCtrl = true; + } + if ((modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0) { + eventShift = true; + } + TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION, - x, y, x, y, mouse1, mouse2, mouse3, false, false); + x, y, x, y, mouse1, mouse2, mouse3, false, false, + eventAlt, eventCtrl, eventShift); synchronized (eventQueue) { eventQueue.add(mouseEvent); @@ -1865,7 +2233,7 @@ public class SwingTerminal extends LogicalScreen * @param mouse mouse event received */ public void mouseEntered(final MouseEvent mouse) { - // Ignore + swing.requestFocusInWindow(); } /** @@ -1887,6 +2255,10 @@ public class SwingTerminal extends LogicalScreen boolean eventMouse1 = false; boolean eventMouse2 = false; boolean eventMouse3 = false; + boolean eventAlt = false; + boolean eventCtrl = false; + boolean eventShift = false; + if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { eventMouse1 = true; } @@ -1896,6 +2268,16 @@ public class SwingTerminal extends LogicalScreen if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { eventMouse3 = true; } + if ((modifiers & MouseEvent.ALT_DOWN_MASK) != 0) { + eventAlt = true; + } + if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { + eventCtrl = true; + } + if ((modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0) { + eventShift = true; + } + mouse1 = eventMouse1; mouse2 = eventMouse2; mouse3 = eventMouse3; @@ -1903,7 +2285,8 @@ public class SwingTerminal extends LogicalScreen int y = textRow(mouse.getY()); TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN, - x, y, x, y, mouse1, mouse2, mouse3, false, false); + x, y, x, y, mouse1, mouse2, mouse3, false, false, + eventAlt, eventCtrl, eventShift); synchronized (eventQueue) { eventQueue.add(mouseEvent); @@ -1926,6 +2309,10 @@ public class SwingTerminal extends LogicalScreen boolean eventMouse1 = false; boolean eventMouse2 = false; boolean eventMouse3 = false; + boolean eventAlt = false; + boolean eventCtrl = false; + boolean eventShift = false; + if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { eventMouse1 = true; } @@ -1935,6 +2322,16 @@ public class SwingTerminal extends LogicalScreen if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { eventMouse3 = true; } + if ((modifiers & MouseEvent.ALT_DOWN_MASK) != 0) { + eventAlt = true; + } + if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { + eventCtrl = true; + } + if ((modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0) { + eventShift = true; + } + if (mouse1) { mouse1 = false; eventMouse1 = true; @@ -1951,7 +2348,8 @@ public class SwingTerminal extends LogicalScreen int y = textRow(mouse.getY()); TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_UP, - x, y, x, y, eventMouse1, eventMouse2, eventMouse3, false, false); + x, y, x, y, eventMouse1, eventMouse2, eventMouse3, false, false, + eventAlt, eventCtrl, eventShift); synchronized (eventQueue) { eventQueue.add(mouseEvent); @@ -1980,6 +2378,10 @@ public class SwingTerminal extends LogicalScreen boolean eventMouse3 = false; boolean mouseWheelUp = false; boolean mouseWheelDown = false; + boolean eventAlt = false; + boolean eventCtrl = false; + boolean eventShift = false; + if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { eventMouse1 = true; } @@ -1989,6 +2391,16 @@ public class SwingTerminal extends LogicalScreen if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { eventMouse3 = true; } + if ((modifiers & MouseEvent.ALT_DOWN_MASK) != 0) { + eventAlt = true; + } + if ((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { + eventCtrl = true; + } + if ((modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0) { + eventShift = true; + } + mouse1 = eventMouse1; mouse2 = eventMouse2; mouse3 = eventMouse3; @@ -2002,7 +2414,8 @@ public class SwingTerminal extends LogicalScreen } TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN, - x, y, x, y, mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown); + x, y, x, y, mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown, + eventAlt, eventCtrl, eventShift); synchronized (eventQueue) { eventQueue.add(mouseEvent);