Merge branch 'develop'

This commit is contained in:
kepler155c@gmail.com 2016-12-27 16:01:30 -05:00
commit 482c52b36f
27 changed files with 2014 additions and 753 deletions

View File

@ -35,26 +35,32 @@ local Browser = UI.Page {
}, },
fileMenu = UI.DropMenu { fileMenu = UI.DropMenu {
buttons = { buttons = {
{ text = 'Run', event = 'run' }, { text = 'Run', event = 'run' },
{ text = 'Edit e', event = 'edit' }, { text = 'Edit e', event = 'edit' },
{ text = 'Shell s', event = 'shell' }, { text = 'Shell s', event = 'shell' },
{ text = 'Quit q', event = 'quit' }, UI.Text { value = ' ------------ ' },
{ text = 'Quit q', event = 'quit' },
UI.Text { },
} }
}, },
editMenu = UI.DropMenu { editMenu = UI.DropMenu {
buttons = { buttons = {
{ text = 'Mark m', event = 'mark' }, { text = 'Cut ^x', event = 'cut' },
{ text = 'Cut ^x', event = 'cut' }, { text = 'Copy ^c', event = 'copy' },
{ text = 'Copy ^c', event = 'copy' }, { text = 'Paste ^v', event = 'paste' },
{ text = 'Paste ^v', event = 'paste' }, UI.Text { value = ' --------------- ' },
{ text = 'Delete del', event = 'delete' }, { text = 'Mark m', event = 'mark' },
{ text = 'Unmark all u', event = 'unmark' }, { text = 'Unmark all u', event = 'unmark' },
UI.Text { value = ' --------------- ' },
{ text = 'Delete del', event = 'delete' },
UI.Text { },
} }
}, },
viewMenu = UI.DropMenu { viewMenu = UI.DropMenu {
buttons = { buttons = {
{ text = 'Refresh r', event = 'refresh' }, { text = 'Refresh r', event = 'refresh' },
{ text = 'Hidden ^h', event = 'toggle_hidden' }, { text = 'Hidden ^h', event = 'toggle_hidden' },
UI.Text { },
} }
}, },
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
@ -69,8 +75,8 @@ local Browser = UI.Page {
}, },
statusBar = UI.StatusBar { statusBar = UI.StatusBar {
columns = { columns = {
{ '', 'status', UI.term.width - 19 }, { '', 'status', UI.term.width - 8 },
{ '', 'info', 10 }, --{ '', 'info', 10 },
{ 'Size: ', 'totalSize', 8 }, { 'Size: ', 'totalSize', 8 },
}, },
}, },

View File

@ -1,4 +1,5 @@
require = requireInjector(getfenv(1)) local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local Util = require('util') local Util = require('util')
local UI = require('ui') local UI = require('ui')
local Event = require('event') local Event = require('event')
@ -6,7 +7,7 @@ local History = require('history')
local sandboxEnv = Util.shallowCopy(getfenv(1)) local sandboxEnv = Util.shallowCopy(getfenv(1))
sandboxEnv.exit = function() Event.exitPullEvents() end sandboxEnv.exit = function() Event.exitPullEvents() end
sandboxEnv.require = requireInjector(sandboxEnv) sandboxEnv.require = injector(sandboxEnv)
setmetatable(sandboxEnv, { __index = _G }) setmetatable(sandboxEnv, { __index = _G })
multishell.setTitle(multishell.getCurrent(), 'Lua') multishell.setTitle(multishell.getCurrent(), 'Lua')
@ -15,12 +16,12 @@ UI:configure('Lua', ...)
local command = '' local command = ''
local history = History.load('.lua_history', 25) local history = History.load('.lua_history', 25)
local resultsPage = UI.Page({ local page = UI.Page({
menuBar = UI.MenuBar({ menuBar = UI.MenuBar({
buttons = { buttons = {
{ text = 'Local', event = 'local' }, { text = 'Local', event = 'local' },
{ text = 'Global', event = 'global' }, { text = 'Global', event = 'global' },
{ text = 'Device', event = 'device' }, { text = 'Device', event = 'device', name = 'Device' },
}, },
}), }),
prompt = UI.TextEntry({ prompt = UI.TextEntry({
@ -29,10 +30,11 @@ local resultsPage = UI.Page({
backgroundFocusColor = colors.black, backgroundFocusColor = colors.black,
limit = 256, limit = 256,
accelerators = { accelerators = {
enter = 'command_enter', enter = 'command_enter',
up = 'history_back', up = 'history_back',
down = 'history_forward', down = 'history_forward',
mouse_rightclick = 'clear_prompt', mouse_rightclick = 'clear_prompt',
-- [ 'control-space' ] = 'autocomplete',
}, },
}), }),
grid = UI.ScrollingGrid({ grid = UI.ScrollingGrid({
@ -47,7 +49,7 @@ local resultsPage = UI.Page({
notification = UI.Notification(), notification = UI.Notification(),
}) })
function resultsPage:setPrompt(value, focus) function page:setPrompt(value, focus)
self.prompt:setValue(value) self.prompt:setValue(value)
self.prompt.scroll = 0 self.prompt.scroll = 0
self.prompt:setPosition(#value) self.prompt:setPosition(#value)
@ -59,29 +61,77 @@ function resultsPage:setPrompt(value, focus)
self.prompt:draw() self.prompt:draw()
if focus then if focus then
resultsPage:setFocus(self.prompt) page:setFocus(self.prompt)
end end
end end
function resultsPage:enable() function page:enable()
self:setFocus(self.prompt) self:setFocus(self.prompt)
UI.Page.enable(self) UI.Page.enable(self)
if not device then
self.menuBar.Device:disable()
end
end end
function resultsPage:eventHandler(event) local function autocomplete(env, oLine, x)
local sLine = oLine:sub(1, x)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
if #sLine > 0 then
local results = textutils.complete(sLine, env)
if #results == 0 then
-- setError('No completions available')
elseif #results == 1 then
return Util.insertString(oLine, results[1], x + 1)
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = results[n]
while #prefix > 0 do
if result:find(prefix, 1, true) == 1 then
break
end
prefix = prefix:sub(1, #prefix - 1)
end
end
if #prefix > 0 then
return Util.insertString(oLine, prefix, x + 1)
else
-- setStatus('Too many results')
end
end
end
return oLine
end
function page:eventHandler(event)
if event.type == 'global' then if event.type == 'global' then
resultsPage:setPrompt('', true) self:setPrompt('', true)
self:executeStatement('getfenv(0)') self:executeStatement('getfenv(0)')
command = nil command = nil
elseif event.type == 'local' then elseif event.type == 'local' then
resultsPage:setPrompt('', true) self:setPrompt('', true)
self:executeStatement('getfenv(1)') self:executeStatement('getfenv(1)')
command = nil command = nil
elseif event.type == 'autocomplete' then
local sz = #self.prompt.value
local pos = self.prompt.pos
self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.pos))
self.prompt:setPosition(pos + #self.prompt.value - sz)
self.prompt:updateCursor()
elseif event.type == 'device' then elseif event.type == 'device' then
resultsPage:setPrompt('device', true) self:setPrompt('device', true)
self:executeStatement('device') self:executeStatement('device')
elseif event.type == 'history_back' then elseif event.type == 'history_back' then
@ -128,7 +178,7 @@ function resultsPage:eventHandler(event)
return true return true
end end
function resultsPage:setResult(result) function page:setResult(result)
local t = { } local t = { }
local function safeValue(v) local function safeValue(v)
@ -169,7 +219,7 @@ function resultsPage:setResult(result)
self:draw() self:draw()
end end
function resultsPage.grid:eventHandler(event) function page.grid:eventHandler(event)
local entry = self:getSelected() local entry = self:getSelected()
@ -199,18 +249,18 @@ function resultsPage.grid:eventHandler(event)
if event.type == 'grid_focus_row' then if event.type == 'grid_focus_row' then
if self.focused then if self.focused then
resultsPage:setPrompt(commandAppend()) page:setPrompt(commandAppend())
end end
elseif event.type == 'grid_select' then elseif event.type == 'grid_select' then
resultsPage:setPrompt(commandAppend(), true) page:setPrompt(commandAppend(), true)
resultsPage:executeStatement(commandAppend()) page:executeStatement(commandAppend())
else else
return UI.Grid.eventHandler(self, event) return UI.Grid.eventHandler(self, event)
end end
return true return true
end end
function resultsPage:rawExecute(s) function page:rawExecute(s)
local fn, m = loadstring("return (" .. s .. ')', 'lua') local fn, m = loadstring("return (" .. s .. ')', 'lua')
if not fn then if not fn then
@ -225,7 +275,7 @@ function resultsPage:rawExecute(s)
return fn, m return fn, m
end end
function resultsPage:executeStatement(statement) function page:executeStatement(statement)
command = statement command = statement
@ -233,6 +283,8 @@ function resultsPage:executeStatement(statement)
if s and m then if s and m then
self:setResult(m) self:setResult(m)
elseif s and type(m) == 'boolean' then
self:setResult(m)
else else
self.grid:setValues({ }) self.grid:setValues({ })
self.grid:draw() self.grid:draw()
@ -242,6 +294,13 @@ function resultsPage:executeStatement(statement)
end end
end end
UI:setPage(resultsPage) local args = { ... }
if args[1] then
command = 'args[1]'
sandboxEnv.args = args
page:setResult(args[1])
end
UI:setPage(page)
Event.pullEvents() Event.pullEvents()
UI.term:reset() UI.term:reset()

View File

@ -7,37 +7,37 @@ multishell.setTitle(multishell.getCurrent(), 'Network')
UI:configure('Network', ...) UI:configure('Network', ...)
local gridColumns = { local gridColumns = {
{ heading = 'Label', key = 'label' }, { heading = 'Label', key = 'label' },
{ heading = 'Dist', key = 'distance' }, { heading = 'Dist', key = 'distance' },
{ heading = 'Status', key = 'status' }, { heading = 'Status', key = 'status' },
} }
if UI.term.width >= 30 then if UI.term.width >= 30 then
table.insert(gridColumns, { heading = 'Fuel', key = 'fuel' }) table.insert(gridColumns, { heading = 'Fuel', key = 'fuel' })
table.insert(gridColumns, { heading = 'Uptime', key = 'uptime' }) table.insert(gridColumns, { heading = 'Uptime', key = 'uptime' })
end end
local page = UI.Page({ local page = UI.Page {
menuBar = UI.MenuBar({ menuBar = UI.MenuBar {
buttons = { buttons = {
{ text = 'Telnet', event = 'telnet' }, { text = 'Telnet', event = 'telnet' },
{ text = 'VNC', event = 'vnc' }, { text = 'VNC', event = 'vnc' },
{ text = 'Reboot', event = 'reboot' }, { text = 'Reboot', event = 'reboot' },
}, },
}), },
grid = UI.ScrollingGrid({ grid = UI.ScrollingGrid {
y = 2, y = 2,
values = network, values = network,
columns = gridColumns, columns = gridColumns,
sortColumn = 'label', sortColumn = 'label',
autospace = true, autospace = true,
}), },
notification = UI.Notification(), notification = UI.Notification { },
accelerators = { accelerators = {
q = 'quit', q = 'quit',
c = 'clear', c = 'clear',
}, },
}) }
function sendCommand(host, command) function sendCommand(host, command)
@ -60,7 +60,7 @@ function sendCommand(host, command)
end end
function page:eventHandler(event) function page:eventHandler(event)
local t = self.grid.selected local t = self.grid:getSelected()
if t then if t then
if event.type == 'telnet' or event.type == 'grid_select' then if event.type == 'telnet' or event.type == 'grid_select' then
multishell.openTab({ multishell.openTab({
@ -113,22 +113,14 @@ function page.grid:getDisplayValues(row)
return row return row
end end
function page.grid:draw() Event.addThread(function()
self:adjustWidth()
UI.Grid.draw(self)
if page.notification.enabled then
page.notification:draw()
end
end
function updateComputers()
while true do while true do
page.grid:update() page.grid:update()
page.grid:draw() page.grid:draw()
page:sync() page:sync()
os.sleep(1) os.sleep(1)
end end
end end)
Event.addHandler('device_attach', function(h, deviceName) Event.addHandler('device_attach', function(h, deviceName)
if deviceName == 'wireless_modem' then if deviceName == 'wireless_modem' then
@ -149,5 +141,5 @@ if not device.wireless_modem then
end end
UI:setPage(page) UI:setPage(page)
Event.pullEvents(updateComputers) Event.pullEvents()
UI.term:reset() UI.term:reset()

View File

@ -6,6 +6,7 @@ local Config = require('config')
local NFT = require('nft') local NFT = require('nft')
local class = require('class') local class = require('class')
local FileUI = require('fileui') local FileUI = require('fileui')
local Tween = require('tween')
multishell.setTitle(multishell.getCurrent(), 'Overview') multishell.setTitle(multishell.getCurrent(), 'Overview')
UI:configure('Overview', ...) UI:configure('Overview', ...)
@ -190,8 +191,9 @@ function page.container:setCategory(categoryName)
-- reposition all children -- reposition all children
for k,child in ipairs(self.children) do for k,child in ipairs(self.children) do
child.x = col child.x = -10
child.y = row child.y = math.floor(self.height)
child.tween = Tween.new(6, child, { x = col, y = row }, 'outSine')
if k < count then if k < count then
col = col + child.width col = col + child.width
@ -203,6 +205,25 @@ function page.container:setCategory(categoryName)
end end
self:initChildren() self:initChildren()
self.animate = true
end
function page.container:draw()
if self.animate then
self.animate = false
for i = 1, 6 do
for _,child in ipairs(self.children) do
child.tween:update(1)
child.x = math.floor(child.x)
child.y = math.floor(child.y)
end
UI.ViewportWindow.draw(self)
self:sync()
os.sleep()
end
else
UI.ViewportWindow.draw(self)
end
end end
function page:refresh() function page:refresh()
@ -223,6 +244,8 @@ function page:eventHandler(event)
self.tabBar:selectTab(event.button.text) self.tabBar:selectTab(event.button.text)
self.container:setCategory(event.button.text) self.container:setCategory(event.button.text)
self.container:draw() self.container:draw()
self:sync()
config.currentCategory = event.button.text config.currentCategory = event.button.text
Config.update('Overview', config) Config.update('Overview', config)
@ -263,9 +286,9 @@ function page:eventHandler(event)
elseif event.type == 'tab_change' then elseif event.type == 'tab_change' then
if event.current > event.last then if event.current > event.last then
self.container:setTransition('left') --self.container:setTransition(UI.effect.slideLeft)
else else
self.container:setTransition('right') --self.container:setTransition(UI.effect.slideRight)
end end
elseif event.type == 'refresh' then elseif event.type == 'refresh' then
@ -308,61 +331,53 @@ function page:eventHandler(event)
end end
local formWidth = math.max(UI.term.width - 14, 26) local formWidth = math.max(UI.term.width - 14, 26)
local gutter = math.floor((UI.term.width - formWidth) / 2) + 1
local editor = UI.Page({ local editor = UI.Dialog {
backgroundColor = colors.blue, height = 11,
form = UI.Form({ width = formWidth,
fields = { title = 'Edit application',
{ label = 'Title', key = 'title', width = 15, limit = 11, display = UI.Form.D.entry, form = UI.Form {
help = 'Application title' }, y = 2,
{ label = 'Run', key = 'run', width = formWidth - 11, limit = 100, display = UI.Form.D.entry,
help = 'Full path to application' },
{ label = 'Category', key = 'category', width = 15, limit = 11, display = UI.Form.D.entry,
help = 'Category of application' },
{ text = 'Accept', event = 'accept', display = UI.Form.D.button,
x = 1, y = 9, width = 10 },
{ text = 'Cancel', event = 'cancel', display = UI.Form.D.button,
x = formWidth - 11, y = 9, width = 10 },
},
labelWidth = 8,
x = gutter + 1,
y = math.max(2, math.floor((UI.term.height - 9) / 2)),
height = 9, height = 9,
width = UI.term.width - (gutter * 2), title = UI.TextEntry {
image = UI.NftImage({ formLabel = 'Title', formKey = 'title', limit = 11, help = 'Application title',
y = 5, required = true,
x = 1, },
run = UI.TextEntry {
formLabel = 'Run', formKey = 'run', limit = 100, help = 'Full path to application',
required = true,
},
category = UI.TextEntry {
formLabel = 'Category', formKey = 'category', limit = 11, help = 'Category of application',
required = true,
},
loadIcon = UI.Button {
x = 11, y = 6,
text = 'Icon', event = 'loadIcon', help = 'Select icon'
},
image = UI.NftImage {
y = 6,
x = 2,
height = 3, height = 3,
width = 8, width = 8,
}), },
button = UI.Button({ },
x = 10,
y = 6,
text = 'Load icon',
width = 11,
event = 'loadIcon',
}),
}),
statusBar = UI.StatusBar(), statusBar = UI.StatusBar(),
notification = UI.Notification(),
iconFile = '', iconFile = '',
}) }
function editor:enable(app) function editor:enable(app)
if app then if app then
self.original = app self.form:setValues(app)
self.form:setValues(Util.shallowCopy(app))
local icon local icon
if app.icon then if app.icon then
icon = parseIcon(app.icon) icon = parseIcon(app.icon)
end end
self.form.image:setImage(icon) self.form.image:setImage(icon)
self:setFocus(self.form.children[1])
end end
UI.Page.enable(self) UI.Page.enable(self)
self:focusFirst()
end end
function editor.form.image:draw() function editor.form.image:draw()
@ -370,11 +385,11 @@ function editor.form.image:draw()
UI.NftImage.draw(self) UI.NftImage.draw(self)
end end
function editor:updateApplications(app, original) function editor:updateApplications(app)
if original.run then for k,v in pairs(applications) do
local _,k = Util.find(applications, 'run', original.run) if v == app then
if k then
applications[k] = nil applications[k] = nil
break
end end
end end
table.insert(applications, app) table.insert(applications, app)
@ -383,7 +398,7 @@ end
function editor:eventHandler(event) function editor:eventHandler(event)
if event.type == 'cancel' then if event.type == 'form_cancel' or event.type == 'cancel' then
UI:setPreviousPage() UI:setPreviousPage()
elseif event.type == 'focus_change' then elseif event.type == 'focus_change' then
@ -391,7 +406,15 @@ function editor:eventHandler(event)
self.statusBar:draw() self.statusBar:draw()
elseif event.type == 'loadIcon' then elseif event.type == 'loadIcon' then
UI:setPage(FileUI(), fs.getDir(self.iconFile), function(fileName) local fileui = FileUI({
x = self.x,
y = self.y,
z = 2,
width = self.width,
height = self.height,
})
--fileui:setTransition(UI.effect.explode)
UI:setPage(fileui, fs.getDir(self.iconFile), function(fileName)
if fileName then if fileName then
self.iconFile = fileName self.iconFile = fileName
local s, m = pcall(function() local s, m = pcall(function()
@ -408,23 +431,21 @@ function editor:eventHandler(event)
self.form.image:draw() self.form.image:draw()
end) end)
if not s and m then if not s and m then
self.notification:error(m:gsub('.*: (.*)', '%1')) local msg = m:gsub('.*: (.*)', '%1')
page.notification:error(msg)
end end
end end
end) end)
elseif event.type == 'accept' then elseif event.type == 'form_invalid' then
page.notification:error(event.message)
elseif event.type == 'form_complete' then
local values = self.form.values local values = self.form.values
if #values.run > 0 and #values.title > 0 and #values.category > 0 then UI:setPreviousPage()
UI:setPreviousPage() self:updateApplications(values)
self:updateApplications(values, self.original) page:refresh()
page:refresh() page:draw()
page:draw()
else
self.notification:error('Require fields missing')
--self.statusBar:setStatus('Require fields missing')
--self.statusBar:draw()
end
else else
return UI.Page.eventHandler(self, event) return UI.Page.eventHandler(self, event)
end end

View File

@ -1,4 +1,5 @@
require = requireInjector(getfenv(1)) local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local Util = require('util') local Util = require('util')
local Event = require('event') local Event = require('event')
local UI = require('ui') local UI = require('ui')

View File

@ -8,7 +8,7 @@ local GROUPS_PATH = '/apps/groups'
local SCRIPTS_PATH = '/apps/scripts' local SCRIPTS_PATH = '/apps/scripts'
multishell.setTitle(multishell.getCurrent(), 'Script') multishell.setTitle(multishell.getCurrent(), 'Script')
UI:configure('Script', ...) UI:configure('script', ...)
local config = { local config = {
showGroups = false, showGroups = false,
@ -17,7 +17,7 @@ local config = {
}]], }]],
} }
Config.load('Script', config) Config.load('script', config)
local width = math.floor(UI.term.width / 2) - 1 local width = math.floor(UI.term.width / 2) - 1
if UI.term.width % 2 ~= 0 then if UI.term.width % 2 ~= 0 then
@ -453,7 +453,7 @@ function mainPage:eventHandler(event)
-- self.statusBar.toggleButton.text = text -- self.statusBar.toggleButton.text = text
self:draw() self:draw()
Config.update('Script', config) Config.update('script', config)
elseif event.type == 'grid_focus_row' then elseif event.type == 'grid_focus_row' then
local computer = self.computers:getSelected() local computer = self.computers:getSelected()

View File

@ -75,7 +75,7 @@ local systemPage = UI.Page {
value = 'Label' value = 'Label'
}, },
label = UI.TextEntry { label = UI.TextEntry {
x = 9, y = 2, rex = -12, x = 9, y = 2, rex = -4,
limit = 32, limit = 32,
value = os.getComputerLabel(), value = os.getComputerLabel(),
backgroundFocusColor = colors.black, backgroundFocusColor = colors.black,

View File

@ -62,11 +62,6 @@ function page.grid:getDisplayValues(row)
return row return row
end end
function page.grid:draw()
self:adjustWidth()
UI.Grid.draw(self)
end
Event.addTimer(1, true, function() Event.addTimer(1, true, function()
page.grid:update() page.grid:update()
page.grid:draw() page.grid:draw()

View File

@ -33,6 +33,12 @@ local clipboard = { size, internal }
local searchPattern local searchPattern
local undo = { chain = { }, pointer = 0 } local undo = { chain = { }, pointer = 0 }
if _G.__CLIPBOARD then
clipboard = _G.__CLIPBOARD
else
_G.__CLIPBOARD = clipboard
end
local color = { local color = {
textColor = '0', textColor = '0',
keywordColor = '4', keywordColor = '4',
@ -66,7 +72,7 @@ local keyMapping = {
pageUp = 'pageUp', pageUp = 'pageUp',
[ 'control-b' ] = 'pageUp', [ 'control-b' ] = 'pageUp',
pageDown = 'pageDown', pageDown = 'pageDown',
[ 'control-f' ] = 'pageDown', -- [ 'control-f' ] = 'pageDown',
home = 'home', home = 'home',
[ 'end' ] = 'toend', [ 'end' ] = 'toend',
[ 'control-home' ] = 'top', [ 'control-home' ] = 'top',
@ -101,6 +107,7 @@ local keyMapping = {
paste = 'paste', paste = 'paste',
tab = 'tab', tab = 'tab',
[ 'control-z' ] = 'undo', [ 'control-z' ] = 'undo',
[ 'control-space' ] = 'autocomplete',
-- copy/paste -- copy/paste
[ 'control-x' ] = 'cut', [ 'control-x' ] = 'cut',
@ -114,6 +121,7 @@ local keyMapping = {
[ 'control-enter' ] = 'run', [ 'control-enter' ] = 'run',
-- search -- search
[ 'control-f' ] = 'find_prompt',
[ 'control-slash' ] = 'find_prompt', [ 'control-slash' ] = 'find_prompt',
[ 'control-n' ] = 'find_next', [ 'control-n' ] = 'find_next',
@ -476,6 +484,41 @@ local __actions = {
end end
end, end,
autocomplete = function()
local sLine = tLines[y]:sub(1, x - 1)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
if #sLine > 0 then
local results = textutils.complete(sLine)
if #results == 0 then
setError('No completions available')
elseif #results == 1 then
actions.insertText(x, y, results[1])
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = results[n]
while #prefix > 0 do
if result:find(prefix, 1, true) == 1 then
break
end
prefix = prefix:sub(1, #prefix - 1)
end
end
if #prefix > 0 then
actions.insertText(x, y, prefix)
else
setStatus('Too many results')
end
end
end
end,
refresh = function() refresh = function()
actions.dirty_all() actions.dirty_all()
mark.continue = mark.active mark.continue = mark.active
@ -528,7 +571,7 @@ local __actions = {
find_prompt = function() find_prompt = function()
local text = actions.input('/') local text = actions.input('/')
if #text > 0 then if #text > 0 then
searchPattern = text searchPattern = text:lower()
if searchPattern then if searchPattern then
actions.unmark() actions.unmark()
actions.find(searchPattern, x) actions.find(searchPattern, x)

View File

@ -14,12 +14,17 @@ local version = "Version 1.1.6"
-- Original code by Bomb Bloke -- Original code by Bomb Bloke
-- Modified to integrate with opus os -- Modified to integrate with opus os
local calls, recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput, callCount, callListCount = {{["delay"] = 0}}, {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, "", 1, 2 local recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput = {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, ""
local curBlink, oldBlink, curCalls, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, calls[1], {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, term.getSize() local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize()
local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"} local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"}
local charW, charH, chars, resp local charW, charH, chars, resp
local filename local filename
local calls = { }
local curCalls = { delay = 0 }
local callListCount = 0
local callCount = 0
local function showSyntax() local function showSyntax()
print('Gif Recorder by Bomb Bloke\n') print('Gif Recorder by Bomb Bloke\n')
print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename') print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename')
@ -123,13 +128,14 @@ recTerm = multishell.term
for key, func in pairs(oldTerm) do for key, func in pairs(oldTerm) do
recTerm[key] = function(...) recTerm[key] = function(...)
local result = {pcall(func, ...)} local result = { func(...) }
if result[1] then if callCount == 0 then
curCalls[callCount] = {key, ...} os.queueEvent('capture_frame')
callCount = callCount + 1 end
return unpack(result, 2) callCount = callCount + 1
else error(result[2], 2) end curCalls[callCount] = { key, ... }
return unpack(result)
end end
end end
@ -149,36 +155,27 @@ for _,tab in pairs(tabs) do
end end
end end
do local curTime = os.clock() - 1
local curTime = os.clock() - 1
while true do while true do
local event = { os.pullEventRaw() } local event = { os.pullEventRaw() }
if event[1] == 'recorder_stop' or event[1] == 'terminate' then if event[1] == 'recorder_stop' or event[1] == 'terminate' then
break break
end end
if event[1] == 'capture_frame' then
local newTime = os.clock() local newTime = os.clock()
if newTime ~= curTime then if callListCount > 0 then
local delay = curCalls.delay + (newTime - curTime) calls[callListCount].delay = (newTime - curTime)
curTime = newTime
if callCount > 1 then
curCalls.delay = curCalls.delay + delay
curCalls, callCount = {["delay"] = 0}, 1
calls[callListCount] = curCalls
callListCount = callListCount + 1
elseif callListCount > 2 then
calls[callListCount - 2].delay = calls[callListCount - 2].delay + delay
end
end end
if showInput and (event[1] == "key" or event[1] == "mouse_click") then curTime = newTime
curCalls[callCount] = {unpack(event)} callListCount = callListCount + 1
callCount = callCount + 1 calls[callListCount] = curCalls
end
curCalls, callCount = { delay = 0 }, 0
end end
end end
@ -196,8 +193,6 @@ if skipLast and #calls > 1 then calls[#calls] = nil end
calls[#calls].delay = lastDelay calls[#calls].delay = lastDelay
-- Recording done, bug user as to whether to encode it:
print(string.format("Encoding %d frames...", #calls)) print(string.format("Encoding %d frames...", #calls))
--Util.writeTable('tmp/raw.txt', calls) --Util.writeTable('tmp/raw.txt', calls)
@ -463,7 +458,14 @@ for i = 1, #calls do
oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos
local thisFrame = {["xstart"] = (xMin - 1) * charW, ["ystart"] = (yMin - 1) * charH, ["xend"] = (xMax - xMin + 1) * charW, ["yend"] = (yMax - yMin + 1) * charH, ["delay"] = curCalls.delay, ["disposal"] = 1} local thisFrame = {
["xstart"] = (xMin - 1) * charW,
["ystart"] = (yMin - 1) * charH,
["xend"] = (xMax - xMin + 1) * charW,
["yend"] = (yMax - yMin + 1) * charH,
["delay"] = curCalls.delay,
["disposal"] = 1
}
for y = 1, (yMax - yMin + 1) * charH do for y = 1, (yMax - yMin + 1) * charH do
local row = {} local row = {}
@ -515,7 +517,11 @@ for i = 1, #calls do
snooze() snooze()
end end
if changed then image[#image + 1] = thisFrame else image[#image].delay = image[#image].delay + curCalls.delay end if changed then
image[#image + 1] = thisFrame
else
image[#image].delay = image[#image].delay + curCalls.delay
end
end end
buffer = nil buffer = nil

View File

@ -212,7 +212,7 @@ function enderChestUnload()
turtle.select(1) turtle.select(1)
turtle.drop(64) turtle.drop(64)
turtle.digDown() turtle.digDown()
end end
end end
@ -246,8 +246,9 @@ function makeWalkableTunnel(action, tpt, pt)
if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source
if not Point.compare(tpt, pt) then -- not at dest if not Point.compare(tpt, pt) then -- not at dest
local r, block = turtle.inspectUp() local r, block = turtle.inspectUp()
if r and block.name ~= 'minecraft:cobblestone' then if r and not turtle.isTurtleAtSide('top') then
if block.name ~= 'minecraft:chest' then if block.name ~= 'minecraft:cobblestone' and
block.name ~= 'minecraft:chest' then
turtle.digUp() turtle.digUp()
end end
end end
@ -483,7 +484,10 @@ function boreCommand()
turtle.clearMoveCallback() turtle.clearMoveCallback()
-- location is either mined, currently being mined or is the
-- dropoff point for a turtle
if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or
inspect(turtle.getAction('up'), 'minecraft:chest') or
inspect(turtle.getAction('down'), 'minecraft:cobblestone') then inspect(turtle.getAction('down'), 'minecraft:cobblestone') then
return true return true
end end

View File

@ -452,51 +452,54 @@ function watchResources(items)
return itemList return itemList
end end
itemPage = UI.Page({ itemPage = UI.Page {
backgroundColor = colors.lightGray, backgroundColor = colors.lightGray,
titleBar = UI.TitleBar({ titleBar = UI.TitleBar {
title = 'Limit Resource', title = 'Limit Resource',
previousPage = true, previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green backgroundColor = colors.green
}), },
idField = UI.Text({ idField = UI.Text {
x = 5, x = 5, y = 3, width = UI.term.width - 10,
y = 3, },
width = UI.term.width - 10 form = UI.Form {
}), x = 4, y = 4, height = 8, rex = -4,
form = UI.Form({ [1] = UI.TextEntry {
fields = { width = 7,
{ label = 'Min', key = 'low', width = 7, display = UI.Form.D.entry, backgroundColor = colors.gray,
help = 'Craft if below min' }, backgroundFocusColor = colors.gray,
{ label = 'Max', key = 'limit', width = 7, display = UI.Form.D.entry, formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
validation = UI.Form.V.number, dataType = UI.Form.T.number,
help = 'Eject if above max' },
{ label = 'Autocraft', key = 'auto', width = 7, display = UI.Form.D.chooser,
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Craft until out of ingredients' },
{ label = 'Ignore Dmg', key = 'ignore_dmg', width = 7, display = UI.Form.D.chooser,
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Ignore damage of item' },
{ text = 'Accept', event = 'accept', display = UI.Form.D.button,
x = 1, y = 6, width = 10 },
{ text = 'Cancel', event = 'cancel', display = UI.Form.D.button,
x = 21, y = 6, width = 10 },
}, },
labelWidth = 10, [2] = UI.TextEntry {
x = 5, width = 7,
y = 5, backgroundColor = colors.gray,
height = 6 backgroundFocusColor = colors.gray,
}), formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
statusBar = UI.StatusBar() },
}) [3] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Craft until out of ingredients'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Ignore damage of item'
},
},
statusBar = UI.StatusBar { }
}
function itemPage:enable() function itemPage:enable()
UI.Page.enable(self) UI.Page.enable(self)
@ -504,12 +507,14 @@ function itemPage:enable()
end end
function itemPage:eventHandler(event) function itemPage:eventHandler(event)
if event.type == 'cancel' then if event.type == 'form_cancel' then
UI:setPreviousPage() UI:setPreviousPage()
elseif event.type == 'focus_change' then elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help) self.statusBar:setStatus(event.focused.help)
self.statusBar:draw() self.statusBar:draw()
elseif event.type == 'accept' then
elseif event.type == 'form_complete' then
local values = self.form.values local values = self.form.values
local t = Util.readTable('resource.limits') or { } local t = Util.readTable('resource.limits') or { }
for k,v in pairs(t) do for k,v in pairs(t) do
@ -527,55 +532,52 @@ function itemPage:eventHandler(event)
table.insert(t, filtered) table.insert(t, filtered)
Util.writeTable('resource.limits', t) Util.writeTable('resource.limits', t)
UI:setPreviousPage() UI:setPreviousPage()
else else
return UI.Page.eventHandler(self, event) return UI.Page.eventHandler(self, event)
end end
return true return true
end end
listingPage = UI.Page({ listingPage = UI.Page {
menuBar = UI.MenuBar({ menuBar = UI.MenuBar {
buttons = { buttons = {
{ text = 'Learn', event = 'learn' }, { text = 'Learn', event = 'learn' },
{ text = 'Forget', event = 'forget' }, { text = 'Forget', event = 'forget' },
}, },
}), },
grid = UI.Grid({ grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = { columns = {
{ heading = 'Name', key = 'name' , width = 22 }, { heading = 'Name', key = 'name' , width = 22 },
{ heading = 'Qty', key = 'qty' , width = 5 }, { heading = 'Qty', key = 'qty' , width = 5 },
{ heading = 'Min', key = 'low' , width = 4 }, { heading = 'Min', key = 'low' , width = 4 },
{ heading = 'Max', key = 'limit', width = 4 }, { heading = 'Max', key = 'limit', width = 4 },
}, },
y = 2,
sortColumn = 'name', sortColumn = 'name',
height = UI.term.height-2, },
}), statusBar = UI.StatusBar {
statusBar = UI.StatusBar({
backgroundColor = colors.gray, backgroundColor = colors.gray,
width = UI.term.width, width = UI.term.width,
filterText = UI.Text({ filterText = UI.Text {
x = 2, width = 6,
value = 'Filter', value = 'Filter',
x = 2, },
width = 6, filter = UI.TextEntry {
}), x = 9, width = 19,
filter = UI.TextEntry({
width = 19,
limit = 50, limit = 50,
x = 9, },
}), refresh = UI.Button {
refresh = UI.Button({ x = 31, width = 8,
text = 'Refresh', text = 'Refresh',
event = 'refresh', event = 'refresh',
x = 31, },
width = 8 },
}),
}),
accelerators = { accelerators = {
r = 'refresh', r = 'refresh',
q = 'quit', q = 'quit',
} }
}) }
function listingPage.grid:getRowTextColor(row, selected) function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then if row.is_craftable then
@ -622,24 +624,26 @@ end
function listingPage:eventHandler(event) function listingPage:eventHandler(event)
if event.type == 'quit' then if event.type == 'quit' then
Event.exitPullEvents() Event.exitPullEvents()
elseif event.type == 'grid_select' then elseif event.type == 'grid_select' then
local selected = event.selected local selected = event.selected
itemPage.form:setValues(selected) itemPage.form:setValues(selected)
itemPage.titleBar.title = selected.name itemPage.titleBar.title = selected.name
itemPage.idField.value = selected.id itemPage.idField.value = selected.id
UI:setPage('item') UI:setPage('item')
elseif event.type == 'refresh' then elseif event.type == 'refresh' then
self:refresh() self:refresh()
self.grid:draw() self.grid:draw()
elseif event.type == 'learn' then elseif event.type == 'learn' then
if not duckAntenna then if not duckAntenna then
self.statusBar:timedStatus('Missing peripherals', 3) self.statusBar:timedStatus('Missing peripherals', 3)
else else
UI:getPage('craft').form:setValues( { ignore_dmg = 'no' } )
UI:setPage('craft') UI:setPage('craft')
end end
elseif event.type == 'forget' then
elseif event.type == 'forget' then
local item = self.grid:getSelected() local item = self.grid:getSelected()
if item then if item then
local recipes = Util.readTable('recipes') or { } local recipes = Util.readTable('recipes') or { }
@ -673,6 +677,7 @@ function listingPage:eventHandler(event)
self:applyFilter() self:applyFilter()
self.grid:draw() self.grid:draw()
self.statusBar.filter:focus() self.statusBar.filter:focus()
else else
UI.Page.eventHandler(self, event) UI.Page.eventHandler(self, event)
end end
@ -747,7 +752,7 @@ local function filter(t, filter)
end end
end end
local function learnRecipe(page, ignore_dmg) local function learnRecipe(page)
local t = Util.readTable('recipes') or { } local t = Util.readTable('recipes') or { }
local recipe = { } local recipe = { }
local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory() local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory()
@ -773,7 +778,7 @@ local function learnRecipe(page, ignore_dmg)
end end
end end
recipe.ingredients = ingredients recipe.ingredients = ingredients
recipe.ignore_dmg = 'no' -- ignore_dmg recipe.ignore_dmg = 'no'
t[key] = recipe t[key] = recipe
@ -794,69 +799,52 @@ local function learnRecipe(page, ignore_dmg)
end end
end end
craftPage = UI.Page({ craftPage = UI.Dialog {
x = 4, height = 7, width = UI.term.width - 6,
y = math.floor((UI.term.height - 8) / 2) + 1,
height = 7,
width = UI.term.width - 6,
backgroundColor = colors.lightGray, backgroundColor = colors.lightGray,
titleBar = UI.TitleBar({ titleBar = UI.TitleBar {
title = 'Learn Recipe', title = 'Learn Recipe',
previousPage = true, previousPage = true,
}), },
idField = UI.Text({ idField = UI.Text {
x = 5, x = 5,
y = 3, y = 3,
width = UI.term.width - 10, width = UI.term.width - 10,
value = 'Place recipe in turtle' value = 'Place recipe in turtle'
}), },
form = UI.Form({ accept = UI.Button {
fields = { rx = -13, ry = -2,
--[[ text = 'Ok', event = 'accept',
{ label = 'Ignore Damage', key = 'ignore_dmg', width = 7, display = UI.Form.D.chooser, },
nochoice = 'No', cancel = UI.Button {
choices = { rx = -8, ry = -2,
{ name = 'Yes', value = 'yes' }, text = 'Cancel', event = 'cancel'
{ name = 'No', value = 'no' }, },
}, statusBar = UI.StatusBar {
help = 'Ignore damage of ingredients' },
--]]
{ text = 'Accept', event = 'accept', display = UI.Form.D.button,
x = 1, y = 1, width = 10 },
{ text = 'Cancel', event = 'cancel', display = UI.Form.D.button,
x = 16, y = 1, width = 10 },
},
labelWidth = 13,
x = 5,
y = 5,
height = 2
}),
statusBar = UI.StatusBar({
status = 'Crafting paused' status = 'Crafting paused'
}) }
}) }
function craftPage:enable() function craftPage:enable()
craftingPaused = true craftingPaused = true
self:focusFirst() self:focusFirst()
UI.Page.enable(self) UI.Dialog.enable(self)
end end
function craftPage:disable() function craftPage:disable()
craftingPaused = false craftingPaused = false
UI.Dialog.disable(self)
end end
function craftPage:eventHandler(event) function craftPage:eventHandler(event)
if event.type == 'cancel' then if event.type == 'cancel' then
UI:setPreviousPage() UI:setPreviousPage()
elseif event.type == 'accept' then elseif event.type == 'accept' then
local values = self.form.values if learnRecipe(self) then
if learnRecipe(self, values.ignore_dmg) then
UI:setPreviousPage() UI:setPreviousPage()
end end
else else
return UI.Page.eventHandler(self, event) return UI.Dialog.eventHandler(self, event)
end end
return true return true
end end

View File

@ -36,6 +36,7 @@ if turtle and device.wireless_modem then
Util.print('Setting turtle point to %d %d %d', pt.x, pt.y, pt.z) Util.print('Setting turtle point to %d %d %d', pt.x, pt.y, pt.z)
turtle.setPoint(pt) turtle.setPoint(pt)
turtle.getState().coordSystem = 'GPS'
if not turtle.pathfind(homePt) then if not turtle.pathfind(homePt) then
error('Failed to return home') error('Failed to return home')

View File

@ -1,4 +1,5 @@
local Util = require('util') local Util = require('util')
local Process = require('process')
local Event = { local Event = {
uid = 1, -- unique id for handlers uid = 1, -- unique id for handlers
@ -120,13 +121,9 @@ end
local exitPullEvents = false local exitPullEvents = false
local function _pullEvents() local function _pullEvents()
--exitPullEvents = false
while true do while true do
local e = Event.pullEvent() local e = { os.pullEvent() }
if exitPullEvents or e == 'terminate' then Event.processEvent(e)
break
end
end end
end end
@ -137,12 +134,23 @@ function Event.sleep(t)
until event == 'timer' and id == timerId until event == 'timer' and id == timerId
end end
function Event.addThread(fn)
return Process:addThread(fn)
end
function Event.pullEvents(...) function Event.pullEvents(...)
Process:addThread(_pullEvents)
local routines = { ... } local routines = { ... }
if #routines > 0 then if #routines > 0 then
parallel.waitForAny(_pullEvents, ...) for _, routine in ipairs(routines) do
else Process:addThread(routine)
_pullEvents() end
end
while true do
local e = Process:pullEvent()
if exitPullEvents or e == 'terminate' then
break
end
end end
end end

View File

@ -1,6 +1,6 @@
local UI = require('ui') local UI = require('ui')
return function() return function(args)
local columns = { local columns = {
{ heading = 'Name', key = 'name', width = UI.term.width - 9 }, { heading = 'Name', key = 'name', width = UI.term.width - 9 },
@ -13,18 +13,19 @@ return function()
) )
end end
local selectFile = UI.Page({ args = args or { }
x = 3,
y = 2, local selectFile = UI.Dialog {
rex = -3, x = args.x or 3,
rey = -3, y = args.y or 2,
z = args.z or 2,
-- rex = args.rex or -3,
-- rey = args.rey or -3,
height = args.height,
width = args.width,
backgroundColor = colors.brown, backgroundColor = colors.brown,
titleBar = UI.TitleBar({ title = 'Select file',
title = 'Select file', grid = UI.ScrollingGrid {
previousPage = true,
event = 'cancel',
}),
grid = UI.ScrollingGrid({
x = 2, x = 2,
y = 2, y = 2,
rex = -2, rex = -2,
@ -32,8 +33,8 @@ return function()
path = '', path = '',
sortColumn = 'name', sortColumn = 'name',
columns = columns, columns = columns,
}), },
path = UI.TextEntry({ path = UI.TextEntry {
x = 2, x = 2,
ry = -1, ry = -1,
rex = -11, rex = -11,
@ -41,14 +42,14 @@ return function()
accelerators = { accelerators = {
enter = 'path_enter', enter = 'path_enter',
} }
}), },
cancel = UI.Button({ cancel = UI.Button {
text = 'Cancel', text = 'Cancel',
rx = -8, rx = -8,
ry = -1, ry = -1,
event = 'cancel', event = 'cancel',
}), },
}) }
function selectFile:enable(path, fn) function selectFile:enable(path, fn)
self:setPath(path) self:setPath(path)

View File

@ -46,13 +46,13 @@ function urlfs.open(node, fn, fl)
synchronized(node.url, function() synchronized(node.url, function()
c = Util.download(node.url) c = Util.download(node.url)
end) end)
if c and #c > 0 then if c then
node.cache = c node.cache = c
node.size = #c node.size = #c
end end
end end
if not c or #c == 0 then if not c then
return return
end end

View File

@ -1,68 +1,164 @@
local resolver, loader local DEFAULT_UPATH = 'https://raw.githubusercontent.com/kepler155c/opus/master/sys/apis'
local PASTEBIN_URL = 'http://pastebin.com/raw'
local GIT_URL = 'https://raw.githubusercontent.com'
local function resolveFile(filename, dir, lua_path) local function standardSearcher(modname, env, shell)
if package.loaded[modname] then
if filename:sub(1, 1) == "/" then return function()
if not fs.exists(filename) then return package.loaded[modname]
error('Unable to load: ' .. filename, 2)
end end
return filename
end end
end
if dir then local function shellSearcher(modname, env, shell)
local path = fs.combine(dir, filename) local fname = modname:gsub('%.', '/') .. '.lua'
if shell and type(shell.dir) == 'function' then
local path = shell.resolve(fname)
if fs.exists(path) and not fs.isDir(path) then if fs.exists(path) and not fs.isDir(path) then
return path return loadfile(path, env)
end end
end end
end
if lua_path then local function pathSearcher(modname, env, shell)
for dir in string.gmatch(lua_path, "[^:]+") do local fname = modname:gsub('%.', '/') .. '.lua'
local path = fs.combine(dir, filename)
if fs.exists(path) and not fs.isDir(path) then for dir in string.gmatch(package.path, "[^:]+") do
return path local path = fs.combine(dir, fname)
if fs.exists(path) and not fs.isDir(path) then
return loadfile(path, env)
end
end
end
-- fix broken http get
local syncLocks = { }
local function sync(obj, fn)
local key = tostring(obj)
if syncLocks[key] then
local cos = tostring(coroutine.running())
table.insert(syncLocks[key], cos)
repeat
local _, co = os.pullEvent('sync_lock')
until co == cos
else
syncLocks[key] = { }
end
local s, m = pcall(fn)
local co = table.remove(syncLocks[key], 1)
if co then
os.queueEvent('sync_lock', co)
else
syncLocks[key] = nil
end
if not s then
error(m)
end
end
local function loadUrl(url)
local c
sync(url, function()
local h = http.get(url)
if h then
c = h.readAll()
h.close()
end
end)
if c and #c > 0 then
return c
end
end
-- require('BniCQPVf')
local function pastebinSearcher(modname, env, shell)
if #modname == 8 and not modname:match('%W') then
local url = PASTEBIN_URL .. '/' .. modname
local c = loadUrl(url)
if c then
return load(c, modname, nil, env)
end
end
end
-- require('kepler155c.opus.master.sys.apis.util')
local function gitSearcher(modname, env, shell)
local fname = modname:gsub('%.', '/') .. '.lua'
local _, count = fname:gsub("/", "")
if count >= 3 then
local url = GIT_URL .. '/' .. fname
local c = loadUrl(url)
if c then
return load(c, modname, nil, env)
end
end
end
local function urlSearcher(modname, env, shell)
local fname = modname:gsub('%.', '/') .. '.lua'
if fname:sub(1, 1) ~= '/' then
for entry in string.gmatch(package.upath, "[^;]+") do
local url = entry .. '/' .. fname
local c = loadUrl(url)
if c then
return load(c, modname, nil, env)
end end
end end
end end
error('Unable to load: ' .. filename, 2)
end end
_G.package = {
path = LUA_PATH or 'sys/apis',
upath = LUA_UPATH or DEFAULT_UPATH,
config = '/\n:\n?\n!\n-',
loaded = {
math = math,
string = string,
table = table,
io = io,
os = os,
},
loaders = {
standardSearcher,
shellSearcher,
pathSearcher,
pastebinSearcher,
gitSearcher,
urlSearcher,
}
}
local function requireWrapper(env) local function requireWrapper(env)
local modules = { } local loaded = { }
return function(filename) return function(modname)
local dir = DIR if loaded[modname] then
if not dir and shell and type(shell.dir) == 'function' then return loaded[modname]
dir = shell.dir()
end end
local fname = resolver(filename:gsub('%.', '/') .. '.lua', for _,searcher in ipairs(package.loaders) do
dir or '', LUA_PATH or '/sys/apis') local fn, msg = searcher(modname, env, shell)
if fn then
local rname = fname:gsub('%/', '.'):gsub('%.lua', '') local module, msg = fn(modname, env)
if not module then
local module = modules[rname] error(msg)
if not module then end
loaded[modname] = module
local f, err = loader(fname, env) return module
if not f then end
error(err) if msg then
end error(msg, 2)
module = f(rname) end
modules[rname] = module
end end
error('Unable to find module ' .. modname)
return module
end end
end end
local args = { ... }
resolver = args[1] or resolveFile
loader = args[2] or loadfile
return function(env) return function(env)
setfenv(requireWrapper, env) setfenv(requireWrapper, env)
return requireWrapper(env) return requireWrapper(env)

View File

@ -27,6 +27,11 @@ function Process:threadEvent(...)
end end
end end
function Process:addThread(fn, ...)
return self:newThread(nil, fn, ...)
end
-- deprecated
function Process:newThread(name, fn, ...) function Process:newThread(name, fn, ...)
self.uid = self.uid + 1 self.uid = self.uid + 1
@ -45,7 +50,7 @@ function Process:newThread(name, fn, ...)
local s, m = pcall(function() fn(unpack(args)) end) local s, m = pcall(function() fn(unpack(args)) end)
if not s and m then if not s and m then
if m == 'Terminated' then if m == 'Terminated' then
printError(thread.name .. ' terminated') --printError(thread.name .. ' terminated')
else else
printError(m) printError(m)
end end
@ -82,8 +87,11 @@ function Process:resume(event, ...)
return true, self.filter return true, self.filter
end end
function Process:pullEvent(filter) -- confusing...
-- pull either one event if no filter or until event matches filter
-- or until terminated (regardless of filter)
function Process:pullEvent(filter)
while true do while true do
local e = { os.pullEventRaw() } local e = { os.pullEventRaw() }
self:threadEvent(unpack(e)) self:threadEvent(unpack(e))
@ -94,12 +102,12 @@ function Process:pullEvent(filter)
end end
end end
-- pull events until either the filter is matched or terminated
function Process:pullEvents(filter) function Process:pullEvents(filter)
while true do while true do
local e = { os.pullEventRaw(filter) } local e = { os.pullEventRaw() }
self:threadEvent(unpack(e)) self:threadEvent(unpack(e))
if e[1] == 'terminate' then if (filter and e[1] == filter) or e[1] == 'terminate' then
return unpack(e) return unpack(e)
end end
end end

358
sys/apis/region.lua Normal file
View File

@ -0,0 +1,358 @@
-------------------------------------------------------------------------------
--
-- tek.lib.region
-- Written by Timm S. Mueller <tmueller at schulze-mueller.de>
--
-- Copyright 2008 - 2016 by the authors and contributors:
--
-- * Timm S. Muller <tmueller at schulze-mueller.de>
-- * Franciska Schulze <fschulze at schulze-mueller.de>
-- * Tobias Schwinger <tschwinger at isonews2.com>
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- === Disclaimer ===
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
-- OVERVIEW::
-- This library implements the management of regions, which are
-- collections of non-overlapping rectangles.
--
-- FUNCTIONS::
-- - Region:andRect() - ''And''s a rectangle to a region
-- - Region:andRegion() - ''And''s a region to a region
-- - Region:checkIntersect() - Checks if a rectangle intersects a region
-- - Region:forEach() - Calls a function for each rectangle in a region
-- - Region:get() - Get region's min/max extents
-- - Region.intersect() - Returns the intersection of two rectangles
-- - Region:isEmpty() - Checks if a Region is empty
-- - Region.new() - Creates a new Region
-- - Region:orRect() - ''Or''s a rectangle to a region
-- - Region:orRegion() - ''Or''s a region to a region
-- - Region:setRect() - Resets a region to the given rectangle
-- - Region:shift() - Displaces a region
-- - Region:subRect() - Subtracts a rectangle from a region
-- - Region:subRegion() - Subtracts a region from a region
-- - Region:xorRect() - ''Exclusive Or''s a rectangle to a region
--
-------------------------------------------------------------------------------
local insert = table.insert
local ipairs = ipairs
local max = math.max
local min = math.min
local setmetatable = setmetatable
local unpack = unpack or table.unpack
local Region = { }
Region._VERSION = "Region 11.3"
Region.__index = Region
-------------------------------------------------------------------------------
-- x0, y0, x1, y1 = Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4):
-- Returns the coordinates of a rectangle where a rectangle specified by
-- the coordinates s1, s2, s3, s4 overlaps with the rectangle specified
-- by the coordinates d1, d2, d3, d4. The return value is '''nil''' if
-- the rectangles do not overlap.
-------------------------------------------------------------------------------
function Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4)
if s3 >= d1 and s1 <= d3 and s4 >= d2 and s2 <= d4 then
return max(s1, d1), max(s2, d2), min(s3, d3), min(s4, d4)
end
end
-------------------------------------------------------------------------------
-- insertrect: insert rect to table, merging with an existing one if possible
-------------------------------------------------------------------------------
local function insertrect(d, s1, s2, s3, s4)
for i = 1, min(4, #d) do
local a = d[i]
local a1, a2, a3, a4 = a[1], a[2], a[3], a[4]
if a2 == s2 and a4 == s4 then
if a3 + 1 == s1 then
a[3] = s3
return
elseif a1 == s3 + 1 then
a[1] = s1
return
end
elseif a1 == s1 and a3 == s3 then
if a4 + 1 == s2 then
a[4] = s4
return
elseif a2 == s4 + 1 then
a[2] = s2
return
end
end
end
insert(d, 1, { s1, s2, s3, s4 })
end
-------------------------------------------------------------------------------
-- cutrect: cut rect d into table of new rects, using rect s as a punch
-------------------------------------------------------------------------------
local function cutrect(d1, d2, d3, d4, s1, s2, s3, s4)
if not Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4) then
return { { d1, d2, d3, d4 } }
end
local r = { }
if d1 < s1 then
insertrect(r, d1, d2, s1 - 1, d4)
d1 = s1
end
if d2 < s2 then
insertrect(r, d1, d2, d3, s2 - 1)
d2 = s2
end
if d3 > s3 then
insertrect(r, s3 + 1, d2, d3, d4)
d3 = s3
end
if d4 > s4 then
insertrect(r, d1, s4 + 1, d3, d4)
end
return r
end
-------------------------------------------------------------------------------
-- cutregion: cut region d, using s as a punch
-------------------------------------------------------------------------------
local function cutregion(d, s1, s2, s3, s4)
local r = { }
for _, dr in ipairs(d) do
local d1, d2, d3, d4 = dr[1], dr[2], dr[3], dr[4]
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
insertrect(r, t[1], t[2], t[3], t[4])
end
end
return r
end
-------------------------------------------------------------------------------
-- region = Region.new(r1, r2, r3, r4): Creates a new region from the given
-- coordinates.
-------------------------------------------------------------------------------
function Region.new(r1, r2, r3, r4)
if r1 then
return setmetatable({ region = { { r1, r2, r3, r4 } } }, Region)
end
return setmetatable({ region = { } }, Region)
end
-------------------------------------------------------------------------------
-- self = region:setRect(r1, r2, r3, r4): Resets an existing region
-- to the specified rectangle.
-------------------------------------------------------------------------------
function Region:setRect(r1, r2, r3, r4)
self.region = { { r1, r2, r3, r4 } }
return self
end
-------------------------------------------------------------------------------
-- region:orRect(r1, r2, r3, r4): Logical ''or''s a rectangle to a region
-------------------------------------------------------------------------------
function Region:orRect(s1, s2, s3, s4)
self.region = cutregion(self.region, s1, s2, s3, s4)
insertrect(self.region, s1, s2, s3, s4)
end
-------------------------------------------------------------------------------
-- region:orRegion(region): Logical ''or''s another region to a region
-------------------------------------------------------------------------------
function Region:orRegion(s)
for _, r in ipairs(s) do
self:orRect(r[1], r[2], r[3], r[4])
end
end
-------------------------------------------------------------------------------
-- region:andRect(r1, r2, r3, r4): Logical ''and''s a rectange to a region
-------------------------------------------------------------------------------
function Region:andRect(s1, s2, s3, s4)
local r = { }
for _, d in ipairs(self.region) do
local t1, t2, t3, t4 =
Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4)
if t1 then
insertrect(r, t1, t2, t3, t4)
end
end
self.region = r
end
-------------------------------------------------------------------------------
-- region:xorRect(r1, r2, r3, r4): Logical ''xor''s a rectange to a region
-------------------------------------------------------------------------------
function Region:xorRect(s1, s2, s3, s4)
local r1 = { }
local r2 = { { s1, s2, s3, s4 } }
for _, d in ipairs(self.region) do
local d1, d2, d3, d4 = d[1], d[2], d[3], d[4]
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
insertrect(r1, t[1], t[2], t[3], t[4])
end
r2 = cutregion(r2, d1, d2, d3, d4)
end
self.region = r1
self:orRegion(r2)
end
-------------------------------------------------------------------------------
-- self = region:subRect(r1, r2, r3, r4): Subtracts a rectangle from a region
-------------------------------------------------------------------------------
function Region:subRect(s1, s2, s3, s4)
local r1 = { }
for _, d in ipairs(self.region) do
local d1, d2, d3, d4 = d[1], d[2], d[3], d[4]
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
insertrect(r1, t[1], t[2], t[3], t[4])
end
end
self.region = r1
return self
end
-------------------------------------------------------------------------------
-- region:getRect - gets an iterator on the rectangles in a region [internal]
-------------------------------------------------------------------------------
function Region:getRects()
local index = 0
return function(object)
index = index + 1
if object[index] then
return unpack(object[index])
end
end, self.region
end
-------------------------------------------------------------------------------
-- success = region:checkIntersect(x0, y0, x1, y1): Returns a boolean
-- indicating whether a rectangle specified by its coordinates overlaps
-- with a region.
-------------------------------------------------------------------------------
function Region:checkIntersect(s1, s2, s3, s4)
for _, d in ipairs(self.region) do
if Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4) then
return true
end
end
return false
end
-------------------------------------------------------------------------------
-- region:subRegion(region2): Subtracts {{region2}} from {{region}}.
-------------------------------------------------------------------------------
function Region:subRegion(region)
if region then
for r1, r2, r3, r4 in region:getRects() do
self:subRect(r1, r2, r3, r4)
end
end
end
-------------------------------------------------------------------------------
-- region:andRegion(r): Logically ''and''s a region to a region
-------------------------------------------------------------------------------
function Region:andRegion(s)
local r = { }
for _, s in ipairs(s.region) do
for _, d in ipairs(self.region) do
local t1, t2, t3, t4 =
Region.intersect(d[1], d[2], d[3], d[4],
s[1], s[2], s[3], s[4])
if t1 then
insertrect(r, t1, t2, t3, t4)
end
end
end
self.region = r
end
-------------------------------------------------------------------------------
-- region:forEach(func, obj, ...): For each rectangle in a region, calls the
-- specified function according the following scheme:
-- func(obj, x0, y0, x1, y1, ...)
-- Extra arguments are passed through to the function.
-------------------------------------------------------------------------------
function Region:forEach(func, obj, ...)
for x0, y0, x1, y1 in self:getRects() do
func(obj, x0, y0, x1, y1, ...)
end
end
-------------------------------------------------------------------------------
-- region:shift(dx, dy): Shifts a region by delta x and y.
-------------------------------------------------------------------------------
function Region:shift(dx, dy)
for _, r in ipairs(self.region) do
r[1] = r[1] + dx
r[2] = r[2] + dy
r[3] = r[3] + dx
r[4] = r[4] + dy
end
end
-------------------------------------------------------------------------------
-- region:isEmpty(): Returns '''true''' if a region is empty.
-------------------------------------------------------------------------------
function Region:isEmpty()
return #self.region == 0
end
-------------------------------------------------------------------------------
-- minx, miny, maxx, maxy = region:get(): Get region's min/max extents
-------------------------------------------------------------------------------
function Region:get()
if #self.region > 0 then
local minx = 1000000 -- ui.HUGE
local miny = 1000000
local maxx = 0
local maxy = 0
for _, r in ipairs(self.region) do
minx = min(minx, r[1])
miny = min(miny, r[2])
maxx = max(maxx, r[3])
maxy = max(maxy, r[4])
end
return minx, miny, maxx, maxy
end
end
return Region

View File

@ -120,6 +120,18 @@ function Terminal.toGrayscale(ct)
end end
end end
function Terminal.getNullTerm(ct)
local nt = Terminal.copy(ct)
local methods = { 'blit', 'clear', 'clearLine', 'scroll',
'setCursorBlink', 'setCursorPos', 'write' }
for _,v in pairs(methods) do
nt[v] = function() end
end
return nt
end
function Terminal.copy(ot) function Terminal.copy(ot)
local ct = { } local ct = { }
for k,v in pairs(ot) do for k,v in pairs(ot) do

367
sys/apis/tween.lua Normal file
View File

@ -0,0 +1,367 @@
local tween = {
_VERSION = 'tween 2.1.1',
_DESCRIPTION = 'tweening for lua',
_URL = 'https://github.com/kikito/tween.lua',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
}
-- easing
-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.
-- For all easing functions:
-- t = time == how much time has to pass for the tweening to complete
-- b = begin == starting property value
-- c = change == ending - beginning
-- d = duration == running time. How much time has passed *right now*
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
-- linear
local function linear(t, b, c, d) return c * t / d + b end
-- quad
local function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end
local function outQuad(t, b, c, d)
t = t / d
return -c * t * (t - 2) + b
end
local function inOutQuad(t, b, c, d)
t = t / d * 2
if t < 1 then return c / 2 * pow(t, 2) + b end
return -c / 2 * ((t - 1) * (t - 3) - 1) + b
end
local function outInQuad(t, b, c, d)
if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end
return inQuad((t * 2) - d, b + c / 2, c / 2, d)
end
-- cubic
local function inCubic (t, b, c, d) return c * pow(t / d, 3) + b end
local function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end
local function inOutCubic(t, b, c, d)
t = t / d * 2
if t < 1 then return c / 2 * t * t * t + b end
t = t - 2
return c / 2 * (t * t * t + 2) + b
end
local function outInCubic(t, b, c, d)
if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end
return inCubic((t * 2) - d, b + c / 2, c / 2, d)
end
-- quart
local function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end
local function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end
local function inOutQuart(t, b, c, d)
t = t / d * 2
if t < 1 then return c / 2 * pow(t, 4) + b end
return -c / 2 * (pow(t - 2, 4) - 2) + b
end
local function outInQuart(t, b, c, d)
if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end
return inQuart((t * 2) - d, b + c / 2, c / 2, d)
end
-- quint
local function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end
local function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end
local function inOutQuint(t, b, c, d)
t = t / d * 2
if t < 1 then return c / 2 * pow(t, 5) + b end
return c / 2 * (pow(t - 2, 5) + 2) + b
end
local function outInQuint(t, b, c, d)
if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end
return inQuint((t * 2) - d, b + c / 2, c / 2, d)
end
-- sine
local function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end
local function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end
local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end
local function outInSine(t, b, c, d)
if t < d / 2 then return outSine(t * 2, b, c / 2, d) end
return inSine((t * 2) -d, b + c / 2, c / 2, d)
end
-- expo
local function inExpo(t, b, c, d)
if t == 0 then return b end
return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
end
local function outExpo(t, b, c, d)
if t == d then return b + c end
return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
end
local function inOutExpo(t, b, c, d)
if t == 0 then return b end
if t == d then return b + c end
t = t / d * 2
if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end
return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
end
local function outInExpo(t, b, c, d)
if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end
return inExpo((t * 2) - d, b + c / 2, c / 2, d)
end
-- circ
local function inCirc(t, b, c, d) return(-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end
local function outCirc(t, b, c, d) return(c * sqrt(1 - pow(t / d - 1, 2)) + b) end
local function inOutCirc(t, b, c, d)
t = t / d * 2
if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end
t = t - 2
return c / 2 * (sqrt(1 - t * t) + 1) + b
end
local function outInCirc(t, b, c, d)
if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end
return inCirc((t * 2) - d, b + c / 2, c / 2, d)
end
-- elastic
local function calculatePAS(p,a,c,d)
p, a = p or d * 0.3, a or 0
if a < abs(c) then return p, c, p / 4 end -- p, a, s
return p, a, p / (2 * pi) * asin(c/a) -- p,a,s
end
local function inElastic(t, b, c, d, a, p)
local s
if t == 0 then return b end
t = t / d
if t == 1 then return b + c end
p,a,s = calculatePAS(p,a,c,d)
t = t - 1
return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
end
local function outElastic(t, b, c, d, a, p)
local s
if t == 0 then return b end
t = t / d
if t == 1 then return b + c end
p,a,s = calculatePAS(p,a,c,d)
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
end
local function inOutElastic(t, b, c, d, a, p)
local s
if t == 0 then return b end
t = t / d * 2
if t == 2 then return b + c end
p,a,s = calculatePAS(p,a,c,d)
t = t - 1
if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b
end
local function outInElastic(t, b, c, d, a, p)
if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end
return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
end
-- back
local function inBack(t, b, c, d, s)
s = s or 1.70158
t = t / d
return c * t * t * ((s + 1) * t - s) + b
end
local function outBack(t, b, c, d, s)
s = s or 1.70158
t = t / d - 1
return c * (t * t * ((s + 1) * t + s) + 1) + b
end
local function inOutBack(t, b, c, d, s)
s = (s or 1.70158) * 1.525
t = t / d * 2
if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end
t = t - 2
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
end
local function outInBack(t, b, c, d, s)
if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end
return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
end
-- bounce
local function outBounce(t, b, c, d)
t = t / d
if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end
if t < 2 / 2.75 then
t = t - (1.5 / 2.75)
return c * (7.5625 * t * t + 0.75) + b
elseif t < 2.5 / 2.75 then
t = t - (2.25 / 2.75)
return c * (7.5625 * t * t + 0.9375) + b
end
t = t - (2.625 / 2.75)
return c * (7.5625 * t * t + 0.984375) + b
end
local function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end
local function inOutBounce(t, b, c, d)
if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end
return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b
end
local function outInBounce(t, b, c, d)
if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end
return inBounce((t * 2) - d, b + c / 2, c / 2, d)
end
tween.easing = {
linear = linear,
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad,
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic,
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart,
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint,
inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine,
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo,
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc,
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,
inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack,
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce
}
-- private stuff
local function copyTables(destination, keysTable, valuesTable)
valuesTable = valuesTable or keysTable
local mt = getmetatable(keysTable)
if mt and getmetatable(destination) == nil then
setmetatable(destination, mt)
end
for k,v in pairs(keysTable) do
if type(v) == 'table' then
destination[k] = copyTables({}, v, valuesTable[k])
else
destination[k] = valuesTable[k]
end
end
return destination
end
local function checkSubjectAndTargetRecursively(subject, target, path)
path = path or {}
local targetType, newPath
for k,targetValue in pairs(target) do
targetType, newPath = type(targetValue), copyTables({}, path)
table.insert(newPath, tostring(k))
if targetType == 'number' then
assert(type(subject[k]) == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' is missing from subject or isn't a number")
elseif targetType == 'table' then
checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
else
assert(targetType == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' must be a number or table of numbers")
end
end
end
local function checkNewParams(duration, subject, target, easing)
assert(type(duration) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
local tsubject = type(subject)
assert(tsubject == 'table' or tsubject == 'userdata', "subject must be a table or userdata. Was " .. tostring(subject))
assert(type(target)== 'table', "target must be a table. Was " .. tostring(target))
assert(type(easing)=='function', "easing must be a function. Was " .. tostring(easing))
checkSubjectAndTargetRecursively(subject, target)
end
local function getEasingFunction(easing)
easing = easing or "linear"
if type(easing) == 'string' then
local name = easing
easing = tween.easing[name]
if type(easing) ~= 'function' then
error("The easing function name '" .. name .. "' is invalid")
end
end
return easing
end
local function performEasingOnSubject(subject, target, initial, clock, duration, easing)
local t,b,c,d
for k,v in pairs(target) do
if type(v) == 'table' then
performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
else
t,b,c,d = clock, initial[k], v - initial[k], duration
subject[k] = easing(t,b,c,d)
end
end
end
-- Tween methods
local Tween = {}
local Tween_mt = {__index = Tween}
function Tween:set(clock)
assert(type(clock) == 'number', "clock must be a positive number or 0")
self.initial = self.initial or copyTables({}, self.target, self.subject)
self.clock = clock
if self.clock <= 0 then
self.clock = 0
copyTables(self.subject, self.initial)
elseif self.clock >= self.duration then -- the tween has expired
self.clock = self.duration
copyTables(self.subject, self.target)
else
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
end
return self.clock >= self.duration
end
function Tween:reset()
return self:set(0)
end
function Tween:update(dt)
assert(type(dt) == 'number', "dt must be a number")
return self:set(self.clock + dt)
end
-- Public interface
function tween.new(duration, subject, target, easing)
easing = getEasingFunction(easing)
checkNewParams(duration, subject, target, easing)
return setmetatable({
duration = duration,
subject = subject,
target = target,
easing = easing,
clock = 0
}, Tween_mt)
end
return tween

File diff suppressed because it is too large Load Diff

View File

@ -365,6 +365,10 @@ function Util.toBytes(n)
return tostring(n) return tostring(n)
end end
function Util.insertString(os, is, pos)
return os:sub(1, pos - 1) .. is .. os:sub(pos)
end
function Util.split(str, pattern) function Util.split(str, pattern)
pattern = pattern or "(.-)\n" pattern = pattern or "(.-)\n"
local t = {} local t = {}
@ -457,28 +461,30 @@ end
local function getopt( arg, options ) local function getopt( arg, options )
local tab = {} local tab = {}
for k, v in ipairs(arg) do for k, v in ipairs(arg) do
if string.sub( v, 1, 2) == "--" then if type(v) == 'string' then
local x = string.find( v, "=", 1, true ) if string.sub( v, 1, 2) == "--" then
if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 ) local x = string.find( v, "=", 1, true )
else tab[ string.sub( v, 3 ) ] = true if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 )
end else tab[ string.sub( v, 3 ) ] = true
elseif string.sub( v, 1, 1 ) == "-" then end
local y = 2 elseif string.sub( v, 1, 1 ) == "-" then
local l = string.len(v) local y = 2
local jopt local l = string.len(v)
while ( y <= l ) do local jopt
jopt = string.sub( v, y, y ) while ( y <= l ) do
if string.find( options, jopt, 1, true ) then jopt = string.sub( v, y, y )
if y < l then if string.find( options, jopt, 1, true ) then
tab[ jopt ] = string.sub( v, y+1 ) if y < l then
y = l tab[ jopt ] = string.sub( v, y+1 )
else y = l
tab[ jopt ] = arg[ k + 1 ] else
end tab[ jopt ] = arg[ k + 1 ]
else end
tab[ jopt ] = true else
tab[ jopt ] = true
end
y = y + 1
end end
y = y + 1
end end
end end
end end

View File

@ -4,8 +4,8 @@ LUA_PATH = '/sys/apis'
math.randomseed(os.clock()) math.randomseed(os.clock())
_G.debug = function() end
_G.Util = dofile('/sys/apis/util.lua') _G.Util = dofile('/sys/apis/util.lua')
_G.debug = function(...) Util.print(...) end
_G.requireInjector = dofile('/sys/apis/injector.lua') _G.requireInjector = dofile('/sys/apis/injector.lua')
os.run(Util.shallowCopy(getfenv(1)), '/sys/extensions/device.lua') os.run(Util.shallowCopy(getfenv(1)), '/sys/extensions/device.lua')
@ -21,6 +21,7 @@ local mounts = Util.readFile('config/fstab')
if mounts then if mounts then
for _,l in ipairs(Util.split(mounts)) do for _,l in ipairs(Util.split(mounts)) do
if l:sub(1, 1) ~= '#' then if l:sub(1, 1) ~= '#' then
print('mounting ' .. l)
fs.mount(unpack(Util.matches(l))) fs.mount(unpack(Util.matches(l)))
end end
end end

View File

@ -57,10 +57,12 @@ function turtle.run(fn, ...)
local e, id, abort = os.pullEventRaw('turtle_ticket') local e, id, abort = os.pullEventRaw('turtle_ticket')
if e == 'terminate' then if e == 'terminate' then
releaseTicket(ticketId) releaseTicket(ticketId)
os.queueEvent('turtle_response')
error('Terminated') error('Terminated')
end end
if abort then if abort then
-- the function was queued, but the queue was cleared -- the function was queued, but the queue was cleared
os.queueEvent('turtle_response')
return false, 'aborted' return false, 'aborted'
end end
if id == ticketId then if id == ticketId then
@ -72,6 +74,7 @@ function turtle.run(fn, ...)
if not s and m then if not s and m then
printError(m) printError(m)
end end
os.queueEvent('turtle_response')
return s, m return s, m
end end
end end

View File

@ -17,6 +17,7 @@ local state = {
moveDig = noop, moveDig = noop,
moveCallback = noop, moveCallback = noop,
locations = {}, locations = {},
coordSystem = 'relative', -- type of coordinate system being used
} }
function turtle.getState() function turtle.getState()
@ -44,6 +45,7 @@ function turtle.reset()
state.moveDig = noop state.moveDig = noop
state.moveCallback = noop state.moveCallback = noop
state.locations = {} state.locations = {}
state.coordSystem = 'relative'
return true return true
end end
@ -231,7 +233,6 @@ turtle.digPolicies = {
if not turtle.isTurtleAtSide(action.side) then if not turtle.isTurtleAtSide(action.side) then
return action.dig() return action.dig()
end end
return Util.tryTimes(6, function() return Util.tryTimes(6, function()
-- if not turtle.isTurtleAtSide(action.side) then -- if not turtle.isTurtleAtSide(action.side) then
-- return true --action.dig() -- return true --action.dig()

View File

@ -117,15 +117,19 @@ process:newThread('discovery_server', function()
end end
end) end)
local info = {
id = os.getComputerID()
}
local function sendInfo() local function sendInfo()
local info = { info.label = os.getComputerLabel()
id = os.getComputerID(), info.uptime = math.floor(os.clock())
label = os.getComputerLabel(),
uptime = math.floor(os.clock()),
}
if turtle then if turtle then
info.fuel = turtle.getFuelLevel() info.fuel = turtle.getFuelLevel()
info.status = turtle.status info.status = turtle.status
info.point = turtle.point
info.inventory = turtle.getInventory()
info.coordSystem = turtle.getState().coordSystem
end end
device.wireless_modem.transmit(999, os.getComputerID(), info) device.wireless_modem.transmit(999, os.getComputerID(), info)
end end
@ -152,13 +156,11 @@ end)
if os.isTurtle() then if os.isTurtle() then
process:newThread('turtle_heartbeat', function() process:newThread('turtle_heartbeat', function()
local lastUpdate = os.clock()
os.sleep(1) os.sleep(1)
while true do while true do
os.pullEvent('turtle_response') os.pullEvent('turtle_response')
if os.clock() - lastUpdate >= 1 then if turtle.status ~= info.status or
lastUpdate = os.clock() turtle.fuel ~= info.fuel then
sendInfo() sendInfo()
end end
end end