1.5.0 release; console stability fix; tetris sample rewritten to be RC-free
This commit is contained in:
parent
222a653040
commit
d17ad9ef0d
@ -10,6 +10,14 @@
|
|||||||
* - Space: hard drop
|
* - Space: hard drop
|
||||||
* - P or Escape: pause
|
* - P or Escape: pause
|
||||||
* - Q: quit
|
* - Q: quit
|
||||||
|
|
||||||
|
Tsted to score:
|
||||||
|
sergeych@sergeych-XPS-17-9720:~$ ~/dev/lyng/examples/tetris_console.lyng
|
||||||
|
Bye.
|
||||||
|
Score: 435480
|
||||||
|
Lines: 271
|
||||||
|
Level: 28
|
||||||
|
Ssergeych@sergeych-XPS-17-9720:~$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import lyng.io.console
|
import lyng.io.console
|
||||||
@ -28,6 +36,7 @@ val DROP_FRAMES_BASE = 15
|
|||||||
val DROP_FRAMES_MIN = 3
|
val DROP_FRAMES_MIN = 3
|
||||||
val FRAME_DELAY_MS = 35
|
val FRAME_DELAY_MS = 35
|
||||||
val RESIZE_WAIT_MS = 250
|
val RESIZE_WAIT_MS = 250
|
||||||
|
val MAX_PENDING_INPUTS = 64
|
||||||
val ROTATION_KICKS = [0, -1, 1, -2, 2]
|
val ROTATION_KICKS = [0, -1, 1, -2, 2]
|
||||||
val ANSI_ESC = "\u001b["
|
val ANSI_ESC = "\u001b["
|
||||||
val ANSI_RESET = ANSI_ESC + "0m"
|
val ANSI_RESET = ANSI_ESC + "0m"
|
||||||
@ -78,7 +87,14 @@ fun clearAndHome() {
|
|||||||
|
|
||||||
fun logError(message: String, err: Object?): Void {
|
fun logError(message: String, err: Object?): Void {
|
||||||
try {
|
try {
|
||||||
val details = if (err == null) "" else ": " + err
|
var details = ""
|
||||||
|
if (err != null) {
|
||||||
|
try {
|
||||||
|
details = ": " + err
|
||||||
|
} catch (_: Object) {
|
||||||
|
details = ": <error-format-failed>"
|
||||||
|
}
|
||||||
|
}
|
||||||
Path(ERROR_LOG_PATH).appendUtf8(message + details + "\n")
|
Path(ERROR_LOG_PATH).appendUtf8(message + details + "\n")
|
||||||
} catch (_: Object) {
|
} catch (_: Object) {
|
||||||
// Never let logging errors affect gameplay.
|
// Never let logging errors affect gameplay.
|
||||||
@ -136,24 +152,30 @@ fun emptyCellText(useColor: Bool): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canPlace(board: Board, boardW: Int, boardH: Int, pieceId: Int, rot: Int, px: Int, py: Int): Bool {
|
fun canPlace(board: Board, boardW: Int, boardH: Int, pieceId: Int, rot: Int, px: Int, py: Int): Bool {
|
||||||
if (pieceId < 1 || pieceId > 7) return false
|
try {
|
||||||
val piece: Piece = PIECES[pieceId - 1]
|
if (pieceId < 1 || pieceId > 7) return false
|
||||||
if (rot < 0 || rot >= piece.rotations.size) return false
|
val piece: Piece = PIECES[pieceId - 1]
|
||||||
val cells = piece.rotations[rot]
|
if (rot < 0 || rot >= piece.rotations.size) return false
|
||||||
|
val cells = piece.rotations[rot]
|
||||||
|
|
||||||
for (cell in cells) {
|
for (cell in cells) {
|
||||||
val x = px + cell[0]
|
val x = px + cell[0]
|
||||||
val y = py + cell[1]
|
val y = py + cell[1]
|
||||||
|
|
||||||
if (x < 0 || x >= boardW) return false
|
if (x < 0 || x >= boardW) return false
|
||||||
if (y >= boardH) return false
|
if (y >= boardH) return false
|
||||||
|
|
||||||
if (y >= 0) {
|
if (y >= 0) {
|
||||||
val row = board[y]
|
if (y >= board.size) return false
|
||||||
if (row[x] != 0) return false
|
val row = board[y]
|
||||||
|
if (row == null) return false
|
||||||
|
if (row[x] != 0) return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
} catch (_: Object) {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lockPiece(board: Board, pieceId: Int, rot: Int, px: Int, py: Int): Void {
|
fun lockPiece(board: Board, pieceId: Int, rot: Int, px: Int, py: Int): Void {
|
||||||
@ -164,9 +186,11 @@ fun lockPiece(board: Board, pieceId: Int, rot: Int, px: Int, py: Int): Void {
|
|||||||
val x = px + cell[0]
|
val x = px + cell[0]
|
||||||
val y = py + cell[1]
|
val y = py + cell[1]
|
||||||
|
|
||||||
if (y >= 0) {
|
if (y >= 0 && y < board.size) {
|
||||||
val row = board[y]
|
val row = board[y]
|
||||||
row[x] = pieceId
|
if (row != null) {
|
||||||
|
row[x] = pieceId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,7 +201,17 @@ fun clearCompletedLines(board: Board, boardW: Int, boardH: Int): Int {
|
|||||||
var cleared = 0
|
var cleared = 0
|
||||||
|
|
||||||
while (y >= 0) {
|
while (y >= 0) {
|
||||||
|
if (y >= b.size) {
|
||||||
|
y--
|
||||||
|
continue
|
||||||
|
}
|
||||||
val row = b[y]
|
val row = b[y]
|
||||||
|
if (row == null) {
|
||||||
|
b.removeAt(y)
|
||||||
|
b.insertAt(0, emptyRow(boardW))
|
||||||
|
cleared++
|
||||||
|
continue
|
||||||
|
}
|
||||||
var full = true
|
var full = true
|
||||||
for (x in 0..<boardW) {
|
for (x in 0..<boardW) {
|
||||||
if (row[x] == 0) {
|
if (row[x] == 0) {
|
||||||
@ -298,8 +332,8 @@ fun render(
|
|||||||
|
|
||||||
for (x in 0..<boardW) {
|
for (x in 0..<boardW) {
|
||||||
val a = activeCellId(state.pieceId, state.rot, state.px, state.py, x, y)
|
val a = activeCellId(state.pieceId, state.rot, state.px, state.py, x, y)
|
||||||
val row = board[y]
|
val row = if (y < board.size) board[y] else null
|
||||||
val b = row[x]
|
val b = if (row == null) 0 else row[x]
|
||||||
val id = if (a > 0) a else b
|
val id = if (a > 0) a else b
|
||||||
line += if (id > 0) blockText(id, useColor) else emptyCellText(useColor)
|
line += if (id > 0) blockText(id, useColor) else emptyCellText(useColor)
|
||||||
}
|
}
|
||||||
@ -507,8 +541,9 @@ if (!Console.isSupported()) {
|
|||||||
)
|
)
|
||||||
var prevFrameLines: List<String> = []
|
var prevFrameLines: List<String> = []
|
||||||
|
|
||||||
val gameMutex = Mutex()
|
val gameMutex: Mutex = Mutex()
|
||||||
var hasResizeEvent = false
|
var forceRedraw = false
|
||||||
|
val pendingInputs: List<String> = []
|
||||||
|
|
||||||
val rawModeEnabled = Console.setRawMode(true)
|
val rawModeEnabled = Console.setRawMode(true)
|
||||||
if (!rawModeEnabled) {
|
if (!rawModeEnabled) {
|
||||||
@ -538,12 +573,12 @@ if (!Console.isSupported()) {
|
|||||||
s.running = false
|
s.running = false
|
||||||
} else {
|
} else {
|
||||||
s.paused = false
|
s.paused = false
|
||||||
prevFrameLines = []
|
forceRedraw = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == "p" || key == "P" || key == "Escape") {
|
else if (key == "p" || key == "P" || key == "Escape") {
|
||||||
s.paused = true
|
s.paused = true
|
||||||
prevFrameLines = []
|
forceRedraw = true
|
||||||
}
|
}
|
||||||
else if (key == "q" || key == "Q") {
|
else if (key == "q" || key == "Q") {
|
||||||
s.running = false
|
s.running = false
|
||||||
@ -607,16 +642,15 @@ if (!Console.isSupported()) {
|
|||||||
logError("Dropped key event with empty/null key", null)
|
logError("Dropped key event with empty/null key", null)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gameMutex.withLock {
|
val mapped = if (ctrl && (key == "c" || key == "C")) "__CTRL_C__" else key
|
||||||
val mapped = if (ctrl && (key == "c" || key == "C")) "__CTRL_C__" else key
|
val mm: Mutex = gameMutex
|
||||||
applyKeyInput(state, mapped)
|
mm.withLock {
|
||||||
|
if (pendingInputs.size >= MAX_PENDING_INPUTS) {
|
||||||
|
pendingInputs.removeAt(0)
|
||||||
|
}
|
||||||
|
pendingInputs.add(mapped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ev is ConsoleResizeEvent) {
|
|
||||||
gameMutex.withLock {
|
|
||||||
hasResizeEvent = true
|
|
||||||
prevFrameLines = []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (eventErr: Object) {
|
} catch (eventErr: Object) {
|
||||||
// Keep the input stream alive; report for diagnostics.
|
// Keep the input stream alive; report for diagnostics.
|
||||||
@ -644,12 +678,6 @@ if (!Console.isSupported()) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var resized = false
|
|
||||||
gameMutex.withLock {
|
|
||||||
resized = hasResizeEvent
|
|
||||||
hasResizeEvent = false
|
|
||||||
}
|
|
||||||
|
|
||||||
val contentCols = boardW * 2 + 2 + 3 + PANEL_WIDTH
|
val contentCols = boardW * 2 + 2 + 3 + PANEL_WIDTH
|
||||||
val contentRows = boardH + 1
|
val contentRows = boardH + 1
|
||||||
val requiredCols = max(MIN_COLS, contentCols)
|
val requiredCols = max(MIN_COLS, contentCols)
|
||||||
@ -663,7 +691,7 @@ if (!Console.isSupported()) {
|
|||||||
|
|
||||||
val originCol = max(1, ((c - contentCols) / 2) + 1)
|
val originCol = max(1, ((c - contentCols) / 2) + 1)
|
||||||
val originRow = max(1, ((r - contentRows) / 2) + 1)
|
val originRow = max(1, ((r - contentRows) / 2) + 1)
|
||||||
LoopFrame(resized, originRow, originCol)
|
LoopFrame(false, originRow, originCol)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun advanceGravity(s: GameState, frame: Int): Int {
|
fun advanceGravity(s: GameState, frame: Int): Int {
|
||||||
@ -709,42 +737,60 @@ if (!Console.isSupported()) {
|
|||||||
var shouldStop = false
|
var shouldStop = false
|
||||||
var prevOriginRow = -1
|
var prevOriginRow = -1
|
||||||
var prevOriginCol = -1
|
var prevOriginCol = -1
|
||||||
|
var prevPaused = false
|
||||||
while (!shouldStop) {
|
while (!shouldStop) {
|
||||||
val frameData = pollLoopFrame()
|
val frameData = pollLoopFrame()
|
||||||
if (frameData == null) {
|
if (frameData == null) {
|
||||||
frame = 0
|
frame = 0
|
||||||
|
prevPaused = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
gameMutex.withLock {
|
val mm: Mutex = gameMutex
|
||||||
if (!state.running || state.gameOver) {
|
mm.withLock {
|
||||||
shouldStop = true
|
if (pendingInputs.size > 0) {
|
||||||
} else {
|
val toApply: List<String> = []
|
||||||
val movedOrigin = frameData.originRow != prevOriginRow || frameData.originCol != prevOriginCol
|
while (pendingInputs.size > 0) {
|
||||||
if (frameData.resized || movedOrigin) {
|
val k = pendingInputs[0]
|
||||||
clearAndHome()
|
pendingInputs.removeAt(0)
|
||||||
prevFrameLines = []
|
toApply.add(k)
|
||||||
}
|
}
|
||||||
prevOriginRow = frameData.originRow
|
for (k in toApply) {
|
||||||
prevOriginCol = frameData.originCol
|
applyKeyInput(state, k)
|
||||||
if (!state.paused) {
|
if (!state.running || state.gameOver) break
|
||||||
frame = advanceGravity(state, frame)
|
|
||||||
}
|
|
||||||
prevFrameLines = render(
|
|
||||||
state,
|
|
||||||
board,
|
|
||||||
boardW,
|
|
||||||
boardH,
|
|
||||||
prevFrameLines,
|
|
||||||
frameData.originRow,
|
|
||||||
frameData.originCol,
|
|
||||||
useColor
|
|
||||||
)
|
|
||||||
if (state.paused) {
|
|
||||||
renderPauseOverlay(frameData.originRow, frameData.originCol, boardW, boardH)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!state.running || state.gameOver) {
|
||||||
|
shouldStop = true
|
||||||
|
} else {
|
||||||
|
val localForceRedraw = forceRedraw
|
||||||
|
forceRedraw = false
|
||||||
|
val movedOrigin = frameData.originRow != prevOriginRow || frameData.originCol != prevOriginCol
|
||||||
|
if (frameData.resized || movedOrigin || localForceRedraw) {
|
||||||
|
clearAndHome()
|
||||||
|
prevFrameLines = []
|
||||||
|
}
|
||||||
|
prevOriginRow = frameData.originRow
|
||||||
|
prevOriginCol = frameData.originCol
|
||||||
|
if (!state.paused) {
|
||||||
|
frame = advanceGravity(state, frame)
|
||||||
|
}
|
||||||
|
prevFrameLines = render(
|
||||||
|
state,
|
||||||
|
board,
|
||||||
|
boardW,
|
||||||
|
boardH,
|
||||||
|
prevFrameLines,
|
||||||
|
frameData.originRow,
|
||||||
|
frameData.originCol,
|
||||||
|
useColor
|
||||||
|
)
|
||||||
|
if (state.paused && (!prevPaused || frameData.resized || movedOrigin)) {
|
||||||
|
renderPauseOverlay(frameData.originRow, frameData.originCol, boardW, boardH)
|
||||||
|
}
|
||||||
|
prevPaused = state.paused
|
||||||
|
}
|
||||||
Console.flush()
|
Console.flush()
|
||||||
delay(FRAME_DELAY_MS)
|
delay(FRAME_DELAY_MS)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,10 +37,7 @@ import net.sergeych.lyng.obj.toObj
|
|||||||
import net.sergeych.lyng.pacman.ImportManager
|
import net.sergeych.lyng.pacman.ImportManager
|
||||||
import net.sergeych.lyng.raiseIllegalOperation
|
import net.sergeych.lyng.raiseIllegalOperation
|
||||||
import net.sergeych.lyng.requireScope
|
import net.sergeych.lyng.requireScope
|
||||||
import net.sergeych.lyngio.console.ConsoleEvent
|
import net.sergeych.lyngio.console.*
|
||||||
import net.sergeych.lyngio.console.ConsoleEventSource
|
|
||||||
import net.sergeych.lyngio.console.LyngConsole
|
|
||||||
import net.sergeych.lyngio.console.getSystemConsole
|
|
||||||
import net.sergeych.lyngio.console.security.ConsoleAccessDeniedException
|
import net.sergeych.lyngio.console.security.ConsoleAccessDeniedException
|
||||||
import net.sergeych.lyngio.console.security.ConsoleAccessPolicy
|
import net.sergeych.lyngio.console.security.ConsoleAccessPolicy
|
||||||
import net.sergeych.lyngio.console.security.LyngConsoleSecured
|
import net.sergeych.lyngio.console.security.LyngConsoleSecured
|
||||||
@ -245,13 +242,27 @@ private class ObjConsoleEventIterator(
|
|||||||
private suspend fun ensureCached(): Boolean {
|
private suspend fun ensureCached(): Boolean {
|
||||||
if (closed) return false
|
if (closed) return false
|
||||||
if (cached != null) return true
|
if (cached != null) return true
|
||||||
val event = source.nextEvent()
|
while (!closed && cached == null) {
|
||||||
if (event == null) {
|
val event = try {
|
||||||
closeSource()
|
source.nextEvent()
|
||||||
return false
|
} 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 cached != null
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun closeSource() {
|
private suspend fun closeSource() {
|
||||||
@ -292,8 +303,14 @@ private class ObjConsoleEventIterator(
|
|||||||
|
|
||||||
private fun ConsoleEvent.toObjEvent(): Obj = when (this) {
|
private fun ConsoleEvent.toObjEvent(): Obj = when (this) {
|
||||||
is ConsoleEvent.Resize -> ObjConsoleResizeEvent(columns, rows)
|
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.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 = 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 {
|
private object ConsoleEnums {
|
||||||
|
|||||||
@ -100,8 +100,9 @@ object MordantLyngConsole : LyngConsole {
|
|||||||
var running = true
|
var running = true
|
||||||
|
|
||||||
globalLaunch {
|
globalLaunch {
|
||||||
var lastWidth = t.updateSize().width
|
val initialSize = runCatching { t.updateSize() }.getOrNull()
|
||||||
var lastHeight = t.updateSize().height
|
var lastWidth = initialSize?.width ?: 0
|
||||||
|
var lastHeight = initialSize?.height ?: 0
|
||||||
val startMark = TimeSource.Monotonic.markNow()
|
val startMark = TimeSource.Monotonic.markNow()
|
||||||
var lastHeartbeatMark = startMark
|
var lastHeartbeatMark = startMark
|
||||||
var loops = 0L
|
var loops = 0L
|
||||||
@ -113,6 +114,18 @@ object MordantLyngConsole : LyngConsole {
|
|||||||
var lastKeyMark = startMark
|
var lastKeyMark = startMark
|
||||||
var lastRawRecoveryMark = 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")
|
consoleFlowDebug("events: collector started")
|
||||||
try {
|
try {
|
||||||
while (currentCoroutineContext().isActive && sourceState.withLock { running }) {
|
while (currentCoroutineContext().isActive && sourceState.withLock { running }) {
|
||||||
@ -122,12 +135,7 @@ object MordantLyngConsole : LyngConsole {
|
|||||||
delay(150)
|
delay(150)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (currentSize.width != lastWidth || currentSize.height != lastHeight) {
|
tryEmitResize(currentSize.width, currentSize.height)
|
||||||
out.trySend(ConsoleEvent.Resize(currentSize.width, currentSize.height))
|
|
||||||
lastWidth = currentSize.width
|
|
||||||
lastHeight = currentSize.height
|
|
||||||
resizeEvents += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
val raw = stateMutex.withLock {
|
val raw = stateMutex.withLock {
|
||||||
if (!rawModeRequested) {
|
if (!rawModeRequested) {
|
||||||
@ -173,10 +181,8 @@ object MordantLyngConsole : LyngConsole {
|
|||||||
val ev = readResult.getOrNull()
|
val ev = readResult.getOrNull()
|
||||||
|
|
||||||
val resized = runCatching { t.updateSize() }.getOrNull()
|
val resized = runCatching { t.updateSize() }.getOrNull()
|
||||||
if (resized != null && (resized.width != lastWidth || resized.height != lastHeight)) {
|
if (resized != null) {
|
||||||
out.trySend(ConsoleEvent.Resize(resized.width, resized.height))
|
tryEmitResize(resized.width, resized.height)
|
||||||
lastWidth = resized.width
|
|
||||||
lastHeight = resized.height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when (ev) {
|
when (ev) {
|
||||||
|
|||||||
@ -215,10 +215,29 @@ object JvmLyngConsole : LyngConsole {
|
|||||||
var reader = activeTerm.reader()
|
var reader = activeTerm.reader()
|
||||||
var keyThread: Thread? = null
|
var keyThread: Thread? = null
|
||||||
var heartbeatThread: Thread? = null
|
var heartbeatThread: Thread? = null
|
||||||
|
val resizeEmitMutex = Any()
|
||||||
|
var lastResizeCols = Int.MIN_VALUE
|
||||||
|
var lastResizeRows = Int.MIN_VALUE
|
||||||
|
|
||||||
fun emitResize() {
|
fun emitResize() {
|
||||||
val size = runCatching { activeTerm.size }.getOrNull() ?: return
|
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() {
|
fun cleanup() {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "1.5.0-RC2"
|
version = "1.5.0"
|
||||||
|
|
||||||
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 124 KiB |
BIN
site/src/jsMain/resources/tetris2.png
Normal file
BIN
site/src/jsMain/resources/tetris2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
Loading…
x
Reference in New Issue
Block a user