001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.hbtop.terminal.impl;
019
020import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.clearRemainingLine;
021import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.color;
022import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.cursor;
023import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.moveCursor;
024import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.normal;
025
026import java.io.PrintWriter;
027import org.apache.hadoop.hbase.hbtop.terminal.Attributes;
028import org.apache.hadoop.hbase.hbtop.terminal.CursorPosition;
029import org.apache.yetus.audience.InterfaceAudience;
030
031/**
032 * Represents a buffer of the terminal screen for double-buffering.
033 */
034@InterfaceAudience.Private
035public class ScreenBuffer {
036  private int columns;
037  private int rows;
038
039  private Cell[][] buffer;
040  private Cell[][] physical;
041
042  private boolean cursorVisible;
043  private int cursorColumn;
044  private int cursorRow;
045
046  public void reallocate(int columns, int rows) {
047    buffer = new Cell[columns][rows];
048    physical = new Cell[columns][rows];
049
050    for (int row = 0; row < rows; row++) {
051      for (int column = 0; column < columns; column++) {
052        buffer[column][row] = new Cell();
053
054        physical[column][row] = new Cell();
055        physical[column][row].unset();
056      }
057    }
058
059    this.columns = columns;
060    this.rows = rows;
061  }
062
063  public void clear() {
064    for (int row = 0; row < rows; row++) {
065      for (int col = 0; col < columns; col++) {
066        buffer[col][row].reset();
067      }
068    }
069  }
070
071  public void flush(PrintWriter output) {
072    StringBuilder sb = new StringBuilder();
073
074    sb.append(normal());
075    Attributes attributes = new Attributes();
076    for (int row = 0; row < rows; row++) {
077      flushRow(row, sb, attributes);
078    }
079
080    if (
081      cursorVisible && cursorRow >= 0 && cursorColumn >= 0 && cursorRow < rows
082        && cursorColumn < columns
083    ) {
084      sb.append(cursor(true));
085      sb.append(moveCursor(cursorColumn, cursorRow));
086    } else {
087      sb.append(cursor(false));
088    }
089
090    output.write(sb.toString());
091    output.flush();
092  }
093
094  private void flushRow(int row, StringBuilder sb, Attributes lastAttributes) {
095    int lastColumn = -1;
096    for (int column = 0; column < columns; column++) {
097      Cell cell = buffer[column][row];
098      Cell pCell = physical[column][row];
099
100      if (!cell.equals(pCell)) {
101        if (lastColumn != column - 1 || lastColumn == -1) {
102          sb.append(moveCursor(column, row));
103        }
104
105        if (cell.isEndOfLine()) {
106          for (int i = column; i < columns; i++) {
107            physical[i][row].set(buffer[i][row]);
108          }
109
110          sb.append(clearRemainingLine());
111          lastAttributes.reset();
112          return;
113        }
114
115        if (!cell.getAttributes().equals(lastAttributes)) {
116          sb.append(color(cell.getForegroundColor(), cell.getBackgroundColor(), cell.isBold(),
117            cell.isReverse(), cell.isBlink(), cell.isUnderline()));
118        }
119
120        sb.append(cell.getChar());
121
122        lastColumn = column;
123        lastAttributes.set(cell.getAttributes());
124
125        physical[column][row].set(cell);
126      }
127    }
128  }
129
130  public CursorPosition getCursorPosition() {
131    return new CursorPosition(cursorColumn, cursorRow);
132  }
133
134  public void setCursorPosition(int column, int row) {
135    cursorVisible = true;
136    cursorColumn = column;
137    cursorRow = row;
138  }
139
140  public void hideCursor() {
141    cursorVisible = false;
142  }
143
144  public void putString(int column, int row, String string, Attributes attributes) {
145    int i = column;
146    for (int j = 0; j < string.length(); j++) {
147      char ch = string.charAt(j);
148      putChar(i, row, ch, attributes);
149      i += 1;
150      if (i == columns) {
151        break;
152      }
153    }
154  }
155
156  public void putChar(int column, int row, char ch, Attributes attributes) {
157    if (column >= 0 && column < columns && row >= 0 && row < rows) {
158      buffer[column][row].setAttributes(attributes);
159      buffer[column][row].setChar(ch);
160    }
161  }
162
163  public void endOfLine(int column, int row) {
164    if (column >= 0 && column < columns && row >= 0 && row < rows) {
165      buffer[column][row].endOfLine();
166      for (int i = column + 1; i < columns; i++) {
167        buffer[i][row].reset();
168      }
169    }
170  }
171}