File: FlashTerminal.java

package info (click to toggle)
jta 2.6%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,384 kB
  • sloc: java: 12,691; ansic: 1,066; makefile: 243; xml: 72; sh: 7
file content (341 lines) | stat: -rw-r--r-- 11,086 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
 * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
 *
 * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. 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);
    }
  }

  public void updateScrollBar() {
    // dont do anything... or? 
  }

  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 &nbsp; (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;
  }
}