about summary refs log tree commit diff stats
path: root/src/local
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2025-02-05 20:25:53 +0100
committerbptato <nincsnevem662@gmail.com>2025-02-05 20:41:58 +0100
commit03b1412771dd1d03676ec546b12432c352b88e4e (patch)
treede7ecc004f8a62af43e14cd3110758066a103d29 /src/local
parent7f6cc3dc7d7f1c46dc56c7432244fca57a63a732 (diff)
downloadchawan-03b1412771dd1d03676ec546b12432c352b88e4e.tar.gz
pager: catch SIGINT for interrupt handler
Significantly more efficient in long running commands (as the context
switch is gone).

For many commands in quick succession...  it replaces the fcntl with
a tcsetattr, so I guess it's the same?
Diffstat (limited to 'src/local')
-rw-r--r--src/local/pager.nim21
-rw-r--r--src/local/term.nim44
2 files changed, 21 insertions, 44 deletions
diff --git a/src/local/pager.nim b/src/local/pager.nim
index ca85b5df..c804fcd3 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -411,18 +411,8 @@ proc loadJSModule(ctx: JSContext; moduleName: cstringConst; opaque: pointer):
     return nil
 
 proc interruptHandler(rt: JSRuntime; opaque: pointer): cint {.cdecl.} =
-  let pager = cast[Pager](opaque)
-  if pager.console != nil and pager.term.istream != nil:
-    try:
-      var buf = [char(0)]
-      let n = pager.term.istream.recvData(buf)
-      if n == 1 and buf[0] == char(3): #C-c
-        pager.term.resetInputBuffer()
-        return 1
-      pager.term.bufferInputChar(buf[0])
-    except ErrorAgain:
-      discard
-  return 0
+  result = cint(term.sigintCaught)
+  term.sigintCaught = false
 
 proc evalJSFree(opaque: RootRef; src, filename: string) =
   let pager = Pager(opaque)
@@ -445,7 +435,7 @@ proc newPager*(config: Config; forkserver: ForkServer; ctx: JSContext;
   )
   pager.timeouts = newTimeoutState(pager.jsctx, evalJSFree, pager)
   JS_SetModuleLoaderFunc(pager.jsrt, normalizeModuleName, loadJSModule, nil)
-  JS_SetInterruptHandler(pager.jsrt, interruptHandler, cast[pointer](pager))
+  JS_SetInterruptHandler(pager.jsrt, interruptHandler, nil)
   let clientConfig = LoaderClientConfig(
     defaultHeaders: newHeaders(pager.config.network.defaultHeaders),
     proxy: pager.config.network.proxy,
@@ -511,7 +501,7 @@ proc runJSJobs(pager: Pager) =
     pager.quit(0)
 
 proc evalJS(pager: Pager; src, filename: string; module = false): JSValue =
-  pager.term.unblockStdin()
+  pager.term.catchSigint()
   let flags = if module:
     JS_EVAL_TYPE_MODULE
   else:
@@ -520,13 +510,13 @@ proc evalJS(pager: Pager; src, filename: string; module = false): JSValue =
   pager.inEval = true
   result = pager.jsctx.eval(src, filename, flags)
   pager.inEval = false
-  pager.term.restoreStdin()
   if pager.exitCode != -1:
     # if we are in a nested eval, then just wait until we are not.
     if not wasInEval:
       pager.quit(pager.exitCode)
   else:
     pager.runJSJobs()
+  pager.term.respectSigint()
 
 proc evalActionJS(pager: Pager; action: string): JSValue =
   if action.startsWith("cmd."):
@@ -735,7 +725,6 @@ proc handleCommandInput(pager: Pager; c: char): EmptyPromise =
 
 proc input(pager: Pager): EmptyPromise =
   var p: EmptyPromise = nil
-  pager.term.restoreStdin()
   var buf = ""
   while true:
     let c = pager.term.readChar()
diff --git a/src/local/term.nim b/src/local/term.nim
index 444f742b..f428d626 100644
--- a/src/local/term.nim
+++ b/src/local/term.nim
@@ -130,9 +130,8 @@ type
     cleared: bool
     smcup: bool
     setTitle: bool
-    stdinUnblocked: bool
-    stdinWasUnblocked: bool
     origTermios: Termios
+    newTermios: Termios
     defaultBackground: RGBColor
     defaultForeground: RGBColor
     ibuf: array[256, char] # buffer for chars when we can't process them
@@ -269,18 +268,6 @@ proc readChar*(term: Terminal): char =
   result = term.ibuf[term.ibufn]
   inc term.ibufn
 
-proc bufferInputChar*(term: Terminal; c: char) =
-  if term.ibufn == term.ibuf.len:
-    return # can't help it, sorry :P
-  term.ibuf[term.ibufn] = c
-  inc term.ibufn
-  if term.ibufn >= term.ibufLen:
-    term.ibufLen = term.ibufn
-
-proc resetInputBuffer*(term: Terminal) =
-  term.ibufn = 0
-  term.ibufLen = 0
-
 proc hasBuffer*(term: Terminal): bool =
   return term.ibufn < term.ibufLen
 
@@ -1009,23 +996,30 @@ proc disableRawMode(term: Terminal) =
   discard tcSetAttr(term.istream.fd, TCSAFLUSH, addr term.origTermios)
 
 proc enableRawMode(term: Terminal) =
+  #TODO check errors
   discard tcGetAttr(term.istream.fd, addr term.origTermios)
   var raw = term.origTermios
   raw.c_iflag = raw.c_iflag and not (BRKINT or ICRNL or INPCK or ISTRIP or IXON)
   raw.c_oflag = raw.c_oflag and not (OPOST)
   raw.c_cflag = raw.c_cflag or CS8
   raw.c_lflag = raw.c_lflag and not (ECHO or ICANON or ISIG or IEXTEN)
+  term.newTermios = raw
   discard tcSetAttr(term.istream.fd, TCSAFLUSH, addr raw)
 
-proc unblockStdin*(term: Terminal) =
-  if term.isatty():
-    term.istream.setBlocking(false)
-    term.stdinUnblocked = true
+# This is checked in the SIGINT handler, set in main.nim.
+var sigintCaught* {.global.} = false
+var acceptSigint* {.global.} = false
+
+proc catchSigint*(term: Terminal) =
+  term.newTermios.c_lflag = term.newTermios.c_lflag or ISIG
+  acceptSigint = true
+  discard tcSetAttr(term.istream.fd, TCSAFLUSH, addr term.newTermios)
 
-proc restoreStdin*(term: Terminal) =
-  if term.stdinUnblocked:
-    term.istream.setBlocking(true)
-    term.stdinUnblocked = false
+proc respectSigint*(term: Terminal) =
+  sigintCaught = false
+  acceptSigint = false
+  term.newTermios.c_lflag = term.newTermios.c_lflag and not ISIG
+  discard tcSetAttr(term.istream.fd, TCSAFLUSH, addr term.newTermios)
 
 proc quit*(term: Terminal) =
   if term.isatty():
@@ -1045,9 +1039,6 @@ proc quit*(term: Terminal) =
       term.write(XTPOPTITLE)
     term.showCursor()
     term.clearCanvas()
-    if term.stdinUnblocked:
-      term.restoreStdin()
-      term.stdinWasUnblocked = true
   term.flush()
 
 when TermcapFound:
@@ -1515,9 +1506,6 @@ proc start*(term: Terminal; istream: PosixStream): TermStartResult =
 proc restart*(term: Terminal) =
   if term.isatty():
     term.enableRawMode()
-    if term.stdinWasUnblocked:
-      term.unblockStdin()
-      term.stdinWasUnblocked = false
     term.initScreen()
 
 const ANSIColorMap = [