about summary refs log tree commit diff stats
path: root/src/display/client.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-11-09 14:27:01 +0100
committerbptato <nincsnevem662@gmail.com>2022-11-09 14:27:01 +0100
commitf3f13da82686c73e5593e3ba3362b2470bd215bc (patch)
treee26b79b1b1510e234da600b456acae5486eb1b9f /src/display/client.nim
parente30b40fc2cd80cd3b66622db8f505dfc9c1db8e9 (diff)
downloadchawan-f3f13da82686c73e5593e3ba3362b2470bd215bc.tar.gz
Do not always assume stdin is /dev/tty
This way we theoretically don't have to wait for stdin input to finish loading
from e.g. a pipe before we start accepting input. (In practice, we still do.)
Diffstat (limited to 'src/display/client.nim')
-rw-r--r--src/display/client.nim51
1 files changed, 32 insertions, 19 deletions
diff --git a/src/display/client.nim b/src/display/client.nim
index c51a4e5b..9f43a623 100644
--- a/src/display/client.nim
+++ b/src/display/client.nim
@@ -5,6 +5,9 @@ import tables
 import terminal
 import times
 
+when defined(posix):
+  import posix
+
 import std/monotimes
 
 import css/sheet
@@ -48,10 +51,11 @@ type
     err*: Stream
     lastbuf*: Buffer
     ibuf: string
+    tty: File
 
 proc readChar(console: Console): char =
   if console.ibuf == "":
-    return stdin.readChar()
+    return console.tty.readChar()
   result = console.ibuf[0]
   console.ibuf = console.ibuf.substr(1)
 
@@ -66,16 +70,13 @@ proc statusMode(client: Client) =
   print(EL())
 
 proc readPipe(client: Client, ctype: string) =
-  let buffer = newBuffer(client.config, client.loader)
+  let buffer = newBuffer(client.config, client.loader, client.console.tty)
   buffer.contenttype = if ctype != "": ctype else: "text/plain"
   buffer.ispipe = true
-  let ifs = newFileStream(stdin)
-  buffer.istream = newStringStream(ifs.readAll())
-  ifs.close()
+  buffer.istream = newFileStream(stdin)
   buffer.location = newURL("file://-")
   client.pager.addBuffer(buffer)
-  #TODO is this portable at all?
-  if reopen(stdin, "/dev/tty", fmRead):
+  if client.console.tty != nil:
     buffer.setupBuffer()
   else:
     buffer.load()
@@ -87,7 +88,7 @@ proc doRequest(client: Client, req: Request): Response {.jsfunc.} =
 proc interruptHandler(rt: JSRuntime, opaque: pointer): int {.cdecl.} =
   let client = cast[Client](opaque)
   try:
-    let c = stdin.readChar()
+    let c = client.console.tty.readChar()
     if c == char(3): #C-c
       client.console.ibuf = ""
       return 1
@@ -98,7 +99,7 @@ proc interruptHandler(rt: JSRuntime, opaque: pointer): int {.cdecl.} =
   return 0
 
 proc evalJS(client: Client, src, filename: string): JSObject =
-  unblockStdin()
+  unblockStdin(client.console.tty.getFileHandle())
   return client.jsctx.eval(src, filename, JS_EVAL_TYPE_GLOBAL)
 
 proc evalJSFree(client: Client, src, filename: string) =
@@ -122,12 +123,12 @@ proc command0(client: Client, src: string, filename = "<command>", silence = fal
   client.added_intervals.clear()
 
 proc command(client: Client, src: string) =
-  restoreStdin()
+  restoreStdin(client.console.tty.getFileHandle())
   let previ = client.console.err.getPosition()
   client.command0(src)
   client.console.err.setPosition(previ)
   if client.console.lastbuf == nil:
-    let buffer = newBuffer(client.config, client.loader)
+    let buffer = newBuffer(client.config, client.loader, client.console.tty)
     buffer.istream = newStringStream(client.console.err.readAll()) #TODO
     buffer.contenttype = "text/plain"
     buffer.location = parseUrl("javascript:void(0);").get
@@ -142,7 +143,7 @@ proc command(client: Client, src: string) =
 proc command(client: Client): bool {.jsfunc.} =
   var iput: string
   client.statusMode()
-  let status = readLine("COMMAND: ", iput, client.attrs.width, config = client.config)
+  let status = readLine("COMMAND: ", iput, client.attrs.width, config = client.config, tty = client.console.tty)
   if status:
     client.command(iput)
   return status
@@ -151,8 +152,11 @@ proc commandMode(client: Client) {.jsfunc.} =
   client.pager.commandMode = client.command()
 
 proc quit(client: Client, code = 0) {.jsfunc.} =
-  print(HVP(getTermAttributes().height, 0))
-  print(EL())
+  if stdout.isatty():
+    print(HVP(getTermAttributes(stdout).height, 0))
+    print(EL())
+  when defined(posix):
+    assert kill(client.loader.process, cint(SIGTERM)) == 0
   quit(code)
 
 proc feedNext(client: Client) {.jsfunc.} =
@@ -166,7 +170,7 @@ proc input(client: Client) =
     client.s = ""
   else:
     client.feednext = false
-  restoreStdin()
+  restoreStdin(client.console.tty.getFileHandle())
   let c = client.console.readChar()
   client.s &= c
 
@@ -175,7 +179,7 @@ proc input(client: Client) =
 
 proc inputLoop(client: Client) =
   while true:
-    restoreStdin()
+    restoreStdin(client.console.tty.getFileHandle())
     client.pager.displayPage()
     client.pager.followRedirect()
     if client.pager.container != nil:
@@ -288,13 +292,16 @@ proc launchClient*(client: Client, pages: seq[string], ctype: string, dump: bool
   client.userstyle = client.config.stylesheet.parseStylesheet()
   if not stdin.isatty:
     client.readPipe(ctype)
+  else:
+    client.console.tty = stdin
+
   for page in pages:
     client.pager.loadURL(page, force = true, ctype = ctype)
 
   if stdout.isatty and not dump:
     if client.pager.container != nil:
       when defined(posix):
-        enableRawMode()
+        enableRawMode(client.console.tty.getFileHandle())
       client.inputLoop()
     else:
       for msg in client.pager.status:
@@ -308,6 +315,8 @@ proc launchClient*(client: Client, pages: seq[string], ctype: string, dump: bool
       client.pager.container.buffer.drawBuffer()
     while client.pager.prevBuffer():
       client.pager.container.buffer.drawBuffer()
+    stdout.close()
+  client.quit()
 
 proc nimGCStats(client: Client): string {.jsfunc.} =
   return GC_getStatistics()
@@ -334,8 +343,12 @@ proc newClient*(config: Config): Client =
   result.config = config
   result.loader = newFileLoader()
   result.console = newConsole()
-  result.attrs = getTermAttributes()
-  result.pager = newPager(config, result.attrs, result.loader)
+  if stdin.isatty():
+    result.console.tty = stdin
+  elif stdout.isatty():
+    discard open(result.console.tty, "/dev/tty", fmRead)
+  result.attrs = getTermAttributes(stdout)
+  result.pager = newPager(config, result.attrs, result.loader, result.console.tty)
   let rt = newJSRuntime()
   rt.setInterruptHandler(interruptHandler, cast[pointer](result))
   let ctx = rt.newJSContext()