How had I not tested on Linux before
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 45s

This commit is contained in:
2026-08-21 21:00:10 -04:00
parent 09600260b5
commit b57d8f960d
10 changed files with 125 additions and 6 deletions
+3
View File
@@ -8,3 +8,6 @@
- IND$FILE CMS and TSO:
Fixed command formatting options handling (empty parenthesis removal for CMS binary/default modes and option spacing) and added Query Reply filtering for DFT/DDM mode.
- Local keyboard input and cursor updates not rendering in terminal:
Fixed in ScreenBuffer, InputProcessor, and TerminalPanel by ensuring display snapshot and cursor address are updated synchronously on user input operations (typing, backspace, delete, cursor movement, erase) so the presentation layer immediately renders user keystrokes.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -190,6 +190,8 @@ public class TerminalPanel extends JPanel {
ScreenBuffer sb = client.getScreenBuffer();
sb.setCursorAddress(selectionStartRow * sb.getCols() + selectionStartCol);
clearSelection();
refreshScreen();
return;
}
}
repaint();
@@ -723,6 +725,9 @@ public class TerminalPanel extends JPanel {
}
private void refreshScreen() {
if (client != null) {
client.getScreenBuffer().updateDisplaySnapshot();
}
repaint();
// Notify parent to update status bar too
Container parent = getParent();
@@ -152,6 +152,7 @@ public class InputProcessor {
}
screen.setCursorAddress(baddr);
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
/**
@@ -206,6 +207,7 @@ public class InputProcessor {
int nextRowAddr = ((row + 1) % screen.getRows()) * cols;
screen.setCursorAddress(nextRowAddr);
screen.markAllChanged();
screen.updateDisplaySnapshot();
setKeyboardLocked(false);
return;
}
@@ -309,6 +311,7 @@ public class InputProcessor {
addr -= screen.getCols();
if (addr < 0) addr += screen.getRows() * screen.getCols();
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
}
public void cursorDown() {
@@ -316,18 +319,21 @@ public class InputProcessor {
addr += screen.getCols();
if (addr >= screen.getRows() * screen.getCols()) addr -= screen.getRows() * screen.getCols();
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
}
public void cursorLeft() {
int addr = screen.getCursorAddress();
addr = screen.decrementAddress(addr);
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
}
public void cursorRight() {
int addr = screen.getCursorAddress();
addr = screen.incrementAddress(addr);
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
}
public void cursorHome() {
@@ -336,11 +342,13 @@ public class InputProcessor {
} else {
screen.setCursorAddress(0);
}
screen.updateDisplaySnapshot();
}
public void tab() {
int addr = screen.findNextUnprotected(screen.getCursorAddress());
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
}
public int getLastAid() { return lastAid; }
@@ -348,6 +356,7 @@ public class InputProcessor {
public void setCursorAddress(int baddr) {
screen.setCursorAddress(baddr);
screen.updateDisplaySnapshot();
}
public void backTab() {
@@ -361,6 +370,7 @@ public class InputProcessor {
if (screen.getCell(addr).isFieldAttribute()) {
if (!faIsProtected(screen.getCell(addr).fa & 0xFF)) {
screen.setCursorAddress(screen.incrementAddress(addr));
screen.updateDisplaySnapshot();
return;
}
}
@@ -377,6 +387,7 @@ public class InputProcessor {
ea.ucs4 = 0;
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
return;
}
int addr = screen.getCursorAddress();
@@ -398,6 +409,7 @@ public class InputProcessor {
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
public void deleteChar() {
@@ -426,6 +438,7 @@ public class InputProcessor {
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
public void backspace() {
@@ -439,6 +452,7 @@ public class InputProcessor {
if (keyboardLocked) return;
screen.eraseAllUnprotected();
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
/** Move cursor to first unprotected field on next line (Newline key in 3270). */
@@ -455,6 +469,7 @@ public class InputProcessor {
}
screen.setCursorAddress(target);
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
/** Insert Duplicate (DUP) code and advance to next field. */
@@ -472,6 +487,8 @@ public class InputProcessor {
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
}
tab();
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
/** Insert Field Mark (FM) code. */
@@ -495,6 +512,7 @@ public class InputProcessor {
}
screen.setCursorAddress(baddr);
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
/** Attention key (sends Telnet IP). */
@@ -571,6 +589,7 @@ public class InputProcessor {
screen.getCell(i).ucs4 = 0;
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
return size;
}
@@ -608,6 +627,7 @@ public class InputProcessor {
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
return fieldLen;
}
@@ -60,6 +60,7 @@ public class ScreenBuffer {
defaultFA.ic = 1;
allocateBuffers();
updateDisplaySnapshot();
}
private void allocateBuffers() {
@@ -139,7 +140,7 @@ public class ScreenBuffer {
public boolean isScreenAlt() { return screenAlt; }
/** Update alternate dimensions from BIND image. Re-allocates buffers if needed. */
public void setAlternateDimensions(int newAltRows, int newAltCols) {
public synchronized void setAlternateDimensions(int newAltRows, int newAltCols) {
if (newAltRows == altRows && newAltCols == altCols) return;
this.altRows = newAltRows;
this.altCols = newAltCols;
@@ -149,11 +150,15 @@ public class ScreenBuffer {
this.maxCols = Math.max(maxCols, newAltCols);
allocateBuffers();
}
updateDisplaySnapshot();
}
// ========== Cursor ==========
public int getCursorAddress() { return cursorAddress; }
public void setCursorAddress(int addr) { this.cursorAddress = addr; }
public synchronized void setCursorAddress(int addr) {
this.cursorAddress = addr;
this.displayCursorAddress = addr;
}
public int getCursorRow() { return cursorAddress / cols; }
public int getCursorCol() { return cursorAddress % cols; }
@@ -168,20 +173,22 @@ public class ScreenBuffer {
/**
* Perform an erase, optionally using the alternate screen size.
*/
public void erase(boolean alt) {
public synchronized void erase(boolean alt) {
clear();
int newRows = alt ? altRows : defRows;
int newCols = alt ? altCols : defCols;
if (alt == screenAlt && rows == newRows && cols == newCols) {
updateDisplaySnapshot();
return;
}
rows = newRows;
cols = newCols;
screenAlt = alt;
updateDisplaySnapshot();
}
/** Clear the entire buffer. */
public void clear() {
public synchronized void clear() {
for (ExtendedAttribute ea : buffer) {
ea.clear();
}
@@ -196,12 +203,13 @@ public class ScreenBuffer {
defaultCs = 0x00;
defaultIc = 0x00;
replyMode = SF_SRM_FIELD;
updateDisplaySnapshot();
}
/**
* Erase all unprotected fields.
*/
public void eraseAllUnprotected() {
public synchronized void eraseAllUnprotected() {
int size = rows * cols;
boolean inUnprotected = false;
@@ -229,6 +237,7 @@ public class ScreenBuffer {
// Move cursor to first unprotected field
cursorAddress = findNextUnprotected(0);
screenChanged = true;
updateDisplaySnapshot();
}
// ========== Field attribute navigation ==========
@@ -322,7 +331,7 @@ public class ScreenBuffer {
// ========== Setters for model reconfiguration ==========
public void setDimensions(int maxRows, int maxCols, int defRows, int defCols,
public synchronized void setDimensions(int maxRows, int maxCols, int defRows, int defCols,
int altRows, int altCols) {
this.maxRows = maxRows;
this.maxCols = maxCols;
@@ -333,6 +342,7 @@ public class ScreenBuffer {
this.rows = defRows;
this.cols = defCols;
allocateBuffers();
updateDisplaySnapshot();
}
public void translateToUnicode() {
@@ -44,10 +44,91 @@ public class InputProcessorTest {
assertEquals('A', screen.getCell(1).ucs4);
assertEquals('B', screen.getCell(2).ucs4);
assertEquals('A', screen.getDisplayCell(1).ucs4);
assertEquals('B', screen.getDisplayCell(2).ucs4);
input.eraseInput();
assertEquals(0, screen.getCell(1).ec);
assertEquals(0, screen.getCell(2).ec);
assertEquals(0, screen.getDisplayCell(1).ec);
assertEquals(0, screen.getDisplayCell(2).ec);
}
@Test
public void testTypeCharacterUpdatesDisplaySnapshot() {
InputProcessor input = new InputProcessor(screen, translator, null);
screen.erase(false);
screen.setCellFA(0, (byte) FA_PRINTABLE); // Unprotected field starting at 1
screen.setCursorAddress(1);
screen.updateDisplaySnapshot(); // Initial snapshot
// Type "logon"
input.typeCharacter('l');
input.typeCharacter('o');
input.typeCharacter('g');
input.typeCharacter('o');
input.typeCharacter('n');
// Verify underlying buffer
assertEquals('l', screen.getCell(1).ucs4);
assertEquals('o', screen.getCell(2).ucs4);
assertEquals('g', screen.getCell(3).ucs4);
assertEquals('o', screen.getCell(4).ucs4);
assertEquals('n', screen.getCell(5).ucs4);
// Verify display snapshot (what TerminalPanel renders)
assertEquals('l', screen.getDisplayCell(1).ucs4);
assertEquals('o', screen.getDisplayCell(2).ucs4);
assertEquals('g', screen.getDisplayCell(3).ucs4);
assertEquals('o', screen.getDisplayCell(4).ucs4);
assertEquals('n', screen.getDisplayCell(5).ucs4);
assertEquals(6, screen.getDisplayCursorAddress());
assertEquals(6, screen.getCursorAddress());
}
@Test
public void testCursorMovementUpdatesDisplayCursorAddress() {
InputProcessor input = new InputProcessor(screen, translator, null);
screen.erase(false);
screen.setCursorAddress(10);
assertEquals(10, screen.getDisplayCursorAddress());
input.cursorRight();
assertEquals(11, screen.getDisplayCursorAddress());
input.cursorLeft();
assertEquals(10, screen.getDisplayCursorAddress());
screen.setCursorAddress(45);
assertEquals(45, screen.getDisplayCursorAddress());
}
@Test
public void testDeleteAndEraseEofUpdateDisplaySnapshot() {
InputProcessor input = new InputProcessor(screen, translator, null);
screen.erase(false);
screen.setCellFA(0, (byte) FA_PRINTABLE);
screen.setCellFA(10, (byte) (FA_PRINTABLE | FA_PROTECT));
screen.setCursorAddress(1);
input.typeCharacter('A');
input.typeCharacter('B');
input.typeCharacter('C');
assertEquals('A', screen.getDisplayCell(1).ucs4);
assertEquals('B', screen.getDisplayCell(2).ucs4);
assertEquals('C', screen.getDisplayCell(3).ucs4);
// Backspace over 'C'
input.backspace();
assertEquals(3, screen.getCursorAddress());
assertEquals(3, screen.getDisplayCursorAddress());
assertEquals(0, screen.getDisplayCell(3).ucs4);
// Erase EOF from position 2 ('B')
screen.setCursorAddress(2);
input.eraseEof();
assertEquals(0, screen.getDisplayCell(2).ucs4);
assertEquals('A', screen.getDisplayCell(1).ucs4);
}
}