IBM-DYNAMIC tweaking
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 1m25s

This commit is contained in:
2026-09-04 20:58:19 -04:00
parent cbb310b889
commit 74e30d1ead
18 changed files with 1374 additions and 86 deletions
@@ -434,7 +434,7 @@ public class ConnectionConfig {
return terminalName;
}
if (isDynamicModel()) {
return extendedDataStream ? "IBM-DYNAMIC-E" : "IBM-DYNAMIC";
return "IBM-DYNAMIC";
}
return extendedDataStream ? model.getTerminalType() : model.getBaseTerminalType();
}
@@ -462,8 +462,23 @@ public class Telnet3270Client {
public void processFieldMark() { inputProcessor.processFieldMark(); }
/** Toggle Insert Mode. */
public void toggleInsert() { inputProcessor.setInsertMode(!inputProcessor.isInsertMode()); }
public void processToggleInsert() { inputProcessor.processToggleInsert(); }
public boolean isInsertMode() { return inputProcessor != null && inputProcessor.isInsertMode(); }
public void toggleInsert() { if (inputProcessor != null) inputProcessor.setInsertMode(!inputProcessor.isInsertMode()); }
public void processToggleInsert() { if (inputProcessor != null) inputProcessor.processToggleInsert(); }
/** Document Mode (Entry Assist) operations. */
public boolean isDocMode() { return screenBuffer != null && screenBuffer.isEntryAssistDOCmode(); }
public void setDocMode(boolean b) { if (screenBuffer != null) screenBuffer.setEntryAssistDOCmode(b); if (oia != null) oia.notifyOIAChanged(); }
public void toggleDocMode() { setDocMode(!isDocMode()); }
public boolean isWordWrap() { return screenBuffer != null && screenBuffer.isEntryAssistWordWrap(); }
public void setWordWrap(boolean b) { if (screenBuffer != null) screenBuffer.setEntryAssistWordWrap(b); if (oia != null) oia.notifyOIAChanged(); }
public void toggleWordWrap() { setWordWrap(!isWordWrap()); }
/** APL Keyboard Mode operations. */
public boolean isAplMode() { return inputProcessor != null && inputProcessor.isAplKeyboardMode(); }
public void setAplMode(boolean b) { if (inputProcessor != null) inputProcessor.setAplKeyboardMode(b); }
public void toggleAplMode() { if (inputProcessor != null) inputProcessor.toggleAplKeyboardMode(); }
/** Move word left. */
public void processWordLeft() { inputProcessor.processWordLeft(); }
@@ -46,11 +46,11 @@ public enum TerminalModel {
/**
* Returns the terminal type string for TN3270E negotiation.
* e.g., "IBM-3279-4-E" for a color model 4 with extended data stream,
* or "IBM-DYNAMIC-E" for dynamic model.
* or "IBM-DYNAMIC" for dynamic model.
*/
public String getTerminalType() {
if (modelNumber == 0) {
return "IBM-DYNAMIC-E";
return "IBM-DYNAMIC";
}
return String.format("IBM-327%c-%d-E", color ? '9' : '8', modelNumber);
}
@@ -178,7 +178,7 @@ public class ECLOIA implements ECLConstants {
}
}
private synchronized void notifyOIAChanged() {
public synchronized void notifyOIAChanged() {
ECLOIAEvent event = new ECLOIAEvent(this, ECLOIAEvent.OIA_UPDATE, getInputInhibited(),
getAlphanumericType(), isInsertMode(), getStatusString());
for (haus.nightmare.lib3270j.ecl.ECLOIANotify l : listeners) {
@@ -484,6 +484,7 @@ public class ECLOIA implements ECLConstants {
if (isNumeric()) s |= STATE_NUMFIELD;
if (screen != null && screen.isEntryAssistDOCmode()) s |= STATE_DOC_MODE;
if (screen != null && screen.isEntryAssistWordWrap()) s |= STATE_WORDWRAP;
if (isApl()) s |= STATE_APL;
if (isXSystem()) s |= STATE_SYS_LOCK;
if (isXComm()) s |= STATE_COMM_CHECK;
return s;
@@ -494,6 +495,21 @@ public class ECLOIA implements ECLConstants {
public int getStatusFlags() { return GetStatusFlags(); }
public long getStatusFlagsEx() { return GetStatusFlagsEx(); }
public boolean isApl() {
return inputProcessor != null && inputProcessor.isAplKeyboardMode();
}
public boolean IsApl() { return isApl(); }
public boolean isDocMode() {
return screen != null && screen.isEntryAssistDOCmode();
}
public boolean IsDocMode() { return isDocMode(); }
public boolean isWordWrap() {
return screen != null && screen.isEntryAssistWordWrap();
}
public boolean IsWordWrap() { return isWordWrap(); }
public synchronized void setBitmaskState(long flag, boolean on) {
this.previousState = this.state;
if (on) {
@@ -118,7 +118,91 @@ public class InputProcessor {
}
}
public boolean isInsertMode() { return insertMode; }
public void setInsertMode(boolean insert) { this.insertMode = insert; }
public void setInsertMode(boolean insert) {
this.insertMode = insert;
if (oia != null) {
oia.notifyOIAChanged();
}
}
@FunctionalInterface
public interface BellListener {
void onBell();
}
private BellListener bellListener;
private boolean bellEnabled = false;
private int bellColumn = 74; // 0-based index for column 75
private boolean insertOffOnAid = true;
private boolean aplKeyboardMode = false;
private boolean numericFieldLock = true;
private boolean autoSkipEnabled = true;
public void setBellListener(BellListener listener) { this.bellListener = listener; }
public BellListener getBellListener() { return bellListener; }
public boolean isBellEnabled() { return bellEnabled; }
public void setBellEnabled(boolean enabled) { this.bellEnabled = enabled; }
public int getBellColumn() { return bellColumn; }
public void setBellColumn(int col) { this.bellColumn = col; }
public boolean isInsertOffOnAid() { return insertOffOnAid; }
public void setInsertOffOnAid(boolean val) { this.insertOffOnAid = val; }
public boolean isAplKeyboardMode() { return aplKeyboardMode; }
public void setAplKeyboardMode(boolean enabled) {
this.aplKeyboardMode = enabled;
if (oia != null) {
oia.notifyOIAChanged();
}
}
public void toggleAplKeyboardMode() {
setAplKeyboardMode(!aplKeyboardMode);
}
public boolean isNumericFieldLock() { return numericFieldLock; }
public void setNumericFieldLock(boolean lock) { this.numericFieldLock = lock; }
public boolean isAutoSkipEnabled() { return autoSkipEnabled; }
public void setAutoSkipEnabled(boolean enabled) { this.autoSkipEnabled = enabled; }
/**
* Map ASCII key character to IBM 3270 APL / Graphic Escape code point.
*/
private int getAplCodeForChar(char ch) {
char upper = Character.toUpperCase(ch);
switch (upper) {
case 'A': return 0x81; // Alpha
case 'B': return 0x82; // ⊥ Up tack / decode
case 'C': return 0x83; // ∩ Intersection
case 'D': return 0x84; // ⌊ Floor
case 'E': return 0x85; // │ Vertical line
case 'F': return 0x87; // ∇ Del / Grad
case 'G': return 0x88; // ∆ Delta
case 'H': return 0x89; // Iota
case 'I': return 0x8A; // → Right arrow
case 'J': return 0x8B; // ⍞ Quote Quad
case 'K': return 0x8C; // ≤ Less than or equal
case 'L': return 0xAD; // [ Bracket left
case 'M': return 0x8E; // × Multiply
case 'N': return 0x8F; // ÷ Divide
case 'O': return 0x90; // ⍟ Circle Star
case 'P': return 0x91; // ⌹ Domino / Quad divide
case 'Q': return 0x92; // Down tack / encode
case 'R': return 0x95; // Rho
case 'S': return 0x94; // ⌈ Ceiling
case 'T': return 0x98; // ⍷ Epsilon underbar
case 'U': return 0x93; // Union
case 'V': return 0x97; // ≠ Not equal
case 'W': return 0x96; // ⍵ Omega
case 'X': return 0xAC; // ⍉ Transpose
case 'Y': return 0xA8; // ↑ Up arrow / Take
case 'Z': return 0xA9; // ↓ Down arrow / Drop
case '-': return 0xA2; // ─ Horizontal line
case '|': return 0x85; // │ Vertical line
case '+': return 0xCB; // ┼ Cross
default: return -1;
}
}
/**
* Enter a character at the current cursor position.
@@ -142,8 +226,8 @@ public class InputProcessor {
int baddr = screen.getCursorAddress();
baddr = ((baddr % size) + size) % size;
// Entry Assist DOC mode / Word Wrap handling
if ((screen.isEntryAssistDOCmode() || screen.isEntryAssistWordWrap()) && !isNvtMode()) {
// Entry Assist Word Wrap handling
if (screen.isEntryAssistWordWrap() && !isNvtMode()) {
int curCol = baddr % screen.getCols();
int endCol = screen.getEntryAssistEndColumn();
int startCol = screen.getEntryAssistStartColumn();
@@ -178,7 +262,7 @@ public class InputProcessor {
}
// Numeric-only field check: digits 0-9, minus (-), period (.), space ( ), DUP, FM
if (faIsNumeric(faVal & 0xFF)) {
if (numericFieldLock && faIsNumeric(faVal & 0xFF)) {
boolean isValidNumeric = (ch >= '0' && ch <= '9') || ch == '-' || ch == '.' || ch == ' '
|| ch == '*' || ch == ';' || ch == (char) FCORDER_DUP || ch == (char) FCORDER_FM;
if (!isValidNumeric) {
@@ -191,8 +275,20 @@ public class InputProcessor {
}
}
// Translate character to EBCDIC
int ebc = translator.unicodeToEbcdic(ch);
// Translate character to EBCDIC (or APL Graphic Escape if in APL Keyboard Mode)
boolean isAplChar = false;
int ebc = -1;
if (aplKeyboardMode) {
int aplCode = getAplCodeForChar(ch);
if (aplCode >= 0) {
ebc = aplCode;
ch = translator.mapAPL(aplCode);
isAplChar = true;
}
}
if (ebc < 0) {
ebc = translator.unicodeToEbcdic(ch);
}
if (ebc < 0) return;
if (insertMode) {
@@ -229,6 +325,9 @@ public class InputProcessor {
ExtendedAttribute ea = screen.getCell(baddr);
ea.ec = (byte) ebc;
ea.ucs4 = ch;
if (isAplChar) {
ea.cs = ExtendedAttribute.CS_GE;
}
// Set MDT on field attribute
if (screen.isFormatted()) {
@@ -244,7 +343,7 @@ public class InputProcessor {
int nextAddr = screen.incrementAddress(baddr);
if (screen.getCell(nextAddr).isFieldAttribute()) {
byte nextFa = screen.getCell(nextAddr).fa;
if (faIsSkip(nextFa & 0xFF)) {
if (autoSkipEnabled && faIsSkip(nextFa & 0xFF)) {
// Auto-skip field (Protected + Numeric): jump to next unprotected field
int skipTarget = screen.findNextUnprotected(nextAddr);
screen.setCursorAddress(skipTarget);
@@ -264,6 +363,28 @@ public class InputProcessor {
screen.setCursorAddress((baddr + 1) % size);
}
// Entry Assist DOC mode: if we typed into or past the right margin, advance to startCol on next row
if (screen != null && screen.isEntryAssistDOCmode() && !screen.isEntryAssistWordWrap() && !isNvtMode()) {
int typedCol = baddr % screen.getCols();
if (typedCol >= screen.getEntryAssistEndColumn()) {
int curRow = baddr / screen.getCols();
int nextRow = (curRow + 1) % screen.getRows();
int targetAddr = nextRow * screen.getCols() + screen.getEntryAssistStartColumn();
if (screen.isFormatted()) {
targetAddr = screen.findNextUnprotected(targetAddr - 1);
}
screen.setCursorAddress(targetAddr);
}
}
// Audible End-of-Line Warning Signal
if (bellEnabled && screen != null) {
int curCol = screen.getCursorCol();
if (curCol == bellColumn && bellListener != null) {
bellListener.onBell();
}
}
screen.markAllChanged();
screen.updateDisplaySnapshot();
}
@@ -523,6 +644,9 @@ public class InputProcessor {
}
if (aidCode == AID_CLEAR) {
if (insertOffOnAid && insertMode) {
setInsertMode(false);
}
screen.clear();
screen.markAllChanged();
if (graphicsPlane != null) {
@@ -538,6 +662,10 @@ public class InputProcessor {
return;
}
if (insertOffOnAid && insertMode) {
setInsertMode(false);
}
lastAid = aidCode;
setKeyboardLocked(true);
@@ -792,6 +920,10 @@ public class InputProcessor {
}
public void tab() {
if (screen != null && screen.isEntryAssistDOCmode()) {
screen.processWordTab(true);
return;
}
int addr = screen.findNextUnprotected(screen.getCursorAddress());
screen.setCursorAddress(addr);
screen.updateDisplaySnapshot();
@@ -806,6 +938,10 @@ public class InputProcessor {
}
public void backTab() {
if (screen != null && screen.isEntryAssistDOCmode()) {
screen.processWordTab(false);
return;
}
if (!screen.isFormatted()) return;
int size = screen.getRows() * screen.getCols();
if (size <= 0) return;
@@ -1546,6 +1682,22 @@ public class InputProcessor {
case "lightpen":
processLightPen();
break;
case "docmode":
if (screen != null) {
screen.setEntryAssistDOCmode(!screen.isEntryAssistDOCmode());
if (oia != null) oia.notifyOIAChanged();
}
break;
case "wordwrap":
if (screen != null) {
screen.setEntryAssistWordWrap(!screen.isEntryAssistWordWrap());
if (oia != null) oia.notifyOIAChanged();
}
break;
case "apl":
case "aplmode":
toggleAplKeyboardMode();
break;
default:
if (token.startsWith("pf")) {
try {
@@ -1071,12 +1071,16 @@ public class ScreenBuffer {
public void setEntryAssistTabStops(int[] stops) { this.tabStops = stops; }
public void SetEntryAssistTabStops(int[] stops) { setEntryAssistTabStops(stops); }
public void setLeftMargin(int n) { setEntryAssistStartColumn(n); }
public void setRightMargin(int n) { setEntryAssistEndColumn(n); }
public void setWordTabPositions(int[] stops) { setEntryAssistTabStops(stops); }
/**
* Perform Entry Assist word wrap if typing near/past end margin.
* Moves any partial word typed on the current line to the beginning of the next line (docStartCol).
*/
public synchronized boolean handleWordWrap(int curAddr, char typedChar) {
if (!docMode && !wordWrap) return false;
if (!wordWrap) return false;
int size = rows * cols;
if (size <= 0) return false;
curAddr = ((curAddr % size) + size) % size;
@@ -75,9 +75,6 @@ public class TelnetFSM {
return list;
}
if (config.isDynamicModel()) {
if (config.isExtendedDataStream()) {
list.add("IBM-DYNAMIC-E");
}
list.add("IBM-DYNAMIC");
list.add("IBM-3279-4-E");
list.add("IBM-3279-4");