Skip to content

Commit

Permalink
Added: Terminal CSI reporting of window and cell pixel size
Browse files Browse the repository at this point in the history
Implement the following CSI escape sequences from
https://invisible-island.net/xterm/ctlseqs/ctlseqs.html:

> CSI Ps ; Ps ; Ps t
> [..]
>    Ps = 1 4  ⇒  Report xterm text area size in pixels.
>    Result is CSI  4 ;  height ;  width t
> [..]
>    Ps = 1 6  ⇒  Report xterm character cell size in pixels.
>    Result is CSI  6 ;  height ;  width t

Extracted from changes in #2973
by @MatanZ and adopted to play well with the just merged #3098 (.ws_xpixel
and .ws_ypixel values in winsize).
  • Loading branch information
fornwall committed Sep 26, 2024
1 parent 4443b65 commit 0314259
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ public final class TerminalEmulator {
/** The number of character rows and columns in the terminal screen. */
public int mRows, mColumns;

/** Size of a terminal cell in pixels. */
private int mCellWidthPixels, mCellHeightPixels;

/** The number of terminal transcript rows that can be scrolled back to. */
public static final int TERMINAL_TRANSCRIPT_ROWS_MIN = 100;
public static final int TERMINAL_TRANSCRIPT_ROWS_MAX = 50000;
Expand Down Expand Up @@ -314,13 +317,15 @@ static int mapDecSetBitToInternalBit(int decsetBit) {
}
}

public TerminalEmulator(TerminalOutput session, int columns, int rows, Integer transcriptRows, TerminalSessionClient client) {
public TerminalEmulator(TerminalOutput session, int columns, int rows, int cellWidthPixels, int cellHeightPixels, Integer transcriptRows, TerminalSessionClient client) {
mSession = session;
mScreen = mMainBuffer = new TerminalBuffer(columns, getTerminalTranscriptRows(transcriptRows), rows);
mAltBuffer = new TerminalBuffer(columns, rows, rows);
mClient = client;
mRows = rows;
mColumns = columns;
mCellWidthPixels = cellWidthPixels;
mCellHeightPixels = cellHeightPixels;
mTabStop = new boolean[mColumns];
reset();
}
Expand Down Expand Up @@ -370,7 +375,10 @@ public void sendMouseEvent(int mouseButton, int column, int row, boolean pressed
}
}

public void resize(int columns, int rows) {
public void resize(int columns, int rows, int cellWidthPixels, int cellHeightPixels) {
this.mCellWidthPixels = cellWidthPixels;
this.mCellHeightPixels = cellHeightPixels;

if (mRows == rows && mColumns == columns) {
return;
} else if (columns < 2 || rows < 2) {
Expand Down Expand Up @@ -1748,8 +1756,10 @@ private void doCsi(int b) {
mSession.write("\033[3;0;0t");
break;
case 14: // Report xterm window in pixels. Result is CSI 4 ; height ; width t
// We just report characters time 12 here.
mSession.write(String.format(Locale.US, "\033[4;%d;%dt", mRows * 12, mColumns * 12));
mSession.write(String.format(Locale.US, "\033[4;%d;%dt", mRows * mCellHeightPixels, mColumns * mCellWidthPixels));
break;
case 16: // Report xterm character cell size in pixels. Result is CSI 6 ; height ; width t
mSession.write(String.format(Locale.US, "\033[6;%d;%dt", mCellHeightPixels, mCellWidthPixels));
break;
case 18: // Report the size of the text area in characters. Result is CSI 8 ; height ; width t
mSession.write(String.format(Locale.US, "\033[8;%d;%dt", mRows, mColumns));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ public void updateTerminalSessionClient(TerminalSessionClient client) {
}

/** Inform the attached pty of the new size and reflow or initialize the emulator. */
public void updateSize(int columns, int rows, int fontWidth, int fontHeight) {
public void updateSize(int columns, int rows, int cellWidthPixels, int cellHeightPixels) {
if (mEmulator == null) {
initializeEmulator(columns, rows, fontWidth, fontHeight);
initializeEmulator(columns, rows, cellWidthPixels, cellHeightPixels);
} else {
JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns, fontWidth, fontHeight);
mEmulator.resize(columns, rows);
JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns, cellWidthPixels, cellHeightPixels);
mEmulator.resize(columns, rows, cellWidthPixels, cellHeightPixels);
}
}

Expand All @@ -120,11 +120,11 @@ public String getTitle() {
* @param columns The number of columns in the terminal window.
* @param rows The number of rows in the terminal window.
*/
public void initializeEmulator(int columns, int rows, int cellWidth, int cellHeight) {
mEmulator = new TerminalEmulator(this, columns, rows, mTranscriptRows, mClient);
public void initializeEmulator(int columns, int rows, int cellWidthPixels, int cellHeightPixels) {
mEmulator = new TerminalEmulator(this, columns, rows, cellWidthPixels, cellHeightPixels, mTranscriptRows, mClient);

int[] processId = new int[1];
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns, cellWidth, cellHeight);
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns, cellWidthPixels, cellHeightPixels);
mShellPid = processId[0];
mClient.setTerminalShellPid(this, mShellPid);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,24 @@ public void testCsi3J() {
assertEquals("y\nz", mTerminal.getScreen().getTranscriptText());
}

public void testReportPixelSize() {
int columns = 3;
int rows = 3;
withTerminalSized(columns, rows);
int cellWidth = TerminalTest.INITIAL_CELL_WIDTH_PIXELS;
int cellHeight = TerminalTest.INITIAL_CELL_HEIGHT_PIXELS;
assertEnteringStringGivesResponse("\033[14t", "\033[4;" + (rows*cellHeight) + ";" + (columns*cellWidth) + "t");
assertEnteringStringGivesResponse("\033[16t", "\033[6;" + cellHeight + ";" + cellWidth + "t");
columns = 23;
rows = 33;
resize(columns, rows);
assertEnteringStringGivesResponse("\033[14t", "\033[4;" + (rows*cellHeight) + ";" + (columns*cellWidth) + "t");
assertEnteringStringGivesResponse("\033[16t", "\033[6;" + cellHeight + ";" + cellWidth + "t");
cellWidth = 8;
cellHeight = 18;
mTerminal.resize(columns, rows, cellWidth, cellHeight);
assertEnteringStringGivesResponse("\033[14t", "\033[4;" + (rows*cellHeight) + ";" + (columns*cellWidth) + "t");
assertEnteringStringGivesResponse("\033[16t", "\033[6;" + cellHeight + ";" + cellWidth + "t");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ public void testHistory() {
assertLinesAre("777", "888", "999");
assertHistoryStartsWith("666", "555");

mTerminal.resize(cols, 2);
resize(cols, 2);
assertHistoryStartsWith("777", "666", "555");

mTerminal.resize(cols, 3);
resize(cols, 3);
assertHistoryStartsWith("666", "555");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ public void testResizeAfterHistoryWraparound() {
enterString("\r\n");
}
assertLinesAre("998 ", "999 ", " ");
mTerminal.resize(cols, 2);
resize(cols, 2);
assertLinesAre("999 ", " ");
mTerminal.resize(cols, 5);
resize(cols, 5);
assertLinesAre("996 ", "997 ", "998 ", "999 ", " ");
mTerminal.resize(cols, rows);
resize(cols, rows);
assertLinesAre("998 ", "999 ", " ");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void testReportTerminalSize() throws Exception {
assertEnteringStringGivesResponse("\033[18t", "\033[8;5;5t");
for (int width = 3; width < 12; width++) {
for (int height = 3; height < 12; height++) {
mTerminal.resize(width, height);
resize(width, height);
assertEnteringStringGivesResponse("\033[18t", "\033[8;" + height + ";" + width + "t");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@

public abstract class TerminalTestCase extends TestCase {

public static class MockTerminalOutput extends TerminalOutput {
public static final int INITIAL_CELL_WIDTH_PIXELS = 13;
public static final int INITIAL_CELL_HEIGHT_PIXELS = 15;

public static class MockTerminalOutput extends TerminalOutput {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
public final List<ChangedTitle> titleChanges = new ArrayList<>();
public final List<String> clipboardPuts = new ArrayList<>();
Expand Down Expand Up @@ -108,7 +111,7 @@ protected void setUp() throws Exception {

protected TerminalTestCase withTerminalSized(int columns, int rows) {
// The tests aren't currently using the client, so a null client will suffice, a dummy client should be implemented if needed
mTerminal = new TerminalEmulator(mOutput, columns, rows, rows * 2, null);
mTerminal = new TerminalEmulator(mOutput, columns, rows, INITIAL_CELL_WIDTH_PIXELS, INITIAL_CELL_HEIGHT_PIXELS, rows * 2, null);
return this;
}

Expand Down Expand Up @@ -201,7 +204,7 @@ public TerminalTestCase assertLinesAre(String... lines) {
}

public TerminalTestCase resize(int cols, int rows) {
mTerminal.resize(cols, rows);
mTerminal.resize(cols, rows, INITIAL_CELL_WIDTH_PIXELS, INITIAL_CELL_HEIGHT_PIXELS);
assertInvariants();
return this;
}
Expand Down

0 comments on commit 0314259

Please sign in to comment.