GDDM Tweaking
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
package haus.nightmare.lib3270j.ft;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class FTDftTest {
|
||||
|
||||
private EbcdicTranslator translator;
|
||||
private ScreenBuffer screen;
|
||||
private InputProcessor input;
|
||||
private TestListener listener;
|
||||
private FTDft dft;
|
||||
private FTConfig config;
|
||||
|
||||
private static class TestListener implements FTDft.FTDftListener {
|
||||
boolean dftRunningCalled = false;
|
||||
boolean completeCalled = false;
|
||||
boolean abortCalled = false;
|
||||
String completeMsg = null;
|
||||
String abortMsg = null;
|
||||
long bytesTransferred = 0;
|
||||
FTConstants.FTState state = FTConstants.FTState.RUNNING;
|
||||
FTConfig config;
|
||||
|
||||
@Override
|
||||
public void onDftRunning() {
|
||||
dftRunningCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransferComplete(String errorMessage) {
|
||||
completeCalled = true;
|
||||
completeMsg = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransferAborted(String errorMessage) {
|
||||
abortCalled = true;
|
||||
abortMsg = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBytesTransferred(long bytes) {
|
||||
bytesTransferred = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FTConstants.FTState getCurrentState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(FTConstants.FTState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FTConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLocalFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
translator = new EbcdicTranslator();
|
||||
screen = new ScreenBuffer(TerminalModel.IBM_3279_4, translator);
|
||||
input = new InputProcessor(screen, translator, null);
|
||||
listener = new TestListener();
|
||||
config = new FTConfig();
|
||||
config.setDirection(FTConfig.Direction.RECEIVE);
|
||||
config.setTransferMode(FTConfig.TransferMode.BINARY);
|
||||
listener.config = config;
|
||||
|
||||
dft = new FTDft(input, translator, listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenMessageStreamAsciiAndHandleTrans14Error() {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
dft.initTransfer(null, outStream);
|
||||
|
||||
// 1. Simulate TR_OPEN_REQ with ASCII "FT:MSG" matching j3270.log:
|
||||
// SF length = 0x0023, SF type = 0xD0, Request code = 0x0012, stream name "FT:MSG " at offset 28
|
||||
byte[] openReq = new byte[] {
|
||||
0x00, 0x23, (byte) 0xD0, 0x00, 0x12, 0x01, 0x06, 0x01, 0x01, 0x04,
|
||||
0x03, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x01,
|
||||
0x00, 0x50, 0x05, 0x52, 0x03, (byte) 0xF0, 0x03, 0x09,
|
||||
0x46, 0x54, 0x3A, 0x4D, 0x53, 0x47, 0x20 // "FT:MSG "
|
||||
};
|
||||
|
||||
dft.processStructuredField(openReq, 0, openReq.length);
|
||||
|
||||
// Should NOT trigger onDftRunning because it's a message stream
|
||||
assertFalse(listener.dftRunningCalled);
|
||||
|
||||
// 2. Simulate TR_DATA_INSERT on the message stream with "TRANS14 Error reading file from host: ..."
|
||||
String errorMsg = "TRANS14 Error reading file from host: file format invalid";
|
||||
byte[] msgBytes = errorMsg.getBytes(StandardCharsets.ISO_8859_1);
|
||||
int totalPayloadLen = msgBytes.length + 5;
|
||||
|
||||
ByteArrayOutputStream sfOut = new ByteArrayOutputStream();
|
||||
sfOut.write(0); sfOut.write(0); // placeholder length
|
||||
sfOut.write(0xD0); // SF_TRANSFER_DATA
|
||||
sfOut.write((FTConstants.TR_DATA_INSERT >> 8) & 0xFF);
|
||||
sfOut.write(FTConstants.TR_DATA_INSERT & 0xFF);
|
||||
sfOut.write((FTConstants.TR_NOT_COMPRESSED >> 8) & 0xFF);
|
||||
sfOut.write(FTConstants.TR_NOT_COMPRESSED & 0xFF);
|
||||
sfOut.write(FTConstants.TR_BEGIN_DATA);
|
||||
sfOut.write((totalPayloadLen >> 8) & 0xFF);
|
||||
sfOut.write(totalPayloadLen & 0xFF);
|
||||
sfOut.write(msgBytes, 0, msgBytes.length);
|
||||
|
||||
byte[] insertData = sfOut.toByteArray();
|
||||
int sfLen = insertData.length;
|
||||
insertData[0] = (byte) ((sfLen >> 8) & 0xFF);
|
||||
insertData[1] = (byte) (sfLen & 0xFF);
|
||||
|
||||
dft.processStructuredField(insertData, 0, insertData.length);
|
||||
|
||||
// Verify that onTransferAborted was called with the error message
|
||||
assertTrue(listener.abortCalled, "onTransferAborted should be called for TRANS14 error");
|
||||
assertNotNull(listener.abortMsg);
|
||||
assertTrue(listener.abortMsg.contains("TRANS14"));
|
||||
assertFalse(listener.completeCalled, "onTransferComplete should NOT be called for an error");
|
||||
|
||||
// Verify that the error message was NOT written to the destination file stream!
|
||||
assertEquals(0, outStream.size(), "Error message should not be written to destination file");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenDataStreamAndWriteData() {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
dft.initTransfer(null, outStream);
|
||||
|
||||
// 1. Simulate TR_OPEN_REQ with ASCII "FT:DATA"
|
||||
byte[] openReq = new byte[] {
|
||||
0x00, 0x23, (byte) 0xD0, 0x00, 0x12, 0x01, 0x06, 0x01, 0x01, 0x04,
|
||||
0x03, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x01,
|
||||
0x00, 0x50, 0x05, 0x52, 0x03, (byte) 0xF0, 0x03, 0x09,
|
||||
0x46, 0x54, 0x3A, 0x44, 0x41, 0x54, 0x41 // "FT:DATA"
|
||||
};
|
||||
|
||||
dft.processStructuredField(openReq, 0, openReq.length);
|
||||
assertTrue(listener.dftRunningCalled, "onDftRunning should be called for file data stream");
|
||||
|
||||
// 2. Simulate TR_DATA_INSERT with binary file payload
|
||||
byte[] fileData = new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E, 0x34 }; // "%PDF-1.4"
|
||||
int totalPayloadLen = fileData.length + 5;
|
||||
|
||||
ByteArrayOutputStream sfOut = new ByteArrayOutputStream();
|
||||
sfOut.write(0); sfOut.write(0);
|
||||
sfOut.write(0xD0);
|
||||
sfOut.write((FTConstants.TR_DATA_INSERT >> 8) & 0xFF);
|
||||
sfOut.write(FTConstants.TR_DATA_INSERT & 0xFF);
|
||||
sfOut.write((FTConstants.TR_NOT_COMPRESSED >> 8) & 0xFF);
|
||||
sfOut.write(FTConstants.TR_NOT_COMPRESSED & 0xFF);
|
||||
sfOut.write(FTConstants.TR_BEGIN_DATA);
|
||||
sfOut.write((totalPayloadLen >> 8) & 0xFF);
|
||||
sfOut.write(totalPayloadLen & 0xFF);
|
||||
sfOut.write(fileData, 0, fileData.length);
|
||||
|
||||
byte[] insertData = sfOut.toByteArray();
|
||||
int sfLen = insertData.length;
|
||||
insertData[0] = (byte) ((sfLen >> 8) & 0xFF);
|
||||
insertData[1] = (byte) (sfLen & 0xFF);
|
||||
|
||||
dft.processStructuredField(insertData, 0, insertData.length);
|
||||
|
||||
// Data must be written to file stream
|
||||
assertArrayEquals(fileData, outStream.toByteArray());
|
||||
assertEquals(8, listener.bytesTransferred);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package haus.nightmare.lib3270j.graphics;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.awt.Point;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class GddmCoordinateTransformTest {
|
||||
|
||||
@Test
|
||||
public void testModel4PresentationSpaceMetrics() {
|
||||
GddmCoordinateTransform transform = new GddmCoordinateTransform(80, 43, 9, 16);
|
||||
|
||||
assertEquals(720, transform.getTotalWidth());
|
||||
assertEquals(688, transform.getTotalHeight());
|
||||
assertEquals(360, transform.getXMax());
|
||||
assertEquals(343, transform.getYMax());
|
||||
|
||||
// Screen center (0, 0)
|
||||
Point baseCenter = transform.gocaToBase(0, 0);
|
||||
assertEquals(360, baseCenter.x);
|
||||
assertEquals(343, baseCenter.y);
|
||||
assertEquals(new Point(0, 0), transform.baseToGoca(360, 343));
|
||||
|
||||
// Bidirectional screen pixel transformation
|
||||
// Cell width = 10, Cell height = 20, Offset = (100, 50)
|
||||
int ox = 100, oy = 50, cw = 10, ch = 20;
|
||||
Point screenPt = transform.gocaToScreenPixel(0, 0, ox, oy, cw, ch);
|
||||
Point roundtripGoca = transform.screenPixelToGoca(screenPt.x, screenPt.y, ox, oy, cw, ch);
|
||||
assertEquals(0, roundtripGoca.x);
|
||||
assertEquals(0, roundtripGoca.y);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModel2PresentationSpaceMetrics() {
|
||||
GddmCoordinateTransform transform = new GddmCoordinateTransform(80, 24, 9, 16);
|
||||
|
||||
assertEquals(720, transform.getTotalWidth());
|
||||
assertEquals(384, transform.getTotalHeight());
|
||||
assertEquals(360, transform.getXMax());
|
||||
assertEquals(191, transform.getYMax());
|
||||
}
|
||||
}
|
||||
@@ -221,6 +221,12 @@ public class GocaDecoderTest {
|
||||
GraphicsPlane plane = new GraphicsPlane(1000, 750);
|
||||
plane.setScreenDimensions(80, 43);
|
||||
|
||||
// Standard IBM 3179G 43-row presentation space (80*9 = 720 wide, 43*16 = 688 high)
|
||||
assertEquals(720, plane.getTotalWidth());
|
||||
assertEquals(688, plane.getTotalHeight());
|
||||
assertEquals(360, plane.getXMax());
|
||||
assertEquals(343, plane.getYMax());
|
||||
|
||||
// Screen center (0, 0) should map to canvas center
|
||||
assertEquals(500, plane.mapX(0));
|
||||
assertEquals(374, plane.mapY(0));
|
||||
@@ -240,6 +246,13 @@ public class GocaDecoderTest {
|
||||
assertEquals(0, plane.unmapY(plane.mapY(0)));
|
||||
assertEquals(100, plane.unmapY(plane.mapY(100)));
|
||||
assertEquals(-200, plane.unmapY(plane.mapY(-200)));
|
||||
|
||||
// Standard IBM 3179G Model 2 (24-row) presentation space (24*16 = 384 high, yMax = 191)
|
||||
plane.setScreenDimensions(80, 24);
|
||||
assertEquals(720, plane.getTotalWidth());
|
||||
assertEquals(384, plane.getTotalHeight());
|
||||
assertEquals(360, plane.getXMax());
|
||||
assertEquals(191, plane.getYMax());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -735,8 +748,9 @@ public class GocaDecoderTest {
|
||||
out.write(0x00); out.write(20); out.write(0x00); out.write(40);
|
||||
out.write(GocaConstants.G_GEAR);
|
||||
|
||||
// 4. Draw White slide boundary frame (Color set to 8/Black before GBAR, GSCOL White inside GBAR)
|
||||
// 4. Draw White slide boundary frame (Color set to 8/Black before GBAR, Pattern 15/Empty, GSCOL White inside GBAR)
|
||||
out.write(GocaConstants.G_GSCOL); out.write(0x08); // Black (background)
|
||||
out.write(GocaConstants.G_GSPT); out.write(0x0F); // Empty pattern (transparent interior)
|
||||
out.write(GocaConstants.G_GBAR); out.write(0x80);
|
||||
out.write(GocaConstants.G_GSCOL); out.write(0x07); // White line color
|
||||
out.write(GocaConstants.G_GSLT); out.write(GocaConstants.LT_DOT); // Dotted line
|
||||
|
||||
@@ -154,38 +154,170 @@ public class InputProcessorTest {
|
||||
assertEquals((byte) 0xFF, sf[34]);
|
||||
assertEquals((byte) AID_ENTER, sf[35]);
|
||||
|
||||
// Mouse Button 1 with picked segment 2 and tag 5
|
||||
// Mouse Button 1 matching HODInput.java (0x04 trigger constants and 0x23 correlation descriptor)
|
||||
byte[] sfMouse = haus.nightmare.lib3270j.graphics.GraphicInputBuilder.buildGraphicInput(
|
||||
294, 192, 1, true, false, false, 2, 5
|
||||
294, 192, 1, true, false, false
|
||||
);
|
||||
assertEquals(56, sfMouse.length);
|
||||
assertEquals(0x00, sfMouse[0]);
|
||||
assertEquals(0x38, sfMouse[1]);
|
||||
assertEquals(0x23, sfMouse[16]);
|
||||
assertEquals(0x00, sfMouse[17]);
|
||||
assertEquals(0x23, sfMouse[18]);
|
||||
assertEquals(0x00, sfMouse[19]);
|
||||
int mx = (sfMouse[24] << 8) | (sfMouse[25] & 0xFF);
|
||||
int my = (sfMouse[26] << 8) | (sfMouse[27] & 0xFF);
|
||||
assertEquals(294, (short) mx);
|
||||
assertEquals(192, (short) my);
|
||||
int segId = ((sfMouse[28] & 0xFF) << 24) | ((sfMouse[29] & 0xFF) << 16) |
|
||||
((sfMouse[30] & 0xFF) << 8) | (sfMouse[31] & 0xFF);
|
||||
assertEquals(2, segId);
|
||||
int tag = ((sfMouse[32] & 0xFF) << 8) | (sfMouse[33] & 0xFF);
|
||||
assertEquals(5, tag);
|
||||
assertEquals(0x00, sfMouse[28]);
|
||||
assertEquals(0x00, sfMouse[29]);
|
||||
assertEquals(0x00, sfMouse[30]);
|
||||
assertEquals(0x04, sfMouse[31]); // Mouse trigger class constant (0x04)
|
||||
assertEquals(0x00, sfMouse[32]);
|
||||
assertEquals(0x04, sfMouse[33]); // Mouse correlation class constant (0x04)
|
||||
assertEquals(0x00, sfMouse[34]);
|
||||
assertEquals(0x01, sfMouse[35]); // Button 1
|
||||
|
||||
// Mouse Button 2 (Action) with no segment (0)
|
||||
// Mouse Button 2 (Action) with Shift modifier
|
||||
byte[] sfMouse2 = haus.nightmare.lib3270j.graphics.GraphicInputBuilder.buildGraphicInput(
|
||||
0, 0, 2, true, true, false, 0, 0
|
||||
0, 0, 2, true, true, false
|
||||
);
|
||||
int segId2 = ((sfMouse2[28] & 0xFF) << 24) | ((sfMouse2[29] & 0xFF) << 16) |
|
||||
((sfMouse2[30] & 0xFF) << 8) | (sfMouse2[31] & 0xFF);
|
||||
assertEquals(0, segId2);
|
||||
int tag2 = ((sfMouse2[32] & 0xFF) << 8) | (sfMouse2[33] & 0xFF);
|
||||
assertEquals(0, tag2);
|
||||
assertEquals(0x23, sfMouse2[16]);
|
||||
assertEquals(0x00, sfMouse2[17]);
|
||||
assertEquals(0x23, sfMouse2[18]);
|
||||
assertEquals(0x00, sfMouse2[19]);
|
||||
assertEquals(0x04, sfMouse2[31]);
|
||||
assertEquals(0x04, sfMouse2[33]);
|
||||
assertEquals((byte) 0x80, sfMouse2[34]); // Shift modifier
|
||||
assertEquals(0x02, sfMouse2[35]); // Button 2
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLightPenSelectImmediateSpace() {
|
||||
screen.erase(false);
|
||||
// Field attribute at pos 0: unprotected, intensified selectable (0x08)
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_INT_HIGH_SEL));
|
||||
// Designator character at pos 1: space (immediate select)
|
||||
screen.getCell(1).ec = 0x40;
|
||||
screen.getCell(1).ucs4 = ' ';
|
||||
screen.getCell(2).ec = (byte) 0xC1;
|
||||
screen.getCell(2).ucs4 = 'A';
|
||||
|
||||
java.util.concurrent.atomic.AtomicInteger sentAid = new java.util.concurrent.atomic.AtomicInteger(-1);
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
public void sendAid(int aidCode) {
|
||||
sentAid.set(aidCode);
|
||||
}
|
||||
};
|
||||
|
||||
boolean result = input.lightPenSelect(2);
|
||||
assertTrue(result);
|
||||
assertEquals(AID_SELECT, sentAid.get());
|
||||
assertTrue((screen.getCell(0).fa & FA_MODIFY) != 0);
|
||||
assertEquals(2, screen.getCursorAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLightPenSelectEnterImmediateAmpersand() {
|
||||
screen.erase(false);
|
||||
// Field attribute at pos 0: normal detectable (0x04)
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_INT_NORM_SEL));
|
||||
// Designator character at pos 1: '&' (enter immediate)
|
||||
screen.getCell(1).ec = 0x50;
|
||||
screen.getCell(1).ucs4 = '&';
|
||||
|
||||
java.util.concurrent.atomic.AtomicInteger sentAid = new java.util.concurrent.atomic.AtomicInteger(-1);
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
public void sendAid(int aidCode) {
|
||||
sentAid.set(aidCode);
|
||||
}
|
||||
};
|
||||
|
||||
boolean result = input.lightPenSelect(1);
|
||||
assertTrue(result);
|
||||
assertEquals(AID_ENTER, sentAid.get());
|
||||
assertTrue((screen.getCell(0).fa & FA_MODIFY) != 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLightPenSelectDeferredToggle() {
|
||||
screen.erase(false);
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_INT_NORM_SEL));
|
||||
// Designator at pos 1: '?' (deferred select)
|
||||
screen.getCell(1).ec = 0x6F;
|
||||
screen.getCell(1).ucs4 = '?';
|
||||
|
||||
java.util.concurrent.atomic.AtomicInteger sentAid = new java.util.concurrent.atomic.AtomicInteger(-1);
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
public void sendAid(int aidCode) {
|
||||
sentAid.set(aidCode);
|
||||
}
|
||||
};
|
||||
|
||||
// First click: ? -> > and MDT=1, no transmission
|
||||
boolean r1 = input.lightPenSelect(1);
|
||||
assertTrue(r1);
|
||||
assertEquals(-1, sentAid.get());
|
||||
assertEquals('>', screen.getCell(1).ucs4);
|
||||
assertEquals((byte) 0x6E, screen.getCell(1).ec);
|
||||
assertTrue((screen.getCell(0).fa & FA_MODIFY) != 0);
|
||||
|
||||
// Second click: > -> ? and MDT=0, no transmission
|
||||
boolean r2 = input.lightPenSelect(1);
|
||||
assertTrue(r2);
|
||||
assertEquals(-1, sentAid.get());
|
||||
assertEquals('?', screen.getCell(1).ucs4);
|
||||
assertEquals((byte) 0x6F, screen.getCell(1).ec);
|
||||
assertFalse((screen.getCell(0).fa & FA_MODIFY) != 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLightPenSelectInvalidDesignatorRejected() {
|
||||
screen.erase(false);
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_INT_NORM_SEL));
|
||||
// Designator at pos 1: 'A' (invalid designator)
|
||||
screen.getCell(1).ec = (byte) 0xC1;
|
||||
screen.getCell(1).ucs4 = 'A';
|
||||
|
||||
java.util.concurrent.atomic.AtomicInteger sentAid = new java.util.concurrent.atomic.AtomicInteger(-1);
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
public void sendAid(int aidCode) {
|
||||
sentAid.set(aidCode);
|
||||
}
|
||||
};
|
||||
|
||||
boolean result = input.lightPenSelect(1);
|
||||
assertFalse(result);
|
||||
assertEquals(-1, sentAid.get());
|
||||
assertFalse((screen.getCell(0).fa & FA_MODIFY) != 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCursorSelectKey() {
|
||||
screen.erase(false);
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_INT_NORM_SEL));
|
||||
screen.getCell(1).ec = 0x50;
|
||||
screen.getCell(1).ucs4 = '&';
|
||||
screen.setCursorAddress(5);
|
||||
|
||||
java.util.concurrent.atomic.AtomicInteger sentAid = new java.util.concurrent.atomic.AtomicInteger(-1);
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
public void sendAid(int aidCode) {
|
||||
sentAid.set(aidCode);
|
||||
}
|
||||
};
|
||||
|
||||
boolean result = input.cursorSelect();
|
||||
assertTrue(result);
|
||||
assertEquals(AID_ENTER, sentAid.get());
|
||||
assertEquals(5, screen.getCursorAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendAidSuppressesNullsInModifiedField() {
|
||||
java.util.concurrent.atomic.AtomicReference<byte[]> sent = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
@@ -231,4 +363,59 @@ public class InputProcessorTest {
|
||||
assertEquals((byte) 0xC9, result[8]); // 'I'
|
||||
assertEquals((byte) 0xE3, result[9]); // 'T'
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendGraphicMouseAidFraming() {
|
||||
screen.erase(false);
|
||||
screen.setCellFA(0, (byte) (FA_PRINTABLE | FA_MODIFY));
|
||||
screen.getCell(1).ec = (byte) 0xC1; // 'A'
|
||||
screen.setCellFA(5, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||
screen.setCursorAddress(2);
|
||||
|
||||
haus.nightmare.lib3270j.graphics.GraphicsPlane plane = new haus.nightmare.lib3270j.graphics.GraphicsPlane(800, 600);
|
||||
haus.nightmare.lib3270j.graphics.GocaDecoder goca = new haus.nightmare.lib3270j.graphics.GocaDecoder(plane);
|
||||
goca.setGraphicsCursorActive(true);
|
||||
goca.setGraphicCursorPosition(150, -80);
|
||||
|
||||
java.util.concurrent.atomic.AtomicReference<byte[]> sent = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
InputProcessor input = new InputProcessor(screen, translator, null) {
|
||||
@Override
|
||||
protected void sendAidResponse(byte[] data) {
|
||||
sent.set(data);
|
||||
}
|
||||
};
|
||||
input.setGocaDecoder(goca);
|
||||
|
||||
input.sendGraphicMouseAid(AID_ENTER, 1, false, false);
|
||||
|
||||
byte[] result = sent.get();
|
||||
assertNotNull(result);
|
||||
// Total expected length:
|
||||
// 1 (AID_SF 0x88) + 56 (SF) + 1 (AID_ENTER 0x7D) + 2 (Cursor Addr) + 1 (SBA) + 2 (Field Addr) + 1 (Data 'A') = 64 bytes
|
||||
assertEquals(64, result.length);
|
||||
assertEquals((byte) AID_SF, result[0]);
|
||||
// SF length = 56 (0x00 0x38)
|
||||
assertEquals(0x00, result[1]);
|
||||
assertEquals(0x38, result[2]);
|
||||
// SF ID = 0x0F0F
|
||||
assertEquals(0x0F, result[3]);
|
||||
assertEquals(0x0F, result[4]);
|
||||
// Coordinates in SF at index 1 + 24 = 25
|
||||
int gx = (result[25] << 8) | (result[26] & 0xFF);
|
||||
int gy = (result[27] << 8) | (result[28] & 0xFF);
|
||||
assertEquals(150, (short) gx);
|
||||
assertEquals(-80, (short) gy);
|
||||
// Mouse constant at index 1 + 31 = 32
|
||||
assertEquals(0x04, result[32]);
|
||||
assertEquals(0x04, result[34]);
|
||||
// Button 1 at index 1 + 35 = 36
|
||||
assertEquals(0x01, result[36]);
|
||||
|
||||
// Trailing AID at index 57
|
||||
assertEquals((byte) AID_ENTER, result[57]);
|
||||
// Trailing SBA at 60
|
||||
assertEquals((byte) ORDER_SBA, result[60]);
|
||||
// Trailing field content 'A' at 63
|
||||
assertEquals((byte) 0xC1, result[63]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user