How had I not tested on Linux before
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 45s
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 45s
This commit is contained in:
@@ -8,3 +8,6 @@
|
|||||||
- IND$FILE CMS and TSO:
|
- 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.
|
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.
Binary file not shown.
Binary file not shown.
@@ -190,6 +190,8 @@ public class TerminalPanel extends JPanel {
|
|||||||
ScreenBuffer sb = client.getScreenBuffer();
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
sb.setCursorAddress(selectionStartRow * sb.getCols() + selectionStartCol);
|
sb.setCursorAddress(selectionStartRow * sb.getCols() + selectionStartCol);
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
refreshScreen();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repaint();
|
repaint();
|
||||||
@@ -723,6 +725,9 @@ public class TerminalPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void refreshScreen() {
|
private void refreshScreen() {
|
||||||
|
if (client != null) {
|
||||||
|
client.getScreenBuffer().updateDisplaySnapshot();
|
||||||
|
}
|
||||||
repaint();
|
repaint();
|
||||||
// Notify parent to update status bar too
|
// Notify parent to update status bar too
|
||||||
Container parent = getParent();
|
Container parent = getParent();
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ public class InputProcessor {
|
|||||||
}
|
}
|
||||||
screen.setCursorAddress(baddr);
|
screen.setCursorAddress(baddr);
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,6 +207,7 @@ public class InputProcessor {
|
|||||||
int nextRowAddr = ((row + 1) % screen.getRows()) * cols;
|
int nextRowAddr = ((row + 1) % screen.getRows()) * cols;
|
||||||
screen.setCursorAddress(nextRowAddr);
|
screen.setCursorAddress(nextRowAddr);
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
setKeyboardLocked(false);
|
setKeyboardLocked(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -309,6 +311,7 @@ public class InputProcessor {
|
|||||||
addr -= screen.getCols();
|
addr -= screen.getCols();
|
||||||
if (addr < 0) addr += screen.getRows() * screen.getCols();
|
if (addr < 0) addr += screen.getRows() * screen.getCols();
|
||||||
screen.setCursorAddress(addr);
|
screen.setCursorAddress(addr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cursorDown() {
|
public void cursorDown() {
|
||||||
@@ -316,18 +319,21 @@ public class InputProcessor {
|
|||||||
addr += screen.getCols();
|
addr += screen.getCols();
|
||||||
if (addr >= screen.getRows() * screen.getCols()) addr -= screen.getRows() * screen.getCols();
|
if (addr >= screen.getRows() * screen.getCols()) addr -= screen.getRows() * screen.getCols();
|
||||||
screen.setCursorAddress(addr);
|
screen.setCursorAddress(addr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cursorLeft() {
|
public void cursorLeft() {
|
||||||
int addr = screen.getCursorAddress();
|
int addr = screen.getCursorAddress();
|
||||||
addr = screen.decrementAddress(addr);
|
addr = screen.decrementAddress(addr);
|
||||||
screen.setCursorAddress(addr);
|
screen.setCursorAddress(addr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cursorRight() {
|
public void cursorRight() {
|
||||||
int addr = screen.getCursorAddress();
|
int addr = screen.getCursorAddress();
|
||||||
addr = screen.incrementAddress(addr);
|
addr = screen.incrementAddress(addr);
|
||||||
screen.setCursorAddress(addr);
|
screen.setCursorAddress(addr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cursorHome() {
|
public void cursorHome() {
|
||||||
@@ -336,11 +342,13 @@ public class InputProcessor {
|
|||||||
} else {
|
} else {
|
||||||
screen.setCursorAddress(0);
|
screen.setCursorAddress(0);
|
||||||
}
|
}
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tab() {
|
public void tab() {
|
||||||
int addr = screen.findNextUnprotected(screen.getCursorAddress());
|
int addr = screen.findNextUnprotected(screen.getCursorAddress());
|
||||||
screen.setCursorAddress(addr);
|
screen.setCursorAddress(addr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLastAid() { return lastAid; }
|
public int getLastAid() { return lastAid; }
|
||||||
@@ -348,6 +356,7 @@ public class InputProcessor {
|
|||||||
|
|
||||||
public void setCursorAddress(int baddr) {
|
public void setCursorAddress(int baddr) {
|
||||||
screen.setCursorAddress(baddr);
|
screen.setCursorAddress(baddr);
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void backTab() {
|
public void backTab() {
|
||||||
@@ -361,6 +370,7 @@ public class InputProcessor {
|
|||||||
if (screen.getCell(addr).isFieldAttribute()) {
|
if (screen.getCell(addr).isFieldAttribute()) {
|
||||||
if (!faIsProtected(screen.getCell(addr).fa & 0xFF)) {
|
if (!faIsProtected(screen.getCell(addr).fa & 0xFF)) {
|
||||||
screen.setCursorAddress(screen.incrementAddress(addr));
|
screen.setCursorAddress(screen.incrementAddress(addr));
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,6 +387,7 @@ public class InputProcessor {
|
|||||||
ea.ucs4 = 0;
|
ea.ucs4 = 0;
|
||||||
}
|
}
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int addr = screen.getCursorAddress();
|
int addr = screen.getCursorAddress();
|
||||||
@@ -398,6 +409,7 @@ public class InputProcessor {
|
|||||||
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
||||||
}
|
}
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteChar() {
|
public void deleteChar() {
|
||||||
@@ -426,6 +438,7 @@ public class InputProcessor {
|
|||||||
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
||||||
}
|
}
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void backspace() {
|
public void backspace() {
|
||||||
@@ -439,6 +452,7 @@ public class InputProcessor {
|
|||||||
if (keyboardLocked) return;
|
if (keyboardLocked) return;
|
||||||
screen.eraseAllUnprotected();
|
screen.eraseAllUnprotected();
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move cursor to first unprotected field on next line (Newline key in 3270). */
|
/** Move cursor to first unprotected field on next line (Newline key in 3270). */
|
||||||
@@ -455,6 +469,7 @@ public class InputProcessor {
|
|||||||
}
|
}
|
||||||
screen.setCursorAddress(target);
|
screen.setCursorAddress(target);
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert Duplicate (DUP) code and advance to next field. */
|
/** 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);
|
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
||||||
}
|
}
|
||||||
tab();
|
tab();
|
||||||
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert Field Mark (FM) code. */
|
/** Insert Field Mark (FM) code. */
|
||||||
@@ -495,6 +512,7 @@ public class InputProcessor {
|
|||||||
}
|
}
|
||||||
screen.setCursorAddress(baddr);
|
screen.setCursorAddress(baddr);
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attention key (sends Telnet IP). */
|
/** Attention key (sends Telnet IP). */
|
||||||
@@ -571,6 +589,7 @@ public class InputProcessor {
|
|||||||
screen.getCell(i).ucs4 = 0;
|
screen.getCell(i).ucs4 = 0;
|
||||||
}
|
}
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +627,7 @@ public class InputProcessor {
|
|||||||
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
screen.getCell(faAddr).fa = (byte) (screen.getCell(faAddr).fa | FA_MODIFY);
|
||||||
}
|
}
|
||||||
screen.markAllChanged();
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
return fieldLen;
|
return fieldLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public class ScreenBuffer {
|
|||||||
defaultFA.ic = 1;
|
defaultFA.ic = 1;
|
||||||
|
|
||||||
allocateBuffers();
|
allocateBuffers();
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void allocateBuffers() {
|
private void allocateBuffers() {
|
||||||
@@ -139,7 +140,7 @@ public class ScreenBuffer {
|
|||||||
public boolean isScreenAlt() { return screenAlt; }
|
public boolean isScreenAlt() { return screenAlt; }
|
||||||
|
|
||||||
/** Update alternate dimensions from BIND image. Re-allocates buffers if needed. */
|
/** 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;
|
if (newAltRows == altRows && newAltCols == altCols) return;
|
||||||
this.altRows = newAltRows;
|
this.altRows = newAltRows;
|
||||||
this.altCols = newAltCols;
|
this.altCols = newAltCols;
|
||||||
@@ -149,11 +150,15 @@ public class ScreenBuffer {
|
|||||||
this.maxCols = Math.max(maxCols, newAltCols);
|
this.maxCols = Math.max(maxCols, newAltCols);
|
||||||
allocateBuffers();
|
allocateBuffers();
|
||||||
}
|
}
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Cursor ==========
|
// ========== Cursor ==========
|
||||||
public int getCursorAddress() { return cursorAddress; }
|
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 getCursorRow() { return cursorAddress / cols; }
|
||||||
public int getCursorCol() { 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.
|
* Perform an erase, optionally using the alternate screen size.
|
||||||
*/
|
*/
|
||||||
public void erase(boolean alt) {
|
public synchronized void erase(boolean alt) {
|
||||||
clear();
|
clear();
|
||||||
int newRows = alt ? altRows : defRows;
|
int newRows = alt ? altRows : defRows;
|
||||||
int newCols = alt ? altCols : defCols;
|
int newCols = alt ? altCols : defCols;
|
||||||
if (alt == screenAlt && rows == newRows && cols == newCols) {
|
if (alt == screenAlt && rows == newRows && cols == newCols) {
|
||||||
|
updateDisplaySnapshot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rows = newRows;
|
rows = newRows;
|
||||||
cols = newCols;
|
cols = newCols;
|
||||||
screenAlt = alt;
|
screenAlt = alt;
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear the entire buffer. */
|
/** Clear the entire buffer. */
|
||||||
public void clear() {
|
public synchronized void clear() {
|
||||||
for (ExtendedAttribute ea : buffer) {
|
for (ExtendedAttribute ea : buffer) {
|
||||||
ea.clear();
|
ea.clear();
|
||||||
}
|
}
|
||||||
@@ -196,12 +203,13 @@ public class ScreenBuffer {
|
|||||||
defaultCs = 0x00;
|
defaultCs = 0x00;
|
||||||
defaultIc = 0x00;
|
defaultIc = 0x00;
|
||||||
replyMode = SF_SRM_FIELD;
|
replyMode = SF_SRM_FIELD;
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erase all unprotected fields.
|
* Erase all unprotected fields.
|
||||||
*/
|
*/
|
||||||
public void eraseAllUnprotected() {
|
public synchronized void eraseAllUnprotected() {
|
||||||
int size = rows * cols;
|
int size = rows * cols;
|
||||||
boolean inUnprotected = false;
|
boolean inUnprotected = false;
|
||||||
|
|
||||||
@@ -229,6 +237,7 @@ public class ScreenBuffer {
|
|||||||
// Move cursor to first unprotected field
|
// Move cursor to first unprotected field
|
||||||
cursorAddress = findNextUnprotected(0);
|
cursorAddress = findNextUnprotected(0);
|
||||||
screenChanged = true;
|
screenChanged = true;
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Field attribute navigation ==========
|
// ========== Field attribute navigation ==========
|
||||||
@@ -322,7 +331,7 @@ public class ScreenBuffer {
|
|||||||
|
|
||||||
// ========== Setters for model reconfiguration ==========
|
// ========== 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) {
|
int altRows, int altCols) {
|
||||||
this.maxRows = maxRows;
|
this.maxRows = maxRows;
|
||||||
this.maxCols = maxCols;
|
this.maxCols = maxCols;
|
||||||
@@ -333,6 +342,7 @@ public class ScreenBuffer {
|
|||||||
this.rows = defRows;
|
this.rows = defRows;
|
||||||
this.cols = defCols;
|
this.cols = defCols;
|
||||||
allocateBuffers();
|
allocateBuffers();
|
||||||
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void translateToUnicode() {
|
public void translateToUnicode() {
|
||||||
|
|||||||
@@ -44,10 +44,91 @@ public class InputProcessorTest {
|
|||||||
|
|
||||||
assertEquals('A', screen.getCell(1).ucs4);
|
assertEquals('A', screen.getCell(1).ucs4);
|
||||||
assertEquals('B', screen.getCell(2).ucs4);
|
assertEquals('B', screen.getCell(2).ucs4);
|
||||||
|
assertEquals('A', screen.getDisplayCell(1).ucs4);
|
||||||
|
assertEquals('B', screen.getDisplayCell(2).ucs4);
|
||||||
|
|
||||||
input.eraseInput();
|
input.eraseInput();
|
||||||
|
|
||||||
assertEquals(0, screen.getCell(1).ec);
|
assertEquals(0, screen.getCell(1).ec);
|
||||||
assertEquals(0, screen.getCell(2).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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user