diff --git a/build.gradle b/build.gradle index 9fc5807..3057998 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,8 @@ android { applicationId "haus.nightmare.a3270" minSdk 24 targetSdk 34 - versionCode 9 - versionName "1.2.4" + versionCode 10 + versionName "1.3.0" } buildTypes { diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index cf9066f..f1ca386 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionCode="10" + android:versionName="1.3.0"> diff --git a/src/main/java/haus/nightmare/a3270/TerminalViewModel.kt b/src/main/java/haus/nightmare/a3270/TerminalViewModel.kt index 9b53c08..19c2c5f 100644 --- a/src/main/java/haus/nightmare/a3270/TerminalViewModel.kt +++ b/src/main/java/haus/nightmare/a3270/TerminalViewModel.kt @@ -94,8 +94,8 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application private val inputExecutor = Executors.newSingleThreadExecutor() private val inputDispatcher = inputExecutor.asCoroutineDispatcher() - // Non-blocking FIFO input channel buffer - private val inputChannel = Channel(Channel.UNLIMITED) + // Bounded FIFO input channel buffer (prevents unbounded queue growth if worker blocks) + private val inputChannel = Channel(256) private val _connectionState = MutableStateFlow(ConnectionState.NOT_CONNECTED) val connectionState: StateFlow = _connectionState.asStateFlow() diff --git a/src/main/java/haus/nightmare/a3270/ui/MacroDialog.kt b/src/main/java/haus/nightmare/a3270/ui/MacroDialog.kt index 9a170fd..13bb63a 100644 --- a/src/main/java/haus/nightmare/a3270/ui/MacroDialog.kt +++ b/src/main/java/haus/nightmare/a3270/ui/MacroDialog.kt @@ -77,7 +77,12 @@ fun MacroDialog( fun appendLog(msg: String) { val line = "[${java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.US).format(java.util.Date())}] $msg\n" - executionLog += line + val combined = executionLog + line + executionLog = if (combined.length > 131072) { + combined.substring(combined.length - 131072) + } else { + combined + } } DisposableEffect(client) { diff --git a/src/main/java/haus/nightmare/a3270/ui/TerminalView.kt b/src/main/java/haus/nightmare/a3270/ui/TerminalView.kt index 7feb078..db264de 100644 --- a/src/main/java/haus/nightmare/a3270/ui/TerminalView.kt +++ b/src/main/java/haus/nightmare/a3270/ui/TerminalView.kt @@ -126,6 +126,15 @@ fun TerminalView( } } + val paint = remember { + Paint().apply { + isAntiAlias = true + typeface = Typeface.MONOSPACE + textAlign = Paint.Align.CENTER + } + } + var cachedGraphicsBitmap by remember { mutableStateOf(null) } + Box(modifier = modifier.fillMaxSize()) { Canvas( modifier = Modifier @@ -289,12 +298,6 @@ fun TerminalView( val offsetX = metrics.offsetX val offsetY = metrics.offsetY - val paint = Paint().apply { - isAntiAlias = true - typeface = Typeface.MONOSPACE - textAlign = Paint.Align.CENTER - } - // Calculate optimal font size ensuring characters fit cleanly in cell without crowding val baseTextSize = cellHeight * 0.78f paint.textSize = baseTextSize @@ -335,8 +338,13 @@ fun TerminalView( val gWidth = graphicsPlane.canvasWidth val gHeight = graphicsPlane.canvasHeight val rgb = graphicsPlane.rgbBuffer - if (gridW > 0 && gridH > 0 && rgb != null && gWidth > 0 && gHeight > 0) { - val bmp = android.graphics.Bitmap.createBitmap(rgb, gWidth, gHeight, android.graphics.Bitmap.Config.ARGB_8888) + if (gridW > 0 && gridH > 0 && rgb != null && gWidth > 0 && gHeight > 0 && rgb.size >= gWidth * gHeight) { + var bmp = cachedGraphicsBitmap + if (bmp == null || bmp.width != gWidth || bmp.height != gHeight || bmp.isRecycled) { + bmp = android.graphics.Bitmap.createBitmap(gWidth, gHeight, android.graphics.Bitmap.Config.ARGB_8888) + cachedGraphicsBitmap = bmp + } + bmp.setPixels(rgb, 0, gWidth, 0, 0, gWidth, gHeight) drawContext.canvas.nativeCanvas.drawBitmap( bmp, null,