From 75c3a781b356370aeb8082f4aff0640d1f259409 Mon Sep 17 00:00:00 2001 From: kimsuktae Date: Thu, 19 Feb 2026 10:19:23 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=EC=97=90=20=EC=BD=94=EB=A3=A8=ED=8B=B4=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EB=B9=84=EB=8F=99=EA=B8=B0=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- 20260218-3files/src/tetris/Input.kt | 47 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/20260218-3files/src/tetris/Input.kt b/20260218-3files/src/tetris/Input.kt index efb79a61..877acd42 100644 --- a/20260218-3files/src/tetris/Input.kt +++ b/20260218-3files/src/tetris/Input.kt @@ -1,22 +1,8 @@ package tetris import java.util.concurrent.ConcurrentLinkedQueue - -/** - * 터미널 raw 모드 키보드 입력 처리. - * - * Why raw 모드 + 별도 스레드? - * → System.in.read()는 blocking call. - * → 메인 스레드에서 호출하면 게임 루프 정지. - * → 별도 daemon 스레드에서 읽고 큐에 적재. - * → 상세: .claude/docs/PL-001-tetris/findings.md "입력 처리" 결정 참고 - * - * 방향키 escape sequence: - * → ↑: ESC [ A (27, 91, 65) - * → ↓: ESC [ B (27, 91, 66) - * → →: ESC [ C (27, 91, 67) - * → ←: ESC [ D (27, 91, 68) - */ +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel enum class Action { LEFT, RIGHT, ROTATE, SOFT_DROP, HARD_DROP, QUIT, NONE @@ -86,4 +72,33 @@ class Input { fun stop() { running = false } + + private val scope = CoroutineScope(Dispatchers.IO) + private val channel = Channel(Channel.CONFLATED) + + fun startAsync() { + scope.launch { + while (isActive) { + val b = withContext(Dispatchers.IO) { System.`in`.read() } + if (b == -1) continue + val action = when (b) { + 27 -> parseEscapeSequence() + 32 -> Action.HARD_DROP + 113, 81 -> Action.QUIT + else -> Action.NONE + } + if (action != Action.NONE) { + channel.send(action) + } + } + } + } + + suspend fun pollAsync(): Action { + return channel.tryReceive().getOrNull() ?: Action.NONE + } + + fun stopAsync() { + scope.cancel() + } }