Parse system theme events (#539)

This commit is contained in:
Jake Wharton
2024-11-21 23:28:49 -05:00
committed by GitHub
parent b1af118fcc
commit 2aa8682c00
7 changed files with 142 additions and 4 deletions

View File

@ -24,6 +24,14 @@ public final class com/jakewharton/mosaic/terminal/event/BracketedPasteEvent : c
public fun toString ()Ljava/lang/String;
}
public final class com/jakewharton/mosaic/terminal/event/DeviceStatusReportEvent : com/jakewharton/mosaic/terminal/event/Event {
public fun <init> (Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class com/jakewharton/mosaic/terminal/event/Event {
}
@ -43,6 +51,14 @@ public final class com/jakewharton/mosaic/terminal/event/PrimaryDeviceAttributes
public fun toString ()Ljava/lang/String;
}
public final class com/jakewharton/mosaic/terminal/event/SystemThemeEvent : com/jakewharton/mosaic/terminal/event/Event {
public fun <init> (Z)V
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public final fun isDark ()Z
public fun toString ()Ljava/lang/String;
}
public final class com/jakewharton/mosaic/terminal/event/UnknownEvent : com/jakewharton/mosaic/terminal/event/Event {
public fun <init> (Ljava/lang/String;[B)V
public fun equals (Ljava/lang/Object;)Z

View File

@ -19,6 +19,17 @@ final class com.jakewharton.mosaic.terminal.event/BracketedPasteEvent : com.jake
final fun toString(): kotlin/String // com.jakewharton.mosaic.terminal.event/BracketedPasteEvent.toString|toString(){}[0]
}
final class com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent : com.jakewharton.mosaic.terminal.event/Event { // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent|null[0]
constructor <init>(kotlin/String) // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.<init>|<init>(kotlin.String){}[0]
final val data // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.data|{}data[0]
final fun <get-data>(): kotlin/String // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.data.<get-data>|<get-data>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.jakewharton.mosaic.terminal.event/DeviceStatusReportEvent.toString|toString(){}[0]
}
final class com.jakewharton.mosaic.terminal.event/FocusEvent : com.jakewharton.mosaic.terminal.event/Event { // com.jakewharton.mosaic.terminal.event/FocusEvent|null[0]
constructor <init>(kotlin/Boolean) // com.jakewharton.mosaic.terminal.event/FocusEvent.<init>|<init>(kotlin.Boolean){}[0]
@ -41,6 +52,17 @@ final class com.jakewharton.mosaic.terminal.event/PrimaryDeviceAttributesEvent :
final fun toString(): kotlin/String // com.jakewharton.mosaic.terminal.event/PrimaryDeviceAttributesEvent.toString|toString(){}[0]
}
final class com.jakewharton.mosaic.terminal.event/SystemThemeEvent : com.jakewharton.mosaic.terminal.event/Event { // com.jakewharton.mosaic.terminal.event/SystemThemeEvent|null[0]
constructor <init>(kotlin/Boolean) // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.<init>|<init>(kotlin.Boolean){}[0]
final val isDark // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.isDark|{}isDark[0]
final fun <get-isDark>(): kotlin/Boolean // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.isDark.<get-isDark>|<get-isDark>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.jakewharton.mosaic.terminal.event/SystemThemeEvent.toString|toString(){}[0]
}
final class com.jakewharton.mosaic.terminal.event/UnknownEvent : com.jakewharton.mosaic.terminal.event/Event { // com.jakewharton.mosaic.terminal.event/UnknownEvent|null[0]
constructor <init>(kotlin/String, kotlin/ByteArray) // com.jakewharton.mosaic.terminal.event/UnknownEvent.<init>|<init>(kotlin.String;kotlin.ByteArray){}[0]

View File

@ -3,7 +3,7 @@ package com.jakewharton.mosaic.terminal
import com.jakewharton.mosaic.terminal.event.BracketedPasteEvent
import com.jakewharton.mosaic.terminal.event.CodepointEvent
import com.jakewharton.mosaic.terminal.event.DecModeReport
import com.jakewharton.mosaic.terminal.event.DeviceStatusReportString
import com.jakewharton.mosaic.terminal.event.DeviceStatusReportEvent
import com.jakewharton.mosaic.terminal.event.Event
import com.jakewharton.mosaic.terminal.event.FocusEvent
import com.jakewharton.mosaic.terminal.event.KeyEscape
@ -11,6 +11,7 @@ import com.jakewharton.mosaic.terminal.event.KittyGraphicsEvent
import com.jakewharton.mosaic.terminal.event.MouseEvent
import com.jakewharton.mosaic.terminal.event.PrimaryDeviceAttributesEvent
import com.jakewharton.mosaic.terminal.event.ResizeEvent
import com.jakewharton.mosaic.terminal.event.SystemThemeEvent
import com.jakewharton.mosaic.terminal.event.UnknownEvent
private const val BufferSize = 8 * 1024
@ -333,6 +334,38 @@ public class TerminalParser(
)
}
'n'.code -> {
if (buffer[b3Index].toInt() == '?'.code) {
val delimiter = buffer.indexOfOrDefault(';'.code.toByte(), start + 3, finalIndex, finalIndex)
when (buffer.parseIntDigits(start + 3, delimiter)) {
997 -> {
if (delimiter + 2 == finalIndex) {
val p2 = buffer[delimiter + 1].toInt()
if (p2 == '1'.code) {
return SystemThemeEvent(isDark = true)
}
if (p2 == '2'.code) {
return SystemThemeEvent(isDark = false)
}
}
return UnknownEvent(
context = "CSI ? 997 ; p2 n sequence has invalid p2",
bytes = buffer.copyOfRange(start, end),
)
}
else -> {
return DeviceStatusReportEvent(
data = buffer.decodeToString(start + 3, finalIndex),
)
}
}
}
return UnknownEvent(
context = "CSI .. n sequence without leading ?",
bytes = buffer.copyOfRange(start, end),
)
}
'c'.code -> {
if (buffer[b3Index].toInt() == '?'.code) {
val data = buffer.decodeToString(start + 3, finalIndex)
@ -417,7 +450,7 @@ public class TerminalParser(
buffer[start + 2].toInt() == '>'.code &&
buffer[start + 3].toInt() == '|'.code
) {
DeviceStatusReportString(
DeviceStatusReportEvent(
data = buffer.decodeToString(start + 4, stIndex),
)
} else {

View File

@ -29,6 +29,8 @@ internal inline fun ByteArray.indexOfFirstOrElse(
}
internal fun ByteArray.parseIntDigits(start: Int, end: Int): Int {
// TODO This needs an orElse path for parsing failure on non-digits.
var value = 0
for (i in start until end) {
value *= 10

View File

@ -84,8 +84,14 @@ public class PrimaryDeviceAttributesEvent(
public val data: String,
) : Event
internal data class DeviceStatusReportString(
val data: String,
@Poko
public class DeviceStatusReportEvent(
public val data: String,
) : Event
@Poko
public class SystemThemeEvent(
public val isDark: Boolean,
) : Event
internal data class KittyGraphicsEvent(

View File

@ -0,0 +1,58 @@
package com.jakewharton.mosaic.terminal
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.jakewharton.mosaic.terminal.event.SystemThemeEvent
import com.jakewharton.mosaic.terminal.event.UnknownEvent
import kotlin.test.AfterTest
import kotlin.test.Test
@OptIn(ExperimentalStdlibApi::class)
class TerminalParserCsiSystemThemeEventTest {
private val writer = Tty.stdinWriter()
private val parser = TerminalParser(writer.reader, true)
@AfterTest fun after() {
writer.close()
}
@Test fun dark() {
writer.writeHex("1b5b3f3939373b316e")
assertThat(parser.next()).isEqualTo(SystemThemeEvent(isDark = true))
}
@Test fun light() {
writer.writeHex("1b5b3f3939373b326e")
assertThat(parser.next()).isEqualTo(SystemThemeEvent(isDark = false))
}
@Test fun missingP2() {
writer.writeHex("1b5b3f3939373b6e")
assertThat(parser.next()).isEqualTo(
UnknownEvent(
context = "CSI ? 997 ; p2 n sequence has invalid p2",
bytes = "1b5b3f3939373b6e".hexToByteArray(),
),
)
}
@Test fun unknownP2() {
writer.writeHex("1b5b3f3939373b346e")
assertThat(parser.next()).isEqualTo(
UnknownEvent(
context = "CSI ? 997 ; p2 n sequence has invalid p2",
bytes = "1b5b3f3939373b346e".hexToByteArray(),
),
)
}
@Test fun tooLongP2() {
writer.writeHex("1b5b3f3939373b31316e")
assertThat(parser.next()).isEqualTo(
UnknownEvent(
context = "CSI ? 997 ; p2 n sequence has invalid p2",
bytes = "1b5b3f3939373b31316e".hexToByteArray(),
),
)
}
}

View File

@ -75,6 +75,7 @@ private class RawModeEchoCommand : CliktCommand("raw-mode-echo") {
}
if (all || colorQuery) {
print("\u001b[?996n") // Color scheme request
print("\u001b[?2031h") // Color scheme enable
}
val reader = Tty.stdinReader()