1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-09 18:56:02 +02:00

[183901] Make the remote-to-terminal communication use an OutputStream

Added TerminalInputStream that provides an OutputStream for connections to write to.
The reading from the input stream is done in Runnables that are called from the Display thread. The time the runnable uses is limited. If data is available a new runnable is posted to the display thread.
This commit is contained in:
Michael Scharf 2007-05-04 22:00:45 +00:00
parent b12c240e50
commit d352a64f77
8 changed files with 460 additions and 169 deletions

View file

@ -58,7 +58,7 @@ public class SerialPortHandler implements
try {
while (fConn.getInputStream() != null && fConn.getInputStream().available() > 0) {
int nBytes = fConn.getInputStream().read(bytes);
fControl.writeToTerminal(new String(bytes, 0, nBytes));
fControl.getRemoteToTerminalOutputStream().write(bytes, 0, nBytes);
}
} catch (IOException ex) {
fControl.displayTextInTerminal(ex.getMessage());

View file

@ -148,8 +148,7 @@ class SshConnection extends Thread {
int n;
// read until the thread gets interrupted....
while( (n=in.read(bytes))!=-1) {
// we assume we get ASCII UTF8 bytes
fControl.writeToTerminal(new String(bytes,0,n,"UTF8")); //$NON-NLS-1$
fControl.getRemoteToTerminalOutputStream().write(bytes,0,n);
}
}

View file

@ -18,6 +18,7 @@ package org.eclipse.tm.internal.terminal.control.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import org.eclipse.jface.dialogs.MessageDialog;
@ -449,24 +450,24 @@ public class TerminalControl implements ITerminalControlForText, ITerminalContro
* @see org.eclipse.tm.terminal.ITerminalControl#displayTextInTerminal(java.lang.String)
*/
public void displayTextInTerminal(String text) {
writeToTerminal(text+"\r\n"); //$NON-NLS-1$
writeToTerminal("\r\n"+text+"\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
public void writeToTerminal(String txt) {
// Do _not_ use asyncExec() here. Class TerminalText requires that
// its run() and setNewText() methods be called in strictly
// alternating order. If we were to call asyncExec() here, this
// loop might race around and call setNewText() twice in a row,
// which would lose data.
getTerminalText().setNewText(new StringBuffer(txt));
if(Display.getDefault().getThread()==Thread.currentThread())
getTerminalText().run();
else
fDisplay.syncExec(getTerminalText());
private void writeToTerminal(String text) {
try {
getRemoteToTerminalOutputStream().write(text.getBytes("ISO-8859-1")); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
// should never happen!
e.printStackTrace();
} catch (IOException e) {
// should never happen!
e.printStackTrace();
}
}
public OutputStream getRemoteToTerminalOutputStream() {
return getTerminalText().getOutputStream();
}
protected boolean isLogCharEnabled() {
return TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_CHAR);
}

View file

@ -0,0 +1,321 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Scharf (Wind River) - initial implementation
*/
package org.eclipse.tm.internal.terminal.control.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.swt.widgets.Display;
/**
* The main purpose of this class is to start a runnable in the
* display thread when data is available and to pretend no data
* is available after a given amount of time the runnable is running.
*
*/
public class TerminalInputStream extends InputStream {
/**
* The maximum time in milli seconds the {@link #fNotifyChange} runs until
* {@link #ready()} returns false.
*/
private final int fUITimeout;
/**
* The output stream used by the terminal backend to write to the terminal
*/
protected final OutputStream fOutputStream;
/**
* This runnable is called every time some characters are available from...
*/
private final Runnable fNotifyChange;
/**
* A shared timer for all terminals. This times is used to limit the
* time used in the display thread....
*/
static Timer fgTimer=new Timer(false);
/**
* A blocking byte queue.
*/
private final BoundedByteBuffer fQueue;
/**
* The maximum amount of data read and written in one shot.
* The timer cannot interrupt reading this amount of data.
* {@link #available()} and {@link #read(byte[], int, int)}
* This is used as optimization, because reading single characters
* can be very inefficient, because each call is synchronized.
*/
// block size must be smaller than the Queue capacity!
final int BLOCK_SIZE=64;
/**
* The runnable that si scheduled in the display tread. Takes care of
* the timeout management. It calls the {@link #fNotifyChange}
*/
// synchronized with fQueue!
private Runnable fRunnable;
/**
* Used as flag to indicate that the current runnable
* has used enough time in the display thread.
* This variable is set by a timer thread after the
* Runnable starts to run in the Display thread after
* {@link #fUITimeout}.
*/
// synchronized with fQueue!
private boolean fEnoughDisplayTime;
/**
* A byte bounded buffer used to synchronize the input and the output stream.
* <p>
* Adapted from BoundedBufferWithStateTracking
* http://gee.cs.oswego.edu/dl/cpj/allcode.java
* http://gee.cs.oswego.edu/dl/cpj/
* <p>
* For some reasons a solution based on
* PipedOutputStream/PipedIntputStream
* does work *very* slowly:
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700
* <p>
*
*/
class BoundedByteBuffer {
protected final byte[] fBuffer; // the elements
protected int fPutPos = 0; // circular indices
protected int fTakePos = 0;
protected int fUsedSlots = 0; // the count
public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
// make sure we don't deadlock on too small capacity
if(capacity<BLOCK_SIZE)
capacity=2*BLOCK_SIZE;
if (capacity <= 0)
throw new IllegalArgumentException();
fBuffer = new byte[capacity];
}
/**
* @return the bytes available for {@link #read()}
*/
public synchronized int size() {
return fUsedSlots;
}
/**
* Writes a single byte to the buffer. Blocks if the buffer is full.
* @param b
* @throws InterruptedException
*/
public synchronized void write(byte b) throws InterruptedException {
while (fUsedSlots == fBuffer.length)
// wait until not full
wait();
fBuffer[fPutPos] = b;
fPutPos = (fPutPos + 1) % fBuffer.length; // cyclically increment
if (fUsedSlots++ == 0) // signal if was empty
notifyAll();
}
/**
* Read a single byte. Blocks until a byte is available.
* @return a byte from the buffer
* @throws InterruptedException
*/
public synchronized byte read() throws InterruptedException {
while (fUsedSlots == 0)
// wait until not empty
wait();
byte b = fBuffer[fTakePos];
fTakePos = (fTakePos + 1) % fBuffer.length;
if (fUsedSlots-- == fBuffer.length) // signal if was full
notifyAll();
return b;
}
}
/**
* An output stream that calls {@link TerminalInputStream#textAvailable}
* every time data is written to the stream. The data is written to
* {@link TerminalInputStream#fQueue}.
*
*/
class TerminalOutputStream extends OutputStream {
public void write(byte[] b, int off, int len) throws IOException {
try {
// optimization to avoid many synchronized
// sections: put the data in junks into the
// queue.
int noff=off;
int end=off+len;
while(noff<end) {
int n=noff+BLOCK_SIZE;
if(n>end)
n=end;
// now block the queue for the time we need to
// add some characters
synchronized(fQueue) {
for(int i=noff;i<n;i++) {
fQueue.write(b[i]);
}
bytesAreAvailable();
}
noff+=BLOCK_SIZE;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void write(int b) throws IOException {
try {
// a kind of optimization, because
// both calls use the fQueue lock...
synchronized(fQueue) {
fQueue.write((byte)b);
bytesAreAvailable();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* @param bufferSize the size of the buffer of the output stream
* @param uiTimeout the maximum time the notifyChange runnable runs. It will be
* rescheduled after uiTimeout if input data is still available.
* @param notifyChange a Runnable that is posted to the Display thread
* via {@link Display#asyncExec}. The runnable is posted several times!
*/
public TerminalInputStream(int bufferSize,int uiTimeout,Runnable notifyChange) {
//if(true) {notifyChange=new Runnable() {public void run() {byte buff[]=new byte[1024];while((available())>0)try {read(buff);} catch (IOException e) {break;}}};}
fOutputStream =new TerminalOutputStream();
fNotifyChange=notifyChange;
fQueue=new BoundedByteBuffer(bufferSize);
fUITimeout=uiTimeout;
}
/**
* Posts the runnable {@link #fNotifyChange} to the display Thread,
* unless the runnable is already scheduled.
* It will make {@link #ready} return false after
* {@link #fUITimeout} milli seconds.
*/
void bytesAreAvailable() {
// synchronize on the Queue to reduce the locks
synchronized(fQueue) {
if(fRunnable==null) {
fRunnable=new Runnable(){
public void run() {
// protect the access to fRunnable
synchronized(fQueue){
fRunnable=null;
}
// end the reading after some time
startTimer(fUITimeout);
// and start the real runnable
fNotifyChange.run();
}
};
// TODO: make sure we don't create a display if the display is disposed...
Display.getDefault().asyncExec(fRunnable);
}
}
}
/**
* Starts a timer that sets {@link #fEnoughDisplayTime} to
* true after milliSec.
* @param milliSec The time after which fEnoughDisplayTime is set to true.
*/
void startTimer(int milliSec) {
synchronized(fQueue) {
fEnoughDisplayTime=false;
}
fgTimer.schedule(new TimerTask(){
public void run() {
synchronized(fQueue) {
fEnoughDisplayTime=true;
// there is some data available
if(fQueue.size()>0) {
// schedule a new runnable to do the work
bytesAreAvailable();
}
}
}}, milliSec);
}
/**
* @return the output stream used by the backend to write to the terminal.
*/
public OutputStream getOutputStream() {
return fOutputStream;
}
/**
* Must be called in the Display Thread!
* @return true if a character is available for the terminal to show.
*/
public int available() {
int available;
synchronized(fQueue) {
if(fEnoughDisplayTime)
return 0;
available=fQueue.size();
}
// Limit the available amount of data.
// else our trick of limiting the time spend
// reading might not work.
if(available>BLOCK_SIZE)
available=BLOCK_SIZE;
return available;
}
/**
* @return the next available byte. Check with {@link #available}
* if characters are available.
*/
public int read() throws IOException {
try {
return fQueue.read();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
}
}
/**
* Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an <tt>IOException</tt>.
* <p>
*/
public void close() throws IOException {
}
public int read(byte[] cbuf, int off, int len) throws IOException {
int n=0;
// read as much as we can using a single synchronized statement
synchronized (fQueue) {
try {
// The assumption is that the caller has used available to
// check if bytes are available! That's why we don't check
// for fEnoughDisplayTime!
// Make sure that not more than BLOCK_SIZE is read in one call
while(fQueue.size()>0 && n<len && n<BLOCK_SIZE) {
cbuf[off+n]=fQueue.read();
n++;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return n;
}
}

View file

@ -17,7 +17,11 @@
package org.eclipse.tm.internal.terminal.control.impl;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
@ -48,7 +52,7 @@ import org.eclipse.tm.terminal.TerminalState;
* @author Fran Litterio <francis.litterio@windriver.com>
* @author Chris Thew <chris.thew@windriver.com>
*/
public class TerminalText implements Runnable, ControlListener {
public class TerminalText implements ControlListener {
/** This is a character processing state: Initial state. */
protected static final int ANSISTATE_INITIAL = 0;
@ -88,19 +92,6 @@ public class TerminalText implements Runnable, ControlListener {
*/
protected StyledText text;
/**
* This field holds the characters received from the remote host before they
* are displayed to the user. Method {@link #processNewText()} scans this
* text looking for ANSI control characters and escape sequences.
*/
protected StringBuffer newText;
/**
* This field holds the index of the current character while the text stored
* in field {@link #newText} is being processed.
*/
protected int characterIndex = 0;
/**
* This field holds the width of a character (in pixels) for the font used
* to display text.
@ -214,12 +205,6 @@ public class TerminalText implements Runnable, ControlListener {
*/
protected boolean reverseVideo = false;
/**
* This field holds the time (in milliseconds) of the previous call to
* method {@link #SetNewText()}.
*/
static long LastNewOutputTime = 0;
/**
* Color object representing the color black. The Color class requires us to
* call dispose() on this object when we no longer need it. We do that in
@ -278,6 +263,8 @@ public class TerminalText implements Runnable, ControlListener {
protected boolean fLimitOutput;
protected int fBufferLineLimit;
final TerminalInputStream fTerminalInputStream;
final Reader fReader;
/**
* The constructor.
*/
@ -291,6 +278,19 @@ public class TerminalText implements Runnable, ControlListener {
for (int i = 0; i < ansiParameters.length; ++i) {
ansiParameters[i] = new StringBuffer();
}
fTerminalInputStream=new TerminalInputStream(1024, 100, new Runnable() {
public void run() {
processText();
}});
Reader reader=null;
try {
// TODO convert byte to char using "ISO-8859-1"
reader=new InputStreamReader(fTerminalInputStream,"ISO-8859-1"); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
// should never happen!
e.printStackTrace();
}
fReader=reader;
}
/**
@ -346,64 +346,17 @@ public class TerminalText implements Runnable, ControlListener {
/**
* This method is required by interface ControlListener. It allows us to
* know when the StyledText widget is resized. This method must be
* synchronized to prevent it from executing at the same time as run(),
* which displays new text. We can't have the fields that represent the
* dimensions of the terminal changing while we are rendering text.
* know when the StyledText widget is resized.
*/
public synchronized void controlResized(ControlEvent event) {
public void controlResized(ControlEvent event) {
Logger.log("entered"); //$NON-NLS-1$
adjustTerminalDimensions();
}
/**
* This method sets field {@link #newText} to a new value. This method must
* not execute at the same time as methods {@link #run()} and {@link
* #clearTerminal()}.
* <p>
*
* IMPORTANT: This method must be called in strict alternation with method
* {@link #run()}.
* <p>
*
* @param newBuffer
* The new buffer containing characters received from the remote
* host.
* This method erases all text from the Terminal view.
*/
public synchronized void setNewText(StringBuffer newBuffer) {
if (Logger.isLogEnabled()) {
Logger.log("new text: '" + Logger.encode(newBuffer.toString()) + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
newText = newBuffer;
// When continuous output is being processed by the Terminal view code, it
// consumes nearly 100% of the CPU. This fixes that. If this method is called
// too frequently, we explicitly sleep for a short time so that the thread
// executing this function (which is the thread reading from the socket or
// serial port) doesn't consume 100% of the CPU. Without this code, the
// Workbench GUI is practically hung when there is continuous output in the
// Terminal view.
long CurrentTime = System.currentTimeMillis();
if (CurrentTime - LastNewOutputTime < 250 && newBuffer.length() > 10) {
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
// Ignore.
}
}
LastNewOutputTime = CurrentTime;
}
/**
* This method erases all text from the Terminal view. This method is called
* when the user chooses "Clear all" from the Terminal view context menu, so
* we need to serialize this method with methods {@link #run()} and {@link
* #setNewText(StringBuffer)}.
*/
public synchronized void clearTerminal() {
public void clearTerminal() {
Logger.log("entered"); //$NON-NLS-1$
text.setText(""); //$NON-NLS-1$
cursorColumn = 0;
@ -412,12 +365,9 @@ public class TerminalText implements Runnable, ControlListener {
/**
* This method is called when the user changes the Terminal view's font. We
* attempt to recompute the pixel width of the new font's characters and fix
* the terminal's dimensions. This method must be synchronized to prevent it
* from executing at the same time as run(), which displays new text. We
* can't have the fields that represent the dimensions of the terminal
* changing while we are rendering text.
* the terminal's dimensions.
*/
public synchronized void fontChanged() {
public void fontChanged() {
Logger.log("entered"); //$NON-NLS-1$
characterPixelWidth = 0;
@ -425,7 +375,6 @@ public class TerminalText implements Runnable, ControlListener {
if (text != null)
adjustTerminalDimensions();
}
/**
* This method executes in the Display thread to process data received from
* the remote host by class {@link TelnetConnection} and
@ -434,7 +383,7 @@ public class TerminalText implements Runnable, ControlListener {
* <p>
* These connectors write text to the terminal's buffer through
* {@link TerminalControl#writeToTerminal(String)} and then have
* this run method exectued in the display thread. This method
* this run method executed in the display thread. This method
* must not execute at the same time as methods
* {@link #setNewText(StringBuffer)} and {@link #clearTerminal()}.
* <p>
@ -442,9 +391,7 @@ public class TerminalText implements Runnable, ControlListener {
* {@link #setNewText(StringBuffer)}.
* <p>
*/
public synchronized void run() {
Logger.log("entered"); //$NON-NLS-1$
void processText() {
try {
// This method can be called just after the user closes the view, so we
// make sure not to cause a widget-disposed exception.
@ -471,20 +418,23 @@ public class TerminalText implements Runnable, ControlListener {
// ISSUE: Is this causing the scroll-to-bottom-on-output behavior?
text.setCaretOffset(caretOffset);
processNewText();
try {
processNewText();
} catch (IOException e) {
Logger.logException(e);
}
caretOffset = text.getCaretOffset();
} catch (Exception ex) {
Logger.logException(ex);
}
}
/**
* This method scans the newly received text, processing ANSI control
* characters and escape sequences and displaying normal text.
* @throws IOException
*/
protected void processNewText() {
protected void processNewText() throws IOException {
Logger.log("entered"); //$NON-NLS-1$
// Stop the StyledText widget from redrawing while we manipulate its contents.
@ -494,10 +444,8 @@ public class TerminalText implements Runnable, ControlListener {
// Scan the newly received text.
characterIndex = 0;
while (characterIndex < newText.length()) {
char character = newText.charAt(characterIndex);
while (hasNextChar()) {
char character = getNextChar();
switch (ansiState) {
case ANSISTATE_INITIAL:
@ -530,7 +478,7 @@ public class TerminalText implements Runnable, ControlListener {
break;
default:
processNonControlCharacters();
processNonControlCharacters(character);
break;
}
break;
@ -614,15 +562,12 @@ public class TerminalText implements Runnable, ControlListener {
ansiState = ANSISTATE_INITIAL;
break;
}
++characterIndex;
}
// Allow the StyledText widget to redraw itself.
text.setRedraw(true);
}
/**
* This method is called when we have parsed an OS Command escape sequence.
* The only one we support is "\e]0;...\u0007", which sets the terminal
@ -1093,7 +1038,7 @@ public class TerminalText implements Runnable, ControlListener {
/**
* Delete one or more lines of text. Any lines below the deleted lines move
* up, which we implmement by appending newlines to the end of the text.
* up, which we implement by appending newlines to the end of the text.
*/
protected void processAnsiCommand_M() {
int totalLines = text.getLineCount();
@ -1355,9 +1300,9 @@ public class TerminalText implements Runnable, ControlListener {
/**
* This method processes a single parameter character in an ANSI escape
* sequence. Paramters are the (optional) characters between the leading
* sequence. Parameters are the (optional) characters between the leading
* "\e[" and the command character in an escape sequence (e.g., in the
* escape sequence "\e[20;10H", the paramter characters are "20;10").
* escape sequence "\e[20;10H", the parameter characters are "20;10").
* Parameters are integers separated by one or more ';'s.
*/
protected void processAnsiParameterCharacter(char ch) {
@ -1368,44 +1313,35 @@ public class TerminalText implements Runnable, ControlListener {
ansiParameters[nextAnsiParameter].append(ch);
}
}
/**
* This method processes a contiguous sequence of non-control characters.
* This is a performance optimization, so that we don't have to insert or
* append each non-control character individually to the StyledText widget.
* A non-control character is any character that passes the condition in the
* below while loop.
* @throws IOException
*/
protected void processNonControlCharacters() {
int firstNonControlCharacterIndex = characterIndex;
int newTextLength = newText.length();
char character = newText.charAt(characterIndex);
protected void processNonControlCharacters(char character) throws IOException {
StringBuffer buffer=new StringBuffer();
buffer.append(character);
// Identify a contiguous sequence of non-control characters, starting at
// firstNonControlCharacterIndex in newText.
while (character != '\u0000' && character != '\b' && character != '\t'
&& character != '\u0007' && character != '\n'
&& character != '\r' && character != '\u001b') {
++characterIndex;
if (characterIndex >= newTextLength)
while(hasNextChar()) {
character=getNextChar();
if(character == '\u0000' || character == '\b' || character == '\t'
|| character == '\u0007' || character == '\n'
|| character == '\r' || character == '\u001b') {
pushBackChar(character);
break;
character = newText.charAt(characterIndex);
}
buffer.append(character);
}
// Move characterIndex back by one character because it gets incremented at the
// bottom of the loop in processNewText().
--characterIndex;
int preDisplayCaretOffset = text.getCaretOffset();
// Now insert the sequence of non-control characters in the StyledText widget
// at the location of the cursor.
displayNewText(firstNonControlCharacterIndex, characterIndex);
displayNewText(buffer.toString());
// If any one of the current font style, foreground color or background color
// differs from the defaults, apply the current style to the newly displayed
@ -1438,23 +1374,18 @@ public class TerminalText implements Runnable, ControlListener {
* text being displayed by this method (this includes newlines, carriage
* returns, and tabs).
* <p>
*
* @param first
* The index (within newText) of the first character to display.
* @param last
* The index (within newText) of the last character to display.
*/
protected void displayNewText(int first, int last) {
protected void displayNewText(String buffer) {
if (text.getCaretOffset() == text.getCharCount()) {
// The cursor is at the very end of the terminal's text, so we append the
// new text to the StyledText widget.
displayNewTextByAppending(first, last);
displayNewTextByAppending(buffer);
} else {
// The cursor is not at the end of the screen's text, so we have to
// overwrite existing text.
displayNewTextByOverwriting(first, last);
displayNewTextByOverwriting(buffer);
}
}
@ -1467,14 +1398,11 @@ public class TerminalText implements Runnable, ControlListener {
* text being displayed by this method (this includes newlines, carriage
* returns, and tabs).
* <p>
*
* @param first
* The index (within newText) of the first character to display.
* @param last
* The index (within newText) of the last character to display.
*/
protected void displayNewTextByAppending(int first, int last) {
int numCharsToOutput = last - first + 1;
protected void displayNewTextByAppending(String newText) {
int first=0;
int last=newText.length()-1;
int numCharsToOutput = newText.length();
int availableSpaceOnLine = widthInColumns - cursorColumn;
if (numCharsToOutput >= availableSpaceOnLine) {
@ -1525,16 +1453,13 @@ public class TerminalText implements Runnable, ControlListener {
* text being displayed by this method (this includes newlines, carriage
* returns, and tabs).
* <p>
*
* @param first
* The index (within newText) of the first character to display.
* @param last
* The index (within newText) of the last character to display.
*/
protected void displayNewTextByOverwriting(int first, int last) {
protected void displayNewTextByOverwriting(String newText) {
// First, break new text into segments, based on where it needs to line wrap,
// so that each segment contains text that will appear on a separate
// line.
int first=0;
int last=newText.length()-1;
List textSegments = new ArrayList(100);
@ -1569,7 +1494,7 @@ public class TerminalText implements Runnable, ControlListener {
if (caretOffset == text.getCharCount()) {
// The cursor is at the end of the text, so just append the current
// segement along with a newline.
// segment along with a newline.
text.append(segment);
@ -1823,7 +1748,7 @@ public class TerminalText implements Runnable, ControlListener {
// terminal.
ITerminalConnector telnetConnection = terminal.getTerminalConnection();
// TODO MSA: send only if dimensions have really changed!
if (telnetConnection != null && widthInColumns != 0 && heightInLines != 0) {
telnetConnection.setTerminalSize(widthInColumns, heightInLines);
}
@ -1898,7 +1823,7 @@ public class TerminalText implements Runnable, ControlListener {
}
/**
* This method returns the relative line number of the line comtaining the
* This method returns the relative line number of the line containing the
* cursor. The returned line number is relative to the topmost visible line,
* which has relative line number 0.
*
@ -2077,4 +2002,43 @@ public class TerminalText implements Runnable, ControlListener {
protected void setLimitOutput(boolean limitOutput) {
fLimitOutput = limitOutput;
}
public OutputStream getOutputStream() {
return fTerminalInputStream.getOutputStream();
}
/**
* Buffer for {@link #pushBackChar(char)}.
*/
private int fNextChar=-1;
private char getNextChar() throws IOException {
int c=-1;
if(fNextChar!=-1) {
c= fNextChar;
fNextChar=-1;
} else {
c = fReader.read();
}
// TODO: better end of file handling
if(c==-1)
c=0;
return (char)c;
}
private boolean hasNextChar() throws IOException {
if(fNextChar>=0)
return true;
return fReader.ready();
}
/**
* Put back one character to the stream. This method can push
* back exactly one character. The character is the next character
* returned by {@link #getNextChar}
* @param c the character to be pushed back.
*/
void pushBackChar(char c) {
//assert fNextChar!=-1: "Already a character waiting:"+fNextChar; //$NON-NLS-1$
fNextChar=c;
}
}

View file

@ -280,8 +280,8 @@ public class TelnetConnection extends Thread implements TelnetCodes {
return localEcho;
}
private void writeToTerminal(String string) {
terminalControl.writeToTerminal(string);
private void displayTextInTerminal(String string) {
terminalControl.displayTextInTerminal(string);
}
/**
@ -304,7 +304,7 @@ public class TelnetConnection extends Thread implements TelnetCodes {
// Announce to the user that the remote endpoint has closed the
// connection.
writeToTerminal("\r"+TelnetMessages.CONNECTION_CLOSED_BY_FOREIGN_HOST+"\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
displayTextInTerminal(TelnetMessages.CONNECTION_CLOSED_BY_FOREIGN_HOST);
// Tell the ITerminalControl object that the connection is
// closed.
@ -321,7 +321,7 @@ public class TelnetConnection extends Thread implements TelnetCodes {
int nProcessedBytes = processTelnetProtocol(nRawBytes);
if (nProcessedBytes > 0) {
writeToTerminal(new String(processedBytes, 0, nProcessedBytes));
terminalControl.getRemoteToTerminalOutputStream().write(processedBytes, 0, nProcessedBytes);
}
}
}

View file

@ -135,8 +135,11 @@ public class TelnetConnector implements ITerminalConnector {
public void setTelnetConnection(TelnetConnection connection) {
fTelnetConnection=connection;
}
public void writeToTerminal(String txt) {
fControl.writeToTerminal(txt);
public void displayTextInTerminal(String text) {
fControl.displayTextInTerminal(text);
}
public OutputStream getRemoteToTerminalOutputStream () {
return fControl.getRemoteToTerminalOutputStream();
}
public void setState(TerminalState state) {

View file

@ -11,6 +11,8 @@
*******************************************************************************/
package org.eclipse.tm.terminal;
import java.io.OutputStream;
import org.eclipse.swt.widgets.Shell;
/**
@ -46,10 +48,11 @@ public interface ITerminalControl {
void displayTextInTerminal(String text);
/**
* Write a string directly to the terminal.
* @param txt
* @return a stream used to write to the terminal. Any bytes written to this
* stream appear in the terminal or are interpreted by the emulator as
* control sequences.
*/
void writeToTerminal(String txt);
OutputStream getRemoteToTerminalOutputStream();
/**
* Set the title of the terminal view.