Overnight churnings
Build and Test j3270 / Build JAR & Run Tests (push) Failing after 1m23s
Release j3270 / Build & Publish Release (push) Failing after 1m15s

This commit is contained in:
2026-09-01 09:45:40 -04:00
parent a56aa5f059
commit 161364a95f
15 changed files with 1489 additions and 33 deletions
@@ -40,6 +40,8 @@ public interface ECLConstants {
// Search directions
int SEARCH_FORWARD = 1;
int SEARCH_BACKWARD = 2;
int DIR_FORWARD = 1;
int DIR_BACKWARD = 2;
// OIA Input Inhibited Reason Codes
int INHIBIT_NOT_INHIBITED = 0;
@@ -28,35 +28,43 @@ public class ECLField {
/** Buffer address of the field attribute character. */
public int getStart() { return startPos; }
public int GetStart() { return getStart(); }
/** First buffer address of the field data (start + 1). */
public int getDataStart() { return dataStart; }
public int GetDataStart() { return getDataStart(); }
/** Last buffer address of the field data inclusive. */
public int getEnd() { return endPos; }
public int GetEnd() { return getEnd(); }
/** Number of data characters in the field. */
public int getLength() { return length; }
public int GetLength() { return getLength(); }
public int getStartRow() {
int cols = ps.getCols();
return cols > 0 ? startPos / cols : 0;
}
public int GetStartRow() { return getStartRow(); }
public int getStartCol() {
int cols = ps.getCols();
return cols > 0 ? startPos % cols : 0;
}
public int GetStartCol() { return getStartCol(); }
public int getEndRow() {
int cols = ps.getCols();
return cols > 0 ? endPos / cols : 0;
}
public int GetEndRow() { return getEndRow(); }
public int getEndCol() {
int cols = ps.getCols();
return cols > 0 ? endPos % cols : 0;
}
public int GetEndCol() { return getEndCol(); }
private byte getLiveAttribute() {
if (ps != null && ps.getScreenBuffer() != null) {
@@ -71,34 +79,42 @@ public class ECLField {
public boolean isModified() {
return faIsModified(getLiveAttribute() & 0xFF);
}
public boolean IsModified() { return isModified(); }
public boolean isProtected() {
return faIsProtected(getLiveAttribute() & 0xFF);
}
public boolean IsProtected() { return isProtected(); }
public boolean isNumeric() {
return faIsNumeric(getLiveAttribute() & 0xFF);
}
public boolean IsNumeric() { return isNumeric(); }
public boolean isHighIntensity() {
return faIsHigh(getLiveAttribute() & 0xFF);
}
public boolean IsHighIntensity() { return isHighIntensity(); }
public boolean isHidden() {
return faIsZero(getLiveAttribute() & 0xFF);
}
public boolean IsHidden() { return isHidden(); }
public boolean isDisplay() {
return !isHidden();
}
public boolean IsDisplay() { return isDisplay(); }
public boolean isPenSelectable() {
return faIsSelectable(getLiveAttribute() & 0xFF);
}
public boolean IsPenSelectable() { return isPenSelectable(); }
public short getAttribute() {
return (short) (getLiveAttribute() & 0xFF);
}
public short GetAttribute() { return getAttribute(); }
/**
* Get the text contents of this field as a String.
@@ -107,6 +123,7 @@ public class ECLField {
if (length <= 0) return "";
return ps.getString(dataStart, length);
}
public String GetText() { return getText(); }
/**
* Set the text contents of this field.
@@ -115,6 +132,7 @@ public class ECLField {
if (isProtected() || length <= 0) return;
ps.setText(text, dataStart);
}
public void SetText(String text) { setText(text); }
/**
* Get selector light pen type.
@@ -129,6 +147,7 @@ public class ECLField {
}
return ' ';
}
public char GetSelectorPenType() { return getSelectorPenType(); }
/**
* Actuate lightpen selection on this field ('?' -> '>').
@@ -140,6 +159,7 @@ public class ECLField {
setText(">" + (t.length() > 1 ? t.substring(1) : ""));
}
}
public void SelectField() { selectField(); }
/**
* Deselect lightpen selection on this field ('>' -> '?').
@@ -151,16 +171,19 @@ public class ECLField {
setText("?" + (t.length() > 1 ? t.substring(1) : ""));
}
}
public void DeSelectField() { deSelectField(); }
/** Return true if this field wraps from bottom of screen to top. */
public boolean isWrapped() {
return startPos > endPos;
}
public boolean IsWrapped() { return isWrapped(); }
/** Last data buffer address (same as getEnd). */
public int getDataEnd() {
return endPos;
}
public int GetDataEnd() { return getDataEnd(); }
/**
* Check if the specified buffer address is contained within this field (including its FA).
@@ -178,6 +201,7 @@ public class ECLField {
return pos >= startPos || pos <= endPos;
}
}
public boolean Contains(int pos) { return contains(pos); }
/**
* Check if the specified row and column is contained within this field.
@@ -187,6 +211,7 @@ public class ECLField {
int cols = ps.getCols();
return contains(row * cols + col);
}
public boolean Contains(int row, int col) { return contains(row, col); }
/**
* Set the Modified Data Tag (MDT) for this field.
@@ -205,6 +230,7 @@ public class ECLField {
}
}
}
public void SetModified(boolean modified) { setModified(modified); }
/**
* Erase all character data within this field to nulls.
@@ -225,6 +251,20 @@ public class ECLField {
sb.markAllChanged();
sb.updateDisplaySnapshot();
}
public void Erase() { erase(); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ECLField)) return false;
ECLField other = (ECLField) o;
return this.startPos == other.startPos && this.endPos == other.endPos && this.length == other.length;
}
@Override
public int hashCode() {
return java.util.Objects.hash(startPos, endPos, length);
}
@Override
public String toString() {
@@ -70,27 +70,38 @@ public class ECLFieldList {
}
}
public void Refresh() { refresh(); }
public synchronized int getFieldCount() {
return fields.size();
}
public int GetFieldCount() { return getFieldCount(); }
public synchronized List<ECLField> getFields() {
return Collections.unmodifiableList(new ArrayList<>(fields));
}
public List<ECLField> GetFields() { return getFields(); }
public synchronized ECLField getFirstField() {
if (fields.isEmpty()) return null;
return fields.get(0);
}
public ECLField GetFirstField() { return getFirstField(); }
public synchronized ECLField getNextField(ECLField prev) {
if (prev == null || fields.isEmpty()) return getFirstField();
int idx = fields.indexOf(prev);
if (idx >= 0 && idx + 1 < fields.size()) {
return fields.get(idx + 1);
for (int i = 0; i < fields.size(); i++) {
ECLField f = fields.get(i);
if (f.equals(prev) || f.getStart() == prev.getStart()) {
if (i + 1 < fields.size()) {
return fields.get(i + 1);
}
return null;
}
}
return null;
}
public ECLField GetNextField(ECLField prev) { return getNextField(prev); }
/**
* Find the field that contains the specified buffer position.
@@ -113,6 +124,7 @@ public class ECLFieldList {
}
return null;
}
public ECLField FindField(int pos) { return findField(pos); }
/**
* Find the field at the specified 0-indexed row and column.
@@ -122,20 +134,26 @@ public class ECLFieldList {
int cols = screen.getCols();
return findField(row * cols + col);
}
public ECLField FindField(int row, int col) { return findField(row, col); }
/**
* Get the field preceding the given field in the field list.
*/
public synchronized ECLField getPreviousField(ECLField next) {
if (next == null || fields.isEmpty()) return null;
int idx = fields.indexOf(next);
if (idx > 0) {
return fields.get(idx - 1);
} else if (idx == 0) {
return fields.get(fields.size() - 1);
for (int i = 0; i < fields.size(); i++) {
ECLField f = fields.get(i);
if (f.equals(next) || f.getStart() == next.getStart()) {
if (i > 0) {
return fields.get(i - 1);
} else {
return fields.get(fields.size() - 1);
}
}
}
return null;
}
public ECLField GetPreviousField(ECLField next) { return getPreviousField(next); }
/**
* Find the field at the given buffer position (alias for findField).
@@ -143,6 +161,7 @@ public class ECLFieldList {
public ECLField findFieldAt(int pos) {
return findField(pos);
}
public ECLField FindFieldAt(int pos) { return findFieldAt(pos); }
/**
* Find the field at the given row and column.
@@ -150,6 +169,7 @@ public class ECLFieldList {
public ECLField findFieldAt(int row, int col) {
return findField(row, col);
}
public ECLField FindFieldAt(int row, int col) { return findFieldAt(row, col); }
/**
* Find the field preceding the one at the given buffer position.
@@ -159,6 +179,7 @@ public class ECLFieldList {
if (curr == null) return null;
return getPreviousField(curr);
}
public ECLField FindPrevField(int pos) { return findPrevField(pos); }
/**
* Find the field succeeding the one at the given buffer position.
@@ -168,6 +189,7 @@ public class ECLFieldList {
if (curr == null) return null;
return getNextField(curr);
}
public ECLField FindNextField(int pos) { return findNextField(pos); }
/**
* Find field containing the given text string.
@@ -202,4 +224,5 @@ public class ECLFieldList {
}
return null;
}
public ECLField FindField(String text, int startPos) { return findField(text, startPos); }
}
@@ -85,24 +85,52 @@ public class ECLOIA implements ECLConstants {
return inputProcessor != null && inputProcessor.isInsertMode();
}
public boolean IsInsertMode() {
return isInsertMode();
}
public boolean isNumeric() {
if (screen == null || !screen.isFormatted()) return false;
byte fa = screen.getFieldAttributeAt(screen.getCursorAddress());
return faIsNumeric(fa & 0xFF);
}
public boolean IsNumeric() {
return isNumeric();
}
public boolean isAlphanumeric() {
return !isNumeric();
}
public boolean IsAlphanumeric() {
return isAlphanumeric();
}
public boolean isDBCS() {
return getAlphanumericType() == TYPE_DBCS;
}
public boolean IsDBCS() {
return isDBCS();
}
public boolean isMessageWaiting() {
return false;
}
public boolean IsMessageWaiting() {
return isMessageWaiting();
}
public boolean isCommError() {
return fsm != null && fsm.getConnectionState() != null && !fsm.getConnectionState().isConnected();
}
public boolean IsCommError() {
return isCommError();
}
private int inhibitOverride = -1;
public void setInputInhibited(int reason) {
@@ -112,6 +140,10 @@ public class ECLOIA implements ECLConstants {
}
}
public void SetInputInhibited(int reason) {
setInputInhibited(reason);
}
/**
* Get the alphanumeric character entry type allowed at current cursor position.
* Returns TYPE_ALPHANUMERIC (0), TYPE_NUMERIC (1), or TYPE_DBCS (2).
@@ -132,6 +164,10 @@ public class ECLOIA implements ECLConstants {
return TYPE_ALPHANUMERIC;
}
public int GetAlphanumericType() {
return getAlphanumericType();
}
public String getAlphanumericTypeString() {
switch (getAlphanumericType()) {
case TYPE_NUMERIC: return "N";
@@ -141,38 +177,74 @@ public class ECLOIA implements ECLConstants {
}
}
public String GetAlphanumericTypeString() {
return getAlphanumericTypeString();
}
public boolean isXSystem() {
return getInputInhibited() == INHIBIT_SYSTEM_LOCK;
}
public boolean IsXSystem() {
return isXSystem();
}
public boolean isXProt() {
return getInputInhibited() == INHIBIT_PROTECTED_FIELD;
}
public boolean IsXProt() {
return isXProt();
}
public boolean isXNum() {
return getInputInhibited() == INHIBIT_NUMERIC_ONLY;
}
public boolean IsXNum() {
return isXNum();
}
public boolean isXWait() {
return isXSystem();
}
public boolean IsXWait() {
return isXWait();
}
public boolean isXInsert() {
return isInsertMode();
}
public boolean IsXInsert() {
return isXInsert();
}
public boolean isXComm() {
return isCommError() || getInputInhibited() == INHIBIT_COMM_CHECK;
}
public boolean IsXComm() {
return isXComm();
}
public boolean isXOverflow() {
return getInputInhibited() == INHIBIT_OVERFLOW;
}
public boolean IsXOverflow() {
return isXOverflow();
}
public boolean isXOperatorDue() {
return getInputInhibited() == INHIBIT_OPERATOR_DUE;
}
public boolean IsXOperatorDue() {
return isXOperatorDue();
}
public String getStatusString() {
int inhibit = getInputInhibited();
switch (inhibit) {
@@ -188,6 +260,10 @@ public class ECLOIA implements ECLConstants {
}
}
public String GetStatusString() {
return getStatusString();
}
/**
* Get the current Input Inhibited code.
* Returns one of INHIBIT_* constants from ECLConstants.
@@ -205,6 +281,18 @@ public class ECLOIA implements ECLConstants {
return INHIBIT_NOT_INHIBITED;
}
public int GetInputInhibited() {
return getInputInhibited();
}
public int getInputInhibitedType() {
return getInputInhibited();
}
public int GetInputInhibitedType() {
return getInputInhibited();
}
/**
* Block until the keyboard becomes unlocked and ready for user input, or timeout expires.
* @return true if keyboard unlocked, false if timeout occurred.
@@ -205,21 +205,75 @@ public class ECLPS implements ECLConstants {
return SearchPSExt(text, 1, getSize(), SEARCH_FORWARD, false, true);
}
public int searchPS(String text) {
return SearchPS(text);
}
public int SearchPS(String text, int startPos) {
return SearchPSExt(text, startPos, getSize(), SEARCH_FORWARD, false, true);
}
public int searchPS(String text, int startPos) {
return SearchPS(text, startPos);
}
public int SearchPS(String text, int startRow, int startCol) {
int pos = (startRow - 1) * getCols() + startCol;
return SearchPSExt(text, pos, getSize(), SEARCH_FORWARD, false, true);
}
public int searchPS(String text, int startRow, int startCol) {
return SearchPS(text, startRow, startCol);
}
public int SearchPS(String text, int startRow, int startCol, int dir) {
int pos = (startRow - 1) * getCols() + startCol;
return SearchPSExt(text, pos, getSize(), dir, false, true);
}
public int searchPS(String text, int startRow, int startCol, int dir) {
return SearchPS(text, startRow, startCol, dir);
}
public int SearchPS(String text, int startRow, int startCol, int dir, boolean ignoreCase) {
int pos = (startRow - 1) * getCols() + startCol;
return SearchPSExt(text, pos, getSize(), dir, ignoreCase, true);
}
public int searchPS(String text, int startRow, int startCol, int dir, boolean ignoreCase) {
return SearchPS(text, startRow, startCol, dir, ignoreCase);
}
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol) {
int sPos = (startRow - 1) * getCols() + startCol;
int ePos = (endRow - 1) * getCols() + endCol;
return SearchPSExt(text, sPos, ePos, SEARCH_FORWARD, false, false);
}
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol) {
return SearchPS(text, startRow, startCol, endRow, endCol);
}
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir) {
int sPos = (startRow - 1) * getCols() + startCol;
int ePos = (endRow - 1) * getCols() + endCol;
return SearchPSExt(text, sPos, ePos, dir, false, false);
}
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir) {
return SearchPS(text, startRow, startCol, endRow, endCol, dir);
}
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase) {
int sPos = (startRow - 1) * getCols() + startCol;
int ePos = (endRow - 1) * getCols() + endCol;
return SearchPSExt(text, sPos, ePos, dir, ignoreCase, false);
}
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase) {
return SearchPS(text, startRow, startCol, endRow, endCol, dir, ignoreCase);
}
/**
* SearchPS extended method conforming to IBM ECL specification.
* Uses 1-based positions and returns 1-based index (or 0 if not found).
@@ -263,6 +317,21 @@ public class ECLPS implements ECLConstants {
return 0;
}
public int searchPSExt(String text, int startPos, int endPos, int dir, boolean ignoreCase, boolean wrap) {
return SearchPSExt(text, startPos, endPos, dir, ignoreCase, wrap);
}
public int SearchPSExt(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase, boolean wrap) {
int cols = getCols();
int sPos = (startRow - 1) * cols + startCol;
int ePos = (endRow - 1) * cols + endCol;
return SearchPSExt(text, sPos, ePos, dir, ignoreCase, wrap);
}
public int searchPSExt(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase, boolean wrap) {
return SearchPSExt(text, startRow, startCol, endRow, endCol, dir, ignoreCase, wrap);
}
private boolean matchesAt(String screenText, String target, int pos, int size) {
int len = target.length();
for (int j = 0; j < len; j++) {
@@ -303,6 +372,10 @@ public class ECLPS implements ECLConstants {
return sb.toString();
}
public String CopyString(int sRow, int sCol, int eRow, int eCol) {
return copyString(sRow, sCol, eRow, eCol);
}
/**
* Paste a multi-line rectangular block of text starting at (row, col).
*/
@@ -320,6 +393,12 @@ public class ECLPS implements ECLConstants {
String line = lines[i];
for (int c = 0; c < line.length() && (col + c) < cols; c++) {
int pos = targetRow * cols + (col + c);
if (screen.isFormatted()) {
byte fa = screen.getFieldAttributeAt(pos);
if (faIsProtected(fa & 0xFF) || screen.getCell(pos).isFieldAttribute()) {
continue;
}
}
setCursorPos(pos);
if (inputProcessor != null) {
inputProcessor.typeCharacter(line.charAt(c));
@@ -330,6 +409,18 @@ public class ECLPS implements ECLConstants {
return count;
}
public int PasteString(String text, int row, int col) {
return pasteString(text, row, col);
}
public int pasteRectangular(String text, int row, int col) {
return pasteString(text, row, col);
}
public int PasteRectangular(String text, int row, int col) {
return pasteString(text, row, col);
}
/**
* Paste text with line wrapping across unprotected fields.
*/
@@ -365,6 +456,108 @@ public class ECLPS implements ECLConstants {
return charsPasted;
}
public int PasteLineWrap(String text, int startPos, int endCol, boolean wordWrap) {
return pasteLineWrap(text, startPos, endCol, wordWrap);
}
// ========== Entry Assist & DOC Mode Operations ==========
public boolean isEntryAssistDOCmode() { return screen != null && screen.isEntryAssistDOCmode(); }
public boolean IsEntryAssistDOCmode() { return isEntryAssistDOCmode(); }
public void setEntryAssistDOCmode(boolean bl) { if (screen != null) screen.setEntryAssistDOCmode(bl); }
public void SetEntryAssistDOCmode(boolean bl) { setEntryAssistDOCmode(bl); }
public boolean isEntryAssistWordWrap() { return screen != null && screen.isEntryAssistWordWrap(); }
public boolean IsEntryAssistWordWrap() { return isEntryAssistWordWrap(); }
public void setEntryAssistWordWrap(boolean bl) { if (screen != null) screen.setEntryAssistWordWrap(bl); }
public void SetEntryAssistWordWrap(boolean bl) { setEntryAssistWordWrap(bl); }
public int getEntryAssistStartColumn() { return screen != null ? screen.getEntryAssistStartColumn() : 0; }
public int GetEntryAssistStartColumn() { return getEntryAssistStartColumn(); }
public void setEntryAssistStartColumn(int n) { if (screen != null) screen.setEntryAssistStartColumn(n); }
public void SetEntryAssistStartColumn(int n) { setEntryAssistStartColumn(n); }
public int getEntryAssistEndColumn() { return screen != null ? screen.getEntryAssistEndColumn() : 0; }
public int GetEntryAssistEndColumn() { return getEntryAssistEndColumn(); }
public void setEntryAssistEndColumn(int n) { if (screen != null) screen.setEntryAssistEndColumn(n); }
public void SetEntryAssistEndColumn(int n) { setEntryAssistEndColumn(n); }
public int[] getEntryAssistTabStops() { return screen != null ? screen.getEntryAssistTabStops() : null; }
public int[] GetEntryAssistTabStops() { return getEntryAssistTabStops(); }
public void setEntryAssistTabStops(int[] stops) { if (screen != null) screen.setEntryAssistTabStops(stops); }
public void SetEntryAssistTabStops(int[] stops) { setEntryAssistTabStops(stops); }
public void processWordTab(boolean forward) { if (inputProcessor != null) inputProcessor.processWordTab(forward); else if (screen != null) screen.processWordTab(forward); }
public void ProcessWordTab(boolean forward) { processWordTab(forward); }
public void wordTab(boolean forward) { processWordTab(forward); }
public void WordTab(boolean forward) { processWordTab(forward); }
public void processDeleteWord() { if (inputProcessor != null) inputProcessor.processDeleteWord(); else if (screen != null) screen.processDeleteWord(); }
public void ProcessDeleteWord() { processDeleteWord(); }
public void deleteWord() { processDeleteWord(); }
public void DeleteWord() { processDeleteWord(); }
public void processWordLeft() { if (inputProcessor != null) inputProcessor.processWordLeft(); }
public void ProcessWordLeft() { processWordLeft(); }
public void wordLeft() { processWordLeft(); }
public void WordLeft() { processWordLeft(); }
public void processWordRight() { if (inputProcessor != null) inputProcessor.processWordRight(); }
public void ProcessWordRight() { processWordRight(); }
public void wordRight() { processWordRight(); }
public void WordRight() { processWordRight(); }
public void processFieldEnd() { if (inputProcessor != null) inputProcessor.processFieldEnd(); }
public void ProcessFieldEnd() { processFieldEnd(); }
public void fieldEnd() { processFieldEnd(); }
public void FieldEnd() { processFieldEnd(); }
// ========== Field Management Accessors ==========
public ECLField getField(int pos) {
return getFieldList().findField(pos);
}
public ECLField GetField(int pos) {
return getField(pos);
}
public ECLField getField(int row, int col) {
return getFieldList().findField(row, col);
}
public ECLField GetField(int row, int col) {
return getField(row, col);
}
public ECLField getFirstField() {
return getFieldList().getFirstField();
}
public ECLField GetFirstField() {
return getFirstField();
}
public ECLField getNextField(ECLField prev) {
return getFieldList().getNextField(prev);
}
public ECLField GetNextField(ECLField prev) {
return getNextField(prev);
}
public ECLField getPreviousField(ECLField next) {
return getFieldList().getPreviousField(next);
}
public ECLField GetPreviousField(ECLField next) {
return getPreviousField(next);
}
public ECLFieldList GetFieldList() {
return getFieldList();
}
// ========== ECLPS Event Listener Management ==========
private final java.util.List<ECLPSListener> psListeners = new java.util.concurrent.CopyOnWriteArrayList<>();
@@ -455,6 +648,29 @@ public class ECLPS implements ECLConstants {
/**
* Send keystrokes with configurable inter-character or inter-mnemonic delay.
*/
public void sendCharacters(String keys) {
sendCharacters(keys, 0);
}
public void SendCharacters(String keys) {
sendCharacters(keys, 0);
}
public void SendCharacters(String keys, int delayMs) {
sendCharacters(keys, delayMs);
}
public void sendCharacters(String keys, int row, int col, int delayMs) {
if (row > 0 && col > 0) {
setCursorPos(row - 1, col - 1);
}
sendCharacters(keys, delayMs);
}
public void SendCharacters(String keys, int row, int col, int delayMs) {
sendCharacters(keys, row, col, delayMs);
}
public void sendCharacters(String keys, int delayMs) {
if (keys == null || keys.isEmpty()) return;
if (delayMs <= 0) {
@@ -466,14 +682,20 @@ public class ECLPS implements ECLConstants {
int len = keys.length();
while (i < len) {
if (keys.charAt(i) == '[') {
int close = keys.indexOf(']', i);
if (close > i) {
String mnemonic = keys.substring(i, close + 1);
sendKeys(mnemonic);
i = close + 1;
if (i + 1 < len && keys.charAt(i + 1) == '[') {
// Escaped bracket "[["
sendKeys("[");
i += 2;
} else {
sendKeys(keys.substring(i, i + 1));
i++;
int close = keys.indexOf(']', i);
if (close > i) {
String mnemonic = keys.substring(i, close + 1);
sendKeys(mnemonic);
i = close + 1;
} else {
sendKeys(keys.substring(i, i + 1));
i++;
}
}
} else {
sendKeys(keys.substring(i, i + 1));
@@ -131,6 +131,22 @@ 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()) {
int curCol = baddr % screen.getCols();
int endCol = screen.getEntryAssistEndColumn();
int startCol = screen.getEntryAssistStartColumn();
if (curCol >= endCol) {
screen.handleWordWrap(baddr, ch);
baddr = screen.getCursorAddress();
if (ch == ' ' && (baddr % screen.getCols()) == startCol) {
screen.markAllChanged();
screen.updateDisplaySnapshot();
return;
}
}
}
if (screen.isFormatted()) {
// Check if cursor is at a field attribute
ExtendedAttribute ea = screen.getCell(baddr);
@@ -909,19 +909,108 @@ public class ScreenBuffer {
// ========== Entry Assist & DOC Mode Operations ==========
public boolean isEntryAssistDOCmode() { return docMode; }
public boolean IsEntryAssistDOCmode() { return isEntryAssistDOCmode(); }
public void setEntryAssistDOCmode(boolean bl) { this.docMode = bl; }
public void SetEntryAssistDOCmode(boolean bl) { setEntryAssistDOCmode(bl); }
public boolean isEntryAssistWordWrap() { return wordWrap; }
public boolean IsEntryAssistWordWrap() { return isEntryAssistWordWrap(); }
public void setEntryAssistWordWrap(boolean bl) { this.wordWrap = bl; }
public void SetEntryAssistWordWrap(boolean bl) { setEntryAssistWordWrap(bl); }
public int getEntryAssistStartColumn() { return docStartCol; }
public int GetEntryAssistStartColumn() { return getEntryAssistStartColumn(); }
public void setEntryAssistStartColumn(int n) { this.docStartCol = Math.max(0, n); }
public void SetEntryAssistStartColumn(int n) { setEntryAssistStartColumn(n); }
public int getEntryAssistEndColumn() { return docEndCol >= 0 ? docEndCol : (cols - 1); }
public int GetEntryAssistEndColumn() { return getEntryAssistEndColumn(); }
public void setEntryAssistEndColumn(int n) { this.docEndCol = n; }
public void SetEntryAssistEndColumn(int n) { setEntryAssistEndColumn(n); }
public int[] getEntryAssistTabStops() { return tabStops; }
public int[] GetEntryAssistTabStops() { return getEntryAssistTabStops(); }
public void setEntryAssistTabStops(int[] stops) { this.tabStops = stops; }
public void SetEntryAssistTabStops(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;
int size = rows * cols;
if (size <= 0) return false;
curAddr = ((curAddr % size) + size) % size;
int curRow = curAddr / cols;
int curCol = curAddr % cols;
int endCol = getEntryAssistEndColumn();
int startCol = getEntryAssistStartColumn();
if (curCol < endCol) return false;
if (typedChar == ' ') {
int nextRow = (curRow + 1) % rows;
setCursorPosition(nextRow, startCol);
return true;
}
// Scan backwards to find the start of the current word on this row
int rowStartAddr = curRow * cols + startCol;
int scan = curAddr - 1;
while (scan >= rowStartAddr) {
ExtendedAttribute ea = getCell(scan);
if (ea.isFieldAttribute()) break;
int ec = ea.ec & 0xFF;
if (ec == 0 || ec == 0x40 || ea.ucs4 == ' ' || ea.ucs4 == 0) {
break;
}
scan--;
}
int wordStartAddr = scan + 1;
int wordStartCol = wordStartAddr % cols;
if (wordStartCol > startCol && wordStartAddr < curAddr) {
int wordLen = curAddr - wordStartAddr;
ExtendedAttribute[] wordCells = new ExtendedAttribute[wordLen];
for (int i = 0; i < wordLen; i++) {
wordCells[i] = new ExtendedAttribute();
wordCells[i].copyFrom(getCell(wordStartAddr + i));
getCell(wordStartAddr + i).clear();
}
int nextRow = (curRow + 1) % rows;
int targetAddr = nextRow * cols + startCol;
if (formatted) {
targetAddr = findNextUnprotected(targetAddr - 1);
}
for (int i = 0; i < wordLen; i++) {
int dst = (targetAddr + i) % size;
if (!getCell(dst).isFieldAttribute()) {
getCell(dst).copyFrom(wordCells[i]);
}
}
setCursorAddress((targetAddr + wordLen) % size);
screenChanged = true;
updateDisplaySnapshot();
return true;
} else {
// Word spans entire line or starts at startCol, wrap to next line
int nextRow = (curRow + 1) % rows;
int targetAddr = nextRow * cols + startCol;
if (formatted) {
targetAddr = findNextUnprotected(targetAddr - 1);
}
setCursorAddress(targetAddr);
screenChanged = true;
updateDisplaySnapshot();
return true;
}
}
public synchronized void processWordTab(boolean forward) {
int size = rows * cols;
@@ -1060,4 +1149,12 @@ public class ScreenBuffer {
screenChanged = true;
updateDisplaySnapshot();
}
/**
* Inspect and balance Shift-Out (0x0E) and Shift-In (0x0F) markers, ensuring DBCS integrity.
*/
public synchronized void balanceSOSI() {
cleanAdjacentSISO(0);
processSOSI();
}
}
@@ -44,11 +44,41 @@ public class TelnetConnection {
*/
public void connect() throws IOException {
ConnectionConfig.ProxyType proxyType = config.getProxyType();
boolean hasProxy = proxyType != null && proxyType != ConnectionConfig.ProxyType.NONE &&
config.getProxyHost() != null && !config.getProxyHost().trim().isEmpty();
String proxyHost = config.getProxyHost();
int proxyPort = config.getProxyPort();
String proxyUser = config.getProxyUsername();
String proxyPass = config.getProxyPassword();
String connectHost = hasProxy ? config.getProxyHost().trim() : config.getHost();
int connectPort = hasProxy ? (config.getProxyPort() > 0 ? config.getProxyPort() : (proxyType == ConnectionConfig.ProxyType.HTTP ? 8080 : 1080)) : config.getPort();
if ((proxyType == null || proxyType == ConnectionConfig.ProxyType.NONE) && (proxyHost == null || proxyHost.trim().isEmpty())) {
// Check JVM system properties (matching IBM HoD browser/system default fallback)
String sysSocks = System.getProperty("socksProxyHost");
String sysHttp = System.getProperty("http.proxyHost");
if (sysSocks != null && !sysSocks.trim().isEmpty()) {
proxyType = ConnectionConfig.ProxyType.SOCKS5;
proxyHost = sysSocks.trim();
String portStr = System.getProperty("socksProxyPort");
if (portStr != null) {
try { proxyPort = Integer.parseInt(portStr.trim()); } catch (NumberFormatException ignored) {}
}
proxyUser = System.getProperty("java.net.socks.username");
proxyPass = System.getProperty("java.net.socks.password");
} else if (sysHttp != null && !sysHttp.trim().isEmpty()) {
proxyType = ConnectionConfig.ProxyType.HTTP;
proxyHost = sysHttp.trim();
String portStr = System.getProperty("http.proxyPort");
if (portStr != null) {
try { proxyPort = Integer.parseInt(portStr.trim()); } catch (NumberFormatException ignored) {}
}
proxyUser = System.getProperty("http.proxyUser");
proxyPass = System.getProperty("http.proxyPassword");
}
}
boolean hasProxy = proxyType != null && proxyType != ConnectionConfig.ProxyType.NONE &&
proxyHost != null && !proxyHost.trim().isEmpty();
String connectHost = hasProxy ? proxyHost.trim() : config.getHost();
int connectPort = hasProxy ? (proxyPort > 0 ? proxyPort : (proxyType == ConnectionConfig.ProxyType.HTTP ? 8080 : 1080)) : config.getPort();
log.info("Connecting TCP socket to " + connectHost + ":" + connectPort +
(hasProxy ? " (via " + proxyType + " proxy for " + config.getHost() + ":" + config.getPort() + ")" : "") +
@@ -67,13 +97,13 @@ public class TelnetConnection {
if (hasProxy) {
switch (proxyType) {
case HTTP:
establishHttpProxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername(), config.getProxyPassword());
establishHttpProxy(rawSocket, config.getHost(), config.getPort(), proxyUser, proxyPass);
break;
case SOCKS4:
establishSocks4Proxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername());
establishSocks4Proxy(rawSocket, config.getHost(), config.getPort(), proxyUser);
break;
case SOCKS5:
establishSocks5Proxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername(), config.getProxyPassword());
establishSocks5Proxy(rawSocket, config.getHost(), config.getPort(), proxyUser, proxyPass);
break;
default:
break;