import java.util.List;
import jexer.TKeypress;
-import jexer.event.TMouseEvent;
+import jexer.backend.GlyphMaker;
import jexer.bits.Color;
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.StringUtils;
+import jexer.event.TInputEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
import jexer.io.ReadTimeoutException;
import jexer.io.TimeoutInputStream;
import static jexer.TKeypress.*;
*/
private int textHeight = 20;
+ /**
+ * The last used height of a character cell in pixels, only used for
+ * full-width chars.
+ */
+ private int lastTextHeight = -1;
+
+ /**
+ * The glyph drawer for full-width chars.
+ */
+ private GlyphMaker glyphMaker = null;
+
+ /**
+ * Input queue for keystrokes and mouse events to send to the remote
+ * side.
+ */
+ private ArrayList<TInputEvent> userQueue = new ArrayList<TInputEvent>();
+
/**
* DECSC/DECRC save/restore a subset of the total state. This class
* encapsulates those specific flags/modes.
char [] readBufferUTF8 = null;
byte [] readBuffer = null;
if (utf8) {
- readBufferUTF8 = new char[128];
+ readBufferUTF8 = new char[2048];
} else {
- readBuffer = new byte[128];
+ readBuffer = new byte[2048];
}
while (!done && !stopReaderThread) {
+ synchronized (userQueue) {
+ while (userQueue.size() > 0) {
+ handleUserEvent(userQueue.remove(0));
+ }
+ }
+
try {
int n = inputStream.available();
// ECMA48 -----------------------------------------------------------------
// ------------------------------------------------------------------------
+ /**
+ * Process keyboard and mouse events from the user.
+ *
+ * @param event the input event to consume
+ */
+ private void handleUserEvent(final TInputEvent event) {
+ if (event instanceof TKeypressEvent) {
+ keypress(((TKeypressEvent) event).getKey());
+ }
+ if (event instanceof TMouseEvent) {
+ mouse((TMouseEvent) event);
+ }
+ }
+
+ /**
+ * Add a keyboard and mouse event from the user to the queue.
+ *
+ * @param event the input event to consume
+ */
+ public void addUserEvent(final TInputEvent event) {
+ synchronized (userQueue) {
+ userQueue.add(event);
+ }
+ }
+
/**
* Return the proper primary Device Attributes string.
*
private void printCharacter(final char ch) {
int rightMargin = this.rightMargin;
+ if (StringUtils.width(ch) == 2) {
+ // This is a full-width character. Save two spaces, and then
+ // draw the character as two image halves.
+ int x0 = currentState.cursorX;
+ int y0 = currentState.cursorY;
+ printCharacter(' ');
+ printCharacter(' ');
+ if ((currentState.cursorX == x0 + 2)
+ && (currentState.cursorY == y0)
+ ) {
+ // We can draw both halves of the character.
+ drawHalves(x0, y0, x0 + 1, y0, ch);
+ } else if ((currentState.cursorX == x0 + 1)
+ && (currentState.cursorY == y0)
+ ) {
+ // VT100 line wrap behavior: we should be at the right
+ // margin. We can draw both halves of the character.
+ drawHalves(x0, y0, x0 + 1, y0, ch);
+ } else {
+ // The character splits across the line. Draw the entire
+ // character on the new line, giving one more space for it.
+ x0 = currentState.cursorX - 1;
+ y0 = currentState.cursorY;
+ printCharacter(' ');
+ drawHalves(x0, y0, x0 + 1, y0, ch);
+ }
+ return;
+ }
+
// Check if we have double-width, and if so chop at 40/66 instead of
// 80/132
if (display.get(currentState.cursorY).isDoubleWidth()) {
CellAttributes newCellAttributes = (CellAttributes) newCell;
newCellAttributes.setTo(currentState.attr);
DisplayLine line = display.get(currentState.cursorY);
- // Insert mode special case
- if (insertMode == true) {
- line.insert(currentState.cursorX, newCell);
- } else {
- // Replace an existing character
- line.replace(currentState.cursorX, newCell);
- }
- // Increment horizontal
- if (wrapLineFlag == false) {
- currentState.cursorX++;
- if (currentState.cursorX > rightMargin) {
- currentState.cursorX--;
+ if (StringUtils.width(ch) == 1) {
+ // Insert mode special case
+ if (insertMode == true) {
+ line.insert(currentState.cursorX, newCell);
+ } else {
+ // Replace an existing character
+ line.replace(currentState.cursorX, newCell);
+ }
+
+ // Increment horizontal
+ if (wrapLineFlag == false) {
+ currentState.cursorX++;
+ if (currentState.cursorX > rightMargin) {
+ currentState.cursorX--;
+ }
}
}
}
*
* @param mouse mouse event received from the local user
*/
- public void mouse(final TMouseEvent mouse) {
+ private void mouse(final TMouseEvent mouse) {
/*
System.err.printf("mouse(): protocol %s encoding %s mouse %s\n",
*
* @param keypress keypress received from the local user
*/
- public void keypress(final TKeypress keypress) {
+ private void keypress(final TKeypress keypress) {
writeRemote(keypressToString(keypress));
}
* DECALN - Screen alignment display.
*/
private void decaln() {
- Cell newCell = new Cell();
- newCell.setChar('E');
+ Cell newCell = new Cell('E');
for (DisplayLine line: display) {
for (int i = 0; i < line.length(); i++) {
line.replace(i, newCell);
}
+ /**
+ * Draw the left and right cells of a two-cell-wide (full-width) glyph.
+ *
+ * @param leftX the x position to draw the left half to
+ * @param leftY the y position to draw the left half to
+ * @param rightX the x position to draw the right half to
+ * @param rightY the y position to draw the right half to
+ * @param ch the character to draw
+ */
+ private void drawHalves(final int leftX, final int leftY,
+ final int rightX, final int rightY, final char ch) {
+
+ // System.err.println("drawHalves(): " + Integer.toHexString(ch));
+
+ if (lastTextHeight != textHeight) {
+ glyphMaker = GlyphMaker.getInstance(textHeight);
+ lastTextHeight = textHeight;
+ }
+
+ Cell cell = new Cell(ch, currentState.attr);
+ BufferedImage image = glyphMaker.getImage(cell, textWidth * 2,
+ textHeight);
+ BufferedImage leftImage = image.getSubimage(0, 0, textWidth,
+ textHeight);
+ BufferedImage rightImage = image.getSubimage(textWidth, 0, textWidth,
+ textHeight);
+
+ Cell left = new Cell(cell);
+ left.setImage(leftImage);
+ left.setWidth(Cell.Width.LEFT);
+ display.get(leftY).replace(leftX, left);
+
+ Cell right = new Cell(cell);
+ right.setImage(rightImage);
+ right.setWidth(Cell.Width.RIGHT);
+ display.get(rightY).replace(rightX, right);
+ }
+
}