From 35a6dfbe531dec3303792415c9d84d69c6277779 Mon Sep 17 00:00:00 2001 From: Alexander Gehrke Date: Mon, 20 May 2024 23:50:06 +0200 Subject: [PATCH] refactor: improve terminal handling --- copret/src/terminal.scala | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/copret/src/terminal.scala b/copret/src/terminal.scala index 991ee6e..75a98ba 100644 --- a/copret/src/terminal.scala +++ b/copret/src/terminal.scala @@ -35,13 +35,15 @@ object Terminal: keymap(key.toList) def queryTerm(query: String): String = - val ttyIn = new java.io.FileInputStream("/dev/tty") - val ttyOut = new java.io.PrintStream(new java.io.FileOutputStream("/dev/tty")) - - ttyOut.println(query) + val ttyIn = new java.io.FileInputStream("/dev/tty") + val ttyOut = new java.io.PrintStream(new java.io.FileOutputStream("/dev/tty")) + logger.info(s"Querying terminal: ${query.replaceAllLiterally("\u001b", "")}") + ttyOut.print(query) var response = scala.collection.mutable.ArrayBuffer[Int]() response += ttyIn.read() + logger.info(s"Response: ${response}") while (ttyIn.available > 0 && response.last != '\u0007') + logger.info(s"Response(cont): ${response}") response += ttyIn.read() new String(response.toArray, 0, response.length) @@ -68,7 +70,7 @@ object Terminal: def stripEscapes(str: String) = // 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 showCursor() = print(s"${csi}?25h") @@ -95,11 +97,23 @@ object Terminal: s"${apc}Gm=0;${chunks.last}${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 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 case SizeResponse(rows, cols) => Some(PixelSize(cols.toInt, rows.toInt)) case _ => None