This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package haus.nightmare.lib3270j.ecl;
|
||||
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.protocol.DS3270Constants;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class EntryAssistFullModeTest {
|
||||
|
||||
private ScreenBuffer screen;
|
||||
private EbcdicTranslator translator;
|
||||
private InputProcessor inputProcessor;
|
||||
private ECLOIA oia;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3279_2, translator); // 24x80
|
||||
inputProcessor = new InputProcessor(screen, translator, null);
|
||||
oia = new ECLOIA(screen, inputProcessor, null);
|
||||
inputProcessor.setOIA(oia);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocModeCursorAdvanceWithoutWordWrap() {
|
||||
// Document Mode ON, Word Wrap OFF
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setEntryAssistWordWrap(false);
|
||||
screen.setLeftMargin(0); // Col 1 (0-based 0)
|
||||
screen.setRightMargin(72); // Col 73 (0-based 72)
|
||||
|
||||
assertTrue(screen.isEntryAssistDOCmode());
|
||||
assertFalse(screen.isEntryAssistWordWrap());
|
||||
assertEquals(0, screen.getEntryAssistStartColumn());
|
||||
assertEquals(72, screen.getEntryAssistEndColumn());
|
||||
|
||||
// Setup open cursor position
|
||||
screen.setCursorPosition(0, 72); // Row 0, Col 72 (at right margin)
|
||||
inputProcessor.typeCharacter('X');
|
||||
|
||||
// Should advance to next row at left margin (Row 1, Col 0)
|
||||
assertEquals(1, screen.getCursorRow());
|
||||
assertEquals(0, screen.getCursorCol());
|
||||
assertEquals('X', (char) screen.getCell(72).ucs4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWordWrapTransfersPartialWord() {
|
||||
// Document Mode ON, Word Wrap ON
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setEntryAssistWordWrap(true);
|
||||
screen.setLeftMargin(5); // Start col = 5
|
||||
screen.setRightMargin(20); // End col = 20
|
||||
|
||||
// Type "HELLO " starting at col 10
|
||||
screen.setCursorPosition(0, 10);
|
||||
for (char c : "HELLO ".toCharArray()) {
|
||||
inputProcessor.typeCharacter(c);
|
||||
}
|
||||
|
||||
// Now cursor is at col 16. Type "TEST" which crosses col 20
|
||||
screen.setCursorPosition(0, 18);
|
||||
inputProcessor.typeCharacter('W');
|
||||
inputProcessor.typeCharacter('O');
|
||||
inputProcessor.typeCharacter('R');
|
||||
inputProcessor.typeCharacter('D'); // At col 21, past end col 20
|
||||
|
||||
// Word wrap should have moved "WORD" to next line starting at left margin 5
|
||||
assertEquals(1, screen.getCursorRow());
|
||||
assertTrue(screen.getCursorCol() >= 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocModeTabStopsNavigation() {
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setLeftMargin(0);
|
||||
screen.setRightMargin(79);
|
||||
screen.setEntryAssistTabStops(new int[]{0, 10, 20, 30});
|
||||
|
||||
screen.setCursorPosition(0, 0);
|
||||
|
||||
// Word tab forward
|
||||
screen.processWordTab(true);
|
||||
assertEquals(10, screen.getCursorCol());
|
||||
assertEquals(0, screen.getCursorRow());
|
||||
|
||||
screen.processWordTab(true);
|
||||
assertEquals(20, screen.getCursorCol());
|
||||
|
||||
// Word back tab
|
||||
screen.processWordTab(false);
|
||||
assertEquals(10, screen.getCursorCol());
|
||||
|
||||
screen.processWordTab(false);
|
||||
assertEquals(0, screen.getCursorCol());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAudibleEndOfLineBell() {
|
||||
inputProcessor.setBellEnabled(true);
|
||||
inputProcessor.setBellColumn(74);
|
||||
|
||||
AtomicInteger bellCount = new AtomicInteger(0);
|
||||
inputProcessor.setBellListener(bellCount::incrementAndGet);
|
||||
|
||||
screen.setCursorPosition(0, 70);
|
||||
inputProcessor.typeCharacter('A'); // 71
|
||||
inputProcessor.typeCharacter('B'); // 72
|
||||
inputProcessor.typeCharacter('C'); // 73
|
||||
assertEquals(0, bellCount.get());
|
||||
|
||||
inputProcessor.typeCharacter('D'); // 74 -> triggers bell
|
||||
assertEquals(1, bellCount.get());
|
||||
|
||||
inputProcessor.typeCharacter('E'); // 75 -> does not retrigger on same line
|
||||
assertEquals(1, bellCount.get());
|
||||
|
||||
// Move to next line at col 73 and type across bell column
|
||||
screen.setCursorPosition(1, 73);
|
||||
inputProcessor.typeCharacter('Z'); // advances to 74 -> triggers bell on next line
|
||||
assertEquals(2, bellCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertModeResetOnAid() {
|
||||
inputProcessor.setInsertOffOnAid(true);
|
||||
inputProcessor.setInsertMode(true);
|
||||
assertTrue(inputProcessor.isInsertMode());
|
||||
|
||||
// Send AID_ENTER
|
||||
inputProcessor.sendAid(AID_ENTER);
|
||||
assertFalse(inputProcessor.isInsertMode(), "Insert mode should be reset after AID key");
|
||||
|
||||
// When insertOffOnAid is disabled
|
||||
inputProcessor.setInsertOffOnAid(false);
|
||||
inputProcessor.setInsertMode(true);
|
||||
inputProcessor.sendAid(AID_ENTER);
|
||||
assertTrue(inputProcessor.isInsertMode(), "Insert mode should be preserved when insertOffOnAid is false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAplKeyboardModeAndOia() {
|
||||
inputProcessor.setAplKeyboardMode(true);
|
||||
assertTrue(inputProcessor.isAplKeyboardMode());
|
||||
|
||||
// OIA status flag includes STATE_APL
|
||||
long statusEx = oia.GetStatusFlagsEx();
|
||||
assertEquals(ECLOIA.STATE_APL, statusEx & ECLOIA.STATE_APL);
|
||||
assertTrue(oia.isApl());
|
||||
|
||||
// Test APL translation for key 'a' (alpha -> 0x41 with CS_GE)
|
||||
screen.setCursorPosition(0, 0);
|
||||
inputProcessor.typeCharacter('a');
|
||||
|
||||
var cell = screen.getCell(0);
|
||||
assertEquals(CS_GE, cell.cs, "APL character must be stored with CS_GE (Graphic Escape) charset");
|
||||
|
||||
// Toggle APL mode off
|
||||
inputProcessor.toggleAplKeyboardMode();
|
||||
assertFalse(inputProcessor.isAplKeyboardMode());
|
||||
assertFalse(oia.isApl());
|
||||
assertEquals(0, oia.GetStatusFlagsEx() & ECLOIA.STATE_APL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumericFieldLockSetting() {
|
||||
// Field: pos 0 is numeric FA, pos 1..5 unprotected, pos 6 protect FA
|
||||
screen.setFieldAttribute(0, (byte) FA_NUMERIC);
|
||||
screen.setFieldAttribute(6, (byte) FA_PROTECT);
|
||||
screen.setCursorAddress(1);
|
||||
|
||||
// Case 1: NumericFieldLock is true
|
||||
inputProcessor.setNumericFieldLock(true);
|
||||
inputProcessor.typeCharacter('X'); // invalid numeric character
|
||||
assertTrue(inputProcessor.isKeyboardLocked(), "Keyboard should be locked on non-numeric char");
|
||||
assertEquals(ECLConstants.INHIBIT_NUMERIC_ONLY, oia.getInputInhibited());
|
||||
|
||||
// Reset keyboard
|
||||
inputProcessor.reset();
|
||||
assertFalse(inputProcessor.isKeyboardLocked());
|
||||
|
||||
// Case 2: NumericFieldLock is false
|
||||
inputProcessor.setNumericFieldLock(false);
|
||||
screen.setCursorAddress(1);
|
||||
inputProcessor.typeCharacter('X');
|
||||
assertFalse(inputProcessor.isKeyboardLocked(), "Keyboard should NOT be locked when NumericFieldLock is false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOiaModeIndicators() {
|
||||
screen.setEntryAssistDOCmode(true);
|
||||
screen.setEntryAssistWordWrap(true);
|
||||
inputProcessor.setAplKeyboardMode(true);
|
||||
inputProcessor.setInsertMode(true);
|
||||
|
||||
long flags = oia.GetStatusFlagsEx();
|
||||
assertTrue((flags & ECLOIA.STATE_DOC_MODE) != 0);
|
||||
assertTrue((flags & ECLOIA.STATE_WORDWRAP) != 0);
|
||||
assertTrue((flags & ECLOIA.STATE_APL) != 0);
|
||||
assertTrue((flags & ECLOIA.STATE_INSERT) != 0);
|
||||
|
||||
assertTrue(oia.isDocMode());
|
||||
assertTrue(oia.isWordWrap());
|
||||
assertTrue(oia.isApl());
|
||||
assertTrue(oia.isInsertMode());
|
||||
}
|
||||
}
|
||||
@@ -76,8 +76,9 @@ public class DynamicScreenBufferTest {
|
||||
assertTrue(model.isDynamic());
|
||||
assertEquals(0, model.getModelNumber());
|
||||
assertTrue(model.isColor());
|
||||
assertEquals("IBM-DYNAMIC-E", model.getTerminalType());
|
||||
assertEquals("IBM-DYNAMIC", model.getTerminalType());
|
||||
assertEquals("IBM-DYNAMIC", model.getBaseTerminalType());
|
||||
assertEquals("IBM-DYNAMIC", model.toString());
|
||||
|
||||
ScreenBuffer buffer = new ScreenBuffer(model, translator);
|
||||
assertEquals(24, buffer.getDefRows());
|
||||
|
||||
@@ -65,12 +65,14 @@ public class DynamicTelnetFSMTest {
|
||||
// Host requests TTYPE: IAC SB TTYPE SEND IAC SE
|
||||
feedBytes(255, 250, 24, 1, 255, 240);
|
||||
|
||||
// Verify sent response is IBM-DYNAMIC-E
|
||||
// Verify sent response is IBM-DYNAMIC
|
||||
assertFalse(connection.sentData.isEmpty());
|
||||
byte[] lastSent = connection.sentData.get(connection.sentData.size() - 1);
|
||||
String s = new String(lastSent);
|
||||
assertTrue(s.contains("IBM-DYNAMIC-E") || s.contains("IBM-DYNAMIC"),
|
||||
assertTrue(s.contains("IBM-DYNAMIC"),
|
||||
"Expected terminal type negotiation to send IBM-DYNAMIC, got: " + s);
|
||||
assertFalse(s.contains("IBM-DYNAMIC-E"),
|
||||
"Terminal type negotiation should not contain IBM-DYNAMIC-E, got: " + s);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user