X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fio%2FSwingScreen.java;fp=src%2Fjexer%2Fio%2FSwingScreen.java;h=0000000000000000000000000000000000000000;hb=42873e30bf487bc0b695d60652dba44f82185dbb;hp=ee8467dd076008970784d1225609f516cbc9a469;hpb=2fef9c6eaa2ba32e7a14ea1e469ec471b05019a2;p=fanfix.git diff --git a/src/jexer/io/SwingScreen.java b/src/jexer/io/SwingScreen.java deleted file mode 100644 index ee8467d..0000000 --- a/src/jexer/io/SwingScreen.java +++ /dev/null @@ -1,997 +0,0 @@ -/* - * Jexer - Java Text User Interface - * - * The MIT License (MIT) - * - * Copyright (C) 2017 Kevin Lamonte - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * @author Kevin Lamonte [kevin.lamonte@gmail.com] - * @version 1 - */ -package jexer.io; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.BufferStrategy; -import java.io.InputStream; -import java.util.Date; -import java.util.HashMap; -import javax.swing.JFrame; -import javax.swing.SwingUtilities; - -import jexer.bits.Cell; -import jexer.bits.CellAttributes; -import jexer.session.SwingSessionInfo; - -/** - * This Screen implementation draws to a Java Swing JFrame. - */ -public final class SwingScreen extends Screen { - - /** - * If true, use triple buffering thread. - */ - private static boolean tripleBuffer = true; - - /** - * Cursor style to draw. - */ - public enum CursorStyle { - /** - * Use an underscore for the cursor. - */ - UNDERLINE, - - /** - * Use a solid block for the cursor. - */ - BLOCK, - - /** - * Use an outlined block for the cursor. - */ - OUTLINE - } - - 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 Swing 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; - } - - /** - * SwingFrame is our top-level hook into the Swing system. - */ - class SwingFrame extends JFrame { - - /** - * Serializable version. - */ - private static final long serialVersionUID = 1; - - /** - * The terminus font resource filename. - */ - private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf"; - - /** - * The BufferStrategy object needed for triple-buffering. - */ - private BufferStrategy bufferStrategy; - - /** - * A cache of previously-rendered glyphs for blinking text, when it - * is not visible. - */ - private HashMap glyphCacheBlink; - - /** - * A cache of previously-rendered glyphs for non-blinking, or - * blinking-and-visible, text. - */ - private HashMap glyphCache; - - /** - * The TUI Screen data. - */ - SwingScreen screen; - - /** - * If true, we were successful getting Terminus. - */ - private boolean gotTerminus = false; - - /** - * Width of a character cell. - */ - private int textWidth = 1; - - /** - * Height of a character cell. - */ - private int textHeight = 1; - - /** - * Descent of a character cell. - */ - private int maxDescent = 0; - - /** - * System-dependent Y adjustment for text in the character cell. - */ - private int textAdjustY = 0; - - /** - * System-dependent X adjustment for text in the character cell. - */ - private int textAdjustX = 0; - - /** - * Top pixel absolute location. - */ - private int top = 30; - - /** - * Left pixel absolute location. - */ - private int left = 30; - - /** - * The cursor style to draw. - */ - private CursorStyle cursorStyle = CursorStyle.UNDERLINE; - - /** - * The number of millis to wait before switching the blink from - * visible to invisible. - */ - private long blinkMillis = 500; - - /** - * If true, the cursor should be visible right now based on the blink - * time. - */ - private boolean cursorBlinkVisible = true; - - /** - * The time that the blink last flipped from visible to invisible or - * from invisible to visible. - */ - private long lastBlinkTime = 0; - - /** - * Convert a CellAttributes foreground color to an Swing Color. - * - * @param attr the text attributes - * @return the Swing Color - */ - private Color attrToForegroundColor(final CellAttributes attr) { - if (attr.isBold()) { - 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 Swing Color. - * - * @param attr the text attributes - * @return the Swing Color - */ - private Color attrToBackgroundColor(final CellAttributes attr) { - 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 - * @param fontSize the size in points. Good values to pick are: 16, - * 20, 22, and 24. - */ - public SwingFrame(final SwingScreen screen, final int fontSize) { - this.screen = screen; - 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; - } - - if (System.getProperty("jexer.Swing.tripleBuffer") != null) { - if (System.getProperty("jexer.Swing.tripleBuffer"). - equals("false")) { - - SwingScreen.tripleBuffer = false; - } - } - - setTitle("Jexer Application"); - setBackground(Color.black); - - try { - // Always try to use Terminus, the one decent font. - 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); - setFont(terminus); - gotTerminus = true; - } catch (Exception e) { - e.printStackTrace(); - // setFont(new Font("Liberation Mono", Font.PLAIN, 24)); - setFont(new Font(Font.MONOSPACED, Font.PLAIN, fontSize)); - } - pack(); - - // Kill the X11 cursor - // Transparent 16 x 16 pixel cursor image. - BufferedImage cursorImg = new BufferedImage(16, 16, - BufferedImage.TYPE_INT_ARGB); - // Create a new blank cursor. - Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( - cursorImg, new Point(0, 0), "blank cursor"); - setCursor(blankCursor); - - // Be capable of seeing Tab / Shift-Tab - setFocusTraversalKeysEnabled(false); - - // Save the text cell width/height - getFontDimensions(); - - // Cache glyphs as they are rendered - glyphCacheBlink = new HashMap(); - glyphCache = new HashMap(); - - // Setup triple-buffering - if (SwingScreen.tripleBuffer) { - setIgnoreRepaint(true); - createBufferStrategy(3); - bufferStrategy = getBufferStrategy(); - } - } - - /** - * 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 - */ - private boolean getFontAdjustments() { - BufferedImage image = null; - - // What SHOULD happen is that the topmost/leftmost white pixel is - // at position (gr2x, gr2y). But it might also be off by a pixel - // in either direction. - - Graphics2D gr2 = null; - int gr2x = 3; - int gr2y = 3; - image = new BufferedImage(textWidth * 2, textHeight * 2, - BufferedImage.TYPE_INT_ARGB); - - gr2 = image.createGraphics(); - gr2.setFont(getFont()); - gr2.setColor(java.awt.Color.BLACK); - gr2.fillRect(0, 0, textWidth * 2, textHeight * 2); - gr2.setColor(java.awt.Color.WHITE); - char [] chars = new char[1]; - chars[0] = jexer.bits.GraphicsChars.VERTICAL_BAR; - gr2.drawChars(chars, 0, 1, gr2x, gr2y + textHeight - 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(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(); - - 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) { - textAdjustX = (gr2x - x); - - // System.err.println("textAdjustX: " + textAdjustX); - return true; - } - } - } - - // Something weird happened, don't rely on this function. - // System.err.println("getFontAdjustments: false"); - return false; - } - - - /** - * Figure out my font dimensions. - */ - private void getFontDimensions() { - 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 produces the same number, but works better for ugly - // monospace. - textHeight = 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; - } - } - } - - /** - * Resize to font dimensions. - */ - public void resizeToScreen() { - // 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); - } - - /** - * Update redraws the whole screen. - * - * @param gr the Swing Graphics context - */ - @Override - public void update(final Graphics gr) { - // The default update clears the area. Don't do that, instead - // just paint it directly. - paint(gr); - } - - /** - * Draw one glyph 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 drawGlyph(final Graphics gr, final Cell cell, - final int xPixel, final int yPixel) { - - BufferedImage image = null; - if (cell.isBlink() && !cursorBlinkVisible) { - image = glyphCacheBlink.get(cell); - } else { - image = glyphCache.get(cell); - } - if (image != null) { - gr.drawImage(image, xPixel, yPixel, this); - return; - } - - // Generate glyph and draw it. - Graphics2D gr2 = null; - int gr2x = xPixel; - int gr2y = yPixel; - if (tripleBuffer) { - image = new BufferedImage(textWidth, textHeight, - BufferedImage.TYPE_INT_ARGB); - gr2 = image.createGraphics(); - gr2.setFont(getFont()); - gr2x = 0; - gr2y = 0; - } else { - gr2 = (Graphics2D) gr; - } - - Cell cellColor = new Cell(); - cellColor.setTo(cell); - - // Check for reverse - if (cell.isReverse()) { - cellColor.setForeColor(cell.getBackColor()); - cellColor.setBackColor(cell.getForeColor()); - } - - // Draw the background rectangle, then the foreground character. - gr2.setColor(attrToBackgroundColor(cellColor)); - gr2.fillRect(gr2x, gr2y, textWidth, textHeight); - - // Handle blink and underline - if (!cell.isBlink() - || (cell.isBlink() && cursorBlinkVisible) - ) { - gr2.setColor(attrToForegroundColor(cellColor)); - char [] chars = new char[1]; - chars[0] = cell.getChar(); - gr2.drawChars(chars, 0, 1, gr2x + textAdjustX, - gr2y + textHeight - maxDescent + textAdjustY); - - if (cell.isUnderline()) { - gr2.fillRect(gr2x, gr2y + textHeight - 2, textWidth, 2); - } - } - - if (tripleBuffer) { - gr2.dispose(); - - // We need a new key that will not be mutated by - // invertCell(). - Cell key = new Cell(); - key.setTo(cell); - if (cell.isBlink() && !cursorBlinkVisible) { - glyphCacheBlink.put(key, image); - } else { - glyphCache.put(key, image); - } - - gr.drawImage(image, xPixel, yPixel, this); - } - - } - - /** - * Check if the cursor is visible, and if so draw it. - * - * @param gr the Swing Graphics context - */ - private void drawCursor(final Graphics gr) { - - if (cursorVisible - && (cursorY <= screen.height - 1) - && (cursorX <= screen.width - 1) - && cursorBlinkVisible - ) { - int xPixel = cursorX * textWidth + left; - int yPixel = cursorY * textHeight + top; - Cell lCell = screen.logical[cursorX][cursorY]; - gr.setColor(attrToForegroundColor(lCell)); - switch (cursorStyle) { - default: - // Fall through... - case UNDERLINE: - gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2); - break; - case BLOCK: - gr.fillRect(xPixel, yPixel, textWidth, textHeight); - break; - case OUTLINE: - gr.drawRect(xPixel, yPixel, textWidth - 1, textHeight - 1); - break; - } - } - } - - /** - * Paint redraws the whole screen. - * - * @param gr the Swing Graphics context - */ - @Override - public void paint(final Graphics gr) { - // Do nothing until the screen reference has been set. - if (screen == null) { - return; - } - if (screen.frame == null) { - return; - } - - // See if it is time to flip the blink time. - long nowTime = (new Date()).getTime(); - if (nowTime > blinkMillis + lastBlinkTime) { - lastBlinkTime = nowTime; - cursorBlinkVisible = !cursorBlinkVisible; - } - - int xCellMin = 0; - int xCellMax = screen.width; - int yCellMin = 0; - int yCellMax = screen.height; - - Rectangle bounds = gr.getClipBounds(); - if (bounds != null) { - // Only update what is in the bounds - xCellMin = screen.textColumn(bounds.x); - xCellMax = screen.textColumn(bounds.x + bounds.width); - if (xCellMax > screen.width) { - xCellMax = screen.width; - } - if (xCellMin >= xCellMax) { - xCellMin = xCellMax - 2; - } - if (xCellMin < 0) { - xCellMin = 0; - } - yCellMin = screen.textRow(bounds.y); - yCellMax = screen.textRow(bounds.y + bounds.height); - if (yCellMax > screen.height) { - yCellMax = screen.height; - } - if (yCellMin >= yCellMax) { - yCellMin = yCellMax - 2; - } - if (yCellMin < 0) { - yCellMin = 0; - } - } else { - // We need a total repaint - reallyCleared = true; - } - - // Prevent updates to the screen's data from the TApplication - // threads. - synchronized (screen) { - /* - 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++) { - - int xPixel = x * textWidth + left; - int yPixel = y * textHeight + top; - - Cell lCell = screen.logical[x][y]; - Cell pCell = screen.physical[x][y]; - - if (!lCell.equals(pCell) - || lCell.isBlink() - || reallyCleared) { - - drawGlyph(gr, lCell, xPixel, yPixel); - - // Physical is always updated - physical[x][y].setTo(lCell); - } - } - } - drawCursor(gr); - - dirty = false; - reallyCleared = false; - } // synchronized (screen) - } - - } // class SwingFrame - - /** - * The raw Swing JFrame. Note package private access. - */ - SwingFrame frame; - - /** - * Restore terminal to normal state. - */ - public void shutdown() { - frame.dispose(); - } - - /** - * Public constructor. - * - * @param windowWidth the number of text columns to start with - * @param windowHeight the number of text rows to start with - * @param fontSize the size in points. Good values to pick are: 16, 20, - * 22, and 24. - */ - public SwingScreen(final int windowWidth, final int windowHeight, - final int fontSize) { - - try { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - SwingScreen.this.frame = new SwingFrame(SwingScreen.this, - fontSize); - SwingScreen.this.sessionInfo = - new SwingSessionInfo(SwingScreen.this.frame, - frame.textWidth, frame.textHeight, - windowWidth, windowHeight); - - SwingScreen.this.setDimensions(sessionInfo.getWindowWidth(), - sessionInfo.getWindowHeight()); - - SwingScreen.this.frame.resizeToScreen(); - SwingScreen.this.frame.setVisible(true); - } - }); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * The sessionInfo. - */ - private SwingSessionInfo sessionInfo; - - /** - * Create the SwingSessionInfo. Note package private access. - * - * @return the sessionInfo - */ - SwingSessionInfo getSessionInfo() { - return sessionInfo; - } - - /** - * Push the logical screen to the physical device. - */ - @Override - public void flushPhysical() { - - /* - System.err.printf("flushPhysical(): reallyCleared %s dirty %s\n", - reallyCleared, dirty); - */ - - // If reallyCleared is set, we have to draw everything. - if ((frame.bufferStrategy != null) && (reallyCleared == true)) { - // Triple-buffering: we have to redraw everything on this thread. - Graphics gr = frame.bufferStrategy.getDrawGraphics(); - frame.paint(gr); - gr.dispose(); - frame.bufferStrategy.show(); - // sync() doesn't seem to help the tearing for me. - // Toolkit.getDefaultToolkit().sync(); - return; - } else if ((frame.bufferStrategy == null) && (reallyCleared == true)) { - // Repaint everything on the Swing thread. - frame.repaint(); - return; - } - - // Do nothing if nothing happened. - if (!dirty) { - return; - } - - if (frame.bufferStrategy != null) { - // See if it is time to flip the blink time. - long nowTime = (new Date()).getTime(); - if (nowTime > frame.blinkMillis + frame.lastBlinkTime) { - frame.lastBlinkTime = nowTime; - frame.cursorBlinkVisible = !frame.cursorBlinkVisible; - } - - Graphics gr = frame.bufferStrategy.getDrawGraphics(); - - synchronized (this) { - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - Cell lCell = logical[x][y]; - Cell pCell = physical[x][y]; - - int xPixel = x * frame.textWidth + frame.left; - int yPixel = y * frame.textHeight + frame.top; - - if (!lCell.equals(pCell) - || ((x == cursorX) - && (y == cursorY) - && cursorVisible) - || (lCell.isBlink()) - ) { - frame.drawGlyph(gr, lCell, xPixel, yPixel); - physical[x][y].setTo(lCell); - } - } - } - frame.drawCursor(gr); - } // synchronized (this) - - gr.dispose(); - frame.bufferStrategy.show(); - // sync() doesn't seem to help the tearing for me. - // Toolkit.getDefaultToolkit().sync(); - return; - } - - // Swing thread version: request a repaint, but limit it to the area - // that has changed. - - // Find the minimum-size damaged region. - int xMin = frame.getWidth(); - int xMax = 0; - int yMin = frame.getHeight(); - int yMax = 0; - - synchronized (this) { - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - Cell lCell = logical[x][y]; - Cell pCell = physical[x][y]; - - int xPixel = x * frame.textWidth + frame.left; - int yPixel = y * frame.textHeight + frame.top; - - if (!lCell.equals(pCell) - || ((x == cursorX) - && (y == cursorY) - && cursorVisible) - || lCell.isBlink() - ) { - if (xPixel < xMin) { - xMin = xPixel; - } - if (xPixel + frame.textWidth > xMax) { - xMax = xPixel + frame.textWidth; - } - if (yPixel < yMin) { - yMin = yPixel; - } - if (yPixel + frame.textHeight > yMax) { - yMax = yPixel + frame.textHeight; - } - } - } - } - } - if (xMin + frame.textWidth >= xMax) { - xMax += frame.textWidth; - } - if (yMin + frame.textHeight >= yMax) { - yMax += frame.textHeight; - } - - // Repaint the desired area - /* - System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax, - yMin, yMax); - */ - if (frame.bufferStrategy != null) { - // This path should never be taken, but is left here for - // completeness. - Graphics gr = frame.bufferStrategy.getDrawGraphics(); - Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin, - yMax - yMin); - gr.setClip(bounds); - frame.paint(gr); - gr.dispose(); - frame.bufferStrategy.show(); - // sync() doesn't seem to help the tearing for me. - // Toolkit.getDefaultToolkit().sync(); - } else { - // Repaint on the Swing thread. - frame.repaint(xMin, yMin, xMax - xMin, yMax - yMin); - } - } - - /** - * Put the cursor at (x,y). - * - * @param visible if true, the cursor should be visible - * @param x column coordinate to put the cursor on - * @param y row coordinate to put the cursor on - */ - @Override - public void putCursor(final boolean visible, final int x, final int y) { - - if ((visible == cursorVisible) && ((x == cursorX) && (y == cursorY))) { - // See if it is time to flip the blink time. - long nowTime = (new Date()).getTime(); - if (nowTime < frame.blinkMillis + frame.lastBlinkTime) { - // Nothing has changed, so don't do anything. - return; - } - } - - if (cursorVisible - && (cursorY <= height - 1) - && (cursorX <= width - 1) - ) { - // Make the current cursor position dirty - if (physical[cursorX][cursorY].getChar() == 'Q') { - physical[cursorX][cursorY].setChar('X'); - } else { - physical[cursorX][cursorY].setChar('Q'); - } - } - - super.putCursor(visible, x, y); - } - - /** - * Convert pixel column position to text cell column position. - * - * @param x pixel column position - * @return text cell column position - */ - public int textColumn(final int x) { - return ((x - frame.left) / frame.textWidth); - } - - /** - * Convert pixel row position to text cell row position. - * - * @param y pixel row position - * @return text cell row position - */ - public int textRow(final int y) { - return ((y - frame.top) / frame.textHeight); - } - - /** - * Set the window title. - * - * @param title the new title - */ - public void setTitle(final String title) { - frame.setTitle(title); - } - -}