about summary refs log tree commit diff stats
path: root/src/buffer
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-11-25 00:32:54 +0100
committerbptato <nincsnevem662@gmail.com>2022-11-25 00:33:39 +0100
commitb086e346afeded51c94c9b77280dcea6f6b3ce8a (patch)
treee2fd9f29e77ab787f960bbd8ba190a14a6d5d7a0 /src/buffer
parent896489a6c500e28f13d0237ab691622cb5c5114f (diff)
downloadchawan-b086e346afeded51c94c9b77280dcea6f6b3ce8a.tar.gz
Buffer improvements
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/buffer.nim33
-rw-r--r--src/buffer/container.nim112
2 files changed, 106 insertions, 39 deletions
diff --git a/src/buffer/buffer.nim b/src/buffer/buffer.nim
index 260fc8ea..0c30ea92 100644
--- a/src/buffer/buffer.nim
+++ b/src/buffer/buffer.nim
@@ -39,7 +39,7 @@ type
   BufferCommand* = enum
     LOAD, RENDER, WINDOW_CHANGE, GOTO_ANCHOR, READ_SUCCESS, READ_CANCELED,
     CLICK, FIND_NEXT_LINK, FIND_PREV_LINK, FIND_NEXT_MATCH, FIND_PREV_MATCH,
-    GET_SOURCE, GET_LINES, MOVE_CURSOR
+    GET_SOURCE, GET_LINES, MOVE_CURSOR, PASS_FD
 
   ContainerCommand* = enum
     SET_LINES, SET_NEEDS_AUTH, SET_CONTENT_TYPE, SET_REDIRECT, SET_TITLE,
@@ -68,6 +68,8 @@ type
     location: Url
     selector: Selector[int]
     istream: Stream
+    sstream: Stream
+    available: int
     pistream: Stream # for input pipe
     postream: Stream # for output pipe
     streamclosed: bool
@@ -76,9 +78,17 @@ type
     prevnode: StyledNode
     loader: FileLoader
     config: BufferConfig
+    userstyle: CSSStylesheet
 
 macro writeCommand(buffer: Buffer, cmd: ContainerCommand, args: varargs[typed]) =
   result = newStmtList()
+  let lens = ident("lens")
+  result.add(quote do:
+    var `lens` = slen(`cmd`))
+  for arg in args:
+    result.add(quote do: `lens` += slen(`arg`))
+  result.add(quote do:
+    `buffer`.postream.swrite(`lens`))
   result.add(quote do: `buffer`.postream.swrite(`cmd`))
   for arg in args:
     result.add(quote do: `buffer`.postream.swrite(`arg`))
@@ -413,7 +423,7 @@ proc render(buffer: Buffer) =
   of "text/html":
     if buffer.viewport == nil:
       buffer.viewport = Viewport(window: buffer.attrs)
-    let ret = renderDocument(buffer.document, buffer.attrs, buffer.config.userstyle, buffer.viewport, buffer.prevstyled)
+    let ret = renderDocument(buffer.document, buffer.attrs, buffer.userstyle, buffer.viewport, buffer.prevstyled)
     buffer.lines = ret[0]
     buffer.prevstyled = ret[1]
   else:
@@ -428,14 +438,15 @@ proc load2(buffer: Buffer) =
     # (We're basically recv'ing single bytes, but nim std/net does buffering
     # for us so we should be ok?)
     if not buffer.streamclosed:
-      let c = buffer.istream.readChar()
-      buffer.source &= c
+      buffer.source &= buffer.istream.readChar()
       buffer.reshape = true
 
 proc finishLoad(buffer: Buffer) =
   if not buffer.streamclosed:
     if not buffer.istream.atEnd:
-      buffer.source &= buffer.istream.readAll()
+      let a = buffer.istream.readAll()
+      buffer.sstream.write(a)
+      buffer.available += a.len
       buffer.reshape = true
     buffer.selector.unregister(int(buffer.getFd()))
     buffer.istream.close()
@@ -717,6 +728,9 @@ proc readCommand(buffer: Buffer) =
   var cmd: BufferCommand
   istream.sread(cmd)
   case cmd
+  of PASS_FD:
+    let fd = SocketStream(istream).recvFileHandle()
+    buffer.bsource.fd = fd
   of LOAD:
     let code = buffer.setupSource()
     buffer.load()
@@ -736,6 +750,10 @@ proc readCommand(buffer: Buffer) =
     istream.sread(w)
     if w.b < 0 or w.b > buffer.lines.high:
       w.b = buffer.lines.high
+    var lens = sizeof(SET_LINES) + sizeof(buffer.lines.len) + sizeof(w)
+    for y in w:
+      lens += slen(buffer.lines[y])
+    ostream.swrite(lens)
     ostream.swrite(SET_LINES)
     ostream.swrite(buffer.lines.len)
     ostream.swrite(w)
@@ -837,18 +855,19 @@ proc launchBuffer*(config: BufferConfig, source: BufferSource,
                    attrs: WindowAttributes, loader: FileLoader,
                    mainproc: Pid) =
   let buffer = new Buffer
+  buffer.userstyle = parseStylesheet(config.userstyle)
   buffer.attrs = attrs
   buffer.windowChange()
+  buffer.sstream = newStringStream()
   buffer.config = config
   buffer.loader = loader
   buffer.bsource = source
   buffer.selector = newSelector[int]()
   let sstream = connectSocketStream(mainproc, false)
   sstream.swrite(getpid())
-  sstream.swrite(BUFFER_READY)
-  sstream.flush()
   buffer.pistream = sstream
   buffer.postream = sstream
+  buffer.writeCommand(BUFFER_READY)
   let rfd = int(sstream.source.getFd())
   buffer.selector.registerHandle(rfd, {Read}, 0)
   buffer.runBuffer(rfd)
diff --git a/src/buffer/container.nim b/src/buffer/container.nim
index 4241e886..cd321b48 100644
--- a/src/buffer/container.nim
+++ b/src/buffer/container.nim
@@ -15,6 +15,7 @@ import io/request
 import io/window
 import ips/forkserver
 import ips/serialize
+import ips/socketstream
 import js/javascript
 import js/regex
 import types/buffersource
@@ -33,7 +34,7 @@ type
 
   ContainerEventType* = enum
     NO_EVENT, FAIL, SUCCESS, NEEDS_AUTH, REDIRECT, ANCHOR, NO_ANCHOR, UPDATE,
-    READ_LINE, OPEN
+    READ_LINE, OPEN, INVALID_COMMAND
 
   ContainerEvent* = object
     case t*: ContainerEventType
@@ -76,12 +77,11 @@ type
     retry*: seq[URL]
     redirect*: Option[URL]
     ispipe: bool
-    jump: bool
     hlon*: bool
-    waitfor: bool
     pipeto: Container
     redraw*: bool
     sourceready*: bool
+    cmdvalid: array[ContainerCommand, bool]
 
 proc newBuffer*(dispatcher: Dispatcher, config: Config, source: BufferSource, ispipe = false, autoload = true): Container =
   let attrs = getWindowAttributes(stdout)
@@ -96,8 +96,9 @@ proc newBuffer*(dispatcher: Dispatcher, config: Config, source: BufferSource, is
   result = Container(
     source: source, attrs: attrs, width: attrs.width,
     height: attrs.height - 1, contenttype: source.contenttype,
-    ispipe: ispipe, waitfor: true
+    ispipe: ispipe
   )
+  result.cmdvalid[BUFFER_READY] = true
   istream.sread(result.process)
   result.pos.setx = -1
 
@@ -249,12 +250,21 @@ macro writeCommand(container: Container, cmd: BufferCommand, args: varargs[typed
     result.add(quote do: `container`.ostream.swrite(`arg`))
   result.add(quote do: `container`.ostream.flush())
 
+proc expect(container: Container, cmd: ContainerCommand) =
+  container.cmdvalid[cmd] = true
+
 proc requestLines*(container: Container, w = container.lineWindow) =
   container.writeCommand(GET_LINES, w)
+  container.expect(SET_LINES)
 
 proc redraw*(container: Container) {.jsfunc.} =
   container.redraw = true
 
+proc sendCursorPosition*(container: Container) =
+  container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
+  container.expect(SET_HOVER)
+  container.expect(RESHAPE)
+
 proc setFromY*(container: Container, y: int) {.jsfunc.} =
   if container.pos.fromy != y:
     container.pos.fromy = max(min(y, container.maxfromy), 0)
@@ -266,7 +276,7 @@ proc setFromX*(container: Container, x: int) {.jsfunc.} =
     container.pos.fromx = max(min(x, container.maxfromx), 0)
     if container.pos.fromx > container.cursorx:
       container.pos.cursorx = min(container.pos.fromx, container.currentLineWidth())
-      container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
+      container.sendCursorPosition()
     container.redraw = true
 
 proc setFromXY*(container: Container, x, y: int) {.jsfunc.} =
@@ -293,7 +303,7 @@ proc setCursorX*(container: Container, x: int, refresh = true, save = true) {.js
   elif x < container.cursorx:
     container.setFromX(x)
     container.pos.cursorx = x
-  container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
+  container.sendCursorPosition()
   if save:
     container.pos.xend = container.cursorx
 
@@ -311,7 +321,7 @@ proc setCursorY*(container: Container, y: int) {.jsfunc.} =
     else:
       container.setFromY(y)
     container.pos.cursory = y
-  container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
+  container.sendCursorPosition()
   container.restoreCursorX()
 
 proc centerLine*(container: Container) {.jsfunc.} =
@@ -496,15 +506,11 @@ proc updateCursor(container: Container) =
 proc pushCursorPos*(container: Container) =
   container.bpos.add(container.pos)
 
-proc sendCursorPosition*(container: Container) =
-  container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
-  container.requestLines()
-
 proc popCursorPos*(container: Container, nojump = false) =
   container.pos = container.bpos.pop()
   container.updateCursor()
   if not nojump:
-    container.writeCommand(MOVE_CURSOR, container.cursorx, container.cursory)
+    container.sendCursorPosition()
     container.requestLines()
 
 macro proxy(fun: typed) =
@@ -535,28 +541,40 @@ macro proxy(fun: typed) =
 
 proc cursorNextLink*(container: Container) {.jsfunc.} =
   container.writeCommand(FIND_NEXT_LINK, container.cursorx, container.cursory)
-  container.jump = true
+  container.expect(JUMP)
 
 proc cursorPrevLink*(container: Container) {.jsfunc.} =
   container.writeCommand(FIND_PREV_LINK, container.cursorx, container.cursory)
-  container.jump = true
+  container.expect(JUMP)
 
 proc cursorNextMatch*(container: Container, regex: Regex, wrap: bool) {.jsfunc.} =
   container.writeCommand(FIND_NEXT_MATCH, container.cursorx, container.cursory, regex, wrap)
-  container.jump = true
+  container.expect(JUMP)
 
 proc cursorPrevMatch*(container: Container, regex: Regex, wrap: bool) {.jsfunc.} =
   container.writeCommand(FIND_PREV_MATCH, container.cursorx, container.cursory, regex, wrap)
-  container.jump = true
+  container.expect(JUMP)
+
+proc load*(container: Container) =
+  container.writeCommand(LOAD)
+  container.expect(LOAD_DONE)
+  container.expect(SET_NEEDS_AUTH)
+  container.expect(SET_REDIRECT)
+  container.expect(SET_CONTENT_TYPE)
+  container.expect(SET_TITLE)
+
+proc gotoAnchor*(container: Container, anchor: string) =
+  container.writeCommand(GOTO_ANCHOR, anchor)
+  container.expect(ANCHOR_FOUND)
+  container.expect(ANCHOR_FAIL)
 
-proc load*(container: Container) {.proxy.} = discard
-proc gotoAnchor*(container: Container, anchor: string) {.proxy.} = discard
 proc readCanceled*(container: Container) {.proxy.} = discard
 proc readSuccess*(container: Container, s: string) {.proxy.} = discard
 
 proc reshape*(container: Container, noreq = false) {.jsfunc.} =
   container.writeCommand(RENDER)
-  container.jump = true # may jump to anchor
+  container.expect(RESHAPE)
+  container.expect(JUMP)
   if not noreq:
     container.requestLines()
 
@@ -569,23 +587,35 @@ proc dupeBuffer*(dispatcher: Dispatcher, container: Container, config: Config, l
   )
   container.pipeto = dispatcher.newBuffer(config, source, container.ispipe)
   container.writeCommand(GET_SOURCE)
+  container.expect(SOURCE_READY)
   return container.pipeto
 
 proc click*(container: Container) {.jsfunc.} =
   container.writeCommand(CLICK, container.cursorx, container.cursory)
+  container.expect(OPEN)
+  container.expect(READ_LINE)
+  container.expect(RESHAPE)
 
 proc windowChange*(container: Container, attrs: WindowAttributes) =
   container.attrs = attrs
   container.width = attrs.width
   container.height = attrs.height - 1
   container.writeCommand(WINDOW_CHANGE, attrs)
+  container.expect(RESHAPE)
 
 proc clearSearchHighlights*(container: Container) =
   for i in countdown(container.highlights.high, 0):
     if container.highlights[i].clear:
       container.highlights.del(i)
 
-proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent =
+proc handleCommand(container: Container, cmd: ContainerCommand, len: int): ContainerEvent =
+  if not container.cmdvalid[cmd]:
+    let len = len - sizeof(cmd)
+    #TODO TODO TODO this is very dumb
+    for i in 0 ..< len:
+      discard container.istream.readChar()
+    return ContainerEvent(t: INVALID_COMMAND)
+  container.cmdvalid[cmd] = false
   case cmd
   of SET_LINES:
     var w: Slice[int]
@@ -603,13 +633,14 @@ proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent
     return ContainerEvent(t: NEEDS_AUTH)
   of SET_CONTENT_TYPE:
     var ctype: string
-    container.istream.sread(ctype)
+    container.istream.sread(ctype, 128)
     container.contenttype = some(ctype)
   of SET_REDIRECT:
     var redirect: URL
     container.istream.sread(redirect)
-    container.redirect = some(redirect)
-    return ContainerEvent(t: REDIRECT)
+    if redirect != nil:
+      container.redirect = some(redirect)
+      return ContainerEvent(t: REDIRECT)
   of SET_TITLE:
     container.istream.sread(container.title)
   of SET_HOVER:
@@ -621,40 +652,49 @@ proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent
       return ContainerEvent(t: FAIL)
     return ContainerEvent(t: SUCCESS)
   of ANCHOR_FOUND:
+    container.cmdvalid[ANCHOR_FAIL] = false
     return ContainerEvent(t: ANCHOR)
   of ANCHOR_FAIL:
+    container.cmdvalid[ANCHOR_FOUND] = false
     return ContainerEvent(t: FAIL)
   of READ_LINE:
     var prompt, str: string
     var pwd: bool
-    container.istream.sread(prompt)
-    container.istream.sread(str)
+    container.istream.sread(prompt, 1024)
+    container.istream.sread(str, 1024)
     container.istream.sread(pwd)
+    container.cmdvalid[OPEN] = false
     return ContainerEvent(t: READ_LINE, prompt: prompt, value: str, password: pwd)
   of JUMP:
     var x, y, ex: int
     container.istream.sread(x)
     container.istream.sread(y)
     container.istream.sread(ex)
-    if x != -1 and y != -1 and container.jump:
+    if x != -1 and y != -1:
       if container.hlon:
         container.clearSearchHighlights()
         let hl = Highlight(x: x, y: y, endx: ex, endy: y, clear: true)
         container.highlights.add(hl)
         container.hlon = false
       container.setCursorXY(x, y)
-      container.jump = false
   of OPEN:
     var request: Request
     container.istream.sread(request)
+    container.cmdvalid[READ_LINE] = false
     return ContainerEvent(t: OPEN, request: request)
   of BUFFER_READY:
-    if container.waitfor:
-      container.waitfor = false
-      container.load()
+    if container.source.t == LOAD_PIPE:
+      container.ostream.swrite(PASS_FD)
+      container.ostream.flush()
+      let s = SocketStream(container.ostream)
+      s.sendFileHandle(container.source.fd)
+      discard close(container.source.fd)
+      container.ostream.flush()
+    container.load()
   of SOURCE_READY:
     if container.pipeto != nil:
       container.pipeto.load()
+      container.pipeto = nil
   of RESHAPE:
     container.requestLines()
 
@@ -662,9 +702,12 @@ proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent
 iterator readLines*(container: Container): SimpleFlexibleLine {.inline.} =
   var cmd: ContainerCommand
   container.requestLines(0 .. -1)
+  var len: int
+  container.istream.sread(len)
   container.istream.sread(cmd)
   while cmd != SET_LINES:
-    discard container.handleCommand(cmd)
+    discard container.handleCommand(cmd, len)
+    container.istream.sread(len)
     container.istream.sread(cmd)
   assert cmd == SET_LINES
   var w: Slice[int]
@@ -676,9 +719,14 @@ iterator readLines*(container: Container): SimpleFlexibleLine {.inline.} =
     yield line
 
 proc handleEvent*(container: Container): ContainerEvent =
+  var len: int
+  container.istream.sread(len)
   var cmd: ContainerCommand
   container.istream.sread(cmd)
-  return container.handleCommand(cmd)
+  if cmd > high(ContainerCommand):
+    return ContainerEvent(t: INVALID_COMMAND)
+  else:
+    return container.handleCommand(cmd, len)
 
 proc addContainerModule*(ctx: JSContext) =
   ctx.registerType(Container, name = "Buffer")