diff options
author | bptato <nincsnevem662@gmail.com> | 2022-11-25 00:32:54 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-11-25 00:33:39 +0100 |
commit | b086e346afeded51c94c9b77280dcea6f6b3ce8a (patch) | |
tree | e2fd9f29e77ab787f960bbd8ba190a14a6d5d7a0 | |
parent | 896489a6c500e28f13d0237ab691622cb5c5114f (diff) | |
download | chawan-b086e346afeded51c94c9b77280dcea6f6b3ce8a.tar.gz |
Buffer improvements
-rw-r--r-- | src/buffer/buffer.nim | 33 | ||||
-rw-r--r-- | src/buffer/container.nim | 112 | ||||
-rw-r--r-- | src/config/bufferconfig.nim | 7 | ||||
-rw-r--r-- | src/display/client.nim | 47 | ||||
-rw-r--r-- | src/display/pager.nim | 4 | ||||
-rw-r--r-- | src/ips/serialize.nim | 84 | ||||
-rw-r--r-- | src/ips/socketstream.nim | 42 | ||||
-rw-r--r-- | src/render/rendertext.nim | 24 | ||||
-rw-r--r-- | src/types/color.nim | 2 |
9 files changed, 274 insertions, 81 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") diff --git a/src/config/bufferconfig.nim b/src/config/bufferconfig.nim index ee776363..6e8d19e6 100644 --- a/src/config/bufferconfig.nim +++ b/src/config/bufferconfig.nim @@ -1,9 +1,8 @@ import config/config import css/sheet -type BufferConfig* = ref object - userstyle*: CSSStylesheet +type BufferConfig* = object + userstyle*: string proc loadBufferConfig*(config: Config): BufferConfig = - new(result) - result.userstyle = parseStylesheet(config.stylesheet) + result.userstyle = config.stylesheet diff --git a/src/display/client.nim b/src/display/client.nim index bd7f3977..f4acaee4 100644 --- a/src/display/client.nim +++ b/src/display/client.nim @@ -242,26 +242,31 @@ proc inputLoop(client: Client) = client.acceptBuffers() let events = client.selector.select(-1) for event in events: - if event.fd == client.console.tty.getFileHandle(): - client.input() - stdout.flushFile() - elif event.fd in client.interval_fdis: - client.intervals[client.interval_fdis[event.fd]].handler() - elif event.fd in client.timeout_fdis: - let id = client.timeout_fdis[event.fd] - let timeout = client.timeouts[id] - timeout.handler() - client.clearTimeout(id) - elif event.fd == sigwinch: - client.attrs = getWindowAttributes(client.console.tty) - client.pager.windowChange(client.attrs) - else: - let container = client.fdmap[event.fd] - if not client.pager.handleEvent(container): - disableRawMode() - for msg in client.pager.status: - eprint msg - client.quit(1) + if Read in event.events: + if event.fd == client.console.tty.getFileHandle(): + client.input() + stdout.flushFile() + elif event.fd in client.interval_fdis: + client.intervals[client.interval_fdis[event.fd]].handler() + elif event.fd in client.timeout_fdis: + let id = client.timeout_fdis[event.fd] + let timeout = client.timeouts[id] + timeout.handler() + client.clearTimeout(id) + elif event.fd == sigwinch: + client.attrs = getWindowAttributes(client.console.tty) + client.pager.windowChange(client.attrs) + else: + let container = client.fdmap[event.fd] + if not client.pager.handleEvent(container): + disableRawMode() + for msg in client.pager.status: + eprint msg + client.quit(1) + elif Error in event.events: + eprint "Error", event + #TODO handle errors + else: assert false if client.pager.scommand != "": client.command(client.pager.scommand) client.pager.scommand = "" @@ -286,7 +291,6 @@ proc newConsole(pager: Pager, tty: File): Console = raise newException(Defect, "Failed to open console pipe.") let url = newURL("javascript:console.show()") result.container = pager.readPipe0(some("text/plain"), pipefd[0], option(url)) - discard close(pipefd[0]) var f: File if not open(f, pipefd[1], fmWrite): raise newException(Defect, "Failed to open file for console pipe.") @@ -333,7 +337,6 @@ proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], du client.userstyle = client.config.stylesheet.parseStylesheet() if not stdin.isatty: client.pager.readPipe(ctype, stdin.getFileHandle()) - stdin.close() else: client.console.tty = stdin diff --git a/src/display/pager.nim b/src/display/pager.nim index 3fdcd504..d04981a0 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -648,6 +648,10 @@ proc handleEvent*(pager: Pager, container: Container): bool = pager.setLineEdit(readLine(event.prompt, pager.statusmsg.width, current = event.value, hide = event.password, config = pager.config, tty = pager.tty), BUFFER) of OPEN: pager.gotoURL(event.request, some(container.source.location)) + of INVALID_COMMAND: + if container == pager.container: + if pager.status.len == 0: + pager.setStatusMessage("Invalid command from buffer") of NO_EVENT: discard return true diff --git a/src/ips/serialize.nim b/src/ips/serialize.nim index 82003715..2114cec0 100644 --- a/src/ips/serialize.nim +++ b/src/ips/serialize.nim @@ -11,10 +11,84 @@ import types/buffersource import types/color import types/url +proc slen*[T](o: T): int = + when T is string: + return sizeof(o.len) + o.len + elif T is bool: + return sizeof(char) + elif T is URL: + return ($o).slen + elif T is seq: + result = slen(o.len) + for x in o: + result += slen(x) + elif T is Option: + result = slen(o.isSome) + if o.isSome: + result += slen(o.get) + elif T is HeaderList: + result += slen(o.table.len) + for k, v in o.table: + result += slen(k) + result += slen(v.len) + for s in v: + result += slen(s) + elif T is MimePart: + result += slen(o.isFile) + result += slen(o.name) + result += slen(o.content) + if o.isFile: + result += slen(o.filename) + result += slen(o.contentType) + result += slen(o.fileSize) + result += slen(o.isStream) + elif T is Request: + result += slen(o.httpmethod) + result += slen(o.url) + result += slen(o.headers) + result += slen(o.body) + result += slen(o.multipart) + elif T is CellColor: + result += slen(o.rgb) + if o.rgb: + result += slen(o.rgbcolor) + else: + result += slen(o.color) + elif T is Format: + result += slen(o.fgcolor) + result += slen(o.bgcolor) + result += slen(o.flags) + elif T is SimpleFormatCell: + result += slen(o.format) + result += slen(o.pos) + elif T is SimpleFlexibleLine: + result += slen(o.str) + result += slen(o.formats) + elif T is FormatCell: + result += slen(o.format) + result += slen(o.pos) + elif T is FlexibleLine: + result += slen(o.str) + result += slen(o.formats) + elif T is Regex: + result += slen(o.plen) + result += o.plen + result += slen(o.buf) + elif T is BufferSource: + result += slen(o.t) + case o.t + of CLONE: result += slen(o.clonepid) + of LOAD_REQUEST: result += slen(o.request) + of LOAD_PIPE: result += slen(o.fd) + result += slen(o.location) + result += slen(o.contenttype) + else: + result += sizeof(o) + template swrite*[T](stream: Stream, o: T) = stream.write(o) -proc swrite*(stream: Stream, s: string) = +proc swrite*(stream: Stream, s: string, maxlen = 8192) = stream.swrite(s.len) stream.write(s) @@ -107,9 +181,11 @@ proc swrite*(stream: Stream, source: BufferSource) = template sread*[T](stream: Stream, o: T) = stream.read(o) -proc sread*(stream: Stream, s: var string) = +proc sread*(stream: Stream, s: var string, maxlen = 8192) = var len: int stream.sread(len) + if maxlen != -1: + len = min(maxlen, len) stream.readStr(len, s) proc sread*(stream: Stream, b: var bool) = @@ -123,8 +199,8 @@ proc sread*(stream: Stream, b: var bool) = proc sread*(stream: Stream, url: var Url) = var s: string - stream.sread(s) - url = parseUrl(s).get + stream.sread(s, 2048) + url = newURL(s) proc sread*(stream: Stream, headers: var HeaderList) = new(headers) diff --git a/src/ips/socketstream.nim b/src/ips/socketstream.nim index efc226bd..3918fe2c 100644 --- a/src/ips/socketstream.nim +++ b/src/ips/socketstream.nim @@ -30,6 +30,48 @@ proc sockClose(s: Stream) = {.cast(tags: []).}: #...sigh let s = SocketStream(s) s.source.close() +# See https://stackoverflow.com/a/4491203 +proc sendFileHandle*(s: SocketStream, fd: FileHandle) = + var hdr: Tmsghdr + var iov: IOVec + var cmsgbuf = alloc(CMSG_SPACE(csize_t(sizeof(FileHandle)))) + var buf = char(0) + iov.iov_base = addr buf + iov.iov_len = csize_t(1) + zeroMem(addr hdr, sizeof(hdr)) + hdr.msg_iov = addr iov + hdr.msg_iovlen = 1 + hdr.msg_control = cmsgbuf + hdr.msg_controllen = CMSG_LEN(csize_t(sizeof(FileHandle))) + let cmsg = CMSG_FIRSTHDR(addr hdr) + cmsg.cmsg_len = CMSG_LEN(csize_t(sizeof(FileHandle))) + cmsg.cmsg_level = SOL_SOCKET + cmsg.cmsg_type = SCM_RIGHTS + cast[ptr FileHandle](CMSG_DATA(cmsg))[] = fd + let n = sendmsg(s.source.getFd(), addr hdr, 0) + dealloc(cmsgbuf) + assert n == int(iov.iov_len) #TODO remove this + +proc recvFileHandle*(s: SocketStream): FileHandle = + var iov: IOVec + var hdr: Tmsghdr + let space = CMSG_SPACE(csize_t(sizeof(FileHandle))) + var buf: char + var cmsgbuf = alloc(space) + iov.iov_base = addr buf + iov.iov_len = 1 + zeroMem(addr hdr, sizeof(hdr)) + hdr.msg_iov = addr iov + hdr.msg_iovlen = 1 + hdr.msg_control = cmsgbuf + hdr.msg_controllen = space + let n = recvmsg(s.source.getFd(), addr hdr, 0) + assert n != 0, "Unexpected EOF" #TODO remove this + assert n > 0, "Failed to receive message " & $osLastError() #TODO remove this + var cmsg = CMSG_FIRSTHDR(addr hdr) + result = cast[ptr FileHandle](CMSG_DATA(cmsg))[] + dealloc(cmsgbuf) + func newSocketStream*(): SocketStream = new(result) result.readDataImpl = cast[proc (s: Stream, buffer: pointer, bufLen: int): int diff --git a/src/render/rendertext.nim b/src/render/rendertext.nim index 7a1c4417..d9cc4dfc 100644 --- a/src/render/rendertext.nim +++ b/src/render/rendertext.nim @@ -46,32 +46,33 @@ proc renderPlainText*(text: string): FlexibleGrid = if result.len > 1 and result[^1].str.len == 0 and result[^1].formats.len == 0: discard result.pop() -proc renderStream*(stream: Stream): FlexibleGrid = +proc renderStream*(grid: var FlexibleGrid, stream: Stream, len: int) = var format = newFormat() template add_format() = if af: af = false - result[result.high].addFormat(result[^1].str.len, format) + grid[grid.high].addFormat(grid[^1].str.len, format) - result.addLine() + if grid.len == 0: grid.addLine() const tabwidth = 8 var spaces = 0 var af = false - while not stream.atEnd(): + var i = 0 + while i < len: let c = stream.readChar() case c of '\n': add_format - result.addLine() + grid.addLine() of '\r': discard of '\t': add_format for i in 0 ..< tabwidth - spaces: - result[^1].str &= ' ' + grid[^1].str &= ' ' spaces = 0 of ' ': add_format - result[^1].str &= c + grid[^1].str &= c inc spaces if spaces == 8: spaces = 0 @@ -80,10 +81,11 @@ proc renderStream*(stream: Stream): FlexibleGrid = af = true elif c.isControlChar(): add_format - result[^1].str &= '^' & c.getControlLetter() + grid[^1].str &= '^' & c.getControlLetter() else: add_format - result[^1].str &= c + grid[^1].str &= c + inc i - if result.len > 1 and result[^1].str.len == 0 and result[^1].formats.len == 0: - discard result.pop() + #if grid.len > 1 and grid[^1].str.len == 0 and grid[^1].formats.len == 0: + # discard grid.pop() diff --git a/src/types/color.nim b/src/types/color.nim index c33e2b5c..9fc00edc 100644 --- a/src/types/color.nim +++ b/src/types/color.nim @@ -212,7 +212,7 @@ func parseHexColor*(s: string): Option[RGBAColor] = case s.len of 6: let c = (hexValue(s[0]) shl 20) or (hexValue(s[1]) shl 16) or - (hexValue(s[1]) shl 12) or (hexValue(s[3]) shl 8) or + (hexValue(s[2]) shl 12) or (hexValue(s[3]) shl 8) or (hexValue(s[4]) shl 4) or hexValue(s[5]) return some(RGBAColor(c)) of 8: |