1.5.0 release; console stability fix; tetris sample rewritten to be RC-free

This commit is contained in:
sergeych committed 2026-03-22 03:11:08 +03:00
1 parent 222a653040
commit d17ad9ef0d
7 files changed
+176 -88

No files matched your search

@@ -37,10 +37,7 @@ import net.sergeych.lyng.obj.toObj
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.raiseIllegalOperation
import net.sergeych.lyng.requireScope
import net.sergeych.lyngio.console.ConsoleEvent
import net.sergeych.lyngio.console.ConsoleEventSource
import net.sergeych.lyngio.console.LyngConsole
import net.sergeych.lyngio.console.getSystemConsole
import net.sergeych.lyngio.console.*
import net.sergeych.lyngio.console.security.ConsoleAccessDeniedException
import net.sergeych.lyngio.console.security.ConsoleAccessPolicy
import net.sergeych.lyngio.console.security.LyngConsoleSecured
@@ -245,13 +242,27 @@ private class ObjConsoleEventIterator(
private suspend fun ensureCached(): Boolean {
if (closed) return false
if (cached != null) return true
val event = source.nextEvent()
if (event == null) {
closeSource()
return false
while (!closed && cached == null) {
val event = try {
source.nextEvent()
} catch (e: Throwable) {
// Consumer loops must survive source/read failures: report and keep polling.
consoleFlowDebug("console-bridge: nextEvent failed; dropping failure and continuing", e)
continue
}
if (event == null) {
closeSource()
return false
}
cached = try {
event.toObjEvent()
} catch (e: Throwable) {
// Malformed/native event payload must not terminate consumer iteration.
consoleFlowDebug("console-bridge: malformed event dropped: $event", e)
null
}
}
cached = event.toObjEvent()
return true
return cached != null
}
private suspend fun closeSource() {
@@ -292,8 +303,14 @@ private class ObjConsoleEventIterator(
private fun ConsoleEvent.toObjEvent(): Obj = when (this) {
is ConsoleEvent.Resize -> ObjConsoleResizeEvent(columns, rows)
is ConsoleEvent.KeyDown -> ObjConsoleKeyEvent(type = ConsoleEnums.KEY_DOWN, key = key, codeName = code, ctrl = ctrl, alt = alt, shift = shift, meta = meta)
is ConsoleEvent.KeyUp -> ObjConsoleKeyEvent(type = ConsoleEnums.KEY_UP, key = key, codeName = code, ctrl = ctrl, alt = alt, shift = shift, meta = meta)
is ConsoleEvent.KeyDown -> ObjConsoleKeyEvent(type = ConsoleEnums.KEY_DOWN, key = sanitizedKeyOrFallback(key), codeName = code, ctrl = ctrl, alt = alt, shift = shift, meta = meta)
is ConsoleEvent.KeyUp -> ObjConsoleKeyEvent(type = ConsoleEnums.KEY_UP, key = sanitizedKeyOrFallback(key), codeName = code, ctrl = ctrl, alt = alt, shift = shift, meta = meta)
}
private fun sanitizedKeyOrFallback(key: String): String {
if (key.isNotEmpty()) return key
consoleFlowDebug("console-bridge: empty key value received; using fallback key name")
return "Unknown"
}
private object ConsoleEnums {
@@ -100,8 +100,9 @@ object MordantLyngConsole : LyngConsole {
var running = true
globalLaunch {
var lastWidth = t.updateSize().width
var lastHeight = t.updateSize().height
val initialSize = runCatching { t.updateSize() }.getOrNull()
var lastWidth = initialSize?.width ?: 0
var lastHeight = initialSize?.height ?: 0
val startMark = TimeSource.Monotonic.markNow()
var lastHeartbeatMark = startMark
var loops = 0L
@@ -113,6 +114,18 @@ object MordantLyngConsole : LyngConsole {
var lastKeyMark = startMark
var lastRawRecoveryMark = startMark
fun tryEmitResize(width: Int, height: Int) {
if (width < 1 || height < 1) {
consoleFlowDebug("events: ignored invalid resize width=$width height=$height")
return
}
if (width == lastWidth && height == lastHeight) return
out.trySend(ConsoleEvent.Resize(width, height))
lastWidth = width
lastHeight = height
resizeEvents += 1
}
consoleFlowDebug("events: collector started")
try {
while (currentCoroutineContext().isActive && sourceState.withLock { running }) {
@@ -122,12 +135,7 @@ object MordantLyngConsole : LyngConsole {
delay(150)
continue
}
if (currentSize.width != lastWidth || currentSize.height != lastHeight) {
out.trySend(ConsoleEvent.Resize(currentSize.width, currentSize.height))
lastWidth = currentSize.width
lastHeight = currentSize.height
resizeEvents += 1
}
tryEmitResize(currentSize.width, currentSize.height)
val raw = stateMutex.withLock {
if (!rawModeRequested) {
@@ -173,10 +181,8 @@ object MordantLyngConsole : LyngConsole {
val ev = readResult.getOrNull()
val resized = runCatching { t.updateSize() }.getOrNull()
if (resized != null && (resized.width != lastWidth || resized.height != lastHeight)) {
out.trySend(ConsoleEvent.Resize(resized.width, resized.height))
lastWidth = resized.width
lastHeight = resized.height
if (resized != null) {
tryEmitResize(resized.width, resized.height)
}
when (ev) {
@@ -215,10 +215,29 @@ object JvmLyngConsole : LyngConsole {
var reader = activeTerm.reader()
var keyThread: Thread? = null
var heartbeatThread: Thread? = null
val resizeEmitMutex = Any()
var lastResizeCols = Int.MIN_VALUE
var lastResizeRows = Int.MIN_VALUE
fun emitResize() {
val size = runCatching { activeTerm.size }.getOrNull() ?: return
out.trySend(ConsoleEvent.Resize(size.columns, size.rows))
val cols = size.columns
val rows = size.rows
if (cols < 1 || rows < 1) {
consoleFlowDebug("jline-events: ignored invalid resize columns=$cols rows=$rows")
return
}
val shouldEmit = synchronized(resizeEmitMutex) {
if (cols == lastResizeCols && rows == lastResizeRows) {
false
} else {
lastResizeCols = cols
lastResizeRows = rows
true
}
}
if (!shouldEmit) return
out.trySend(ConsoleEvent.Resize(cols, rows))
}
fun cleanup() {