Skip to content
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
4 changes: 2 additions & 2 deletions core/js/src/main/scala/terminus/Terminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import scala.concurrent.Future
import scala.concurrent.Promise

class Terminal(root: HTMLElement, options: XtermJsOptions)
extends effect.Color[Terminal],
extends effect.Color,
effect.Cursor,
effect.Format[Terminal],
effect.Format,
effect.Erase,
effect.Dimensions,
effect.Writer {
Expand Down
12 changes: 6 additions & 6 deletions core/jvm/src/main/scala/terminus/JLineTerminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ class JLineTerminal(terminal: JTerminal) extends Terminal, TerminalKeyReader {

def write(string: String): Unit = writer.write(string)

def raw[A](f: Terminal ?=> A): A = {
def raw[A](f: () => A): A = {
val attrs = terminal.enterRawMode()
try {
val result = f(using this)
val result = f()
result
} finally {
terminal.setAttributes(attrs)
Expand All @@ -73,20 +73,20 @@ class JLineTerminal(terminal: JTerminal) extends Terminal, TerminalKeyReader {
def setDimensions(dimensions: TerminalDimensions): Unit =
terminal.setSize(Size(dimensions.columns, dimensions.rows))

def application[A](f: Terminal ?=> A): A = {
def application[A](f: () => A): A = {
try {
terminal.puts(Capability.keypad_xmit)
val result = f(using this)
val result = f()
result
} finally {
val _ = terminal.puts(Capability.keypad_local)
}
}

def alternateScreen[A](f: Terminal ?=> A): A = {
def alternateScreen[A](f: () => A): A = {
try {
terminal.puts(Capability.enter_ca_mode)
val result = f(using this)
val result = f()
result
} finally {
val _ = terminal.puts(Capability.exit_ca_mode)
Expand Down
10 changes: 5 additions & 5 deletions core/jvm/src/main/scala/terminus/Terminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
package terminus

trait Terminal
extends effect.AlternateScreenMode[Terminal],
effect.ApplicationMode[Terminal],
effect.Color[Terminal],
extends effect.AlternateScreenMode,
effect.ApplicationMode,
effect.Color,
effect.Cursor,
effect.Format[Terminal],
effect.Format,
effect.Dimensions,
effect.Erase,
effect.KeyReader,
effect.NonBlockingReader,
effect.Peeker,
effect.RawMode[Terminal],
effect.RawMode,
effect.Reader,
effect.Writer
type Program[A] = Terminal ?=> A
Expand Down
19 changes: 8 additions & 11 deletions core/native/src/main/scala/terminus/NativeTerminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@ import scala.concurrent.duration.Duration
import scala.scalanative.libc
import scala.scalanative.meta.LinktimeInfo
import scala.scalanative.posix
import scala.scalanative.unsigned.UInt
import scala.scalanative.unsigned.*

import scalanative.unsafe.*

/** A Terminal implementation for Scala Native. */
object NativeTerminal
extends Terminal,
WithEffect[Terminal],
TerminalKeyReader {
object NativeTerminal extends Terminal, WithEffect, TerminalKeyReader {

private given termiosAccess: TermiosAccess[?] =
if LinktimeInfo.isMac then clongTermiosAccess
Expand Down Expand Up @@ -70,10 +67,10 @@ object NativeTerminal
val origAttrs = termios.getAttributes()
val attrs = termios.getAttributes()
try {
attrs.setSpecialCharacter(posix.termios.VMIN, 0)
attrs.setSpecialCharacter(posix.termios.VMIN, 0.toUByte)
attrs.setSpecialCharacter(
posix.termios.VTIME,
(duration.toMillis / 100).toByte
(duration.toMillis / 100).toUByte
)
termios.setAttributes(attrs)

Expand Down Expand Up @@ -111,23 +108,23 @@ object NativeTerminal
}
}

def application[A](f: (terminus.Terminal) ?=> A): A = {
def application[A](f: () => A): A = {
withEffect(AnsiCodes.mode.application.on, AnsiCodes.mode.application.off)(f)
}

def alternateScreen[A](f: (terminus.Terminal) ?=> A): A = {
def alternateScreen[A](f: () => A): A = {
withEffect(
AnsiCodes.mode.alternateScreen.on,
AnsiCodes.mode.alternateScreen.off
)(f)
}

def raw[A](f: Terminal ?=> A): A = {
def raw[A](f: () => A): A = {
Zone {
val origAttrs = termios.getAttributes()
try {
termios.setRawMode()
f(using this)
f()
} finally {
termios.setAttributes(origAttrs)
}
Expand Down
10 changes: 5 additions & 5 deletions core/native/src/main/scala/terminus/Terminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
package terminus

trait Terminal
extends effect.AlternateScreenMode[Terminal],
effect.ApplicationMode[Terminal],
effect.Color[Terminal],
extends effect.AlternateScreenMode,
effect.ApplicationMode,
effect.Color,
effect.Cursor,
effect.Format[Terminal],
effect.Format,
effect.Erase,
effect.KeyReader,
effect.NonBlockingReader,
effect.RawMode[Terminal],
effect.RawMode,
effect.Reader,
effect.Writer
type Program[A] = Terminal ?=> A
Expand Down
56 changes: 40 additions & 16 deletions core/native/src/main/scala/terminus/TermiosAccess.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import scala.annotation.implicitNotFound
import scala.scalanative.posix
import scala.scalanative.posix.unistd.STDIN_FILENO
import scala.scalanative.unsafe.*
import scala.scalanative.unsigned.*

/** Typeclass for handling the messiness of reading, updating, and writing
* termios structs in a platform specific way.
Expand Down Expand Up @@ -89,11 +90,17 @@ trait TermiosAccess[T] {
/** Remove flags from termios c_lflag struct member */
def removeLocalFlags(attrs: Ptr[T], flags: CInt): Unit

/** Get the value at the given index of the termios c_cc struct member. Valid
* indices are defined as constants such as `VMIN` and `VTIME` in
* [[scala.scalanative.posix.termios]]
*/
def getSpecialCharacter(attrs: Ptr[T], idx: CInt): UByte

/** Set the value at the given index of the termios c_cc struct member. Valid
* indices are defined as constants such as `VMIN` and `VTIME` in
* [[scala.scalanative.posix.termios]]
*/
def setSpecialCharacter(attrs: Ptr[T], idx: CInt, value: CChar): Unit
def setSpecialCharacter(attrs: Ptr[T], idx: CInt, value: UByte): Unit
}

/** Extension methods to simplify modifying a termios struct */
Expand All @@ -106,12 +113,16 @@ extension [T](ptr: Ptr[T])(using au: TermiosAccess[T]) {
def removeControlFlags(flags: CInt): Unit = au.removeControlFlags(ptr, flags)
def addLocalFlags(flags: CInt): Unit = au.addLocalFlags(ptr, flags)
def removeLocalFlags(flags: CInt): Unit = au.removeLocalFlags(ptr, flags)
def setSpecialCharacter(idx: CInt, value: CChar) =
def getSpecialCharacter(idx: CInt): UByte =
au.getSpecialCharacter(ptr, idx)
def setSpecialCharacter(idx: CInt, value: UByte) =
au.setSpecialCharacter(ptr, idx, value)
}

/** [[TermiosAccess]] instance for structs with CLong bitflags */
given clongTermiosAccess: TermiosAccess[TermiosStruct.clong_flags] =
import posix.termiosOps.*

new TermiosAccess[TermiosStruct.clong_flags] {
override type FlagType = CLong

Expand All @@ -124,63 +135,68 @@ given clongTermiosAccess: TermiosAccess[TermiosStruct.clong_flags] =

override def set(ptr: Ptr[TermiosStruct.clong_flags]): Unit = {
val _ =
posix.termios.tcsetattr(STDIN_FILENO, posix.termios.TCSAFLUSH, ptr)
posix.termios.tcsetattr(STDIN_FILENO, posix.termios.TCSANOW, ptr)
()
}

override def addInputFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._1 = attrs._1 | flags
attrs.c_iflag = attrs.c_iflag | flags.toUInt

override def removeInputFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._1 = attrs._1 & ~flags
attrs.c_iflag = attrs.c_iflag & ~(flags.toUInt)

override def addOutputFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._2 = attrs._2 | flags
attrs.c_oflag = attrs.c_oflag | flags.toUInt

override def removeOutputFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._2 = attrs._2 & ~flags
attrs.c_oflag = attrs.c_oflag & ~(flags.toUInt)

override def addControlFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._3 = attrs._3 | flags
attrs.c_cflag = attrs.c_cflag | flags.toUInt

override def removeControlFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._3 = attrs._3 & ~flags
attrs.c_cflag = attrs.c_cflag & ~(flags.toUInt)

override def addLocalFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._4 = attrs._4 | flags
attrs.c_lflag = attrs.c_lflag | flags.toUInt

override def removeLocalFlags(
attrs: Ptr[TermiosStruct.clong_flags],
flags: CInt
): Unit =
attrs._4 = attrs._4 & ~flags
attrs.c_lflag = attrs.c_lflag & ~(flags.toUInt)

override def getSpecialCharacter(
attrs: Ptr[TermiosStruct.clong_flags],
idx: CInt
): UByte = attrs.c_cc(idx)

override def setSpecialCharacter(
attrs: Ptr[TermiosStruct.clong_flags],
idx: CInt,
value: CChar
): Unit = attrs._5(idx) = value
value: UByte
): Unit = attrs.c_cc(idx) = value
}

/** [[TermiosAccess]] instance for structs with CInt bitflags */
Expand All @@ -196,7 +212,7 @@ given cintTermiosAccess: TermiosAccess[TermiosStruct.cint_flags] =
}

override def set(ptr: Ptr[TermiosStruct.cint_flags]): Unit = {
val _ = tcsetattr(STDIN_FILENO, posix.termios.TCSAFLUSH, ptr)
val _ = tcsetattr(STDIN_FILENO, posix.termios.TCSANOW, ptr)
()
}

Expand Down Expand Up @@ -246,11 +262,19 @@ given cintTermiosAccess: TermiosAccess[TermiosStruct.cint_flags] =
flags: CInt
): Unit = attrs._4 = attrs._4 & ~flags

override def getSpecialCharacter(
attrs: Ptr[TermiosStruct.cint_flags],
idx: CInt
): UByte = {
val c_cc = attrs._5
c_cc(idx)
}

override def setSpecialCharacter(
attrs: Ptr[TermiosStruct.cint_flags],
idx: CInt,
value: CChar
): Unit = attrs._5(idx) = value
value: UByte
): Unit = attrs._5(idx) = value.toUByte

// Custom `tcgetattr` and `tcsetattr` definitions, since we can't use the ones defined in scala native since
// they use CLong for bitflags. These should point to the functions defined in the systems termios library
Expand Down
3 changes: 2 additions & 1 deletion core/native/src/main/scala/terminus/TermiosStruct.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package terminus
import scala.scalanative.posix
import scala.scalanative.unsafe.CInt
import scala.scalanative.unsafe.CStruct7
import scala.scalanative.unsafe.CUnsignedLong

/** Type aliases for the two possible termios structures, one with CInt bitflags
* and one with CLong bitflags. CLong types are the default in scala-native,
Expand All @@ -44,7 +45,7 @@ object TermiosStruct {
linux_tcflag_t, /* c_oflag - output flags */
linux_tcflag_t, /* c_cflag - control flags */
linux_tcflag_t, /* c_lflag - local flags */
posix.termios.c_cc, /* cc_t c_cc[NCCS] - control chars */
posix.termios.cc_t_arr, /* cc_t c_cc[NCCS] - control chars */
linux_speed_t, /* c_ispeed - input speed */
linux_speed_t /* c_ospeed - output speed */
]
Expand Down
16 changes: 16 additions & 0 deletions core/native/src/test/scala/terminus/TermiosAccessSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright 2024 Creative Scala
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

Loading
Loading