Skip to content

Commit

Permalink
Split out SyscallHandler.nativeimage.macos (#187)
Browse files Browse the repository at this point in the history
This is a follow on to #186 that fixes the `termios` struct definition on macos.
  • Loading branch information
ajalt authored Jul 9, 2024
1 parent 0a54a13 commit 00b3abe
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Changelog

## Unreleased
## Added
- Added support for raw mode on GraalVM native Image. Contributed by @hubvd [(#186)](https://github.com/ajalt/mordant/issues/186)

### Fixed
- Fix markdown rendering not supporting math blocks [(#182)](https://github.com/ajalt/mordant/issues/182)
- Fix exception thrown when using `readEvent` in raw mode when some windows terminals lose focus


## 2.7.0
### Added
- Added raw mode support for reading keyboard and mouse events. See the docs at [https://ajalt.github.io/mordant/](https://ajalt.github.io/mordant/input/) for details. This feature is currently supported on all targets except JS, wasmJS, and Graal Native Image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import com.github.ajalt.mordant.internal.syscalls.SyscallHandler
import com.github.ajalt.mordant.internal.syscalls.jna.SyscallHandlerJnaLinux
import com.github.ajalt.mordant.internal.syscalls.jna.SyscallHandlerJnaMacos
import com.github.ajalt.mordant.internal.syscalls.jna.SyscallHandlerJnaWindows
import com.github.ajalt.mordant.internal.syscalls.nativeimage.SyscallHandlerNativeImagePosix
import com.github.ajalt.mordant.internal.syscalls.nativeimage.SyscallHandlerNativeImageLinux
import com.github.ajalt.mordant.internal.syscalls.nativeimage.SyscallHandlerNativeImageMacos
import com.github.ajalt.mordant.internal.syscalls.nativeimage.SyscallHandlerNativeImageWindows
import com.github.ajalt.mordant.terminal.*
import java.io.File
Expand Down Expand Up @@ -130,7 +131,8 @@ internal actual fun getSyscallHandler(): SyscallHandler {
val os = System.getProperty("os.name")
when {
isNativeImage && os.startsWith("Windows") -> SyscallHandlerNativeImageWindows()
isNativeImage && (os == "Linux" || os == "Mac OS X") -> SyscallHandlerNativeImagePosix()
isNativeImage && (os == "Linux") -> SyscallHandlerNativeImageLinux()
isNativeImage && (os == "Mac OS X") -> SyscallHandlerNativeImageMacos()
os.startsWith("Windows") -> SyscallHandlerJnaWindows
os == "Linux" -> SyscallHandlerJnaLinux
os == "Mac OS X" -> SyscallHandlerJnaMacos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import org.graalvm.nativeimage.c.struct.CStruct
import org.graalvm.nativeimage.c.type.CCharPointer
import org.graalvm.word.PointerBase

@CContext(PosixLibC.Directives::class)
@Platforms(Platform.LINUX::class, Platform.MACOS::class)
@CContext(LinuxLibC.Directives::class)
@Platforms(Platform.LINUX::class)
@Suppress("ClassName", "PropertyName", "SpellCheckingInspection", "FunctionName")
private object PosixLibC {
private object LinuxLibC {

class Directives : CContext.Directives {
override fun getHeaderFiles() = listOf("<unistd.h>", "<sys/ioctl.h>", "<termios.h>")
Expand Down Expand Up @@ -89,44 +89,44 @@ private object PosixLibC {
external fun tcsetattr(fd: Int, cmd: Int, termios: termios?): Int
}

@Platforms(Platform.LINUX::class, Platform.MACOS::class)
internal class SyscallHandlerNativeImagePosix : SyscallHandlerJvmPosix() {
override fun isatty(fd: Int): Boolean = PosixLibC.isatty(fd)
@Platforms(Platform.LINUX::class)
internal class SyscallHandlerNativeImageLinux : SyscallHandlerJvmPosix() {
override fun isatty(fd: Int): Boolean = LinuxLibC.isatty(fd)

override fun getTerminalSize(): Size? {
val size = StackValue.get(PosixLibC.winsize::class.java)
return if (PosixLibC.ioctl(0, PosixLibC.TIOCGWINSZ(), size) < 0) {
val size = StackValue.get(LinuxLibC.winsize::class.java)
return if (LinuxLibC.ioctl(0, LinuxLibC.TIOCGWINSZ(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}

override fun getStdinTermios(): Termios {
val termios = StackValue.get(PosixLibC.termios::class.java)
if (PosixLibC.tcgetattr(STDIN_FILENO, termios) != 0) {
val termios = StackValue.get(LinuxLibC.termios::class.java)
if (LinuxLibC.tcgetattr(STDIN_FILENO, termios) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
return Termios(
iflag = termios.c_iflag.toUInt(),
oflag = termios.c_oflag.toUInt(),
cflag = termios.c_cflag.toUInt(),
lflag = termios.c_lflag.toUInt(),
cc = ByteArray(PosixLibC.NCCS()) { termios.c_cc.read(it) },
cc = ByteArray(LinuxLibC.NCCS()) { termios.c_cc.read(it) },
)
}

override fun setStdinTermios(termios: Termios) {
val nativeTermios = StackValue.get(PosixLibC.termios::class.java)
if (PosixLibC.tcgetattr(STDIN_FILENO, nativeTermios) != 0) {
val nativeTermios = StackValue.get(LinuxLibC.termios::class.java)
if (LinuxLibC.tcgetattr(STDIN_FILENO, nativeTermios) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
nativeTermios.c_iflag = termios.iflag.toInt()
nativeTermios.c_oflag = termios.oflag.toInt()
nativeTermios.c_cflag = termios.cflag.toInt()
nativeTermios.c_lflag = termios.lflag.toInt()
termios.cc.forEachIndexed { i, b -> nativeTermios.c_cc.write(i, b) }
if (PosixLibC.tcsetattr(STDIN_FILENO, PosixLibC.TCSADRAIN(), nativeTermios) != 0) {
if (LinuxLibC.tcsetattr(STDIN_FILENO, LinuxLibC.TCSADRAIN(), nativeTermios) != 0) {
throw RuntimeException("Error setting terminal attributes")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.github.ajalt.mordant.internal.syscalls.nativeimage

import com.github.ajalt.mordant.internal.Size
import com.github.ajalt.mordant.internal.syscalls.SyscallHandlerJvmPosix
import org.graalvm.nativeimage.Platform
import org.graalvm.nativeimage.Platforms
import org.graalvm.nativeimage.StackValue
import org.graalvm.nativeimage.c.CContext
import org.graalvm.nativeimage.c.constant.CConstant
import org.graalvm.nativeimage.c.function.CFunction
import org.graalvm.nativeimage.c.struct.CField
import org.graalvm.nativeimage.c.struct.CFieldAddress
import org.graalvm.nativeimage.c.struct.CStruct
import org.graalvm.nativeimage.c.type.CCharPointer
import org.graalvm.word.PointerBase

@CContext(MacosLibC.Directives::class)
@Platforms(Platform.MACOS::class)
@Suppress("ClassName", "PropertyName", "SpellCheckingInspection", "FunctionName")
private object MacosLibC {

class Directives : CContext.Directives {
override fun getHeaderFiles() = listOf("<unistd.h>", "<sys/ioctl.h>", "<termios.h>")
}

@CConstant("TIOCGWINSZ")
external fun TIOCGWINSZ(): Int

@CConstant("TCSADRAIN")
external fun TCSADRAIN(): Int

@CConstant("NCCS")
external fun NCCS(): Int

@CStruct("winsize", addStructKeyword = true)
interface winsize : PointerBase {

@get:CField("ws_row")
val ws_row: Short

@get:CField("ws_col")
val ws_col: Short
}

@CStruct("termios", addStructKeyword = true)
interface termios : PointerBase {
@get:CField("c_iflag")
@set:CField("c_iflag")
var c_iflag: Long

@get:CField("c_oflag")
@set:CField("c_oflag")
var c_oflag: Long

@get:CField("c_cflag")
@set:CField("c_cflag")
var c_cflag: Long

@get:CField("c_lflag")
@set:CField("c_lflag")
var c_lflag: Long

@get:CFieldAddress("c_cc")
val c_cc: CCharPointer

@get:CField("c_ispeed")
@set:CField("c_ispeed")
var c_ispeed: Long

@get:CField("c_ospeed")
@set:CField("c_ospeed")
var c_ospeed: Long
}

@CFunction("isatty")
external fun isatty(fd: Int): Boolean

@CFunction("ioctl")
external fun ioctl(fd: Int, cmd: Int, winSize: winsize?): Int

@CFunction("tcgetattr")
external fun tcgetattr(fd: Int, termios: termios?): Int

@CFunction("tcsetattr")
external fun tcsetattr(fd: Int, cmd: Int, termios: termios?): Int
}

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

override fun getTerminalSize(): Size? {
val size = StackValue.get(MacosLibC.winsize::class.java)
return if (MacosLibC.ioctl(0, MacosLibC.TIOCGWINSZ(), size) < 0) {
null
} else {
Size(width = size.ws_col.toInt(), height = size.ws_row.toInt())
}
}

override fun getStdinTermios(): Termios {
val termios = StackValue.get(MacosLibC.termios::class.java)
if (MacosLibC.tcgetattr(STDIN_FILENO, termios) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
return Termios(
iflag = termios.c_iflag.toUInt(),
oflag = termios.c_oflag.toUInt(),
cflag = termios.c_cflag.toUInt(),
lflag = termios.c_lflag.toUInt(),
cc = ByteArray(MacosLibC.NCCS()) { termios.c_cc.read(it) },
)
}

override fun setStdinTermios(termios: Termios) {
val nativeTermios = StackValue.get(MacosLibC.termios::class.java)
if (MacosLibC.tcgetattr(STDIN_FILENO, nativeTermios) != 0) {
throw RuntimeException("Error reading terminal attributes")
}
nativeTermios.c_iflag = termios.iflag.toLong()
nativeTermios.c_oflag = termios.oflag.toLong()
nativeTermios.c_cflag = termios.cflag.toLong()
nativeTermios.c_lflag = termios.lflag.toLong()
termios.cc.forEachIndexed { i, b -> nativeTermios.c_cc.write(i, b) }
if (MacosLibC.tcsetattr(STDIN_FILENO, MacosLibC.TCSADRAIN(), nativeTermios) != 0) {
throw RuntimeException("Error setting terminal attributes")
}
}
}

0 comments on commit 00b3abe

Please sign in to comment.