about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--adapter/img/stbi.nim12
-rw-r--r--src/local/container.nim2
-rw-r--r--src/local/pager.nim61
-rw-r--r--src/local/term.nim38
-rw-r--r--src/utils/sandbox.nim2
5 files changed, 84 insertions, 31 deletions
diff --git a/adapter/img/stbi.nim b/adapter/img/stbi.nim
index e4580d5c..f32c7f77 100644
--- a/adapter/img/stbi.nim
+++ b/adapter/img/stbi.nim
@@ -8,9 +8,11 @@ import utils/twtstr
 {.passc: "-fno-strict-aliasing".}
 {.passl: "-fno-strict-aliasing".}
 
-{.compile: "stb_image.c".}
+{.compile("stb_image.c", "-O3").}
 
-type stbi_io_callbacks = object
+{.push header: "stb_image.h".}
+
+type stbi_io_callbacks {.importc.} = object
   read: proc(user: pointer; data: ptr uint8; size: cint): cint {.cdecl.}
   skip: proc(user: pointer; n: cint) {.cdecl.}
   eof: proc(user: pointer): cint {.cdecl.}
@@ -26,6 +28,8 @@ proc stbi_failure_reason(): cstring {.importc.}
 
 proc stbi_image_free(retval_from_stbi_load: pointer) {.importc.}
 
+{.pop.}
+
 proc myRead(user: pointer; data: ptr uint8; size: cint): cint {.cdecl.} =
   return cint(stdin.readBuffer(data, size))
 
@@ -41,20 +45,24 @@ proc myEof(user: pointer): cint {.cdecl.} =
 
 type stbi_write_func = proc(context, data: pointer; size: cint) {.cdecl.}
 
+{.push header: "stb_image_write.h".}
 proc stbi_write_png_to_func(fun: stbi_write_func; context: pointer;
   w, h, comp: cint; data: pointer; stride_in_bytes: cint) {.importc.}
 proc stbi_write_bmp_to_func(fun: stbi_write_func; context: pointer;
   w, h, comp: cint; data: pointer) {.importc.}
 proc stbi_write_jpg_to_func(fun: stbi_write_func; context: pointer;
   w, h, comp: cint; data: pointer; quality: cint) {.importc.}
+{.pop.}
 
 proc myWriteFunc(context, data: pointer; size: cint) {.cdecl.} =
   discard stdout.writeBuffer(data, size)
 
+{.push header: "stb_image_resize.h".}
 proc stbir_resize_uint8(input_pixels: ptr uint8;
   input_w, input_h, input_stride_in_bytes: cint; output_pixels: ptr uint8;
   output_w, output_h, output_stride_in_bytes, num_channels: cint): cint
   {.importc.}
+{.pop.}
 
 proc main() =
   enterNetworkSandbox()
diff --git a/src/local/container.nim b/src/local/container.nim
index cc1e75e5..9f8dbd9e 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -22,6 +22,7 @@ import monoucha/javascript
 import monoucha/jsregex
 import monoucha/jstypes
 import server/buffer
+import types/blob
 import types/cell
 import types/color
 import types/cookie
@@ -101,6 +102,7 @@ type
     loaded*: bool
     width*: int
     height*: int
+    data*: Blob
     bmp*: NetworkBitmap
 
   Container* = ref object
diff --git a/src/local/pager.nim b/src/local/pager.nim
index 9f5eb593..f3d4d79a 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -40,6 +40,7 @@ import monoucha/quickjs
 import monoucha/tojs
 import server/buffer
 import server/forkserver
+import types/blob
 import types/cell
 import types/color
 import types/cookie
@@ -208,6 +209,8 @@ proc clearStatus(pager: Pager) =
   )
 
 proc setContainer*(pager: Pager; c: Container) {.jsfunc.} =
+  if pager.term.imageMode != imNone and pager.container != nil:
+    pager.container.cachedImages.setLen(0)
   pager.container = c
   if c != nil:
     c.queueDraw()
@@ -474,8 +477,8 @@ proc redraw(pager: Pager) {.jsfunc.} =
       pager.container.select.redraw = true
 
 proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) =
-  #TODO we should only cache the final output in memory, not the full bitmap.
-  let bmp = NetworkBitmap(image.bmp)
+  let bmp = NetworkBitmap()
+  bmp[] = NetworkBitmap(image.bmp)[]
   let request = newRequest(newURL("cache:" & $bmp.cacheId).get)
   let cachedImage = CachedImage(
     bmp: bmp,
@@ -484,9 +487,11 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) =
   )
   pager.loader.shareCachedItem(bmp.cacheId, pager.loader.clientPid,
     container.process)
+  let imageMode = pager.term.imageMode
   pager.loader.fetch(request).then(proc(res: JSResult[Response]):
       Promise[JSResult[Response]] =
     if res.isNone:
+      pager.loader.removeCachedItem(bmp.cacheId)
       return
     let response = res.get
     let headers = newHeaders()
@@ -504,19 +509,46 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) =
     response.unregisterFun()
     response.body.sclose()
     return r
-  ).then(proc(res: JSResult[Response]): EmptyPromise =
+  ).then(proc(res: JSResult[Response]) =
     if res.isNone:
       pager.loader.removeCachedItem(bmp.cacheId)
-      return newResolvedPromise()
+      return
     let response = res.get
     # take target sizes
     bmp.width = uint64(image.width)
     bmp.height = uint64(image.height)
-    return response.saveToBitmap(bmp).then(proc() =
-      container.redraw = true
-      cachedImage.loaded = true
-      pager.loader.removeCachedItem(bmp.cacheId)
-    )
+    case imageMode
+    of imSixel:
+      #TODO we should only cache the final output in memory, not the full
+      # bitmap.
+      response.saveToBitmap(bmp).then(proc() =
+        container.redraw = true
+        cachedImage.loaded = true
+        pager.loader.removeCachedItem(bmp.cacheId)
+      )
+    of imKitty:
+      let headers = newHeaders({
+        "Cha-Image-Dimensions": $image.width & 'x' & $image.height
+      })
+      let request = newRequest(
+        newURL("img-codec+png:encode").get,
+        httpMethod = hmPost,
+        headers = headers,
+        body = RequestBody(t: rbtOutput, outputId: response.outputId),
+      )
+      let r = pager.loader.fetch(request)
+      response.resume()
+      response.unregisterFun()
+      response.body.sclose()
+      r.then(proc(res: JSResult[Response]): Promise[JSResult[Blob]] =
+        return res.get.blob()
+      ).then(proc(res: JSResult[Blob]) =
+        container.redraw = true
+        cachedImage.data = res.get
+        cachedImage.loaded = true
+        pager.loader.removeCachedItem(bmp.cacheId)
+      )
+    of imNone: assert false
   )
   container.cachedImages.add(cachedImage)
 
@@ -524,6 +556,8 @@ proc initImages(pager: Pager; container: Container) =
   var newImages: seq[CanvasImage] = @[]
   for image in container.images:
     var imageId = -1
+    var data: Blob = nil
+    var bmp0 = image.bmp
     if image.bmp of NetworkBitmap:
       let bmp = NetworkBitmap(image.bmp)
       let cached = container.findCachedImage(image)
@@ -531,15 +565,16 @@ proc initImages(pager: Pager; container: Container) =
       if cached == nil:
         pager.loadCachedImage(container, image)
         continue
-      image.bmp = cached.bmp
+      bmp0 = cached.bmp
+      data = cached.data
       if not cached.loaded:
         continue # loading
     else:
       imageId = pager.imageId
       inc pager.imageId
-    let canvasImage = pager.term.loadImage(image, container.process, imageId,
-      image.x - container.fromx, image.y - container.fromy, pager.bufWidth,
-      pager.bufHeight)
+    let canvasImage = pager.term.loadImage(bmp0, data, container.process,
+      imageId, image.x - container.fromx, image.y - container.fromy,
+      image.x, image.y, pager.bufWidth, pager.bufHeight)
     if canvasImage != nil:
       newImages.add(canvasImage)
   pager.term.clearImages(pager.bufHeight)
diff --git a/src/local/term.nim b/src/local/term.nim
index f2b48f36..50eac042 100644
--- a/src/local/term.nim
+++ b/src/local/term.nim
@@ -14,7 +14,7 @@ import config/config
 import img/bitmap
 import io/posixstream
 import js/base64
-import layout/renderdocument
+import types/blob
 import types/cell
 import types/color
 import types/opt
@@ -66,7 +66,11 @@ type
     damaged: bool
     marked*: bool
     kittyId: int
-    pbmp: PosBitmap
+    bmp: Bitmap
+    # absolute x, y in container
+    rx: int
+    ry: int
+    data: Blob
 
   Terminal* = ref object
     cs*: Charset
@@ -222,9 +226,6 @@ const ANSIColorMap = [
   rgb(255, 255, 255)
 ]
 
-template bmp(image: CanvasImage): Bitmap =
-  image.pbmp.bmp
-
 proc flush*(term: Terminal) =
   term.outfile.flushFile()
 
@@ -619,12 +620,12 @@ proc outputGrid*(term: Terminal) =
   term.cursorx = -1
   term.cursory = -1
 
-func findImage(term: Terminal; pid, imageId: int; pbmp: PosBitmap):
+func findImage(term: Terminal; pid, imageId: int; bmp: Bitmap; rx, ry: int):
     CanvasImage =
   for it in term.canvasImages:
     if it.pid == pid and it.imageId == imageId and
-        it.pbmp.width == pbmp.width and it.pbmp.height == pbmp.height and
-        it.pbmp.x == pbmp.x and it.pbmp.y == pbmp.y:
+        it.bmp.width == bmp.width and it.bmp.height == bmp.height and
+        it.rx == rx and it.ry == ry:
       return it
   return nil
 
@@ -666,9 +667,9 @@ proc clearImages*(term: Terminal; maxh: int) =
       term.clearImage(image, maxh)
     image.marked = false
 
-proc loadImage*(term: Terminal; pbmp: PosBitmap; pid, imageId, x, y, maxw,
-    maxh: int): CanvasImage =
-  if (let image = term.findImage(pid, imageId, pbmp); image != nil):
+proc loadImage*(term: Terminal; bmp: Bitmap; data: Blob; pid, imageId,
+    x, y, rx, ry, maxw, maxh: int): CanvasImage =
+  if (let image = term.findImage(pid, imageId, bmp, rx, ry); image != nil):
     # reuse image on screen
     if image.x != x or image.y != y:
       # only clear sixels; with kitty we just move the existing image
@@ -690,7 +691,14 @@ proc loadImage*(term: Terminal; pbmp: PosBitmap; pid, imageId, x, y, maxw,
     image.marked = true
     return image
   # new image
-  let image = CanvasImage(pbmp: pbmp, pid: pid, imageId: imageId)
+  let image = CanvasImage(
+    bmp: bmp,
+    pid: pid,
+    imageId: imageId,
+    data: data,
+    rx: rx,
+    ry: ry
+  )
   if term.positionImage(image, x, y, maxw, maxh):
     return image
   # no longer on screen
@@ -802,10 +810,10 @@ proc outputKittyImage(term: Terminal; x, y: int; image: CanvasImage) =
   const MaxBytes = 4096 * 3 div 4
   var i = MaxBytes
   # transcode to RGB
-  let p = cast[ptr UncheckedArray[uint8]](addr image.bmp.px[0])
-  let L = image.bmp.px.len * 4
+  let p = cast[ptr UncheckedArray[uint8]](image.data.buffer)
+  let L = int(image.data.size)
   let m = if i < L: '1' else: '0'
-  outs &= ",a=T,f=32,m=" & m & ';'
+  outs &= ",a=T,f=100,m=" & m & ';'
   outs.btoa(p.toOpenArray(0, min(L, i) - 1))
   outs &= ST
   term.write(outs)
diff --git a/src/utils/sandbox.nim b/src/utils/sandbox.nim
index d4312a49..a700ea2e 100644
--- a/src/utils/sandbox.nim
+++ b/src/utils/sandbox.nim
@@ -127,7 +127,7 @@ elif defined(linux) and not disableSandbox:
     onSignal SIGSYS:
       discard sig
       raise newException(Defect, "Sandbox violation in network process")
-    let ctx = seccomp_init(SCMP_ACT_KILL_PROCESS)
+    let ctx = seccomp_init(SCMP_ACT_TRAP)
     doAssert pointer(ctx) != nil
     const allowList = [
       cstring"close", "exit_group", # duh