j3270 updates applied for lightpen
Build and Test a3270 / Build Android APK (push) Successful in 4m34s
Build and Test a3270 / Build Android APK (push) Successful in 4m34s
This commit is contained in:
@@ -9,9 +9,11 @@ echo "=== Building a3270 (Android APK) ==="
|
|||||||
# Set up JAVA_HOME (prefer JDK 21 if available)
|
# Set up JAVA_HOME (prefer JDK 21 if available)
|
||||||
if [ -d "/Users/rudi/.sdkman/candidates/java/21.0.6-sem" ]; then
|
if [ -d "/Users/rudi/.sdkman/candidates/java/21.0.6-sem" ]; then
|
||||||
export JAVA_HOME="/Users/rudi/.sdkman/candidates/java/21.0.6-sem"
|
export JAVA_HOME="/Users/rudi/.sdkman/candidates/java/21.0.6-sem"
|
||||||
|
elif [ -d "$HOME/.sdkman/candidates/java/21.0.2-open" ]; then
|
||||||
|
export JAVA_HOME="$HOME/.sdkman/candidates/java/21.0.2-open"
|
||||||
elif [ -z "$JAVA_HOME" ]; then
|
elif [ -z "$JAVA_HOME" ]; then
|
||||||
if [ -d "/Users/rudi/.sdkman/candidates/java/current" ]; then
|
if [ -d "$HOME/.sdkman/candidates/java/current" ]; then
|
||||||
export JAVA_HOME="/Users/rudi/.sdkman/candidates/java/current"
|
export JAVA_HOME="$HOME/.sdkman/candidates/java/current"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -19,6 +21,8 @@ fi
|
|||||||
if [ -z "$ANDROID_HOME" ]; then
|
if [ -z "$ANDROID_HOME" ]; then
|
||||||
if [ -d "$HOME/Library/Android/sdk" ]; then
|
if [ -d "$HOME/Library/Android/sdk" ]; then
|
||||||
export ANDROID_HOME="$HOME/Library/Android/sdk"
|
export ANDROID_HOME="$HOME/Library/Android/sdk"
|
||||||
|
elif [ -d "$HOME/Android/Sdk" ]; then
|
||||||
|
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
viewModel.resetKeyboard()
|
viewModel.resetKeyboard()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
KeyEvent.KEYCODE_L -> {
|
||||||
|
viewModel.toggleLightPen()
|
||||||
|
val isLp = viewModel.isLightPenMode.value
|
||||||
|
Toast.makeText(this, "Light Pen: " + (if (isLp) "ON" else "OFF"), Toast.LENGTH_SHORT).show()
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,6 +405,7 @@ fun MainScreen(
|
|||||||
val isTlsVerified by viewModel.isTlsVerified.collectAsState()
|
val isTlsVerified by viewModel.isTlsVerified.collectAsState()
|
||||||
val untrustedCertPrompt by viewModel.untrustedCertPrompt.collectAsState()
|
val untrustedCertPrompt by viewModel.untrustedCertPrompt.collectAsState()
|
||||||
val ftState by viewModel.ftState.collectAsState()
|
val ftState by viewModel.ftState.collectAsState()
|
||||||
|
val isLightPenMode by viewModel.isLightPenMode.collectAsState()
|
||||||
|
|
||||||
var showConnectDialog by remember { mutableStateOf(false) }
|
var showConnectDialog by remember { mutableStateOf(false) }
|
||||||
var showFtDialog by remember { mutableStateOf(false) }
|
var showFtDialog by remember { mutableStateOf(false) }
|
||||||
@@ -466,12 +473,21 @@ fun MainScreen(
|
|||||||
screenVersion = screenVersion,
|
screenVersion = screenVersion,
|
||||||
programSymbolManager = viewModel.getClient()?.programSymbolManager,
|
programSymbolManager = viewModel.getClient()?.programSymbolManager,
|
||||||
graphicsPlane = viewModel.getClient()?.graphicsPlane,
|
graphicsPlane = viewModel.getClient()?.graphicsPlane,
|
||||||
|
gocaDecoder = viewModel.getGocaDecoder(),
|
||||||
|
isLightPenMode = isLightPenMode,
|
||||||
maskHiddenFields = maskHiddenInput,
|
maskHiddenFields = maskHiddenInput,
|
||||||
blinkCursor = cursorBlink,
|
blinkCursor = cursorBlink,
|
||||||
onTapAddress = { addr ->
|
onTapAddress = { addr ->
|
||||||
viewModel.setCursor(addr)
|
viewModel.setCursor(addr)
|
||||||
terminalInputViewRef?.showSoftKeyboard()
|
terminalInputViewRef?.showSoftKeyboard()
|
||||||
},
|
},
|
||||||
|
onLightPenSelect = { addr ->
|
||||||
|
viewModel.selectLightPen(addr)
|
||||||
|
terminalInputViewRef?.showSoftKeyboard()
|
||||||
|
},
|
||||||
|
onGraphicTouch = { px, py ->
|
||||||
|
viewModel.handleGraphicTouch(px, py)
|
||||||
|
},
|
||||||
onPasteText = { text ->
|
onPasteText = { text ->
|
||||||
viewModel.pasteString(text)
|
viewModel.pasteString(text)
|
||||||
},
|
},
|
||||||
@@ -512,11 +528,13 @@ fun MainScreen(
|
|||||||
TwoRowKeyBar(
|
TwoRowKeyBar(
|
||||||
connectionState = connectionState,
|
connectionState = connectionState,
|
||||||
isShiftPressed = isShiftPressed,
|
isShiftPressed = isShiftPressed,
|
||||||
|
isLightPenMode = isLightPenMode,
|
||||||
hapticFeedbackEnabled = hapticFeedback,
|
hapticFeedbackEnabled = hapticFeedback,
|
||||||
onClearShift = onClearShift,
|
onClearShift = onClearShift,
|
||||||
onConnectClick = { showConnectDialog = true },
|
onConnectClick = { showConnectDialog = true },
|
||||||
onDisconnectClick = { viewModel.disconnect() },
|
onDisconnectClick = { viewModel.disconnect() },
|
||||||
onFtClick = { showFtDialog = true },
|
onFtClick = { showFtDialog = true },
|
||||||
|
onToggleLightPen = { viewModel.toggleLightPen() },
|
||||||
onSettingsClick = { showSettingsDialog = true },
|
onSettingsClick = { showSettingsDialog = true },
|
||||||
onSendAid = { aid ->
|
onSendAid = { aid ->
|
||||||
viewModel.sendAid(aid)
|
viewModel.sendAid(aid)
|
||||||
@@ -563,7 +581,8 @@ fun MainScreen(
|
|||||||
cols = cols,
|
cols = cols,
|
||||||
isTls = isTlsActive,
|
isTls = isTlsActive,
|
||||||
isTlsVerified = isTlsVerified,
|
isTlsVerified = isTlsVerified,
|
||||||
graphicsMode = defaultGraphicsMode
|
graphicsMode = defaultGraphicsMode,
|
||||||
|
isLightPenMode = isLightPenMode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ sealed interface TerminalInputAction {
|
|||||||
data object Newline : TerminalInputAction
|
data object Newline : TerminalInputAction
|
||||||
data object Reset : TerminalInputAction
|
data object Reset : TerminalInputAction
|
||||||
data class SetCursor(val baddr: Int) : TerminalInputAction
|
data class SetCursor(val baddr: Int) : TerminalInputAction
|
||||||
|
data class LightPenSelect(val baddr: Int) : TerminalInputAction
|
||||||
}
|
}
|
||||||
|
|
||||||
data class UntrustedCertPromptState(
|
data class UntrustedCertPromptState(
|
||||||
@@ -124,6 +125,17 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
private val _untrustedCertPrompt = MutableStateFlow<UntrustedCertPromptState?>(null)
|
private val _untrustedCertPrompt = MutableStateFlow<UntrustedCertPromptState?>(null)
|
||||||
val untrustedCertPrompt: StateFlow<UntrustedCertPromptState?> = _untrustedCertPrompt.asStateFlow()
|
val untrustedCertPrompt: StateFlow<UntrustedCertPromptState?> = _untrustedCertPrompt.asStateFlow()
|
||||||
|
|
||||||
|
private val _isLightPenMode = MutableStateFlow(false)
|
||||||
|
val isLightPenMode: StateFlow<Boolean> = _isLightPenMode.asStateFlow()
|
||||||
|
|
||||||
|
fun toggleLightPen() {
|
||||||
|
_isLightPenMode.value = !_isLightPenMode.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightPenMode(enabled: Boolean) {
|
||||||
|
_isLightPenMode.value = enabled
|
||||||
|
}
|
||||||
|
|
||||||
// File Transfer State
|
// File Transfer State
|
||||||
private var fileTransferCoordinator: FileTransfer? = null
|
private var fileTransferCoordinator: FileTransfer? = null
|
||||||
private val _ftState = MutableStateFlow(FTProgressState())
|
private val _ftState = MutableStateFlow(FTProgressState())
|
||||||
@@ -240,6 +252,13 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
buf.cursorAddress = action.baddr
|
buf.cursorAddress = action.baddr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is TerminalInputAction.LightPenSelect -> {
|
||||||
|
if (action.baddr in 0 until (buf.rows * buf.cols)) {
|
||||||
|
buf.cursorAddress = action.baddr
|
||||||
|
val res = ip.lightPenSelect(action.baddr)
|
||||||
|
log.info("LightPenSelect at addr ${action.baddr}, result=$res")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.warning("Error processing input action: ${e.message}")
|
log.warning("Error processing input action: ${e.message}")
|
||||||
@@ -627,6 +646,27 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
inputChannel.trySend(TerminalInputAction.CursorHome)
|
inputChannel.trySend(TerminalInputAction.CursorHome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectLightPen(baddr: Int) {
|
||||||
|
inputChannel.trySend(TerminalInputAction.LightPenSelect(baddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGocaDecoder(): org.lib3270j.graphics.GocaDecoder? = client?.gocaDecoder
|
||||||
|
|
||||||
|
fun handleGraphicTouch(px: Int, py: Int, button: Int = 1, shift: Boolean = false, ctrl: Boolean = false) {
|
||||||
|
val c = client ?: return
|
||||||
|
val goca = c.gocaDecoder ?: return
|
||||||
|
if (goca.isGraphicsCursorActive) {
|
||||||
|
goca.setGraphicCursorFromPixel(px, py)
|
||||||
|
c.inputProcessor.sendGraphicMouseAid(
|
||||||
|
AID_ENTER,
|
||||||
|
button,
|
||||||
|
shift,
|
||||||
|
ctrl
|
||||||
|
)
|
||||||
|
_screenVersion.value = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun extractScreenSnippet(buf: ScreenBuffer?): String {
|
private fun extractScreenSnippet(buf: ScreenBuffer?): String {
|
||||||
if (buf == null) return "Mainframe screen update received"
|
if (buf == null) return "Mainframe screen update received"
|
||||||
val rows = buf.rows
|
val rows = buf.rows
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ import org.lib3270j.protocol.DS3270Constants.*
|
|||||||
fun TwoRowKeyBar(
|
fun TwoRowKeyBar(
|
||||||
connectionState: ConnectionState,
|
connectionState: ConnectionState,
|
||||||
isShiftPressed: Boolean = false,
|
isShiftPressed: Boolean = false,
|
||||||
|
isLightPenMode: Boolean = false,
|
||||||
hapticFeedbackEnabled: Boolean = true,
|
hapticFeedbackEnabled: Boolean = true,
|
||||||
onClearShift: () -> Unit = {},
|
onClearShift: () -> Unit = {},
|
||||||
onConnectClick: () -> Unit,
|
onConnectClick: () -> Unit,
|
||||||
onDisconnectClick: () -> Unit,
|
onDisconnectClick: () -> Unit,
|
||||||
onFtClick: () -> Unit,
|
onFtClick: () -> Unit,
|
||||||
|
onToggleLightPen: () -> Unit = {},
|
||||||
onSettingsClick: () -> Unit = {},
|
onSettingsClick: () -> Unit = {},
|
||||||
onSendAid: (Int) -> Unit,
|
onSendAid: (Int) -> Unit,
|
||||||
onReset: () -> Unit,
|
onReset: () -> Unit,
|
||||||
@@ -53,7 +55,7 @@ fun TwoRowKeyBar(
|
|||||||
.padding(horizontal = 1.dp, vertical = 1.dp),
|
.padding(horizontal = 1.dp, vertical = 1.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 1. Menu Dropdown Button (Connect/Disconnect/FT/Settings)
|
// 1. Menu Dropdown Button (Connect/Disconnect/FT/Light Pen/Settings)
|
||||||
Box(modifier = Modifier.weight(1f)) {
|
Box(modifier = Modifier.weight(1f)) {
|
||||||
KeyButton(
|
KeyButton(
|
||||||
label = "⋮",
|
label = "⋮",
|
||||||
@@ -92,6 +94,19 @@ fun TwoRowKeyBar(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = if (isLightPenMode) "Light Pen: ON" else "Light Pen: OFF",
|
||||||
|
color = if (isLightPenMode) Color(0xFFFFFF50) else Color.White,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
menuExpanded = false
|
||||||
|
onToggleLightPen()
|
||||||
|
}
|
||||||
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Settings", color = Color.White, fontWeight = FontWeight.SemiBold) },
|
text = { Text("Settings", color = Color.White, fontWeight = FontWeight.SemiBold) },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ fun OiaStatusBar(
|
|||||||
isTls: Boolean = false,
|
isTls: Boolean = false,
|
||||||
isTlsVerified: Boolean = true,
|
isTlsVerified: Boolean = true,
|
||||||
graphicsMode: String = "NONE",
|
graphicsMode: String = "NONE",
|
||||||
|
isLightPenMode: Boolean = false,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val row = if (cols > 0) (cursorAddr / cols) + 1 else 1
|
val row = if (cols > 0) (cursorAddr / cols) + 1 else 1
|
||||||
@@ -119,6 +120,24 @@ fun OiaStatusBar(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Light Pen Badge
|
||||||
|
if (isLightPenMode) {
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
Surface(
|
||||||
|
color = Color(0xFFFFFF50).copy(alpha = 0.25f),
|
||||||
|
shape = RoundedCornerShape(4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "LP",
|
||||||
|
color = Color(0xFFFFFF50),
|
||||||
|
fontSize = 9.sp,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(horizontal = 4.dp, vertical = 1.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminal Keyboard Lock Status Badge (X SYSTEM vs READY)
|
// Terminal Keyboard Lock Status Badge (X SYSTEM vs READY)
|
||||||
|
|||||||
@@ -60,9 +60,13 @@ fun TerminalView(
|
|||||||
screenVersion: Long,
|
screenVersion: Long,
|
||||||
programSymbolManager: org.lib3270j.graphics.ProgramSymbolManager? = null,
|
programSymbolManager: org.lib3270j.graphics.ProgramSymbolManager? = null,
|
||||||
graphicsPlane: org.lib3270j.graphics.GraphicsPlane? = null,
|
graphicsPlane: org.lib3270j.graphics.GraphicsPlane? = null,
|
||||||
|
gocaDecoder: org.lib3270j.graphics.GocaDecoder? = null,
|
||||||
|
isLightPenMode: Boolean = false,
|
||||||
maskHiddenFields: Boolean = true,
|
maskHiddenFields: Boolean = true,
|
||||||
blinkCursor: Boolean = true,
|
blinkCursor: Boolean = true,
|
||||||
onTapAddress: (Int) -> Unit,
|
onTapAddress: (Int) -> Unit,
|
||||||
|
onLightPenSelect: ((Int) -> Unit)? = null,
|
||||||
|
onGraphicTouch: ((Int, Int) -> Unit)? = null,
|
||||||
onPasteText: (String) -> Unit = {},
|
onPasteText: (String) -> Unit = {},
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -97,7 +101,7 @@ fun TerminalView(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(COLOR_BLACK)
|
.background(COLOR_BLACK)
|
||||||
.pointerInput(cols, rows) {
|
.pointerInput(cols, rows, isLightPenMode, gocaDecoder) {
|
||||||
awaitPointerEventScope {
|
awaitPointerEventScope {
|
||||||
while (true) {
|
while (true) {
|
||||||
val down = awaitFirstDown(requireUnconsumed = false)
|
val down = awaitFirstDown(requireUnconsumed = false)
|
||||||
@@ -106,6 +110,7 @@ fun TerminalView(
|
|||||||
var currentPos = startPos
|
var currentPos = startPos
|
||||||
var isDragStarted = false
|
var isDragStarted = false
|
||||||
val slop = viewConfiguration.touchSlop
|
val slop = viewConfiguration.touchSlop
|
||||||
|
val isGraphicCursor = gocaDecoder != null && gocaDecoder.isGraphicsCursorActive
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val event = awaitPointerEvent()
|
val event = awaitPointerEvent()
|
||||||
@@ -116,7 +121,7 @@ fun TerminalView(
|
|||||||
currentPos = change.position
|
currentPos = change.position
|
||||||
val distance = (currentPos - startPos).getDistance()
|
val distance = (currentPos - startPos).getDistance()
|
||||||
|
|
||||||
if (distance > slop) {
|
if (distance > slop && !isLightPenMode && !isGraphicCursor) {
|
||||||
if (!isDragStarted) {
|
if (!isDragStarted) {
|
||||||
isDragStarted = true
|
isDragStarted = true
|
||||||
selectionStart = startPos
|
selectionStart = startPos
|
||||||
@@ -133,12 +138,12 @@ fun TerminalView(
|
|||||||
if (isDragStarted) {
|
if (isDragStarted) {
|
||||||
// Drag completed: Keep selection box highlighted, DO NOT open context menu
|
// Drag completed: Keep selection box highlighted, DO NOT open context menu
|
||||||
showContextMenu = false
|
showContextMenu = false
|
||||||
} else if (duration >= 600L && totalDistance <= slop) {
|
} else if (duration >= 600L && totalDistance <= slop && !isLightPenMode && !isGraphicCursor) {
|
||||||
// Stationary Long Press (held >= 600ms without dragging)
|
// Stationary Long Press (held >= 600ms without dragging) - only in normal alphanumeric mode
|
||||||
contextMenuOffset = startPos
|
contextMenuOffset = startPos
|
||||||
showContextMenu = true
|
showContextMenu = true
|
||||||
} else if (totalDistance <= slop) {
|
} else if (totalDistance <= slop) {
|
||||||
// Short Tap (Move Cursor)
|
// Short Tap
|
||||||
showContextMenu = false
|
showContextMenu = false
|
||||||
selectionStart = null
|
selectionStart = null
|
||||||
selectionEnd = null
|
selectionEnd = null
|
||||||
@@ -146,16 +151,29 @@ fun TerminalView(
|
|||||||
if (metrics.cellWidth > 0 && metrics.cellHeight > 0) {
|
if (metrics.cellWidth > 0 && metrics.cellHeight > 0) {
|
||||||
val col = ((startPos.x - metrics.offsetX) / metrics.cellWidth).toInt().coerceIn(0, cols - 1)
|
val col = ((startPos.x - metrics.offsetX) / metrics.cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val row = ((startPos.y - metrics.offsetY) / metrics.cellHeight).toInt().coerceIn(0, rows - 1)
|
val row = ((startPos.y - metrics.offsetY) / metrics.cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
var addr = row * cols + col
|
val clickAddr = row * cols + col
|
||||||
|
|
||||||
val buf = screenBuffer
|
if (isGraphicCursor) {
|
||||||
if (buf != null && buf.isFormatted) {
|
val gridW = metrics.gridWidth
|
||||||
val faVal = buf.getFieldAttributeAt(addr)
|
val gridH = metrics.gridHeight
|
||||||
if (faIsProtected(faVal.toInt() and 0xFF) || buf.getCell(addr).isFieldAttribute) {
|
val gWidth = graphicsPlane?.canvasWidth ?: gridW.toInt()
|
||||||
addr = buf.findNextUnprotected(addr)
|
val gHeight = graphicsPlane?.canvasHeight ?: gridH.toInt()
|
||||||
|
val px = if (gridW > 0 && gWidth > 0) (((startPos.x - metrics.offsetX) * gWidth) / gridW).toInt().coerceIn(0, gWidth - 1) else (startPos.x - metrics.offsetX).toInt()
|
||||||
|
val py = if (gridH > 0 && gHeight > 0) (((startPos.y - metrics.offsetY) * gHeight) / gridH).toInt().coerceIn(0, gHeight - 1) else (startPos.y - metrics.offsetY).toInt()
|
||||||
|
onGraphicTouch?.invoke(px, py)
|
||||||
|
} else if (isLightPenMode) {
|
||||||
|
onLightPenSelect?.invoke(clickAddr)
|
||||||
|
} else {
|
||||||
|
var addr = clickAddr
|
||||||
|
val buf = screenBuffer
|
||||||
|
if (buf != null && buf.isFormatted) {
|
||||||
|
val faVal = buf.getFieldAttributeAt(addr)
|
||||||
|
if (faIsProtected(faVal.toInt() and 0xFF) || buf.getCell(addr).isFieldAttribute) {
|
||||||
|
addr = buf.findNextUnprotected(addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
onTapAddress(addr)
|
||||||
}
|
}
|
||||||
onTapAddress(addr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,4 +165,51 @@ class HardwareKeyboardInputTest {
|
|||||||
assertFalse(inputProcessor.isKeyboardLocked)
|
assertFalse(inputProcessor.isKeyboardLocked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLightPenSelection() {
|
||||||
|
// Field 1 at pos 0: Selectable Unprotected (FA_INT_HIGH)
|
||||||
|
// Designator at pos 1: '?' (EBCDIC 0x6F)
|
||||||
|
val fa = (FA_PRINTABLE or FA_INT_HIGH_SEL).toByte()
|
||||||
|
assertTrue(org.lib3270j.protocol.DS3270Constants.faIsSelectable(fa.toInt() and 0xFF))
|
||||||
|
|
||||||
|
screenBuffer.setCellFA(0, fa)
|
||||||
|
screenBuffer.getCell(1).ucs4 = '?'
|
||||||
|
screenBuffer.getCell(1).ec = translator.unicodeToEbcdic('?').toByte()
|
||||||
|
|
||||||
|
// Field 2 at pos 20: Non-selectable (normal intensity)
|
||||||
|
screenBuffer.setCellFA(20, FA_PRINTABLE.toByte())
|
||||||
|
screenBuffer.getCell(21).ucs4 = '?'
|
||||||
|
|
||||||
|
// Selecting field 1 ('?') toggles designator to '>' and sets modified bit
|
||||||
|
val result1 = inputProcessor.lightPenSelect(1)
|
||||||
|
assertTrue(result1)
|
||||||
|
assertEquals('>', screenBuffer.getCell(1).ucs4)
|
||||||
|
assertEquals(0x6E.toByte(), screenBuffer.getCell(1).ec)
|
||||||
|
val fa1 = screenBuffer.getCell(0).fa.toInt() and 0xFF
|
||||||
|
assertTrue((fa1 and FA_MODIFY) != 0)
|
||||||
|
|
||||||
|
// Selecting field 1 again ('>') toggles back to '?' and clears modified bit
|
||||||
|
val result2 = inputProcessor.lightPenSelect(1)
|
||||||
|
assertTrue(result2)
|
||||||
|
assertEquals('?', screenBuffer.getCell(1).ucs4)
|
||||||
|
assertEquals(0x6F.toByte(), screenBuffer.getCell(1).ec)
|
||||||
|
|
||||||
|
// Selecting field 2 (non-selectable) returns false
|
||||||
|
val resultNonSelectable = inputProcessor.lightPenSelect(21)
|
||||||
|
assertFalse(resultNonSelectable)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLightPenEnterSelection() {
|
||||||
|
// Field with '&' designator immediately sends AID_ENTER
|
||||||
|
val fa = (FA_PRINTABLE or FA_INT_HIGH_SEL).toByte()
|
||||||
|
screenBuffer.setCellFA(0, fa)
|
||||||
|
screenBuffer.getCell(1).ucs4 = '&'
|
||||||
|
screenBuffer.getCell(1).ec = translator.unicodeToEbcdic('&').toByte()
|
||||||
|
|
||||||
|
val result = inputProcessor.lightPenSelect(1)
|
||||||
|
assertTrue(result)
|
||||||
|
assertEquals(AID_ENTER, inputProcessor.lastAid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user