diff options
Diffstat (limited to 'desktop')
| -rw-r--r-- | desktop/build.gradle.kts | 7 | ||||
| -rw-r--r-- | desktop/src/main/kotlin/coffee/liz/rookery/desktop/CubeCli.kt | 23 |
2 files changed, 27 insertions, 3 deletions
diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index d143c5f..e0fd832 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -19,6 +19,8 @@ compose.desktop { mainClass = "coffee.liz.rookery.desktop.MainKt" jvmArgs += listOf("-Xmx256m", "-XX:+UseSerialGC") nativeDistributions { + // From `./gradlew :desktop:suggestRuntimeModules`; jdk.security.auth is needed by dbus-java (Bluetooth). + modules("java.instrument", "jdk.security.auth", "jdk.unsupported") targetFormats(TargetFormat.AppImage, TargetFormat.Deb) packageName = "rookery" packageVersion = "0.1.0" @@ -27,4 +29,7 @@ compose.desktop { } // `./gradlew :desktop:run --args="--import file.txt"` passes args through; env lets tests point at a scratch home. -tasks.withType<JavaExec>().configureEach { environment.putAll(System.getenv().filterKeys { it.startsWith("XDG_") || it.startsWith("ROOKERY_") }) } +tasks.withType<JavaExec>().configureEach { + environment.putAll(System.getenv().filterKeys { it.startsWith("XDG_") || it.startsWith("ROOKERY_") }) + standardInput = System.`in` // so `--cube monitor` can take commands +} diff --git a/desktop/src/main/kotlin/coffee/liz/rookery/desktop/CubeCli.kt b/desktop/src/main/kotlin/coffee/liz/rookery/desktop/CubeCli.kt index b183420..171a6d9 100644 --- a/desktop/src/main/kotlin/coffee/liz/rookery/desktop/CubeCli.kt +++ b/desktop/src/main/kotlin/coffee/liz/rookery/desktop/CubeCli.kt @@ -47,6 +47,8 @@ private fun scan(seconds: Long, all: Boolean) = BlueZTransport().use { transport } } +private const val SOLVED_FACELETS = "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB" + private fun monitor(mac: String) = BlueZTransport().use { transport -> runBlocking { println("Connecting to $mac...") @@ -55,14 +57,20 @@ private fun monitor(mac: String) = BlueZTransport().use { transport -> val done = CompletableDeferred<Unit>() Runtime.getRuntime().addShutdownHook(Thread { runBlocking { withTimeoutOrNull(2000) { cube.disconnect() } } }) var firstMoveHost: Long? = null + var lastFacelets: String? = null val collector = launch(start = CoroutineStart.UNDISPATCHED) { cube.events.collect { e -> when (e) { is SmartCubeEvent.Move -> { val t0 = firstMoveHost ?: e.hostTimestampMs.also { firstMoveHost = it } + if (e.afterGap) println(" … moves missing here (the cube never re-sent them); resyncing from facelets") println("move %-3s host +%6d ms cube %s%s".format(e.move, e.hostTimestampMs - t0, e.cubeTimestampMs?.let { "$it ms" } ?: "-", if (e.recovered) " (recovered)" else "")) } - is SmartCubeEvent.Facelets -> println("facelets ${e.facelets}") + // The cube repeats facelets periodically; only show changes. + is SmartCubeEvent.Facelets -> if (e.facelets != lastFacelets) { + lastFacelets = e.facelets + println("facelets ${e.facelets}" + if (e.facelets == SOLVED_FACELETS) " (solved)" else "") + } is SmartCubeEvent.Battery -> println("battery ${e.percent}%") is SmartCubeEvent.Hardware -> println("hardware ${e.name} hw ${e.hardwareVersion} sw ${e.softwareVersion}" + (e.productDate?.let { " made $it" } ?: "") + if (e.gyroSupported) " gyro" else "") is SmartCubeEvent.Gyro -> Unit // too chatty to print @@ -73,7 +81,18 @@ private fun monitor(mac: String) = BlueZTransport().use { transport -> cube.requestHardware(); delay(300) cube.requestBattery(); delay(300) cube.requestFacelets() - println("Streaming moves; Ctrl-C to quit.") + println("Streaming moves. Type r+Enter when the cube is physically solved to resync it, f+Enter to re-read state, Ctrl-C to quit.") + // stdin commands, read on a plain thread so the coroutine side never blocks + val scope = this + Thread { + while (true) { + when (readlnOrNull()?.trim()) { + null -> break + "r" -> scope.launch { cube.resetToSolved(); delay(200); cube.requestFacelets(); println("told the cube it's solved") } + "f" -> scope.launch { lastFacelets = null; cube.requestFacelets() } + } + } + }.apply { isDaemon = true }.start() done.await() collector.cancel() } |
