From 27976dd31f5959a5a41ad142f52d9f0cbd1499da Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 30 Aug 2026 23:46:29 +0000 Subject: [PATCH] Add missing menus back --- .../java/haus/nightmare/j3270/J3270App.java | 4 +- .../nightmare/j3270/ui/TerminalPanel.java | 2 +- .../datastream/QueryReplyBuilder.java | 15 +++-- .../lib3270j/graphics/GocaConstants.java | 5 +- .../lib3270j/graphics/GocaDecoder.java | 55 +++++++++---------- .../datastream/QueryReplyBuilderTest.java | 37 ++++++++----- 6 files changed, 62 insertions(+), 56 deletions(-) diff --git a/j3270/src/main/java/haus/nightmare/j3270/J3270App.java b/j3270/src/main/java/haus/nightmare/j3270/J3270App.java index 3f410cd..0543ca9 100644 --- a/j3270/src/main/java/haus/nightmare/j3270/J3270App.java +++ b/j3270/src/main/java/haus/nightmare/j3270/J3270App.java @@ -48,7 +48,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate public J3270App() { super("j3270 — Java TN3270 Terminal Emulator"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBackground(new Color(10, 10, 10)); + setBackground(Color.BLACK); buildUI(); buildMenuBar(); @@ -92,7 +92,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate statusBar = new StatusBar(); getContentPane().setLayout(new BorderLayout()); - getContentPane().setBackground(new Color(10, 10, 10)); + getContentPane().setBackground(Color.BLACK); getContentPane().add(terminalPanel, BorderLayout.CENTER); getContentPane().add(statusBar, BorderLayout.SOUTH); } diff --git a/j3270/src/main/java/haus/nightmare/j3270/ui/TerminalPanel.java b/j3270/src/main/java/haus/nightmare/j3270/ui/TerminalPanel.java index eba175c..1ffa1fa 100644 --- a/j3270/src/main/java/haus/nightmare/j3270/ui/TerminalPanel.java +++ b/j3270/src/main/java/haus/nightmare/j3270/ui/TerminalPanel.java @@ -123,7 +123,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable { public static final Color DEFAULT_MONO_PROTECTED_HIGH = new Color(255, 255, 255); // Default Background - public static final Color DEFAULT_BG_COLOR = new Color(10, 10, 10); + public static final Color DEFAULT_BG_COLOR = Color.BLACK; public TerminalPanel() { setupColors(); diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java index 2ca7b47..3c97cde 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java @@ -18,9 +18,9 @@ public class QueryReplyBuilder { private static final int SW_3279_2 = 0x09; private static final int SH_3279_2 = 0x0c; - // Usable Area physical dimensions matching IBM Host On-Demand QR_USEAREA_STRING (Inches) - private static final int Xr_HOD = 0x00020089; - private static final int Yr_HOD = 0x00020085; + // Usable Area physical dimensions matching IBM Host On-Demand DS3270.java (Inches, 96 dpi: 0x00010060) + private static final int Xr_HOD = 0x00010060; + private static final int Yr_HOD = 0x00010060; private final ScreenBuffer screen; private GraphicsMode graphicsMode = GraphicsMode.BOTH; @@ -309,7 +309,7 @@ public class QueryReplyBuilder { private byte[] buildUsableArea(int maxCols, int maxRows, int bufferSize) { ByteArrayOutputStream out = new ByteArrayOutputStream(19); - out.write(0x01); // 12/14-bit addressing + out.write(graphicsMode.isVectorGraphicsEnabled() ? 0x03 : 0x01); // 12/14-bit addressing + graphics flag (matching HOD DS3270.java) out.write(0x00); // no special character features out.write((maxCols >> 8) & 0xFF); // usable width high out.write(maxCols & 0xFF); // usable width low @@ -380,8 +380,8 @@ public class QueryReplyBuilder { cpgid = screen.getTranslator().getCpgid(); } - if (graphicsMode.isProgrammedSymbolsEnabled()) { - // Programmed Symbols mode (3279 PS with LoadPS 0x0A, flags1 = 0xA2 for GE + PS + CGCSGID) + if (graphicsMode == GraphicsMode.PROGRAMMED_SYMBOLS) { + // Programmed Symbols only mode (3279 PS with LoadPS 0x0A, flags1 = 0xA2 for GE + PS + CGCSGID) ByteArrayOutputStream out = new ByteArrayOutputStream(65); out.write(0xa2); // flags: GE (0x80), PS/Loadable Charsets (0x20), CGCSGID present (0x02) out.write(0x00); // more flags @@ -615,11 +615,10 @@ public class QueryReplyBuilder { cgcsgid = screen.getTranslator().getCgcsgid(); cpgid = screen.getTranslator().getCpgid(); } - // HOD QueryReply3270Constants.java QR_GRSYMBOLSET_S_STRING ("\u0000!\u0081\u00b6...") return new byte[]{ 0x00, 0x00, (byte) charW, (byte) charH, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, (byte) 0xF0, (byte) ((cgcsgid >> 8) & 0xFF), (byte) (cgcsgid & 0xFF), 0x00, 0x00, - (byte) charW, (byte) charH, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + (byte) charW, (byte) charH, 0x00, 0x00, (byte) 0xF0, (byte) ((cgcsgid >> 8) & 0xFF), (byte) (cgcsgid & 0xFF), 0x00, 0x00 }; } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaConstants.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaConstants.java index 7020541..2040d67 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaConstants.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaConstants.java @@ -60,10 +60,9 @@ public final class GocaConstants { public static final int G_GSCR = 0x35; // Set Character Shear public static final int G_GSMCEL = 0x37; // Set Marker Cell public static final int G_GSCS = 0x38; // Set Character Set - public static final int G_GSMP = 0x39; // Set Marker Precision - public static final int G_GSETAG = 0x39; // Set Pick Identifier / Tag + public static final int G_GSCC = 0x39; // Set Character Precision public static final int G_GSCD = 0x3A; // Set Character Direction - public static final int G_GSCC = 0x3B; // Set Character Precision + public static final int G_GSMP = 0x3B; // Set Marker Precision public static final int G_GSMS_SET = 0x3C; // Set Marker Set public static final int G_ENDPROLOGUE = 0x3E; // End Prologue public static final int G_GPOP = 0x3F; // Pop Attribute diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaDecoder.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaDecoder.java index 3be663f..55d6b06 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaDecoder.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/graphics/GocaDecoder.java @@ -27,6 +27,7 @@ public class GocaDecoder { private int markerType = GocaConstants.MK_PLUS; private int markerSize = 5; private int markerColor = GocaConstants.GOCA_COLORS[0]; + private int markerPrecision = 0; private int pattern = GocaConstants.PT_SOLID; private int patternSet = 0; private int fillColor = GocaConstants.GOCA_COLORS[0]; @@ -225,6 +226,7 @@ public class GocaDecoder { markerType = GocaConstants.MK_PLUS; markerSize = 5; markerColor = curColor; + markerPrecision = 0; pattern = GocaConstants.PT_SOLID; patternSet = 0; fillColor = curColor; @@ -281,7 +283,7 @@ public class GocaDecoder { // Flexible 1-byte attribute orders (support both short 2-byte or long 3-byte if len byte == 1) if (order == GocaConstants.G_GSPT || order == GocaConstants.G_GSMT || order == GocaConstants.G_GSCS || order == GocaConstants.G_GSCD || - order == GocaConstants.G_GSCC || + order == GocaConstants.G_GSCC || order == GocaConstants.G_GSMP || order == GocaConstants.G_GSMS_SET || order == GocaConstants.G_GBAR) { return (data[idx + 1] == 0x01 && idx + 2 < end) ? 3 : 2; } @@ -536,17 +538,7 @@ public class GocaDecoder { } break; } - case GocaConstants.G_GSETAG: { // Set Pick Identifier / Tag (0x39) - if (currentSegId != 0 && payloadLen >= 2 && idx + 3 < end) { - int tag = ((inputData[idx + 2] & 0xFF) << 8) | (inputData[idx + 3] & 0xFF); - SegmentBounds sb = segmentBoundsMap.get(currentSegId); - if (sb != null) { - sb.tag = tag; - } - } - idx += orderLen; - break; - } + case GocaConstants.G_ENDPROLOGUE: { // End Prologue (0x3E) idx += orderLen; break; @@ -706,16 +698,22 @@ public class GocaDecoder { break; } case GocaConstants.G_GSCS: { // Set Character Set (0x38) - charSet = (orderLen == 3) ? (inputData[idx + 2] & 0xFF) : (inputData[idx + 1] & 0xFF); + int cs = (orderLen == 3) ? (inputData[idx + 2] & 0xFF) : (inputData[idx + 1] & 0xFF); + charSet = (cs == 0xF0) ? 0 : cs; idx += orderLen; break; } - case GocaConstants.G_GSCC: { // Set Character Precision (0x3B) + case GocaConstants.G_GSCC: { // Set Character Precision (0x39) charPrecision = (orderLen == 3) ? (inputData[idx + 2] & 0xFF) : (inputData[idx + 1] & 0xFF); if (charPrecision == 0) charPrecision = GocaConstants.CP_STRING; idx += orderLen; break; } + case GocaConstants.G_GSMP: { // Set Marker Precision (0x3B) + markerPrecision = (orderLen == 3) ? (inputData[idx + 2] & 0xFF) : (inputData[idx + 1] & 0xFF); + idx += orderLen; + break; + } case GocaConstants.G_GSMX: case GocaConstants.G_GSMS_SET: case GocaConstants.G_GPOP: { @@ -1393,7 +1391,15 @@ public class GocaDecoder { break; } - if (charSet != 0 && programSymbolManager != null) { + if (charSet == 0xF8 || charPrecision == GocaConstants.CP_STROKE) { + char[] chars = new char[textLen]; + for (int i = 0; i < textLen; i++) { + chars[i] = EbcdicTranslator.ebcdicToAscii(data[pos + i]); + } + String text = new String(chars); + plane.drawVectorText(plane.mapXDouble(startX), plane.mapYDouble(startY), text, + curColor, cw, ch, charDir, charAngle); + } else if (charSet != 0 && programSymbolManager != null) { for (int i = 0; i < textLen; i++) { int code = data[pos + i] & 0xFF; double px = plane.mapXDouble(startX); @@ -1417,27 +1423,18 @@ public class GocaDecoder { } } } - } else { - char c = EbcdicTranslator.ebcdicToAscii(data[pos + i]); - plane.drawVectorText(px, py, String.valueOf(c), curColor, cw, ch, charDir, charAngle); } startX += (charWidth > 0 ? charWidth : 9); } curX = startX; curY = startY; return; - } - - char[] chars = new char[textLen]; - for (int i = 0; i < textLen; i++) { - chars[i] = EbcdicTranslator.ebcdicToAscii(data[pos + i]); - } - String text = new String(chars); - - if (charPrecision == GocaConstants.CP_STROKE) { - plane.drawVectorText(plane.mapXDouble(startX), plane.mapYDouble(startY), text, - curColor, cw, ch, charDir, charAngle); } else { + char[] chars = new char[textLen]; + for (int i = 0; i < textLen; i++) { + chars[i] = EbcdicTranslator.ebcdicToAscii(data[pos + i]); + } + String text = new String(chars); plane.drawText(plane.mapXDouble(startX), plane.mapYDouble(startY), text, curColor, cw, ch, charDir, charAngle); } diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilderTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilderTest.java index 488693f..dce4449 100644 --- a/lib3270j/src/test/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilderTest.java +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilderTest.java @@ -81,21 +81,19 @@ public class QueryReplyBuilderTest { byte[] replies = qrBuilder.buildCompleteQueryReplies(80, 43, 80 * 43); assertNotNull(replies); - // Vector Graphics (0xB0) must be present and Charsets must have LoadPS (0x0A) + // Vector Graphics (0xB0) must be present and Charsets must be present (0x85) boolean hasB0 = false; - boolean hasCharsetsWithLoadPs = false; + boolean hasCharsets = false; for (int i = 0; i < replies.length - 3; i++) { if ((replies[i] & 0xFF) == 0x81 && (replies[i + 1] & 0xFF) == QR_SEGMENT) { hasB0 = true; } if ((replies[i] & 0xFF) == 0x81 && (completeReplyIsCharsets(replies, i))) { - if (i + 6 < replies.length && (replies[i + 6] & 0xFF) == 0x0A) { - hasCharsetsWithLoadPs = true; - } + hasCharsets = true; } } assertTrue(hasB0); - assertTrue(hasCharsetsWithLoadPs); + assertTrue(hasCharsets); } private boolean completeReplyIsCharsets(byte[] replies, int i) { @@ -283,24 +281,24 @@ public class QueryReplyBuilderTest { assertEquals(0x81, replies[3] & 0xFF); assertEquals(QR_USABLE_AREA, replies[4] & 0xFF); - // Flags: 12/14 bit addressing (0x01), 0x00 - assertEquals(0x01, replies[5] & 0xFF); + // Flags: 12/14 bit addressing | Graphics (0x03), 0x00 + assertEquals(0x03, replies[5] & 0xFF); assertEquals(0x00, replies[6] & 0xFF); // Usable width and height: 80, 43 assertEquals(80, ((replies[7] & 0xFF) << 8) | (replies[8] & 0xFF)); assertEquals(43, ((replies[9] & 0xFF) << 8) | (replies[10] & 0xFF)); - // Units: 0x00 (Inches, matching IBM Host On-Demand QR_USEAREA_STRING) + // Units: 0x00 (Inches, matching IBM Host On-Demand DS3270.java) assertEquals(0x00, replies[11] & 0xFF); - // Xr (4 bytes): 0x00020089 (matching HOD) + // Xr (4 bytes): 0x00010060 (96 dpi matching HOD) int xr = ((replies[12] & 0xFF) << 24) | ((replies[13] & 0xFF) << 16) | ((replies[14] & 0xFF) << 8) | (replies[15] & 0xFF); - assertEquals(0x00020089, xr); + assertEquals(0x00010060, xr); - // Yr (4 bytes): 0x00020085 (matching HOD) + // Yr (4 bytes): 0x00010060 (96 dpi matching HOD) int yr = ((replies[16] & 0xFF) << 24) | ((replies[17] & 0xFF) << 16) | ((replies[18] & 0xFF) << 8) | (replies[19] & 0xFF); - assertEquals(0x00020085, yr); + assertEquals(0x00010060, yr); // AW and AH: 9 and 16 assertEquals(9, replies[20] & 0xFF); @@ -309,4 +307,17 @@ public class QueryReplyBuilderTest { // Buffer size: 80 * 43 = 3440 assertEquals(80 * 43, ((replies[22] & 0xFF) << 8) | (replies[23] & 0xFF)); } + + @Test + public void testGrSymbolSetMatchesHOD() { + qrBuilder.setGraphicsMode(GraphicsMode.VECTOR_GRAPHICS); + byte[] requested = new byte[] { (byte) QR_GRSYMBOLSET }; + byte[] replies = qrBuilder.buildQueryReplies(requested, 80, 43, 80 * 43); + + assertNotNull(replies); + assertEquals((byte) AID_SF, replies[0]); + assertEquals(33, ((replies[1] & 0xFF) << 8) | (replies[2] & 0xFF)); + assertEquals(0x81, replies[3] & 0xFF); + assertEquals(QR_GRSYMBOLSET, replies[4] & 0xFF); + } }