diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 199c920fe..98fe1c60a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,9 +20,9 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] include: - os: ubuntu-latest - EXTRA_GRADLE_ARGS: :test:proguard:r8jar apiCheck + EXTRA_GRADLE_ARGS: apiCheck :test:proguard:r8jar - os: macos-latest - EXTRA_GRADLE_ARGS: :mordant:compilePosixMainKotlinMetadata + EXTRA_GRADLE_ARGS: :mordant:compileNativeMainKotlinMetadata :mordant:compilePosixMainKotlinMetadata :mordant:compileAppleMainKotlinMetadata runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 @@ -39,11 +39,11 @@ jobs: - name: Run tests run: >- ./gradlew + ${{matrix.EXTRA_GRADLE_ARGS}} :mordant:check :mordant-coroutines:check :mordant-markdown:check :test:graalvm:nativeTest - ${{matrix.EXTRA_GRADLE_ARGS}} --stacktrace - name: Run R8 Jar if: ${{ matrix.os == 'ubuntu-latest' }} diff --git a/buildSrc/src/main/kotlin/mordant-native-conventions.gradle.kts b/buildSrc/src/main/kotlin/mordant-native-conventions.gradle.kts index 44bf8aeef..da143de6c 100644 --- a/buildSrc/src/main/kotlin/mordant-native-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/mordant-native-conventions.gradle.kts @@ -8,7 +8,6 @@ kotlin { tvosX64() tvosArm64() tvosSimulatorArm64() - watchosArm32() watchosArm64() watchosX64() } diff --git a/mordant/src/appleNonDesktopMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.appleNonDesktop.kt b/mordant/src/appleNonDesktopMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.appleNonDesktop.kt index c741c066c..8ab0c23b1 100644 --- a/mordant/src/appleNonDesktopMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.appleNonDesktop.kt +++ b/mordant/src/appleNonDesktopMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.appleNonDesktop.kt @@ -1,3 +1,3 @@ package com.github.ajalt.mordant.internal -internal actual fun hasFileSystem(): Boolean = false +internal actual fun testsHaveFileSystem(): Boolean = false diff --git a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.kt b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.kt index 48d11081a..5087f2a59 100644 --- a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.kt +++ b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.kt @@ -53,7 +53,8 @@ internal expect fun exitProcessMpp(status: Int) internal expect fun readFileIfExists(filename: String): String? -internal expect fun hasFileSystem(): Boolean +/** Whether tests running on this platform can access the filesystem */ +internal expect fun testsHaveFileSystem(): Boolean internal expect fun getStandardTerminalInterface(): TerminalInterface diff --git a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/platform/MultiplatformSystemTest.kt b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/platform/MultiplatformSystemTest.kt index 5b3ca3f5f..19e8032ac 100644 --- a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/platform/MultiplatformSystemTest.kt +++ b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/platform/MultiplatformSystemTest.kt @@ -1,6 +1,6 @@ package com.github.ajalt.mordant.platform -import com.github.ajalt.mordant.internal.hasFileSystem +import com.github.ajalt.mordant.internal.testsHaveFileSystem import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldNotBeEmpty @@ -13,7 +13,7 @@ class MultiplatformSystemTest { // The kotlin.test plugin doesn't provide a way to set environment variables that works on // all targets, so just pick a common one that should exist everywhere. val actual = MultiplatformSystem.readEnvironmentVariable("PATH") - if (!hasFileSystem()) return + if (!testsHaveFileSystem()) return actual.shouldNotBeNull().shouldNotBeEmpty() } @@ -26,7 +26,7 @@ class MultiplatformSystemTest { // js targets have a cwd of $projectDir/build/js/packages/mordant-mordant-test "../../../../mordant/src/commonTest/resources/multiplatform_system_test.txt" ) - if (!hasFileSystem()) return + if (!testsHaveFileSystem()) return actual?.trim() shouldBe "pass" } } diff --git a/mordant/src/appleMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.apple.kt b/mordant/src/iosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.ios.kt similarity index 76% rename from mordant/src/appleMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.apple.kt rename to mordant/src/iosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.ios.kt index 672faed6f..256b039a3 100644 --- a/mordant/src/appleMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.apple.kt +++ b/mordant/src/iosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.ios.kt @@ -1,8 +1,8 @@ package com.github.ajalt.mordant.internal import com.github.ajalt.mordant.terminal.TerminalInterface -import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeApple +import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeCopyPasted internal actual fun getStandardTerminalInterface(): TerminalInterface { - return TerminalInterfaceNativeApple() + return TerminalInterfaceNativeCopyPasted() } diff --git a/mordant/src/appleMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.apple.kt b/mordant/src/iosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.ios.kt similarity index 60% rename from mordant/src/appleMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.apple.kt rename to mordant/src/iosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.ios.kt index eb2fb36af..a1ed59222 100644 --- a/mordant/src/appleMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.apple.kt +++ b/mordant/src/iosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.ios.kt @@ -4,11 +4,11 @@ import com.github.ajalt.mordant.rendering.Size import kotlinx.cinterop.* import platform.posix.* -// The source code for this file is identical between linux and apple targets, but they have -// different bit widths for some of the termios fields, so the compileMetadata task would fail if we -// don't use separate files. +// XXX: The source code for this file is identical between linux and the various apple targets, but +// they have different bit widths for some fields, so the compileMetadata task fails if we don't use +// separate files. Hopefully some day there will be solution that doesn't require copy-pasting. -internal class TerminalInterfaceNativeApple : TerminalInterfaceNativePosix() { +internal class TerminalInterfaceNativeCopyPasted : TerminalInterfaceNativePosix() { override val termiosConstants: TermiosConstants = TermiosConstants( VTIME = VTIME, VMIN = VMIN, @@ -32,7 +32,7 @@ internal class TerminalInterfaceNativeApple : TerminalInterfaceNativePosix() { override fun getTerminalSize(): Size? = memScoped { val size = alloc() - if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) { + if (ioctl(STDIN_FILENO, TIOCGWINSZ.convert(), size) < 0) { null } else { Size(width = size.ws_col.toInt(), height = size.ws_row.toInt()) @@ -41,15 +41,15 @@ internal class TerminalInterfaceNativeApple : TerminalInterfaceNativePosix() { override fun getStdinTermios(): Termios = memScoped { val termios = alloc() - if (tcgetattr(STDIN_FILENO, termios.ptr) != 0) { + if (tcgetattr(platform.posix.STDIN_FILENO, termios.ptr) != 0) { throw RuntimeException("Error reading terminal attributes") } return Termios( - iflag = termios.c_iflag.convert(), - oflag = termios.c_oflag.convert(), - cflag = termios.c_cflag.convert(), - lflag = termios.c_lflag.convert(), - cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, + iflag = termios.c_iflag.convert(), + oflag = termios.c_oflag.convert(), + cflag = termios.c_cflag.convert(), + lflag = termios.c_lflag.convert(), + cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, ) } @@ -57,15 +57,15 @@ internal class TerminalInterfaceNativeApple : TerminalInterfaceNativePosix() { val nativeTermios = alloc() // different platforms have different fields in termios, so we need to read the current // struct before we set the fields we care about. - if (tcgetattr(STDIN_FILENO, nativeTermios.ptr) != 0) { + if (tcgetattr(platform.posix.STDIN_FILENO, nativeTermios.ptr) != 0) { throw RuntimeException("Error reading terminal attributes") } - nativeTermios.c_iflag = termios.iflag.convert() - nativeTermios.c_oflag = termios.oflag.convert() - nativeTermios.c_cflag = termios.cflag.convert() - nativeTermios.c_lflag = termios.lflag.convert() + nativeTermios.c_iflag = termios.iflag.convert() + nativeTermios.c_oflag = termios.oflag.convert() + nativeTermios.c_cflag = termios.cflag.convert() + nativeTermios.c_lflag = termios.lflag.convert() repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert() } - if (tcsetattr(STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { + if (tcsetattr(platform.posix.STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { throw RuntimeException("Error setting terminal attributes") } } diff --git a/mordant/src/jsCommonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jsCommon.kt b/mordant/src/jsCommonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jsCommon.kt index 46306831c..b9ba914d4 100644 --- a/mordant/src/jsCommonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jsCommon.kt +++ b/mordant/src/jsCommonMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jsCommon.kt @@ -79,4 +79,4 @@ internal actual fun sendInterceptedPrintRequest( ) } -internal actual fun hasFileSystem(): Boolean = impls !is TerminalInterfaceBrowser +internal actual fun testsHaveFileSystem(): Boolean = impls !is TerminalInterfaceBrowser diff --git a/mordant/src/jvmMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jvm.kt b/mordant/src/jvmMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jvm.kt index 8996abc6b..314873410 100644 --- a/mordant/src/jvmMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jvm.kt +++ b/mordant/src/jvmMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.jvm.kt @@ -143,7 +143,7 @@ internal actual fun getStandardTerminalInterface(): TerminalInterface { } internal actual val CR_IMPLIES_LF: Boolean = false -internal actual fun hasFileSystem(): Boolean = true +internal actual fun testsHaveFileSystem(): Boolean = true internal actual fun exitProcessMpp(status: Int) { exitProcess(status) diff --git a/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.linux.kt b/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.linux.kt index a4865421f..7d768baf8 100644 --- a/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.linux.kt +++ b/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.linux.kt @@ -1,7 +1,9 @@ package com.github.ajalt.mordant.internal import com.github.ajalt.mordant.terminal.TerminalInterface -import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeLinux +import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeCopyPasted -internal actual fun hasFileSystem(): Boolean = true -internal actual fun getStandardTerminalInterface(): TerminalInterface = TerminalInterfaceNativeLinux +internal actual fun testsHaveFileSystem(): Boolean = true +internal actual fun getStandardTerminalInterface(): TerminalInterface { + return TerminalInterfaceNativeCopyPasted() +} diff --git a/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt b/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt index 3a39cdcb6..a1ed59222 100644 --- a/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt +++ b/mordant/src/linuxMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt @@ -4,11 +4,11 @@ import com.github.ajalt.mordant.rendering.Size import kotlinx.cinterop.* import platform.posix.* -// The source code for this file is identical between linux and apple targets, but they have -// different bit widths for some of the termios fields, so the compileMetadata task would fail if we -// don't use separate files. +// XXX: The source code for this file is identical between linux and the various apple targets, but +// they have different bit widths for some fields, so the compileMetadata task fails if we don't use +// separate files. Hopefully some day there will be solution that doesn't require copy-pasting. -internal object TerminalInterfaceNativeLinux : TerminalInterfaceNativePosix() { +internal class TerminalInterfaceNativeCopyPasted : TerminalInterfaceNativePosix() { override val termiosConstants: TermiosConstants = TermiosConstants( VTIME = VTIME, VMIN = VMIN, @@ -32,7 +32,7 @@ internal object TerminalInterfaceNativeLinux : TerminalInterfaceNativePosix() { override fun getTerminalSize(): Size? = memScoped { val size = alloc() - if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) { + if (ioctl(STDIN_FILENO, TIOCGWINSZ.convert(), size) < 0) { null } else { Size(width = size.ws_col.toInt(), height = size.ws_row.toInt()) @@ -45,11 +45,11 @@ internal object TerminalInterfaceNativeLinux : TerminalInterfaceNativePosix() { throw RuntimeException("Error reading terminal attributes") } return Termios( - iflag = termios.c_iflag.convert(), - oflag = termios.c_oflag.convert(), - cflag = termios.c_cflag.convert(), - lflag = termios.c_lflag.convert(), - cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, + iflag = termios.c_iflag.convert(), + oflag = termios.c_oflag.convert(), + cflag = termios.c_cflag.convert(), + lflag = termios.c_lflag.convert(), + cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, ) } @@ -60,10 +60,10 @@ internal object TerminalInterfaceNativeLinux : TerminalInterfaceNativePosix() { if (tcgetattr(platform.posix.STDIN_FILENO, nativeTermios.ptr) != 0) { throw RuntimeException("Error reading terminal attributes") } - nativeTermios.c_iflag = termios.iflag.convert() - nativeTermios.c_oflag = termios.oflag.convert() - nativeTermios.c_cflag = termios.cflag.convert() - nativeTermios.c_lflag = termios.lflag.convert() + nativeTermios.c_iflag = termios.iflag.convert() + nativeTermios.c_oflag = termios.oflag.convert() + nativeTermios.c_cflag = termios.cflag.convert() + nativeTermios.c_lflag = termios.lflag.convert() repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert() } if (tcsetattr(platform.posix.STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { throw RuntimeException("Error setting terminal attributes") diff --git a/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.macos.kt b/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.macos.kt index 50ea57066..7d768baf8 100644 --- a/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.macos.kt +++ b/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.macos.kt @@ -1,3 +1,9 @@ package com.github.ajalt.mordant.internal -internal actual fun hasFileSystem(): Boolean = true +import com.github.ajalt.mordant.terminal.TerminalInterface +import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeCopyPasted + +internal actual fun testsHaveFileSystem(): Boolean = true +internal actual fun getStandardTerminalInterface(): TerminalInterface { + return TerminalInterfaceNativeCopyPasted() +} diff --git a/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt b/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt new file mode 100644 index 000000000..a1ed59222 --- /dev/null +++ b/mordant/src/macosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.linux.kt @@ -0,0 +1,72 @@ +package com.github.ajalt.mordant.terminal.terminalinterface + +import com.github.ajalt.mordant.rendering.Size +import kotlinx.cinterop.* +import platform.posix.* + +// XXX: The source code for this file is identical between linux and the various apple targets, but +// they have different bit widths for some fields, so the compileMetadata task fails if we don't use +// separate files. Hopefully some day there will be solution that doesn't require copy-pasting. + +internal class TerminalInterfaceNativeCopyPasted : TerminalInterfaceNativePosix() { + override val termiosConstants: TermiosConstants = TermiosConstants( + VTIME = VTIME, + VMIN = VMIN, + INPCK = INPCK.convert(), + ISTRIP = ISTRIP.convert(), + INLCR = INLCR.convert(), + IGNCR = IGNCR.convert(), + ICRNL = ICRNL.convert(), + IXON = IXON.convert(), + OPOST = OPOST.convert(), + CS8 = CS8.convert(), + ISIG = ISIG.convert(), + ICANON = ICANON.convert(), + ECHO = ECHO.convert(), + IEXTEN = IEXTEN.convert(), + ) + + override fun readIntoBuffer(c: ByteVar): Long { + return read(platform.posix.STDIN_FILENO, c.ptr, 1u).convert() + } + + override fun getTerminalSize(): Size? = memScoped { + val size = alloc() + if (ioctl(STDIN_FILENO, TIOCGWINSZ.convert(), size) < 0) { + null + } else { + Size(width = size.ws_col.toInt(), height = size.ws_row.toInt()) + } + } + + override fun getStdinTermios(): Termios = memScoped { + val termios = alloc() + if (tcgetattr(platform.posix.STDIN_FILENO, termios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + return Termios( + iflag = termios.c_iflag.convert(), + oflag = termios.c_oflag.convert(), + cflag = termios.c_cflag.convert(), + lflag = termios.c_lflag.convert(), + cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, + ) + } + + override fun setStdinTermios(termios: Termios) = memScoped { + val nativeTermios = alloc() + // different platforms have different fields in termios, so we need to read the current + // struct before we set the fields we care about. + if (tcgetattr(platform.posix.STDIN_FILENO, nativeTermios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + nativeTermios.c_iflag = termios.iflag.convert() + nativeTermios.c_oflag = termios.oflag.convert() + nativeTermios.c_cflag = termios.cflag.convert() + nativeTermios.c_lflag = termios.lflag.convert() + repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert() } + if (tcsetattr(platform.posix.STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { + throw RuntimeException("Error setting terminal attributes") + } + } +} diff --git a/mordant/src/mingwMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.mingw.kt b/mordant/src/mingwMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.mingw.kt index 216ce2ec9..7902250f8 100644 --- a/mordant/src/mingwMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.mingw.kt +++ b/mordant/src/mingwMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.mingw.kt @@ -4,6 +4,6 @@ import com.github.ajalt.mordant.terminal.TerminalInterface import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeWindows internal actual fun ttySetEcho(echo: Boolean) = TerminalInterfaceNativeWindows.ttySetEcho(echo) -internal actual fun hasFileSystem(): Boolean = true +internal actual fun testsHaveFileSystem(): Boolean = true internal actual fun getStandardTerminalInterface(): TerminalInterface = TerminalInterfaceNativeWindows diff --git a/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.tvos.kt b/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.tvos.kt new file mode 100644 index 000000000..256b039a3 --- /dev/null +++ b/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.tvos.kt @@ -0,0 +1,8 @@ +package com.github.ajalt.mordant.internal + +import com.github.ajalt.mordant.terminal.TerminalInterface +import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeCopyPasted + +internal actual fun getStandardTerminalInterface(): TerminalInterface { + return TerminalInterfaceNativeCopyPasted() +} diff --git a/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.tvos.kt b/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.tvos.kt new file mode 100644 index 000000000..a1ed59222 --- /dev/null +++ b/mordant/src/tvosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.tvos.kt @@ -0,0 +1,72 @@ +package com.github.ajalt.mordant.terminal.terminalinterface + +import com.github.ajalt.mordant.rendering.Size +import kotlinx.cinterop.* +import platform.posix.* + +// XXX: The source code for this file is identical between linux and the various apple targets, but +// they have different bit widths for some fields, so the compileMetadata task fails if we don't use +// separate files. Hopefully some day there will be solution that doesn't require copy-pasting. + +internal class TerminalInterfaceNativeCopyPasted : TerminalInterfaceNativePosix() { + override val termiosConstants: TermiosConstants = TermiosConstants( + VTIME = VTIME, + VMIN = VMIN, + INPCK = INPCK.convert(), + ISTRIP = ISTRIP.convert(), + INLCR = INLCR.convert(), + IGNCR = IGNCR.convert(), + ICRNL = ICRNL.convert(), + IXON = IXON.convert(), + OPOST = OPOST.convert(), + CS8 = CS8.convert(), + ISIG = ISIG.convert(), + ICANON = ICANON.convert(), + ECHO = ECHO.convert(), + IEXTEN = IEXTEN.convert(), + ) + + override fun readIntoBuffer(c: ByteVar): Long { + return read(platform.posix.STDIN_FILENO, c.ptr, 1u).convert() + } + + override fun getTerminalSize(): Size? = memScoped { + val size = alloc() + if (ioctl(STDIN_FILENO, TIOCGWINSZ.convert(), size) < 0) { + null + } else { + Size(width = size.ws_col.toInt(), height = size.ws_row.toInt()) + } + } + + override fun getStdinTermios(): Termios = memScoped { + val termios = alloc() + if (tcgetattr(platform.posix.STDIN_FILENO, termios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + return Termios( + iflag = termios.c_iflag.convert(), + oflag = termios.c_oflag.convert(), + cflag = termios.c_cflag.convert(), + lflag = termios.c_lflag.convert(), + cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, + ) + } + + override fun setStdinTermios(termios: Termios) = memScoped { + val nativeTermios = alloc() + // different platforms have different fields in termios, so we need to read the current + // struct before we set the fields we care about. + if (tcgetattr(platform.posix.STDIN_FILENO, nativeTermios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + nativeTermios.c_iflag = termios.iflag.convert() + nativeTermios.c_oflag = termios.oflag.convert() + nativeTermios.c_cflag = termios.cflag.convert() + nativeTermios.c_lflag = termios.lflag.convert() + repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert() } + if (tcsetattr(platform.posix.STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { + throw RuntimeException("Error setting terminal attributes") + } + } +} diff --git a/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.watchos.kt b/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.watchos.kt new file mode 100644 index 000000000..256b039a3 --- /dev/null +++ b/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/internal/MppInternal.watchos.kt @@ -0,0 +1,8 @@ +package com.github.ajalt.mordant.internal + +import com.github.ajalt.mordant.terminal.TerminalInterface +import com.github.ajalt.mordant.terminal.terminalinterface.TerminalInterfaceNativeCopyPasted + +internal actual fun getStandardTerminalInterface(): TerminalInterface { + return TerminalInterfaceNativeCopyPasted() +} diff --git a/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.watchos.kt b/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.watchos.kt new file mode 100644 index 000000000..a1ed59222 --- /dev/null +++ b/mordant/src/watchosMain/kotlin/com/github/ajalt/mordant/terminal/terminalinterface/TerminalInterface.native.watchos.kt @@ -0,0 +1,72 @@ +package com.github.ajalt.mordant.terminal.terminalinterface + +import com.github.ajalt.mordant.rendering.Size +import kotlinx.cinterop.* +import platform.posix.* + +// XXX: The source code for this file is identical between linux and the various apple targets, but +// they have different bit widths for some fields, so the compileMetadata task fails if we don't use +// separate files. Hopefully some day there will be solution that doesn't require copy-pasting. + +internal class TerminalInterfaceNativeCopyPasted : TerminalInterfaceNativePosix() { + override val termiosConstants: TermiosConstants = TermiosConstants( + VTIME = VTIME, + VMIN = VMIN, + INPCK = INPCK.convert(), + ISTRIP = ISTRIP.convert(), + INLCR = INLCR.convert(), + IGNCR = IGNCR.convert(), + ICRNL = ICRNL.convert(), + IXON = IXON.convert(), + OPOST = OPOST.convert(), + CS8 = CS8.convert(), + ISIG = ISIG.convert(), + ICANON = ICANON.convert(), + ECHO = ECHO.convert(), + IEXTEN = IEXTEN.convert(), + ) + + override fun readIntoBuffer(c: ByteVar): Long { + return read(platform.posix.STDIN_FILENO, c.ptr, 1u).convert() + } + + override fun getTerminalSize(): Size? = memScoped { + val size = alloc() + if (ioctl(STDIN_FILENO, TIOCGWINSZ.convert(), size) < 0) { + null + } else { + Size(width = size.ws_col.toInt(), height = size.ws_row.toInt()) + } + } + + override fun getStdinTermios(): Termios = memScoped { + val termios = alloc() + if (tcgetattr(platform.posix.STDIN_FILENO, termios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + return Termios( + iflag = termios.c_iflag.convert(), + oflag = termios.c_oflag.convert(), + cflag = termios.c_cflag.convert(), + lflag = termios.c_lflag.convert(), + cc = ByteArray(NCCS) { termios.c_cc[it].convert() }, + ) + } + + override fun setStdinTermios(termios: Termios) = memScoped { + val nativeTermios = alloc() + // different platforms have different fields in termios, so we need to read the current + // struct before we set the fields we care about. + if (tcgetattr(platform.posix.STDIN_FILENO, nativeTermios.ptr) != 0) { + throw RuntimeException("Error reading terminal attributes") + } + nativeTermios.c_iflag = termios.iflag.convert() + nativeTermios.c_oflag = termios.oflag.convert() + nativeTermios.c_cflag = termios.cflag.convert() + nativeTermios.c_lflag = termios.lflag.convert() + repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert() } + if (tcsetattr(platform.posix.STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) { + throw RuntimeException("Error setting terminal attributes") + } + } +}