/******************************************************************************* * Copyright (c) 2007, 2009 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 API and implementation * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend *******************************************************************************/ package org.eclipse.tm.terminal.model; /** * This class maintains a snapshot of an instance of {@link ITerminalTextData}. * While the {@link ITerminalTextData} continues changing, the snapshot remains * unchanged until the next snapshot is taken by calling * {@link #updateSnapshot(boolean)}. This is important, because the * {@link ITerminalTextData} might get modified by another thread. Suppose you * would want to draw the content of the {@link ITerminalTextData} using the * following loop: * *
* for (int line = 0; line < term.getHeight(); line++) * for (int column = 0; column < term.getWidth(); column++) * drawCharacter(column, line, term.getChar(column, line), term.getStyle(column, line)); ** * This might fail because the background thread could change the dimensions of * the {@link ITerminalTextData} while you iterate the loop. One solution would * be to put a
synchronized(term){}
statement around the code. This
* has two problems: 1. you would have to know about the internals of the
* synchronisation of {@link ITerminalTextData}. 2. The other thread that
* changes {@link ITerminalTextData} is blocked while the potentially slow
* drawing is done.
* * Solution: Take a snapshot of the terminal and use the snapshot to draw * the content. There is no danger that the data structure get changed while you * draw. There are also methods to find out what has changed to minimize the * number of lines that get redrawn. *
* ** Drawing optimization: To optimize redrawing of changed lines, this * class keeps track of lines that have changed since the previous snapshot. *
* ** // iterate over the potentially changed lines * for (int line = snap.getFirstChangedLine(); line <= snap.getLastChangedLine(); line++) * // redraw only if the line has changed * if (snap.hasLineChanged(line)) * for (int column = 0; column < snap.getWidth(); column++) * drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line)); ** *
* Scroll optimization: Often new lines are appended at the bottom of the
* terminal and the rest of the lines are scrolled up. In this case all lines
* would be marked as changed. To optimize for this case,
* {@link #updateSnapshot(boolean)} can be called with true
for the
* detectScrolling
parameter. The object will keep track of
* scrolling. The UI must first handle the scrolling and then use the
* {@link #hasLineChanged(int)} method to determine scrolling:
*
*
* // scroll the visible region of the UI <b>before</b> drawing the changed lines. * doUIScrolling(snap.getScrollChangeY(), snap.getScrollChangeN(), snap.getScrollChangeShift()); * // iterate over the potentially changed lines * for (int line = snap.getFirstChangedLine(); line <= snap.getFirstChangedLine(); line++) * // redraw only if the line has changed * if (snap.hasLineChanged(line)) * for (int column = 0; column < snap.getWidth(); column++) * drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line)); ** * *
* Threading Note: This class is not thread safe! All methods have to be * called by the a same thread, that created the instance by calling * {@link ITerminalTextDataReadOnly#makeSnapshot()}. *
* * @noimplement This interface is not intended to be implemented by clients. * @noextend This interface is not intended to be extended by clients. */ public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly { /** * This listener gets called when the current snapshot * is out of date. Calling {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} * will have an effect. Once the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, * it will not be called until {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} * is called and a new snapshot needs to be updated again. ** A typical terminal view would not update the snapshot immediately * after the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} has been called. It would introduce a * delay to update the UI (and the snapshot} 10 or 20 times per second. * *
Make sure you don't spend too much time in this method. */ interface SnapshotOutOfDateListener { /** * Gets called when the snapshot is out of date. To get the snapshot up to date, * call {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}. * @param snapshot The snapshot that is out of date */ void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot); } void addListener(SnapshotOutOfDateListener listener); void removeListener(SnapshotOutOfDateListener listener); /** * Ends the listening to the {@link ITerminalTextData}. After this * has been called no new snapshot data is collected. */ void detach(); /** * @return true if the data has changed since the previous snapshot. */ boolean isOutOfDate(); /** * The window of interest is the region the snapshot should track. * Changes outside this region are ignored. The change takes effect after * an update! * @param startLine -1 means track the end of the data * @param size number of lines to track. A size of -1 means track all. */ void setInterestWindow(int startLine, int size); int getInterestWindowStartLine(); int getInterestWindowSize(); /** * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently * copy the data of the {@link ITerminalTextData} into an internal representation. * The snapshot also keeps track of the changes since the previous snapshot. *
With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and
* {@link #hasLineChanged(int)}
* you can find out what has changed in the current snapshot since the previous snapshot.
* @param detectScrolling if true
the snapshot tries to identify scroll
* changes since the last snapshot. In this case the information about scrolling
* can be retrieved using the following methods:
* {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()}
*
Note: The method {@link #hasLineChanged(int)} returns changes after the
* scrolling has been applied.
*/
void updateSnapshot(boolean detectScrolling);
/**
* @return The first line changed in this snapshot compared
* to the previous snapshot.
*
*
Note: If no line has changed, this * returns {@link Integer#MAX_VALUE} * *
Note: if {@link #updateSnapshot(boolean)} has been called with true
,
* then this does not include lines that only have been scrolled. This is the
* first line that has changed after the scroll has been applied.
*/
int getFirstChangedLine();
/**
* @return The last line changed in this snapshot compared
* to the previous snapshot. If the height has changed since the
* last update of the snapshot, then the returned value is within
* the new dimensions.
*
*
Note: If no line has changed, this returns -1
*
*
Note: if {@link #updateSnapshot(boolean)} has been called with true
,
* then this does not include lines that only have been scrolled. This is the
* last line that has changed after the scroll has been applied.
*
*
A typical for loop using this method would look like this (note the <=
in the for loop):
*
* for(int line=snap.{@link #getFirstChangedLine()}; line <= snap.getLastChangedLine(); line++) * if(snap.{@link #hasLineChanged(int) hasLineChanged(line)}) * doSomething(line); **/ int getLastChangedLine(); /** * @param line * @return true if the line has changed since the previous snapshot */ boolean hasLineChanged(int line); boolean hasDimensionsChanged(); /** * @return true if the terminal has changed (and not just the * window of interest) */ boolean hasTerminalChanged(); /** * If {@link #updateSnapshot(boolean)} was called with
true
, then this method
* returns the top of the scroll region.
* @return The first line scrolled in this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}.
*/
int getScrollWindowStartLine();
/**
* If {@link #updateSnapshot(boolean)} was called with true
, then this method
* returns the size of the scroll region.
* @return The number of lines scrolled in this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}
* If nothing has changed, 0 is returned.
*/
int getScrollWindowSize();
/**
* If {@link #updateSnapshot(boolean)} was called with true
, then this method
* returns number of lines moved by the scroll region.
* @return The the scroll shift of this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}
*/
int getScrollWindowShift();
/**
* @return The {@link ITerminalTextData} on that this instance is observing.
*/
ITerminalTextData getTerminalTextData();
}