Keep par0dy with a3270
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 1m1s

This commit is contained in:
2026-08-24 20:46:45 +00:00
parent d2bdb9bb0a
commit b5a4978b24
9 changed files with 458 additions and 131 deletions
@@ -86,6 +86,10 @@ public class StatusBar extends JPanel {
public void setClient(Telnet3270Client client, TerminalPanel terminalPanel) {
this.client = client;
this.terminalPanel = terminalPanel;
if (terminalPanel != null) {
terminalPanel.setOnLightPenToggle(this::updateStatus);
}
updateStatus();
}
public void updateStatus() {
@@ -159,11 +159,20 @@ public class TerminalPanel extends JPanel {
col = Math.max(0, Math.min(col, displayCols - 1));
row = Math.max(0, Math.min(row, displayRows - 1));
// Start selection
selectionStartRow = row;
selectionStartCol = col;
selectionEndRow = row;
selectionEndCol = col;
// IMPORTANT ARCHITECTURE NOTE:
// 1. In GDDM Graphic Cursor Mode (3179G / GOCA), mouse events represent graphic light-pen touches.
// Text drag-selection is explicitly suppressed so blue selection boxes do not artifact over graphics.
// 2. In Text Light Pen mode (Alt+L on 3270 formatted screens), clicks toggle selectable fields.
// 3. In standard alphanumeric mode, click-drag selects text for copy-paste.
boolean isGraphic = client.getGocaDecoder() != null && client.getGocaDecoder().isGraphicsCursorActive();
if (isGraphic || lightPenMode) {
clearSelection();
} else {
selectionStartRow = row;
selectionStartCol = col;
selectionEndRow = row;
selectionEndCol = col;
}
isDragging = true;
repaint();
}
@@ -172,16 +181,29 @@ public class TerminalPanel extends JPanel {
@Override
public void mouseDragged(MouseEvent e) {
if (isDragging && client != null && client.getConnectionState().isFullSession()) {
ScreenBuffer sb = client.getScreenBuffer();
int ox = getRenderOffsetX();
int oy = getRenderOffsetY();
int col = (e.getX() - ox) / cellWidth;
int row = (e.getY() - oy) / cellHeight;
col = Math.max(0, Math.min(col, sb.getDisplayCols() - 1));
row = Math.max(0, Math.min(row, sb.getDisplayRows() - 1));
ScreenBuffer sb = client.getScreenBuffer();
boolean isGraphic = client.getGocaDecoder() != null && client.getGocaDecoder().isGraphicsCursorActive();
if (isGraphic && client.getGraphicsPlane() != null) {
int gridW = sb.getDisplayCols() * cellWidth;
int gridH = sb.getDisplayRows() * cellHeight;
int gWidth = client.getGraphicsPlane().getCanvasWidth();
int gHeight = client.getGraphicsPlane().getCanvasHeight();
int px = (gridW > 0 && gWidth > 0) ? (int) Math.round((double) (e.getX() - ox) * gWidth / gridW) : (e.getX() - ox);
int py = (gridH > 0 && gHeight > 0) ? (int) Math.round((double) (e.getY() - oy) * gHeight / gridH) : (e.getY() - oy);
px = Math.max(0, Math.min(px, gWidth - 1));
py = Math.max(0, Math.min(py, gHeight - 1));
client.getGocaDecoder().setGraphicCursorFromPixel(px, py);
} else if (!lightPenMode) {
int col = (e.getX() - ox) / cellWidth;
int row = (e.getY() - oy) / cellHeight;
col = Math.max(0, Math.min(col, sb.getDisplayCols() - 1));
row = Math.max(0, Math.min(row, sb.getDisplayRows() - 1));
selectionEndRow = row;
selectionEndCol = col;
selectionEndRow = row;
selectionEndCol = col;
}
repaint();
}
}
@@ -198,6 +220,8 @@ public class TerminalPanel extends JPanel {
int gHeight = client.getGraphicsPlane().getCanvasHeight();
int px = (gridW > 0 && gWidth > 0) ? (int) Math.round((double) (e.getX() - ox) * gWidth / gridW) : (e.getX() - ox);
int py = (gridH > 0 && gHeight > 0) ? (int) Math.round((double) (e.getY() - oy) * gHeight / gridH) : (e.getY() - oy);
px = Math.max(0, Math.min(px, gWidth - 1));
py = Math.max(0, Math.min(py, gHeight - 1));
client.getGocaDecoder().setGraphicCursorFromPixel(px, py);
}
}
@@ -206,51 +230,63 @@ public class TerminalPanel extends JPanel {
public void mouseReleased(MouseEvent e) {
if (isDragging) {
isDragging = false;
// If start == end, treat as a click (position cursor, clear selection)
if (selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol) {
if (client != null && client.getConnectionState().isFullSession()) {
ScreenBuffer sb = client.getScreenBuffer();
int displayCols = sb.getDisplayCols();
int newAddr = selectionStartRow * displayCols + selectionStartCol;
sb.setCursorAddress(newAddr);
if (client != null && client.getConnectionState().isFullSession()) {
ScreenBuffer sb = client.getScreenBuffer();
int ox = getRenderOffsetX();
int oy = getRenderOffsetY();
int displayCols = sb.getDisplayCols();
int displayRows = sb.getDisplayRows();
int col = (e.getX() - ox) / cellWidth;
int row = (e.getY() - oy) / cellHeight;
col = Math.max(0, Math.min(col, displayCols - 1));
row = Math.max(0, Math.min(row, displayRows - 1));
int clickAddr = row * displayCols + col;
boolean isGraphic = client.getGocaDecoder() != null && client.getGocaDecoder().isGraphicsCursorActive() && client.getGraphicsPlane() != null;
if (isGraphic) {
clearSelection();
if (lightPenMode) {
System.out.println("LP click: row=" + selectionStartRow + " col=" + selectionStartCol
+ " addr=" + newAddr + " mouseY=" + e.getY() + " oy=" + getRenderOffsetY()
+ " cellH=" + cellHeight);
boolean result = client.getInputProcessor().lightPenSelect(newAddr);
System.out.println("LP lightPenSelect result: " + result);
if (result) {
refreshScreen();
return;
}
int gridW = displayCols * cellWidth;
int gridH = displayRows * cellHeight;
int gWidth = client.getGraphicsPlane().getCanvasWidth();
int gHeight = client.getGraphicsPlane().getCanvasHeight();
int px = (gridW > 0 && gWidth > 0) ? (int) Math.round((double) (e.getX() - ox) * gWidth / gridW) : (e.getX() - ox);
int py = (gridH > 0 && gHeight > 0) ? (int) Math.round((double) (e.getY() - oy) * gHeight / gridH) : (e.getY() - oy);
px = Math.max(0, Math.min(px, gWidth - 1));
py = Math.max(0, Math.min(py, gHeight - 1));
client.getGocaDecoder().setGraphicCursorFromPixel(px, py);
int gx = client.getGocaDecoder().getGraphicCursorX();
int gy = client.getGocaDecoder().getGraphicCursorY();
int button = javax.swing.SwingUtilities.isRightMouseButton(e) ? 2 : 1;
System.err.println(String.format(
"TerminalPanel.mouseReleased: mouse=(%d, %d) offset=(%d, %d) grid=(%dx%d) px=(%d, %d) goca=(%d, %d) btn=%d",
e.getX(), e.getY(), ox, oy, gridW, gridH, px, py, gx, gy, button
));
client.getInputProcessor().sendGraphicMouseAid(
org.lib3270j.protocol.DS3270Constants.AID_ENTER,
button,
e.isShiftDown(),
e.isControlDown()
);
refreshScreen();
return;
}
if (lightPenMode) {
clearSelection();
sb.setCursorAddress(clickAddr);
boolean result = client.getInputProcessor().lightPenSelect(clickAddr);
if (result) {
refreshScreen();
return;
}
if (client.getGocaDecoder() != null && client.getGraphicsPlane() != null) {
int ox = getRenderOffsetX();
int oy = getRenderOffsetY();
int gridW = sb.getDisplayCols() * cellWidth;
int gridH = sb.getDisplayRows() * cellHeight;
int gWidth = client.getGraphicsPlane().getCanvasWidth();
int gHeight = client.getGraphicsPlane().getCanvasHeight();
int px = (gridW > 0 && gWidth > 0) ? (int) Math.round((double) (e.getX() - ox) * gWidth / gridW) : (e.getX() - ox);
int py = (gridH > 0 && gHeight > 0) ? (int) Math.round((double) (e.getY() - oy) * gHeight / gridH) : (e.getY() - oy);
client.getGocaDecoder().setGraphicCursorFromPixel(px, py);
if (client.getGocaDecoder().isGraphicsCursorActive()) {
// Light-pen / Graphic cursor touch event (matches IBM Host On-Demand PS3179G)
int button = javax.swing.SwingUtilities.isRightMouseButton(e) ? 2 : 1;
client.getInputProcessor().sendGraphicMouseAid(
org.lib3270j.protocol.DS3270Constants.AID_ENTER,
button,
e.isShiftDown(),
e.isControlDown()
);
refreshScreen();
return;
}
}
}
if (selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol) {
sb.setCursorAddress(clickAddr);
clearSelection();
refreshScreen();
return;
}
@@ -1140,9 +1176,18 @@ public class TerminalPanel extends JPanel {
if (blinkTimer != null)
blinkTimer.stop();
}
private Runnable onLightPenToggle;
public void setOnLightPenToggle(Runnable callback) {
this.onLightPenToggle = callback;
}
public void toggleLightPen() {
this.lightPenMode = !this.lightPenMode;
System.out.println("Light Pen mode: " + (this.lightPenMode ? "ON" : "OFF"));
if (onLightPenToggle != null) {
onLightPenToggle.run();
}
repaint();
}