Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use platform-specific values for termios constants #190

Merged
merged 1 commit into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## Unreleased
### Fixed
- Fixed raw mode on macos for JVM and native not using the correct termios constants. [(#180)](https://github.com/ajalt/mordant/issues/180)

## 2.7.1
## Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.ajalt.mordant.internal

import com.github.ajalt.mordant.internal.syscalls.SyscallHandler
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerNativePosix
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerNativeApple

internal actual fun getSyscallHandler(): SyscallHandler = SyscallHandlerNativePosix
internal actual fun getSyscallHandler(): SyscallHandler = SyscallHandlerNativeApple
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,65 @@ import com.github.ajalt.mordant.internal.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 the termios fields, so the compileMetadata task fails.
// 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.

internal actual fun getTerminalSizeNative(): Size? = memScoped {
val size = alloc<winsize>()
if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}
internal object SyscallHandlerNativeApple : SyscallHandlerNativePosix() {
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(),
)

internal actual fun getStdinTermiosNative(): SyscallHandlerPosix.Termios = memScoped {
val termios = alloc<termios>()
if (tcgetattr(STDIN_FILENO, termios.ptr) != 0) {
throw RuntimeException("Error reading terminal attributes")
override fun getTerminalSize(): Size? = memScoped {
val size = alloc<winsize>()
if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}
return SyscallHandlerPosix.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() },
)
}

internal actual fun setStdinTermiosNative(termios: SyscallHandlerPosix.Termios): Unit = memScoped {
val nativeTermios = alloc<termios>()
// 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) {
throw RuntimeException("Error reading terminal attributes")
override fun getStdinTermios(): Termios = memScoped {
val termios = alloc<termios>()
if (tcgetattr(STDIN_FILENO, termios.ptr) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
return Termios(
iflag = termios.c_iflag.convert<UInt>(),
oflag = termios.c_oflag.convert<UInt>(),
cflag = termios.c_cflag.convert<UInt>(),
lflag = termios.c_lflag.convert<UInt>(),
cc = ByteArray(NCCS) { termios.c_cc[it].convert<Byte>() },
)
}
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) {
throw RuntimeException("Error setting terminal attributes")

override fun setStdinTermios(termios: Termios) = memScoped {
val nativeTermios = alloc<termios>()
// 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) {
throw RuntimeException("Error reading terminal attributes")
}
nativeTermios.c_iflag = termios.iflag.convert<tcflag_t>()
nativeTermios.c_oflag = termios.oflag.convert<tcflag_t>()
nativeTermios.c_cflag = termios.cflag.convert<tcflag_t>()
nativeTermios.c_lflag = termios.lflag.convert<tcflag_t>()
repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert<UByte>() }
if (tcsetattr(STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) {
throw RuntimeException("Error setting terminal attributes")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.ajalt.mordant.internal.syscalls.jna

import com.github.ajalt.mordant.internal.Size
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerJvmPosix
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerPosix
import com.oracle.svm.core.annotate.Delete
import com.sun.jna.*

Expand Down Expand Up @@ -69,6 +70,7 @@ private interface PosixLibC : Library {
internal object SyscallHandlerJnaLinux : SyscallHandlerJvmPosix() {
private const val TIOCGWINSZ = 0x00005413
private const val TCSADRAIN: Int = 0x1
override val termiosConstants: TermiosConstants get() = LinuxTermiosConstants
private val libC: PosixLibC = Native.load(Platform.C_LIBRARY_NAME, PosixLibC::class.java)
override fun isatty(fd: Int): Boolean = libC.isatty(fd) != 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ private interface MacosLibC : Library {
@Delete
@Suppress("SpellCheckingInspection")
internal object SyscallHandlerJnaMacos : SyscallHandlerJvmPosix() {
override val termiosConstants: TermiosConstants get() = MacosTermiosConstants
private const val TCSANOW: Int = 0x0
private val TIOCGWINSZ = when {
Platform.isMIPS() || Platform.isPPC() || Platform.isSPARC() -> 0x40087468L
Expand Down Expand Up @@ -104,12 +105,11 @@ internal object SyscallHandlerJnaMacos : SyscallHandlerJvmPosix() {
override fun setStdinTermios(termios: Termios) {
val nativeTermios = MacosLibC.termios()
libC.tcgetattr(STDIN_FILENO, nativeTermios)
nativeTermios.c_iflag = NativeLong(termios.iflag.toLong())
nativeTermios.c_oflag = NativeLong(termios.oflag.toLong())
nativeTermios.c_cflag = NativeLong(termios.cflag.toLong())
nativeTermios.c_lflag = NativeLong(termios.lflag.toLong())
nativeTermios.c_iflag.setValue(termios.iflag.toLong())
nativeTermios.c_oflag.setValue(termios.oflag.toLong())
nativeTermios.c_cflag.setValue(termios.cflag.toLong())
nativeTermios.c_lflag.setValue(termios.lflag.toLong())
termios.cc.copyInto(nativeTermios.c_cc)
libC.tcsetattr(STDIN_FILENO, TCSANOW, nativeTermios)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ private object LinuxLibC {

@Platforms(Platform.LINUX::class)
internal class SyscallHandlerNativeImageLinux : SyscallHandlerJvmPosix() {
override val termiosConstants: TermiosConstants get() = LinuxTermiosConstants
override fun isatty(fd: Int): Boolean = LinuxLibC.isatty(fd)

override fun getTerminalSize(): Size? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private object MacosLibC {

@Platforms(Platform.MACOS::class)
internal class SyscallHandlerNativeImageMacos : SyscallHandlerJvmPosix() {
override val termiosConstants: TermiosConstants get() = MacosTermiosConstants
override fun isatty(fd: Int): Boolean = MacosLibC.isatty(fd)

override fun getTerminalSize(): Size? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package com.github.ajalt.mordant.internal

import com.github.ajalt.mordant.internal.syscalls.SyscallHandler
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerNativeLinux

internal actual fun hasFileSystem(): Boolean = true
internal actual fun getSyscallHandler(): SyscallHandler = SyscallHandlerNativeLinux
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,65 @@ import com.github.ajalt.mordant.internal.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 the termios fields, so the compileMetadata task fails.
// 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.

internal actual fun getTerminalSizeNative(): Size? = memScoped {
val size = alloc<winsize>()
if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}
internal object SyscallHandlerNativeLinux : SyscallHandlerNativePosix() {
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(),
)

internal actual fun getStdinTermiosNative(): SyscallHandlerPosix.Termios = memScoped {
val termios = alloc<termios>()
if (tcgetattr(STDIN_FILENO, termios.ptr) != 0) {
throw RuntimeException("Error reading terminal attributes")
override fun getTerminalSize(): Size? = memScoped {
val size = alloc<winsize>()
if (ioctl(STDIN_FILENO, TIOCGWINSZ.toULong(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}
return SyscallHandlerPosix.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() },
)
}

internal actual fun setStdinTermiosNative(termios: SyscallHandlerPosix.Termios): Unit = memScoped {
val nativeTermios = alloc<termios>()
// 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) {
throw RuntimeException("Error reading terminal attributes")
override fun getStdinTermios(): Termios = memScoped {
val termios = alloc<termios>()
if (tcgetattr(STDIN_FILENO, termios.ptr) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
return Termios(
iflag = termios.c_iflag.convert<UInt>(),
oflag = termios.c_oflag.convert<UInt>(),
cflag = termios.c_cflag.convert<UInt>(),
lflag = termios.c_lflag.convert<UInt>(),
cc = ByteArray(NCCS) { termios.c_cc[it].convert<Byte>() },
)
}
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) {
throw RuntimeException("Error setting terminal attributes")

override fun setStdinTermios(termios: Termios) = memScoped {
val nativeTermios = alloc<termios>()
// 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) {
throw RuntimeException("Error reading terminal attributes")
}
nativeTermios.c_iflag = termios.iflag.convert<tcflag_t>()
nativeTermios.c_oflag = termios.oflag.convert<tcflag_t>()
nativeTermios.c_cflag = termios.cflag.convert<tcflag_t>()
nativeTermios.c_lflag = termios.lflag.convert<tcflag_t>()
repeat(NCCS) { nativeTermios.c_cc[it] = termios.cc[it].convert<UByte>() }
if (tcsetattr(STDIN_FILENO, TCSADRAIN, nativeTermios.ptr) != 0) {
throw RuntimeException("Error setting terminal attributes")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,15 @@ internal actual fun printStderr(message: String, newline: Boolean) {
internal expect fun ttySetEcho(echo: Boolean)

internal actual fun readLineOrNullMpp(hideInput: Boolean): String? {
if (hideInput) ttySetEcho(false)
try {
return readlnOrNull()
} finally {
if (hideInput) ttySetEcho(true)
return try {
if (hideInput) ttySetEcho(false)
try {
readlnOrNull()
} finally {
if (hideInput) ttySetEcho(true)
}
} catch (e: Throwable) {
null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ inline fun Terminal.interactiveSelectList(
return InteractiveSelectListBuilder(this)
.apply(block)
.createSingleSelectInputAnimation()
.receiveEvents()
.receiveEvents(MouseTracking.Off)
}

/**
Expand Down Expand Up @@ -65,7 +65,7 @@ inline fun Terminal.interactiveMultiSelectList(
return InteractiveSelectListBuilder(this)
.apply(block)
.createMultiSelectInputAnimation()
.receiveEvents()
.receiveEvents(MouseTracking.Off)
}

/**
Expand Down
Loading