Compare commits
2 Commits
0837de8db3
...
e28b7dc30a
| Author | SHA1 | Date | |
|---|---|---|---|
| e28b7dc30a | |||
| d9df1052a1 |
@@ -1,4 +1,7 @@
|
|||||||
j3270.log.*
|
j3270.log.*
|
||||||
|
*.txt
|
||||||
|
*.py
|
||||||
|
*.pcap
|
||||||
build/*
|
build/*
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
|
|
||||||
// Actions menu
|
// Actions menu
|
||||||
JMenu actionsMenu = createMenu("Actions");
|
JMenu actionsMenu = createMenu("Actions");
|
||||||
actionsMenu.add(createMenuItem("Clear", KeyEvent.VK_L, () -> {
|
actionsMenu.add(createMenuItem("Clear", KeyEvent.VK_K, () -> {
|
||||||
if (client != null)
|
if (client != null)
|
||||||
client.sendClear();
|
client.sendClear();
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
@@ -131,6 +131,10 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
}));
|
}));
|
||||||
actionsMenu.addSeparator();
|
actionsMenu.addSeparator();
|
||||||
|
actionsMenu.add(createMenuItem("Toggle Light Pen (Alt+L)", KeyEvent.VK_L, () -> {
|
||||||
|
terminalPanel.toggleLightPen();
|
||||||
|
}, true));
|
||||||
|
actionsMenu.addSeparator();
|
||||||
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog));
|
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog));
|
||||||
menuBar.add(actionsMenu);
|
menuBar.add(actionsMenu);
|
||||||
|
|
||||||
@@ -149,18 +153,22 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JMenuItem createMenuItem(String text, int acceleratorKey, Runnable action) {
|
private JMenuItem createMenuItem(String name, int mnemonic, Runnable action, boolean useAlt) {
|
||||||
JMenuItem item = new JMenuItem(text);
|
JMenuItem item = new JMenuItem(name);
|
||||||
item.setBackground(new Color(40, 40, 40));
|
item.setBackground(new Color(40, 40, 40));
|
||||||
item.setForeground(new Color(200, 200, 200));
|
item.setForeground(new Color(200, 200, 200));
|
||||||
if (acceleratorKey > 0) {
|
if (mnemonic > 0) {
|
||||||
item.setAccelerator(KeyStroke.getKeyStroke(acceleratorKey,
|
int modifier = useAlt ? KeyEvent.ALT_DOWN_MASK : Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
||||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
|
item.setAccelerator(KeyStroke.getKeyStroke(mnemonic, modifier));
|
||||||
}
|
}
|
||||||
item.addActionListener(e -> action.run());
|
item.addActionListener(e -> action.run());
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JMenuItem createMenuItem(String name, int mnemonic, Runnable action) {
|
||||||
|
return createMenuItem(name, mnemonic, action, false);
|
||||||
|
}
|
||||||
|
|
||||||
private void showFileTransferDialog() {
|
private void showFileTransferDialog() {
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
JOptionPane.showMessageDialog(this, "Connect to a host before using File Transfer.",
|
JOptionPane.showMessageDialog(this, "Connect to a host before using File Transfer.",
|
||||||
@@ -278,7 +286,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
client.addScreenUpdateListener(this);
|
client.addScreenUpdateListener(this);
|
||||||
|
|
||||||
terminalPanel.setClient(client);
|
terminalPanel.setClient(client);
|
||||||
statusBar.setClient(client);
|
statusBar.setClient(client, terminalPanel);
|
||||||
|
|
||||||
String tlsIndicator = config.isUseTls() ? (config.isTlsVerifyCert() ? " [TLS]" : " [TLS/Unverified]") : "";
|
String tlsIndicator = config.isUseTls() ? (config.isTlsVerifyCert() ? " [TLS]" : " [TLS/Unverified]") : "";
|
||||||
setTitle("j3270 — " + config.getHost() + ":" + config.getPort() + tlsIndicator);
|
setTitle("j3270 — " + config.getHost() + ":" + config.getPort() + tlsIndicator);
|
||||||
@@ -312,7 +320,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
client.disconnect();
|
client.disconnect();
|
||||||
client = null;
|
client = null;
|
||||||
terminalPanel.setClient(null);
|
terminalPanel.setClient(null);
|
||||||
statusBar.setClient(null);
|
statusBar.setClient(null, terminalPanel);
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
setTitle("j3270 — Java TN3270 Terminal Emulator");
|
setTitle("j3270 — Java TN3270 Terminal Emulator");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class StatusBar extends JPanel {
|
|||||||
private final JLabel modelInfo;
|
private final JLabel modelInfo;
|
||||||
|
|
||||||
private Telnet3270Client client;
|
private Telnet3270Client client;
|
||||||
|
private TerminalPanel terminalPanel;
|
||||||
|
|
||||||
// OIA colors
|
// OIA colors
|
||||||
private static final Color OIA_BG = new Color(20, 20, 20);
|
private static final Color OIA_BG = new Color(20, 20, 20);
|
||||||
@@ -44,6 +45,19 @@ public class StatusBar extends JPanel {
|
|||||||
modelInfo = createLabel("", oiaFont, OIA_DIM);
|
modelInfo = createLabel("", oiaFont, OIA_DIM);
|
||||||
cursorPosition = createLabel("001/001", oiaFont, OIA_FG);
|
cursorPosition = createLabel("001/001", oiaFont, OIA_FG);
|
||||||
|
|
||||||
|
JButton lpButton = new JButton("LightPen: OFF");
|
||||||
|
lpButton.setFont(oiaFont);
|
||||||
|
lpButton.setForeground(OIA_FG);
|
||||||
|
lpButton.setBackground(OIA_BG);
|
||||||
|
lpButton.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
|
||||||
|
lpButton.setFocusable(false);
|
||||||
|
lpButton.addActionListener(e -> {
|
||||||
|
if (terminalPanel != null) {
|
||||||
|
terminalPanel.toggleLightPen();
|
||||||
|
lpButton.setText("LightPen: " + (terminalPanel.isLightPenMode() ? "ON" : "OFF"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
add(Box.createHorizontalStrut(6));
|
add(Box.createHorizontalStrut(6));
|
||||||
add(connectionStatus);
|
add(connectionStatus);
|
||||||
add(Box.createHorizontalStrut(10));
|
add(Box.createHorizontalStrut(10));
|
||||||
@@ -52,6 +66,8 @@ public class StatusBar extends JPanel {
|
|||||||
add(luName);
|
add(luName);
|
||||||
add(Box.createHorizontalStrut(12));
|
add(Box.createHorizontalStrut(12));
|
||||||
add(lockStatus);
|
add(lockStatus);
|
||||||
|
add(Box.createHorizontalStrut(12));
|
||||||
|
add(lpButton);
|
||||||
add(Box.createHorizontalGlue());
|
add(Box.createHorizontalGlue());
|
||||||
add(modelInfo);
|
add(modelInfo);
|
||||||
add(Box.createHorizontalStrut(12));
|
add(Box.createHorizontalStrut(12));
|
||||||
@@ -66,8 +82,9 @@ public class StatusBar extends JPanel {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setClient(Telnet3270Client client) {
|
public void setClient(Telnet3270Client client, TerminalPanel terminalPanel) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
this.terminalPanel = terminalPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateStatus() {
|
public void updateStatus() {
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ public class TerminalPanel extends JPanel {
|
|||||||
private static final Color SELECTION_COLOR = new Color(75, 110, 175, 100);
|
private static final Color SELECTION_COLOR = new Color(75, 110, 175, 100);
|
||||||
|
|
||||||
// ========== Resize guard ==========
|
// ========== Resize guard ==========
|
||||||
|
private boolean lightPenMode = false;
|
||||||
|
private long lastGraphicsUpdateCount = -1;
|
||||||
|
private java.awt.image.BufferedImage cachedGraphicsImage = null;
|
||||||
private boolean resizeGuard = false;
|
private boolean resizeGuard = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,7 +73,7 @@ public class TerminalPanel extends JPanel {
|
|||||||
*/
|
*/
|
||||||
private int getRenderOffsetX() {
|
private int getRenderOffsetX() {
|
||||||
int termCols = 80;
|
int termCols = 80;
|
||||||
if (client != null) termCols = client.getScreenBuffer().getCols();
|
if (client != null) termCols = client.getScreenBuffer().getDisplayCols();
|
||||||
int gridW = termCols * cellWidth;
|
int gridW = termCols * cellWidth;
|
||||||
int extra = getWidth() - gridW;
|
int extra = getWidth() - gridW;
|
||||||
return Math.max(padding, extra / 2);
|
return Math.max(padding, extra / 2);
|
||||||
@@ -78,10 +81,11 @@ public class TerminalPanel extends JPanel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the vertical render offset to center the grid within the panel.
|
* Compute the vertical render offset to center the grid within the panel.
|
||||||
|
* Must use getDisplayRows() to match paintComponent's iteration.
|
||||||
*/
|
*/
|
||||||
private int getRenderOffsetY() {
|
private int getRenderOffsetY() {
|
||||||
int termRows = 24;
|
int termRows = 24;
|
||||||
if (client != null) termRows = client.getScreenBuffer().getRows();
|
if (client != null) termRows = client.getScreenBuffer().getDisplayRows();
|
||||||
int gridH = termRows * cellHeight;
|
int gridH = termRows * cellHeight;
|
||||||
int extra = getHeight() - gridH;
|
int extra = getHeight() - gridH;
|
||||||
return Math.max(padding, extra / 2);
|
return Math.max(padding, extra / 2);
|
||||||
@@ -148,10 +152,12 @@ public class TerminalPanel extends JPanel {
|
|||||||
ScreenBuffer sb = client.getScreenBuffer();
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
int ox = getRenderOffsetX();
|
int ox = getRenderOffsetX();
|
||||||
int oy = getRenderOffsetY();
|
int oy = getRenderOffsetY();
|
||||||
|
int displayCols = sb.getDisplayCols();
|
||||||
|
int displayRows = sb.getDisplayRows();
|
||||||
int col = (e.getX() - ox) / cellWidth;
|
int col = (e.getX() - ox) / cellWidth;
|
||||||
int row = (e.getY() - oy) / cellHeight;
|
int row = (e.getY() - oy) / cellHeight;
|
||||||
col = Math.max(0, Math.min(col, sb.getCols() - 1));
|
col = Math.max(0, Math.min(col, displayCols - 1));
|
||||||
row = Math.max(0, Math.min(row, sb.getRows() - 1));
|
row = Math.max(0, Math.min(row, displayRows - 1));
|
||||||
|
|
||||||
// Start selection
|
// Start selection
|
||||||
selectionStartRow = row;
|
selectionStartRow = row;
|
||||||
@@ -171,8 +177,8 @@ public class TerminalPanel extends JPanel {
|
|||||||
int oy = getRenderOffsetY();
|
int oy = getRenderOffsetY();
|
||||||
int col = (e.getX() - ox) / cellWidth;
|
int col = (e.getX() - ox) / cellWidth;
|
||||||
int row = (e.getY() - oy) / cellHeight;
|
int row = (e.getY() - oy) / cellHeight;
|
||||||
col = Math.max(0, Math.min(col, sb.getCols() - 1));
|
col = Math.max(0, Math.min(col, sb.getDisplayCols() - 1));
|
||||||
row = Math.max(0, Math.min(row, sb.getRows() - 1));
|
row = Math.max(0, Math.min(row, sb.getDisplayRows() - 1));
|
||||||
|
|
||||||
selectionEndRow = row;
|
selectionEndRow = row;
|
||||||
selectionEndCol = col;
|
selectionEndCol = col;
|
||||||
@@ -204,9 +210,16 @@ public class TerminalPanel extends JPanel {
|
|||||||
if (selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol) {
|
if (selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol) {
|
||||||
if (client != null && client.getConnectionState().isFullSession()) {
|
if (client != null && client.getConnectionState().isFullSession()) {
|
||||||
ScreenBuffer sb = client.getScreenBuffer();
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
int newAddr = selectionStartRow * sb.getCols() + selectionStartCol;
|
int displayCols = sb.getDisplayCols();
|
||||||
|
int newAddr = selectionStartRow * displayCols + selectionStartCol;
|
||||||
sb.setCursorAddress(newAddr);
|
sb.setCursorAddress(newAddr);
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
if (lightPenMode) {
|
||||||
|
if (client.getInputProcessor().lightPenSelect(newAddr)) {
|
||||||
|
refreshScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (client.getGocaDecoder() != null && client.getGraphicsPlane() != null) {
|
if (client.getGocaDecoder() != null && client.getGraphicsPlane() != null) {
|
||||||
int ox = getRenderOffsetX();
|
int ox = getRenderOffsetX();
|
||||||
@@ -435,8 +448,8 @@ public class TerminalPanel extends JPanel {
|
|||||||
int termRows = 24;
|
int termRows = 24;
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
ScreenBuffer sb = client.getScreenBuffer();
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
termCols = sb.getCols();
|
termCols = sb.getDisplayCols();
|
||||||
termRows = sb.getRows();
|
termRows = sb.getDisplayRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use minimal padding for the fit calculation
|
// Use minimal padding for the fit calculation
|
||||||
@@ -558,6 +571,7 @@ public class TerminalPanel extends JPanel {
|
|||||||
int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, shortcutMask), "j3270-COPY");
|
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, shortcutMask), "j3270-COPY");
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, shortcutMask), "j3270-PASTE");
|
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, shortcutMask), "j3270-PASTE");
|
||||||
|
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, java.awt.event.InputEvent.ALT_DOWN_MASK), "j3270-LIGHTPEN");
|
||||||
|
|
||||||
// Create actions for all bound keys
|
// Create actions for all bound keys
|
||||||
am.put("j3270-ENTER", createAction(this::handleEnter));
|
am.put("j3270-ENTER", createAction(this::handleEnter));
|
||||||
@@ -586,6 +600,7 @@ public class TerminalPanel extends JPanel {
|
|||||||
// Copy/Paste actions
|
// Copy/Paste actions
|
||||||
am.put("j3270-COPY", createAction(this::copySelection));
|
am.put("j3270-COPY", createAction(this::copySelection));
|
||||||
am.put("j3270-PASTE", createAction(this::pasteClipboard));
|
am.put("j3270-PASTE", createAction(this::pasteClipboard));
|
||||||
|
am.put("j3270-LIGHTPEN", createAction(this::toggleLightPen));
|
||||||
|
|
||||||
for (int i = 1; i <= 24; i++) {
|
for (int i = 1; i <= 24; i++) {
|
||||||
final int pf = i;
|
final int pf = i;
|
||||||
@@ -858,8 +873,8 @@ public class TerminalPanel extends JPanel {
|
|||||||
// Use the CURRENT screen dimensions (not max) to eliminate extra space.
|
// Use the CURRENT screen dimensions (not max) to eliminate extra space.
|
||||||
// When the host switches to alternate screen, onScreenSizeChanged fires
|
// When the host switches to alternate screen, onScreenSizeChanged fires
|
||||||
// and the frame re-packs.
|
// and the frame re-packs.
|
||||||
int displayCols = sb.getCols();
|
int displayCols = sb.getDisplayCols();
|
||||||
int displayRows = sb.getRows();
|
int displayRows = sb.getDisplayRows();
|
||||||
return new Dimension(displayCols * cellWidth + padding * 2,
|
return new Dimension(displayCols * cellWidth + padding * 2,
|
||||||
displayRows * cellHeight + padding * 2);
|
displayRows * cellHeight + padding * 2);
|
||||||
}
|
}
|
||||||
@@ -900,9 +915,13 @@ public class TerminalPanel extends JPanel {
|
|||||||
int gHeight = client.getGraphicsPlane().getCanvasHeight();
|
int gHeight = client.getGraphicsPlane().getCanvasHeight();
|
||||||
int[] rgb = client.getGraphicsPlane().getRgbBuffer();
|
int[] rgb = client.getGraphicsPlane().getRgbBuffer();
|
||||||
if (rgb != null && gWidth > 0 && gHeight > 0) {
|
if (rgb != null && gWidth > 0 && gHeight > 0) {
|
||||||
java.awt.image.BufferedImage img = new java.awt.image.BufferedImage(gWidth, gHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
|
long currentUpdateCount = client.getGraphicsPlane().getUpdateCount();
|
||||||
img.setRGB(0, 0, gWidth, gHeight, rgb, 0, gWidth);
|
if (cachedGraphicsImage == null || currentUpdateCount != lastGraphicsUpdateCount || cachedGraphicsImage.getWidth() != gWidth || cachedGraphicsImage.getHeight() != gHeight) {
|
||||||
g2.drawImage(img, ox, oy, gridW, gridH, null);
|
cachedGraphicsImage = new java.awt.image.BufferedImage(gWidth, gHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
|
||||||
|
cachedGraphicsImage.setRGB(0, 0, gWidth, gHeight, rgb, 0, gWidth);
|
||||||
|
lastGraphicsUpdateCount = currentUpdateCount;
|
||||||
|
}
|
||||||
|
g2.drawImage(cachedGraphicsImage, ox, oy, gridW, gridH, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1039,8 +1058,14 @@ public class TerminalPanel extends JPanel {
|
|||||||
if (client.getGocaDecoder() != null && client.getGocaDecoder().isGraphicsCursorActive() && client.getGraphicsPlane() != null) {
|
if (client.getGocaDecoder() != null && client.getGocaDecoder().isGraphicsCursorActive() && client.getGraphicsPlane() != null) {
|
||||||
int gocaX = client.getGocaDecoder().getGraphicCursorX();
|
int gocaX = client.getGocaDecoder().getGraphicCursorX();
|
||||||
int gocaY = client.getGocaDecoder().getGraphicCursorY();
|
int gocaY = client.getGocaDecoder().getGraphicCursorY();
|
||||||
int px = ox + client.getGraphicsPlane().mapX(gocaX);
|
int canvasPx = client.getGraphicsPlane().mapX(gocaX);
|
||||||
int py = oy + client.getGraphicsPlane().mapY(gocaY);
|
int canvasPy = client.getGraphicsPlane().mapY(gocaY);
|
||||||
|
int gridW = cols * cellWidth;
|
||||||
|
int gridH = rows * cellHeight;
|
||||||
|
int gWidth = client.getGraphicsPlane().getCanvasWidth();
|
||||||
|
int gHeight = client.getGraphicsPlane().getCanvasHeight();
|
||||||
|
int px = ox + (gWidth > 0 ? (int)Math.round((double)canvasPx * gridW / gWidth) : canvasPx);
|
||||||
|
int py = oy + (gHeight > 0 ? (int)Math.round((double)canvasPy * gridH / gHeight) : canvasPy);
|
||||||
|
|
||||||
g2.setColor(Color.WHITE);
|
g2.setColor(Color.WHITE);
|
||||||
g2.setXORMode(Color.BLACK);
|
g2.setXORMode(Color.BLACK);
|
||||||
@@ -1063,6 +1088,13 @@ public class TerminalPanel extends JPanel {
|
|||||||
g2.fillRect(cx, cy, cellWidth, cellHeight);
|
g2.fillRect(cx, cy, cellWidth, cellHeight);
|
||||||
g2.setPaintMode();
|
g2.setPaintMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw Light Pen mode indicator
|
||||||
|
if (lightPenMode) {
|
||||||
|
g2.setFont(new Font(Font.MONOSPACED, Font.BOLD, 12));
|
||||||
|
g2.setColor(new Color(50, 255, 50));
|
||||||
|
g2.drawString("LP", ox + 2, oy + rows * cellHeight + 14);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getColorForAttribute(ExtendedAttribute ea, ExtendedAttribute currentFieldEa, byte currentFA) {
|
private Color getColorForAttribute(ExtendedAttribute ea, ExtendedAttribute currentFieldEa, byte currentFA) {
|
||||||
@@ -1103,4 +1135,13 @@ public class TerminalPanel extends JPanel {
|
|||||||
if (blinkTimer != null)
|
if (blinkTimer != null)
|
||||||
blinkTimer.stop();
|
blinkTimer.stop();
|
||||||
}
|
}
|
||||||
|
public void toggleLightPen() {
|
||||||
|
this.lightPenMode = !this.lightPenMode;
|
||||||
|
System.out.println("Light Pen mode: " + (this.lightPenMode ? "ON" : "OFF"));
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLightPenMode() {
|
||||||
|
return this.lightPenMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -829,39 +829,51 @@ public class GocaDecoder {
|
|||||||
|
|
||||||
private void processArc(byte[] data, int off, int len, boolean fromCurPos, boolean isFull) {
|
private void processArc(byte[] data, int off, int len, boolean fromCurPos, boolean isFull) {
|
||||||
int pos = off;
|
int pos = off;
|
||||||
int startX = curX;
|
int centerX = curX;
|
||||||
int startY = curY;
|
int centerY = curY;
|
||||||
|
|
||||||
if (!fromCurPos && pos + 4 <= off + len) {
|
if (!fromCurPos && pos + 4 <= off + len) {
|
||||||
startX = readCoord(data, pos);
|
centerX = readCoord(data, pos);
|
||||||
startY = readCoord(data, pos + 2);
|
centerY = readCoord(data, pos + 2);
|
||||||
pos += 4;
|
pos += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
double multiplier = 1.0;
|
double sweepFraction = 1.0;
|
||||||
if (pos + 2 <= off + len) {
|
if (!isFull && pos + 2 <= off + len) {
|
||||||
multiplier = (data[pos] & 0xFF) + ((data[pos + 1] & 0xFF) / 255.0);
|
byte intPart = data[pos];
|
||||||
if (multiplier == 0.0) multiplier = 1.0;
|
double fracPart = (data[pos + 1] & 0xFF) / 256.0;
|
||||||
|
sweepFraction = intPart + (intPart >= 0 ? fracPart : -fracPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dxP = Math.abs(arcParamP - arcParamR);
|
int pxCenter = plane.mapX(centerX);
|
||||||
int dyQ = Math.abs(arcParamQ - arcParamS);
|
int pyCenter = plane.mapY(centerY);
|
||||||
if (dxP == 0) dxP = 10;
|
int pxCur = plane.mapX(curX);
|
||||||
if (dyQ == 0) dyQ = 10;
|
int pyCur = plane.mapY(curY);
|
||||||
|
|
||||||
int rxVirtual = (int) (dxP * multiplier);
|
double radius = Math.sqrt(Math.pow(pxCur - pxCenter, 2) + Math.pow(pyCur - pyCenter, 2));
|
||||||
int ryVirtual = (int) (dyQ * multiplier);
|
int rx = (int) Math.round(radius);
|
||||||
|
int ry = rx;
|
||||||
|
if (rx <= 0) {
|
||||||
|
rx = 10;
|
||||||
|
ry = 10;
|
||||||
|
}
|
||||||
|
|
||||||
int rx = Math.abs(plane.mapX(rxVirtual) - plane.mapX(0));
|
double startAngleRad = Math.atan2(pyCenter - pyCur, pxCur - pxCenter);
|
||||||
int ry = Math.abs(plane.mapY(ryVirtual) - plane.mapY(0));
|
double startAngleDeg = Math.toDegrees(startAngleRad);
|
||||||
if (rx <= 0) rx = 10;
|
if (startAngleDeg < 0) startAngleDeg += 360.0;
|
||||||
if (ry <= 0) ry = 10;
|
|
||||||
|
double sweepAngleDeg = isFull ? 360.0 : (sweepFraction * 360.0);
|
||||||
|
|
||||||
plane.drawArc(plane.mapX(startX), plane.mapY(startY), rx, ry, 0.0, 360.0,
|
plane.drawArc(pxCenter, pyCenter, rx, ry, startAngleDeg, sweepAngleDeg,
|
||||||
curColor, lineType, lineWidth, isFull);
|
curColor, lineType, lineWidth, isFull);
|
||||||
|
|
||||||
curX = startX;
|
// Arc ends at the end of the sweep
|
||||||
curY = startY;
|
double endAngleRad = Math.toRadians(startAngleDeg + sweepAngleDeg);
|
||||||
|
int pxEnd = (int) Math.round(pxCenter + rx * Math.cos(endAngleRad));
|
||||||
|
int pyEnd = (int) Math.round(pyCenter - ry * Math.sin(endAngleRad));
|
||||||
|
|
||||||
|
curX = plane.unmapX(pxEnd);
|
||||||
|
curY = plane.unmapY(pyEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFillet(byte[] data, int off, int len, boolean fromCurPos) {
|
private void processFillet(byte[] data, int off, int len, boolean fromCurPos) {
|
||||||
@@ -932,8 +944,9 @@ public class GocaDecoder {
|
|||||||
int textLen = end - pos;
|
int textLen = end - pos;
|
||||||
if (textLen <= 0) return;
|
if (textLen <= 0) return;
|
||||||
|
|
||||||
int cw = charWidth > 0 ? (int) Math.round((double) charWidth * plane.getCanvasWidth() / (plane.getScreenCols() * 9.0)) : 12;
|
// IBM 3279 vector graphics base cell is 9x12
|
||||||
int ch = charHeight > 0 ? (int) Math.round((double) charHeight * plane.getCanvasHeight() / (plane.getScreenRows() * 16.0)) : 20;
|
int cw = charWidth > 0 ? (int) Math.round((double) charWidth * plane.getCanvasWidth() / (plane.getScreenCols() * 9.0)) : 10;
|
||||||
|
int ch = charHeight > 0 ? (int) Math.round((double) charHeight * plane.getCanvasHeight() / (plane.getScreenRows() * 12.0)) : 14;
|
||||||
|
|
||||||
if (charSet != 0 && programSymbolManager != null) {
|
if (charSet != 0 && programSymbolManager != null) {
|
||||||
for (int i = 0; i < textLen; i++) {
|
for (int i = 0; i < textLen; i++) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class GraphicsPlane {
|
|||||||
private int canvasHeight = 600;
|
private int canvasHeight = 600;
|
||||||
private int[] rgbBuffer;
|
private int[] rgbBuffer;
|
||||||
private boolean hasContent = false;
|
private boolean hasContent = false;
|
||||||
|
private long updateCount = 0;
|
||||||
|
|
||||||
private int screenCols = 80;
|
private int screenCols = 80;
|
||||||
private int screenRows = 24;
|
private int screenRows = 24;
|
||||||
@@ -79,6 +80,11 @@ public class GraphicsPlane {
|
|||||||
Arrays.fill(rgbBuffer, 0);
|
Arrays.fill(rgbBuffer, 0);
|
||||||
}
|
}
|
||||||
hasContent = false;
|
hasContent = false;
|
||||||
|
updateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized long getUpdateCount() {
|
||||||
|
return updateCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean hasContent() {
|
public synchronized boolean hasContent() {
|
||||||
@@ -162,6 +168,7 @@ public class GraphicsPlane {
|
|||||||
if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight) {
|
if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight) {
|
||||||
rgbBuffer[y * canvasWidth + x] = colorArgb;
|
rgbBuffer[y * canvasWidth + x] = colorArgb;
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +210,7 @@ public class GraphicsPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldPlotLinePixel(int step, int lineType) {
|
private boolean shouldPlotLinePixel(int step, int lineType) {
|
||||||
@@ -266,6 +274,7 @@ public class GraphicsPlane {
|
|||||||
prevY = nextY;
|
prevY = nextY;
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -304,6 +313,7 @@ public class GraphicsPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -380,6 +390,7 @@ public class GraphicsPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -445,6 +456,7 @@ public class GraphicsPlane {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int[] VSS_OFFSETS = new int[256];
|
private static final int[] VSS_OFFSETS = new int[256];
|
||||||
@@ -493,6 +505,7 @@ public class GraphicsPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawVssChar(int x, int y, char c, int color, int cw, int ch) {
|
private void drawVssChar(int x, int y, char c, int color, int cw, int ch) {
|
||||||
@@ -558,5 +571,6 @@ public class GraphicsPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
updateCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,6 +352,59 @@ public class InputProcessor {
|
|||||||
|
|
||||||
// ========== Cursor movement ==========
|
// ========== Cursor movement ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate a Text Light Pen selection.
|
||||||
|
*/
|
||||||
|
public boolean lightPenSelect(int address) {
|
||||||
|
if (screen == null || !screen.isFormatted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int size = screen.getRows() * screen.getCols();
|
||||||
|
int faPos = screen.findFieldAttribute(address);
|
||||||
|
if (faPos < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedAttribute faCell = screen.getCell(faPos);
|
||||||
|
int fa = faCell.fa & 0xFF;
|
||||||
|
if (!org.lib3270j.protocol.DS3270Constants.faIsSelectable(fa)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int designatorPos = (faPos + 1) % size;
|
||||||
|
ExtendedAttribute desCell = screen.getCell(designatorPos);
|
||||||
|
int ebcdic = desCell.ec & 0xFF;
|
||||||
|
char ascii = (char) desCell.ucs4;
|
||||||
|
|
||||||
|
screen.setCursorAddress(designatorPos);
|
||||||
|
|
||||||
|
if (ebcdic == 0x00 || ebcdic == 0x40 || ascii == ' ' || ascii == 0 || (ebcdic != 0x50 && ascii != '&' && ebcdic != 0x6F && ascii != '?' && ebcdic != 0x6E && ascii != '>')) {
|
||||||
|
faCell.fa |= org.lib3270j.protocol.DS3270Constants.FA_MODIFY;
|
||||||
|
sendAid(org.lib3270j.protocol.DS3270Constants.AID_SELECT);
|
||||||
|
return true;
|
||||||
|
} else if (ebcdic == 0x50 || ascii == '&') {
|
||||||
|
faCell.fa |= org.lib3270j.protocol.DS3270Constants.FA_MODIFY;
|
||||||
|
sendAid(org.lib3270j.protocol.DS3270Constants.AID_ENTER);
|
||||||
|
return true;
|
||||||
|
} else if (ebcdic == 0x6F || ascii == '?') {
|
||||||
|
desCell.ec = (byte) 0x6E;
|
||||||
|
desCell.ucs4 = '>';
|
||||||
|
faCell.fa |= org.lib3270j.protocol.DS3270Constants.FA_MODIFY;
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
|
return true;
|
||||||
|
} else if (ebcdic == 0x6E || ascii == '>') {
|
||||||
|
desCell.ec = (byte) 0x6F;
|
||||||
|
desCell.ucs4 = '?';
|
||||||
|
faCell.fa &= ~org.lib3270j.protocol.DS3270Constants.FA_MODIFY;
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
faCell.fa |= org.lib3270j.protocol.DS3270Constants.FA_MODIFY;
|
||||||
|
sendAid(org.lib3270j.protocol.DS3270Constants.AID_SELECT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void cursorUp() {
|
public void cursorUp() {
|
||||||
int addr = screen.getCursorAddress();
|
int addr = screen.getCursorAddress();
|
||||||
addr -= screen.getCols();
|
addr -= screen.getCols();
|
||||||
|
|||||||
Reference in New Issue
Block a user