From 324ed86335de9f0e0a81b61b104f6e2df2832f9d Mon Sep 17 00:00:00 2001 From: 20kdc Date: Sat, 28 Jul 2018 17:20:43 +0100 Subject: [PATCH 1/3] Update KMT to v1: be faster at reading --- claw/repo-claw.lua | 2 +- repository/apps/app-kmt.lua | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/claw/repo-claw.lua b/claw/repo-claw.lua index 752f539..24aeef8 100644 --- a/claw/repo-claw.lua +++ b/claw/repo-claw.lua @@ -91,7 +91,7 @@ return { }, ["app-kmt"] = { desc = "Line-terminal for MUDs & such", - v = 0, + v = 1, deps = { "neo", "zzz-license-pd" diff --git a/repository/apps/app-kmt.lua b/repository/apps/app-kmt.lua index 0f728a2..b36f378 100644 --- a/repository/apps/app-kmt.lua +++ b/repository/apps/app-kmt.lua @@ -113,7 +113,7 @@ while true do local e = {coroutine.yield()} if e[1] == "k.timer" then while true do - local b, e = tcp.read(1) + local b, e = tcp.read(neo.readBufSize) if not b then if e then incoming(":Warning: " .. e) @@ -123,12 +123,16 @@ while true do break elseif b == "" then break - elseif b ~= "\r" then - if b == "\n" then - incoming("<" .. tcpBuf) - tcpBuf = "" - else - tcpBuf = tcpBuf .. b + else + tcpBuf = tcpBuf .. b:gsub("\r", "") + while true do + local nlp = tcpBuf:find("\n") + if nlp then + incoming("<" .. tcpBuf:sub(1, nlp - 1)) + tcpBuf = tcpBuf:sub(nlp + 1) + else + break + end end end end From 99cb58d9fca503f58acf1e43ad31137712245a30 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Wed, 26 Sep 2018 01:46:16 +0100 Subject: [PATCH 2/3] Add a tape interface application, 'app-tapedeck' --- claw/repo-claw.lua | 17 ++ repository/apps/app-tapedeck.lua | 206 +++++++++++++++++++++++ repository/docs/repoauthors/app-tapedeck | 2 + 3 files changed, 225 insertions(+) create mode 100644 repository/apps/app-tapedeck.lua create mode 100644 repository/docs/repoauthors/app-tapedeck diff --git a/claw/repo-claw.lua b/claw/repo-claw.lua index 24aeef8..5791869 100644 --- a/claw/repo-claw.lua +++ b/claw/repo-claw.lua @@ -200,6 +200,23 @@ return { "docs/repoauthors/app-nbcompose" }, }, + ["app-tapedeck"] = { + desc = "Computronics Tape Drive interface", + v = 0, + deps = { + "neo", + "zzz-license-pd" + }, + dirs = { + "apps", + "docs", + "docs/repoauthors" + }, + files = { + "apps/app-tapedeck.lua", + "docs/repoauthors/app-tapedeck" + }, + }, ["app-launchbar"] = { desc = "Application launcher bar", v = 0, diff --git a/repository/apps/app-tapedeck.lua b/repository/apps/app-tapedeck.lua new file mode 100644 index 0000000..991b1f2 --- /dev/null +++ b/repository/apps/app-tapedeck.lua @@ -0,0 +1,206 @@ +-- This is released into the public domain. +-- No warranty is provided, implied or otherwise. + +-- app-tapedeck.lua : Computronics Tape interface. +-- Added note: Computerized record discs aren't available, so it can't be called vinylscratch. +-- Authors: 20kdc + +local tapes = {} +for v in neo.requireAccess("c.tape_drive", "tapedrives").list() do + table.insert(tapes, v) +end + +local event = require("event")(neo) +local neoux = require("neoux")(event, neo) + +-- There's no way to get these, so they get reset +local pcvals = {vol = 100, spd = 100} +local function pcbox(x, y, low, high, id, fun) + return neoux.tcfield(x, y, 5, function (tx) + if tx then + pcvals[id] = math.min(math.max(0, math.floor(tonumber(tx) or 0)), high) + fun(math.max(pcvals[id], low) / 100) + end + return tostring(pcvals[id]) + end) +end + +local window +local running = true +local focused = true + +local updateTick + +local function genPlayer(inst) + local cachedLabel = inst.getLabel() or "" + local cachedState = inst.getState() + local function pausePlay() + if inst.getState() == "PLAYING" then + inst.stop() + else + inst.play() + end + window.reset(genPlayer(inst)) + end + -- Common code for reading/writing tapes. + -- Note that it tries to allow playback to resume later. + local function rwButton(mode) + local fh = neoux.fileDialog(mode) + if not fh then return end + inst.stop() + local sp = inst.getPosition() + local tapeSize = inst.getSize() + inst.seek(-tapeSize) + local tapeReadBuf = 8192 + local tapePos = 0 + while tapePos < tapeSize do + if mode then + local data = inst.read(tapeReadBuf) + if not data then break end + tapePos = tapePos + tapeReadBuf + local res, ifo = fh.write(data) + if not res then + neoux.startDialog(tostring(ifo), "issue") + break + end + else + local data = fh.read(tapeReadBuf) + if not data then break end + tapePos = tapePos + #data + inst.write(data) + end + end + inst.seek(-tapeSize) + inst.seek(sp) + fh.close() + end + local elems = { + { + x = 1, + y = 5, + w = 20, + h = 1, + selectable = true, + line = function (w, x, y, lined, bg, fg, selected) + local lx = "" + local pos = inst.getPosition() + if inst.isReady() then + -- Show a bar + local tick = inst.getSize() / 13 + for i = 1, 13 do + local alpos = (tick * i) - (tick / 2) + if pos > alpos then + lx = lx .. "=" + else + lx = lx .. "-" + end + end + else + lx = "NO TAPE HERE." + end + local sec = pos / 4096 + lx = lx .. string.format(" %03i:%02i", math.floor(sec / 60), math.floor(sec) % 60) + if selected then bg, fg = fg, bg end + window.span(x, y, lx, bg, fg) + end, + key = function (w, update, a, b, c, kf) + local amount = 40960 + if kf.shift or kf.rshift then + amount = amount * 24 + end + if c then + if a == 32 then + pausePlay() + elseif b == 203 then + inst.seek(-amount) + update() + return true + elseif b == 205 then + inst.seek(amount) + update() + return true + end + end + end + }, + neoux.tcrawview(14, 3, { + "% Vol. ", + "% Speed" + }), + pcbox(9, 3, 0, 100, "vol", inst.setVolume), + pcbox(9, 4, 25, 200, "spd", inst.setSpeed), + neoux.tcbutton(1, 3, "{", function (w) + inst.seek(-inst.getSize()) + end), + neoux.tcbutton(5, 3, "}", function (w) + inst.seek(inst.getSize()) + end), + neoux.tcbutton(1, 4, ((inst.getState() == "PLAYING") and "Pause") or "Play", function (w) + pausePlay() + end), + -- R/W buttons + neoux.tcbutton(1, 2, "Read", function (w) + rwButton(true) + end), + neoux.tcbutton(8, 2, "Write", function (w) + rwButton(false) + end), + neoux.tcfield(1, 1, 20, function (tx) + if tx then + inst.setLabel(tx) + cachedLabel = tx + end + return cachedLabel + end) + } + updateTick = function () + local lcl = cachedLabel + cachedLabel = inst.getLabel() or "" + elems[1].update(window) + if inst.getState() ~= cachedState then + window.reset(genPlayer(inst)) + elseif lcl ~= cachedLabel then + elems[#elems].update(window) + end + end + local n = neoux.tcwindow(20, 5, elems, function (w) + updateTick = nil + running = false + w.close() + end, 0xFFFFFF, 0) + return 20, 5, inst.address, function (a, ...) + if a == "focus" then + focused = (...) or true + end + return n(a, ...) + end +end +local function genList() + local elems = {} + for k, v in ipairs(tapes) do + elems[k] = neoux.tcbutton(1, k, v.address, function (w) + window.reset(genPlayer(v)) + end) + end + tapes = nil + return 40, #elems, "choose", neoux.tcwindow(40, #elems, elems, function (w) + running = false + w.close() + end, 0xFFFFFF, 0) +end + + +window = neoux.create(genList()) + +-- Timer for time update +local function tick() + if updateTick then + updateTick() + end + event.runAt(os.uptime() + ((focused and 1) or 10), tick) +end +event.runAt(0, tick) + +while running do + event.pull() +end diff --git a/repository/docs/repoauthors/app-tapedeck b/repository/docs/repoauthors/app-tapedeck new file mode 100644 index 0000000..2d3b037 --- /dev/null +++ b/repository/docs/repoauthors/app-tapedeck @@ -0,0 +1,2 @@ +repository/apps/app-tapedeck.lua: 20kdc, Public Domain + From 71a0aa0b08bc059a24fd79bc929bb6d417b603e7 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Wed, 26 Sep 2018 22:46:03 +0100 Subject: [PATCH 3/3] Update the tape interface application to do everything it should need to be able to do --- claw/repo-claw.lua | 2 +- repository/apps/app-tapedeck.lua | 166 +++++++++++++++++++++++++------ 2 files changed, 135 insertions(+), 33 deletions(-) diff --git a/claw/repo-claw.lua b/claw/repo-claw.lua index 5791869..f4bbf3a 100644 --- a/claw/repo-claw.lua +++ b/claw/repo-claw.lua @@ -202,7 +202,7 @@ return { }, ["app-tapedeck"] = { desc = "Computronics Tape Drive interface", - v = 0, + v = 1, deps = { "neo", "zzz-license-pd" diff --git a/repository/apps/app-tapedeck.lua b/repository/apps/app-tapedeck.lua index 991b1f2..b63e769 100644 --- a/repository/apps/app-tapedeck.lua +++ b/repository/apps/app-tapedeck.lua @@ -10,6 +10,8 @@ for v in neo.requireAccess("c.tape_drive", "tapedrives").list() do table.insert(tapes, v) end +local tapeRate = 4096 + local event = require("event")(neo) local neoux = require("neoux")(event, neo) @@ -31,7 +33,92 @@ local focused = true local updateTick -local function genPlayer(inst) +local downloadCancelled = false + +local genPlayer -- used to return to player + +local function genDownloading(inst) + local lclLabelText = {"downloading..."} + local lclLabel = neoux.tcrawview(1, 1, lclLabelText) + local thr = { + "/", + "-", + "\\", + "|" + } + local thri = 0 + updateTick = function () + lclLabelText[1] = "downloading... " .. (inst.getPosition() / (1024 * 1024)) .. "MB " .. thr[(thri % #thr) + 1] + thri = thri + 1 + lclLabel.update(window) + end + return 40, 1, nil, neoux.tcwindow(40, 1, { + lclLabel + }, function (w) + downloadCancelled = true + end, 0xFFFFFF, 0) +end + +local function doINetThing(inet, url, inst) + inet = inet.list()() + assert(inet, "No available card") + inst.stop() + inst.seek(-inst.getSize()) + downloadCancelled = false + downloadPercent = 0 + window.reset(genDownloading(inst)) + local req = assert(inet.request(url)) + req.finishConnect() + local tapePos = 0 + local tapeSize = inst.getSize() + while (not downloadCancelled) and tapePos < tapeSize do + local n, n2 = req.read(neo.readBufSize) + if not n then + if n2 then + req.close() + error(n2) + end + break + elseif n == "" then + event.sleepTo(os.uptime() + 0.05) + else + inst.write(n) + tapePos = tapePos + #n + end + end + req.close() + inst.seek(-inst.getSize()) +end + +local function genWeb(inst) + updateTick = nil + local url = "" + local lockout = false + return 40, 3, nil, neoux.tcwindow(40, 3, { + neoux.tcrawview(1, 1, {"URL to write to tape?"}), + neoux.tcfield(1, 2, 40, function (t) + url = t or url + return url + end), + neoux.tcbutton(1, 3, "Download & Write", function (w) + lockout = true + local inet = neo.requestAccess("c.internet") + lockout = false + if inet then + local ok, err = pcall(doINetThing, inet, url, inst) + if not ok then + neoux.startDialog("Couldn't download: " .. tostring(err), "error") + end + end + w.reset(genPlayer(inst)) + end) + }, function (w) + w.reset(genPlayer(inst)) + end, 0xFFFFFF, 0) +end + +-- The actual main UI -- +genPlayer = function (inst) local cachedLabel = inst.getLabel() or "" local cachedState = inst.getState() local function pausePlay() @@ -51,20 +138,19 @@ local function genPlayer(inst) local sp = inst.getPosition() local tapeSize = inst.getSize() inst.seek(-tapeSize) - local tapeReadBuf = 8192 local tapePos = 0 while tapePos < tapeSize do if mode then - local data = inst.read(tapeReadBuf) + local data = inst.read(neo.readBufSize) if not data then break end - tapePos = tapePos + tapeReadBuf + tapePos = tapePos + #data local res, ifo = fh.write(data) if not res then neoux.startDialog(tostring(ifo), "issue") break end else - local data = fh.read(tapeReadBuf) + local data = fh.read(neo.readBufSize) if not data then break end tapePos = tapePos + #data inst.write(data) @@ -75,19 +161,31 @@ local function genPlayer(inst) fh.close() end local elems = { + neoux.tcrawview(1, 1, { + "Label:", + "Contents:" + }), + neoux.tcfield(7, 1, 34, function (tx) + if tx then + inst.setLabel(tx) + cachedLabel = tx + end + return cachedLabel + end), { x = 1, y = 5, - w = 20, + w = 40, h = 1, selectable = true, line = function (w, x, y, lined, bg, fg, selected) local lx = "" local pos = inst.getPosition() + local sz = inst.getSize() if inst.isReady() then -- Show a bar - local tick = inst.getSize() / 13 - for i = 1, 13 do + local tick = sz / 23 + for i = 1, 23 do local alpos = (tick * i) - (tick / 2) if pos > alpos then lx = lx .. "=" @@ -98,13 +196,16 @@ local function genPlayer(inst) else lx = "NO TAPE HERE." end - local sec = pos / 4096 - lx = lx .. string.format(" %03i:%02i", math.floor(sec / 60), math.floor(sec) % 60) + local sec = pos / tapeRate + local secz = sz / tapeRate + lx = lx .. string.format(" %03i:%02i / %03i:%02i ", + math.floor(sec / 60), math.floor(sec) % 60, + math.floor(secz / 60), math.floor(secz) % 60) if selected then bg, fg = fg, bg end window.span(x, y, lx, bg, fg) end, key = function (w, update, a, b, c, kf) - local amount = 40960 + local amount = tapeRate * 10 if kf.shift or kf.rshift then amount = amount * 24 end @@ -123,52 +224,53 @@ local function genPlayer(inst) end end }, - neoux.tcrawview(14, 3, { - "% Vol. ", + neoux.tcrawview(33, 3, { + "% Volume" + }), + neoux.tcrawview(20, 3, { "% Speed" }), - pcbox(9, 3, 0, 100, "vol", inst.setVolume), - pcbox(9, 4, 25, 200, "spd", inst.setSpeed), - neoux.tcbutton(1, 3, "{", function (w) + pcbox(15, 3, 25, 200, "spd", inst.setSpeed), + pcbox(28, 3, 0, 100, "vol", inst.setVolume), + neoux.tcrawview(1, 4, { + "Seeker: use ◃/▹ (shift goes faster)" + }), + neoux.tcbutton(1, 3, "«", function (w) inst.seek(-inst.getSize()) end), - neoux.tcbutton(5, 3, "}", function (w) + neoux.tcbutton(11, 3, "»", function (w) inst.seek(inst.getSize()) end), - neoux.tcbutton(1, 4, ((inst.getState() == "PLAYING") and "Pause") or "Play", function (w) + neoux.tcbutton(4, 3, ((inst.getState() == "PLAYING") and "Pause") or "Play", function (w) pausePlay() end), -- R/W buttons - neoux.tcbutton(1, 2, "Read", function (w) + neoux.tcbutton(11, 2, "Read", function (w) rwButton(true) end), - neoux.tcbutton(8, 2, "Write", function (w) + neoux.tcbutton(17, 2, "Write", function (w) rwButton(false) end), - neoux.tcfield(1, 1, 20, function (tx) - if tx then - inst.setLabel(tx) - cachedLabel = tx - end - return cachedLabel + neoux.tcbutton(24, 2, "Write From Web", function (w) + w.reset(genWeb(inst)) end) } updateTick = function () local lcl = cachedLabel cachedLabel = inst.getLabel() or "" - elems[1].update(window) + elems[3].update(window) if inst.getState() ~= cachedState then window.reset(genPlayer(inst)) elseif lcl ~= cachedLabel then - elems[#elems].update(window) + elems[2].update(window) end end - local n = neoux.tcwindow(20, 5, elems, function (w) + local n = neoux.tcwindow(40, 5, elems, function (w) updateTick = nil running = false w.close() end, 0xFFFFFF, 0) - return 20, 5, inst.address, function (a, ...) + return 40, 5, inst.address, function (a, ...) if a == "focus" then focused = (...) or true end @@ -178,12 +280,12 @@ end local function genList() local elems = {} for k, v in ipairs(tapes) do - elems[k] = neoux.tcbutton(1, k, v.address, function (w) + elems[k] = neoux.tcbutton(1, k, v.address:sub(1, 38), function (w) window.reset(genPlayer(v)) end) end tapes = nil - return 40, #elems, "choose", neoux.tcwindow(40, #elems, elems, function (w) + return 40, #elems, nil, neoux.tcwindow(40, #elems, elems, function (w) running = false w.close() end, 0xFFFFFF, 0)