refactor: improve terminal handling

This commit is contained in:
Alexander Gehrke 2024-05-20 23:50:06 +02:00
parent 38632a992d
commit 35a6dfbe53

View file

@ -37,11 +37,13 @@ object Terminal:
def queryTerm(query: String): String = def queryTerm(query: String): String =
val ttyIn = new java.io.FileInputStream("/dev/tty") val ttyIn = new java.io.FileInputStream("/dev/tty")
val ttyOut = new java.io.PrintStream(new java.io.FileOutputStream("/dev/tty")) val ttyOut = new java.io.PrintStream(new java.io.FileOutputStream("/dev/tty"))
logger.info(s"Querying terminal: ${query.replaceAllLiterally("\u001b", "<ESC>")}")
ttyOut.println(query) ttyOut.print(query)
var response = scala.collection.mutable.ArrayBuffer[Int]() var response = scala.collection.mutable.ArrayBuffer[Int]()
response += ttyIn.read() response += ttyIn.read()
logger.info(s"Response: ${response}")
while (ttyIn.available > 0 && response.last != '\u0007') while (ttyIn.available > 0 && response.last != '\u0007')
logger.info(s"Response(cont): ${response}")
response += ttyIn.read() response += ttyIn.read()
new String(response.toArray, 0, response.length) new String(response.toArray, 0, response.length)
@ -68,7 +70,7 @@ object Terminal:
def stripEscapes(str: String) = def stripEscapes(str: String) =
// match CSI until letter or OSC/APC until ST // match CSI until letter or OSC/APC until ST
str.replaceAll("\u001b(\\[[^a-zA-Z]*[a-zA-Z]|(\\]|_).*?(\u0007|\u001b\\\\))", "") str.replaceAll("\u001b(\\[[^@-~]*[@-~]|(\\]|_).*?(\u0007|\u001b\\\\))", "")
def hideCursor() = print(s"${csi}?25l") def hideCursor() = print(s"${csi}?25l")
def showCursor() = print(s"${csi}?25h") def showCursor() = print(s"${csi}?25h")
@ -95,11 +97,23 @@ object Terminal:
s"${apc}Gm=0;${chunks.last}${st}" s"${apc}Gm=0;${chunks.last}${st}"
else s"${apc}Gf=100,t=d,a=T;${image}${st}" else s"${apc}Gf=100,t=d,a=T;${image}${st}"
def getSize = queryTerm(s"${csi}s${csi}999;999H${csi}6n${csi}u") private val CursorPosResponse = """\u001b\[(\d+);(\d+)R""".r
def getCursorPos(): Option[CursorPos] =
queryTerm(s"${csi}6n") match
case CursorPosResponse(rows, cols) => Some(CursorPos(cols.toInt, rows.toInt))
case _ => None
def getSize(): Option[CellsSize] =
queryTerm(s"${csi}s${csi}999;999H${csi}6n${csi}u") match
case CursorPosResponse(rows, cols) => Some(CellsSize(cols.toInt, rows.toInt))
case _ => None
private val SizeResponse = """\u001b\[4;(\d+);(\d+)t""".r private val SizeResponse = """\u001b\[4;(\d+);(\d+)t""".r
case class PixelSize(width: Int, height: Int) case class PixelSize(width: Int, height: Int)
case class CellsSize(cols: Int, rows: Int)
case class CursorPos(cols: Int, rows: Int)
def getSizePixels: Option[PixelSize] = queryTerm(s"${csi}14t") match def getSizePixels: Option[PixelSize] = queryTerm(s"${csi}14t") match
case SizeResponse(rows, cols) => Some(PixelSize(cols.toInt, rows.toInt)) case SizeResponse(rows, cols) => Some(PixelSize(cols.toInt, rows.toInt))
case _ => None case _ => None