📄 flashterminal.java
字号:
/* * This file is part of "The Java Telnet Application". * * (c) Matthias L. Jugel, Marcus Mei遪er 1996-2002. All Rights Reserved. * * Please visit http://javatelnet.org/ for updates and contact. * * --LICENSE NOTICE-- * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * --LICENSE NOTICE-- * */package de.mud.flash;import de.mud.terminal.VDUBuffer;import de.mud.terminal.VDUDisplay;import de.mud.terminal.VDUInput;import org.jdom.Element;import org.jdom.JDOMException;import org.jdom.Text;import org.jdom.input.SAXBuilder;import org.jdom.output.XMLOutputter;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.StringReader;import java.net.Socket;import java.util.Iterator;public class FlashTerminal implements VDUDisplay, Runnable { private final static int debug = 0; private boolean simpleMode = true; private boolean terminalReady = false; private VDUBuffer buffer; private BufferedWriter writer; private BufferedReader reader; /** A list of colors used for representation of the display */ private String color[] = {"#000000", "#ff0000", "#00ff00", "#ffff00", "#0000ff", "#ff00ff", "#00ffff", "#ffffff", null, // bold color null, // inverted color }; public void start(Socket flashSocket) { try { if (debug > 0) System.err.println("FlashTerminal: got connection: " + flashSocket); writer = new BufferedWriter(new OutputStreamWriter(flashSocket.getOutputStream())); reader = new BufferedReader(new InputStreamReader(flashSocket.getInputStream())); (new Thread(this)).run(); } catch (IOException e) { System.err.println("FlashTerminal: unable to accept connection: " + e); } } protected void disconnect() { // do nothing by default } /** * Output performance information * @param msg message from the flash client */ private void perf(String msg) { System.err.print(System.currentTimeMillis()); System.err.println(" " + msg); } public void run() { char buf[] = new char[1024]; int n = 0; do { try { if (debug > 0) System.err.println("FlashTerminal: waiting for keyboard input ..."); // read from flash frontend n = reader.read(buf); if (n > 0 && buf[0] == '<') { handleXMLCommand(new String(buf, 0, n - 1)); continue; } if (debug > 0) System.err.println("FlashTerminal: got " + n + " keystokes: " + (n > 0 ? new String(buf, 0, n) : "")); if (n > 0 && (buffer instanceof VDUInput)) { if (simpleMode) { // in simple mode simply write the data to the remote host // we have to convert the chars to bytes ... byte tmp[] = new byte[n]; for (int i = 0; i < n - 1; i++) { tmp[i] = (byte) buf[i]; } ((VDUInput) buffer).write((byte[]) tmp); } else { // write each key for it's own for (int i = 0; i < n - 1; i++) { ((VDUInput) buffer).keyTyped((int) buf[i], buf[i], 0); } } } } catch (IOException e) { System.err.println("FlashTerminal: i/o exception reading keyboard input"); } } while (n >= 0); if (debug > 0) System.err.println("FlashTerminal: end of keyboard input"); disconnect(); } private SAXBuilder builder = new SAXBuilder(); /** * Handle XML Commands sent by the remote host. * @param xml string containing the xml commands */ private void handleXMLCommand(String xml) { System.err.println("handleXMLCommand(" + xml + ")"); StringReader src = new StringReader("<root>" + xml.replace('\0', ' ') + "</root>"); try { Element root = builder.build(src).getRootElement(); Iterator cmds = root.getChildren().iterator(); while (cmds.hasNext()) { Element command = (Element) cmds.next(); String name = command.getName(); if ("mode".equals(name)) { simpleMode = "true".equals(command.getAttribute("simple").getValue().toLowerCase()); } else if ("timestamp".equals(name)) { perf(command.getAttribute("msg").getValue()); } else if ("start".equals(name)) { terminalReady = true; buffer.update[0] = true; redraw(); } } } catch (JDOMException e) { System.err.println("error reading command: " + e); } } // placeholder for the terminal element and the xml outputter private Element terminal = new Element("terminal"); private XMLOutputter xmlOutputter = new XMLOutputter(); /** * Redraw terminal (send new/changed terminal lines to flash frontend). */ public void redraw() { if (debug > 0) System.err.println("FlashTerminal: redraw()"); if (terminalReady && writer != null) { try { // remove children from terminal terminal.removeChildren(); if (simpleMode) { Element result = redrawSimpleTerminal(terminal); if (result.hasChildren()) { xmlOutputter.output(result, writer); } } else { xmlOutputter.output(redrawFullTerminal(terminal), writer); } writer.write(0); writer.flush(); if (debug > 0) System.err.println("FlashTerminal: flushed data ..."); } catch (IOException e) { System.err.println("FlashTerminal: error writing to client: " + e); writer = null; } } } /** * The simple terminal only draws new lines and ignores * changes on lines aready written. * @param terminal * @return */ private Element redrawSimpleTerminal(Element terminal) { terminal.setAttribute("simple", "true"); int checkPoint = buffer.scrollMarker < 0 ? 0 : buffer.scrollMarker; // first check whether our check point is in the back buffer while (checkPoint < buffer.screenBase) { terminal.addContent(redrawLine(0, checkPoint++)); } // then dive into the screen area ... while (checkPoint < buffer.bufSize) { int line = checkPoint - (buffer.screenBase - 1); if (line > buffer.getCursorRow()) break; terminal.addContent(redrawLine(0, checkPoint++)); } // update scroll marker buffer.scrollMarker = checkPoint; buffer.update[0] = false; return terminal; } /** * Redraw a complete terminal with updates on all visible lines. * @param terminal the root terminal tag to add changed lines to * @return the final terminal tag */ private Element redrawFullTerminal(Element terminal) { // cycle through buffer and create terminal update ... for (int l = 0; l < buffer.height; l++) { if (!buffer.update[0] && !buffer.update[l + 1]) continue; buffer.update[l + 1] = false; terminal.addContent(redrawLine(l, buffer.windowBase)); } buffer.update[0] = false; return terminal; } /** * Redraw a sinle line by looking at chunks and formatting them. * @param l the current line * @param base the "window"-base within the buffer * @return an element with the formatted line */ private Element redrawLine(int l, int base) { Element line = new Element("line"); line.setAttribute("row", "" + l); // determine the maximum of characters we can print in one go for (int c = 0; c < buffer.width; c++) { int addr = 0; int currAttr = buffer.charAttributes[base + l][c]; while ((c + addr < buffer.width) && ((buffer.charArray[base + l][c + addr] < ' ') || (buffer.charAttributes[base + l][c + addr] == currAttr))) { if (buffer.charArray[base + l][c + addr] < ' ') { buffer.charArray[base + l][c + addr] = ' '; buffer.charAttributes[base + l][c + addr] = 0; continue; } addr++; } if (addr > 0) { String tmp = new String(buffer.charArray[base + l], c, addr); // create new text node and make sure we insert (160) Text text = new Text(tmp.replace(' ', (char) 160)); Element chunk = null; if ((currAttr & 0xfff) != 0) { if ((currAttr & VDUBuffer.BOLD) != 0) chunk = addChunk(new Element("B"), chunk, text); if ((currAttr & VDUBuffer.UNDERLINE) != 0) chunk = addChunk(new Element("U"), chunk, text); if ((currAttr & VDUBuffer.INVERT) != 0) chunk = addChunk(new Element("I"), chunk, text); if ((currAttr & buffer.COLOR_FG) != 0) { String fg = color[((currAttr & buffer.COLOR_FG) >> 4) - 1]; Element font = new Element("FONT").setAttribute("COLOR", fg); chunk = addChunk(font, chunk, text); } /* if ((currAttr & buffer.COLOR_BG) != 0) { Color bg = color[((currAttr & buffer.COLOR_BG) >> 8) - 1]; } */ } if (chunk == null) { line.addContent(text); } else { line.addContent(chunk); } } c += addr - 1; } return line; } /** * Helper method to wrap a chunk or piece of text in another element. * @param el the element to put the text or chunk into * @param chunk a chunk of elements * @param text a text element * @return a new chunk made up from the element */ private Element addChunk(Element el, Element chunk, Text text) { if (chunk == null) return el.addContent(text); else return el.addContent(chunk); } /** * Set the VDUBuffer that contains the terminal screen and back-buffer * @param buffer the terminal buffer */ public void setVDUBuffer(VDUBuffer buffer) { this.buffer = buffer; if (simpleMode) { this.buffer.setCursorPosition(0, 23); } this.buffer.setDisplay(this); this.buffer.update[0] = true; } /** * Get the current buffer. * @return the VDUBuffer */ public VDUBuffer getVDUBuffer() { return buffer; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -