1
0
mirror of https://github.com/kepler155c/opus synced 2025-01-25 22:56:53 +00:00

format and installer branches

This commit is contained in:
kepler155c@gmail.com 2018-01-24 17:39:38 -05:00
parent 1eea0d7cd8
commit 5a32fe208e
80 changed files with 10742 additions and 10156 deletions

View File

@ -93,3 +93,4 @@ if bootOptions[bootOption].args then
else else
print(bootOptions[bootOption].prompt) print(bootOptions[bootOption].prompt)
end end

151
sys/apis/entry.lua Normal file
View File

@ -0,0 +1,151 @@
local class = require('class')
local os = _G.os
local Entry = class()
function Entry:init(args)
self.pos = 0
self.scroll = 0
self.value = ''
self.width = args.width
self.limit = 1024
end
function Entry:reset()
self.pos = 0
self.scroll = 0
self.value = ''
end
local function nextWord(line, cx)
local result = { line:find("(%w+)", cx) }
if #result > 1 and result[2] > cx then
return result[2] + 1
elseif #result > 0 and result[1] == cx then
result = { line:find("(%w+)", result[2] + 1) }
if #result > 0 then
return result[1]
end
end
end
function Entry:updateScroll()
if self.pos - self.scroll > self.width then
self.scroll = self.pos - (self.width)
elseif self.pos < self.scroll then
self.scroll = self.pos
end
end
function Entry:process(ie)
local updated = false
if ie.code == 'left' then
if self.pos > 0 then
self.pos = math.max(self.pos - 1, 0)
updated = true
end
elseif ie.code == 'right' then
local input = tostring(self.value)
if self.pos < #input then
self.pos = math.min(self.pos + 1, #input)
updated = true
end
elseif ie.code == 'home' then
if self.pos ~= 0 then
self.pos = 0
updated = true
end
elseif ie.code == 'end' then
if self.pos ~= #tostring(self.value) then
self.pos = #tostring(self.value)
updated = true
end
elseif ie.code == 'backspace' then
if self.pos > 0 then
local input = tostring(self.value)
self.value = input:sub(1, self.pos - 1) .. input:sub(self.pos + 1)
self.pos = self.pos - 1
updated = true
end
elseif ie.code == 'control-right' then
local nx = nextWord(self.value, self.pos + 1)
if nx then
self.pos = math.min(nx - 1, #self.value)
elseif self.pos < #self.value then
self.pos = #self.value
end
updated = true
elseif ie.code == 'control-left' then
if self.pos ~= 0 then
local lx = 1
while true do
local nx = nextWord(self.value, lx)
if not nx or nx >= self.pos then
break
end
lx = nx
end
if not lx then
self.pos = 0
else
self.pos = lx - 1
end
updated = true
end
elseif ie.code == 'delete' then
local input = tostring(self.value)
if self.pos < #input then
self.value = input:sub(1, self.pos) .. input:sub(self.pos + 2)
self.update = true
updated = true
end
elseif ie.code == 'char' then
local input = tostring(self.value)
if #input < self.limit then
self.value = input:sub(1, self.pos) .. ie.ch .. input:sub(self.pos + 1)
self.pos = self.pos + 1
self.update = true
updated = true
end
elseif ie.code == 'copy' then
os.queueEvent('clipboard_copy', self.value)
elseif ie.code == 'paste' then
local input = tostring(self.value)
if #input + #ie.text > self.limit then
ie.text = ie.text:sub(1, self.limit-#input)
end
self.value = input:sub(1, self.pos) .. ie.text .. input:sub(self.pos + 1)
self.pos = self.pos + #ie.text
updated = true
elseif ie.code == 'mouse_click' then
-- need starting x passed in instead of hardcoding 3
self.pos = math.min(ie.x - 3 + self.scroll, #self.value)
updated = true
elseif ie.code == 'mouse_rightclick' then
local input = tostring(self.value)
if #input > 0 then
self:reset()
updated = true
end
end
self:updateScroll()
return updated
end
return Entry

View File

@ -24,15 +24,22 @@ end
function input:toCode(ch, code) function input:toCode(ch, code)
local result = { } local result = { }
if not ch and code == 1 then
ch = 'escape'
end
if keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl] or if keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl] or
code == keys.leftCtrl or code == keys.rightCtrl then code == keys.leftCtrl or code == keys.rightCtrl then
table.insert(result, 'control') table.insert(result, 'control')
end end
if keyboard.state[keys.leftAlt] or keyboard.state[keys.rightAlt] or -- the key-up event for alt keys is not generated if the minecraft
code == keys.leftAlt or code == keys.rightAlt then -- window loses focus
table.insert(result, 'alt') --
end -- if keyboard.state[keys.leftAlt] or keyboard.state[keys.rightAlt] or
-- code == keys.leftAlt or code == keys.rightAlt then
-- table.insert(result, 'alt')
-- end
if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or
code == keys.leftShift or code == keys.rightShift then code == keys.leftShift or code == keys.rightShift then
@ -65,13 +72,13 @@ function input:translate(event, code, p1, p2)
if p1 then -- key is held down if p1 then -- key is held down
if not modifiers[code] then if not modifiers[code] then
self.fired = true self.fired = true
return input:toCode(keys.getName(code), code) return { code = input:toCode(keys.getName(code), code) }
end end
else else
self.state[code] = true self.state[code] = true
if self:modifierPressed() and not modifiers[code] or code == 57 then if self:modifierPressed() and not modifiers[code] or code == 57 then
self.fired = true self.fired = true
return input:toCode(keys.getName(code), code) return { code = input:toCode(keys.getName(code), code) }
else else
self.fired = false self.fired = false
end end
@ -80,7 +87,7 @@ function input:translate(event, code, p1, p2)
elseif event == 'char' then elseif event == 'char' then
if not self:modifierPressed() then if not self:modifierPressed() then
self.fired = true self.fired = true
return input:toCode(code) return { code = event, ch = input:toCode(code) }
end end
elseif event == 'key_up' then elseif event == 'key_up' then
@ -89,16 +96,18 @@ function input:translate(event, code, p1, p2)
self.fired = true self.fired = true
local ch = input:toCode(keys.getName(code), code) local ch = input:toCode(keys.getName(code), code)
self.state[code] = nil self.state[code] = nil
return ch return { code = ch }
end end
end end
self.state[code] = nil self.state[code] = nil
elseif event == 'paste' then elseif event == 'paste' then
--self.state[keys.leftCtrl] = nil
--self.state[keys.rightCtrl] = nil
self.fired = true self.fired = true
return input:toCode('paste', 255) if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] then
return { code = 'shift-paste', text = code }
else
return { code = 'paste', text = code }
end
elseif event == 'mouse_click' then elseif event == 'mouse_click' then
local buttons = { 'mouse_click', 'mouse_rightclick' } local buttons = { 'mouse_click', 'mouse_rightclick' }
@ -108,7 +117,12 @@ function input:translate(event, code, p1, p2)
elseif event == 'mouse_drag' then elseif event == 'mouse_drag' then
self.mfired = true self.mfired = true
self.fired = true self.fired = true
return input:toCode('mouse_drag', 255) return {
code = input:toCode('mouse_drag', 255),
button = code,
x = p1,
y = p2,
}
elseif event == 'mouse_up' then elseif event == 'mouse_up' then
if not self.mfired then if not self.mfired then
@ -130,15 +144,27 @@ function input:translate(event, code, p1, p2)
self.mfired = input:toCode(self.mch, 255) self.mfired = input:toCode(self.mch, 255)
end end
self.fired = true self.fired = true
return self.mfired return {
code = self.mfired,
button = code,
x = p1,
y = p2,
}
elseif event == "mouse_scroll" then elseif event == "mouse_scroll" then
local directions = { local directions = {
[ -1 ] = 'scrollUp', [ -1 ] = 'scroll_up',
[ 1 ] = 'scrollDown' [ 1 ] = 'scroll_down'
} }
self.fired = true self.fired = true
return input:toCode(directions[code], 255) return {
code = input:toCode(directions[code], 255),
x = p1,
y = p2,
}
elseif event == 'terminate' then
return { code = 'terminate' }
end end
end end
@ -146,7 +172,7 @@ function input:test()
while true do while true do
local ch = self:translate(os.pullEvent()) local ch = self:translate(os.pullEvent())
if ch then if ch then
print('GOT: ' .. tostring(ch)) Util.print(ch)
end end
end end
end end

View File

@ -9,23 +9,8 @@
This work is under MIT-LICENSE This work is under MIT-LICENSE
Copyright (c) 2012-2013 Roland Yonaba. Copyright (c) 2012-2013 Roland Yonaba.
Permission is hereby granted, free of charge, to any person obtaining a copy -- https://opensource.org/licenses/MIT
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.
--]] --]]
local _VERSION = "" local _VERSION = ""

View File

@ -11,24 +11,7 @@ local sha1 = {
Copyright (c) 2013 Enrique Garcia Cota + Eike Decker + Jeffrey Friedl Copyright (c) 2013 Enrique Garcia Cota + Eike Decker + Jeffrey Friedl
Permission is hereby granted, free of charge, to any person obtaining a https://opensource.org/licenses/MIT
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.
]] ]]
} }

View File

@ -50,8 +50,9 @@ function Terminal.scrollable(win, maxScroll)
function win.write(text) function win.write(text)
local _, h = win.getSize() local _, h = win.getSize()
text = tostring(text) or ''
scrollTo(#lines - h) scrollTo(#lines - h)
win.blit(tostring(text), win.blit(text,
_rep(palette[win.getTextColor()], #text), _rep(palette[win.getTextColor()], #text),
_rep(palette[win.getBackgroundColor()], #text)) _rep(palette[win.getBackgroundColor()], #text))
local x, y = win.getCursorPos() local x, y = win.getCursorPos()

View File

@ -1,5 +1,3 @@
_G.requireInjector()
local Grid = require('jumper.grid') local Grid = require('jumper.grid')
local Pathfinder = require('jumper.pathfinder') local Pathfinder = require('jumper.pathfinder')
local Point = require('point') local Point = require('point')

View File

@ -3,7 +3,6 @@ local class = require('class')
local Event = require('event') local Event = require('event')
local Input = require('input') local Input = require('input')
local Peripheral = require('peripheral') local Peripheral = require('peripheral')
local Terminal = require('terminal')
local Transition = require('ui.transition') local Transition = require('ui.transition')
local Util = require('util') local Util = require('util')
@ -46,12 +45,12 @@ end
local Manager = class() local Manager = class()
function Manager:init() function Manager:init()
local function keyFunction(event, code, held) local function keyFunction(event, code, held)
local ch = Input:translate(event, code, held) local ie = Input:translate(event, code, held)
if ch and self.currentPage then if ie and self.currentPage then
local target = self.currentPage.focused or self.currentPage local target = self.currentPage.focused or self.currentPage
self:inputEvent(target, self:inputEvent(target,
{ type = 'key', key = ch, element = target }) { type = 'key', key = ie.code == 'char' and ie.ch or ie.code, element = target })
self.currentPage:sync() self.currentPage:sync()
end end
end end
@ -94,10 +93,10 @@ function Manager:init()
-- this should be moved to the device ! -- this should be moved to the device !
monitor_touch = function(_, side, x, y) monitor_touch = function(_, side, x, y)
Input:translate('mouse_click', 1, x, y) Input:translate('mouse_click', 1, x, y)
local ch = Input:translate('mouse_up', 1, x, y) local ie = Input:translate('mouse_up', 1, x, y)
if self.currentPage then if self.currentPage then
if self.currentPage.parent.device.side == side then if self.currentPage.parent.device.side == side then
self:click(ch, 1, x, y) self:click(ie.code, 1, x, y)
end end
end end
end, end,
@ -117,27 +116,27 @@ function Manager:init()
end, end,
mouse_up = function(_, button, x, y) mouse_up = function(_, button, x, y)
local ch = Input:translate('mouse_up', button, x, y) local ie = Input:translate('mouse_up', button, x, y)
if ch == 'control-shift-mouse_click' then -- hack if ie.code == 'control-shift-mouse_click' then -- hack
local event = self.currentPage:pointToChild(x, y) local event = self.currentPage:pointToChild(x, y)
_ENV.multishell.openTab({ _ENV.multishell.openTab({
path = 'sys/apps/Lua.lua', path = 'sys/apps/Lua.lua',
args = { event.element }, args = { event.element },
focused = true }) focused = true })
elseif ch and self.currentPage then elseif ie and self.currentPage then
--if not self.currentPage.parent.device.side then --if not self.currentPage.parent.device.side then
self:click(ch, button, x, y) self:click(ie.code, button, x, y)
--end --end
end end
end, end,
mouse_drag = function(_, button, x, y) mouse_drag = function(_, button, x, y)
local ch = Input:translate('mouse_drag', button, x, y) local ie = Input:translate('mouse_drag', button, x, y)
if ch and self.currentPage then if ie and self.currentPage then
local event = self.currentPage:pointToChild(x, y) local event = self.currentPage:pointToChild(x, y)
event.type = ch event.type = ie.code
self:inputEvent(event.element, event) self:inputEvent(event.element, event)
self.currentPage:sync() self.currentPage:sync()
end end
@ -2298,7 +2297,7 @@ function UI.Wizard:enable()
child:disable() child:disable()
end end
end end
self:emit({ type = 'enable_view', view = Util.find(self.pages, 'index', 1) }) self:emit({ type = 'enable_view', next = Util.find(self.pages, 'index', 1) })
end end
function UI.Wizard:nextView() function UI.Wizard:nextView()
@ -2327,29 +2326,44 @@ end
function UI.Wizard:eventHandler(event) function UI.Wizard:eventHandler(event)
if event.type == 'nextView' then if event.type == 'nextView' then
self:nextView() local currentView = Util.find(self.pages, 'enabled', true)
self:draw() local nextView = Util.find(self.pages, 'index', currentView.index + 1)
return true currentView:emit({ type = 'enable_view', next = nextView, current = currentView })
elseif event.type == 'previousView' then elseif event.type == 'previousView' then
self:prevView() local currentView = Util.find(self.pages, 'enabled', true)
self:draw() local nextView = Util.find(self.pages, 'index', currentView.index - 1)
currentView:emit({ type = 'enable_view', prev = nextView, current = currentView })
return true return true
elseif event.type == 'enable_view' then elseif event.type == 'enable_view' then
if Util.find(self.pages, 'index', event.view.index - 1) then if event.current then
if event.next then
self:addTransition('slideLeft')
elseif event.prev then
self:addTransition('slideRight')
end
event.current:disable()
end
-- a new current view
local current = event.next or event.prev
current:enable()
if Util.find(self.pages, 'index', current.index - 1) then
self.previousButton:enable() self.previousButton:enable()
else else
self.previousButton:disable() self.previousButton:disable()
end end
if Util.find(self.pages, 'index', event.view.index + 1) then if Util.find(self.pages, 'index', current.index + 1) then
self.nextButton.text = 'Next >' self.nextButton.text = 'Next >'
self.nextButton.event = 'nextView' self.nextButton.event = 'nextView'
else else
self.nextButton.text = 'Accept' self.nextButton.text = 'Accept'
self.nextButton.event = 'accept' self.nextButton.event = 'accept'
end end
self:draw()
end end
end end
@ -2422,8 +2436,9 @@ UI.Embedded.defaults = {
function UI.Embedded:setParent() function UI.Embedded:setParent()
UI.Window.setParent(self) UI.Window.setParent(self)
self.win = window.create(UI.term.device, 1, 1, self.width, self.height, false) self.win = window.create(UI.term.device, 1, 1, self.width, self.height, false)
Canvas.convertWindow(self.win, UI.term.device, self.x, self.y) Canvas.scrollingWindow(self.win, self.x, self.y)
Terminal.scrollable(self.win, 100) self.win.setParent(UI.term.device)
self.win.setMaxScroll(100)
local canvas = self:getCanvas() local canvas = self:getCanvas()
self.win.canvas.parent = canvas self.win.canvas.parent = canvas

View File

@ -128,7 +128,7 @@ function Canvas:write(x, y, text, bg, fg)
end end
function Canvas:writeBlit(x, y, text, bg, fg) function Canvas:writeBlit(x, y, text, bg, fg)
if y > 0 and y <= self.height and x <= self.width then if y > 0 and y <= #self.lines and x <= self.width then
local width = #text local width = #text
-- fix ffs -- fix ffs
@ -367,4 +367,132 @@ function Canvas.convertWindow(win, parent, wx, wy)
win.clear() win.clear()
end end
function Canvas.scrollingWindow(win, wx, wy)
local w, h = win.getSize()
local scrollPos = 0
local maxScroll = h
-- canvas lines are are a sliding window within the local lines table
local lines = { }
local parent
local canvas = Canvas({
x = wx,
y = wy,
width = w,
height = h,
isColor = win.isColor(),
})
win.canvas = canvas
local function scrollTo(p, forceRedraw)
local ms = #lines - canvas.height -- max scroll
p = math.min(math.max(p, 0), ms) -- normalize
if p ~= scrollPos or forceRedraw then
scrollPos = p
for i = 1, canvas.height do
canvas.lines[i] = lines[i + scrollPos]
end
canvas:dirty()
end
end
function win.blit(text, fg, bg)
local x, y = win.getCursorPos()
win.canvas:writeBlit(x, y, text, bg, fg)
win.redraw()
end
function win.clear()
lines = { }
for i = 1, canvas.height do
lines[i] = canvas.lines[i]
end
scrollPos = 0
canvas:clear(win.getBackgroundColor(), win.getTextColor())
win.redraw()
end
function win.clearLine()
local _, y = win.getCursorPos()
scrollTo(#lines - canvas.height)
win.canvas:write(1,
y,
_rep(' ', win.canvas.width),
win.getBackgroundColor(),
win.getTextColor())
win.redraw()
end
function win.redraw()
if parent and canvas.visible then
local x, y = win.getCursorPos()
for i = 1, canvas.height do
local line = canvas.lines[i]
if line and line.dirty then
parent.setCursorPos(canvas.x, canvas.y + i - 1)
parent.blit(line.text, line.fg, line.bg)
line.dirty = false
end
end
win.setCursorPos(x, y)
end
end
-- doesn't support negative scrolling...
function win.scroll(n)
for _ = 1, n do
lines[#lines + 1] = {
text = _rep(' ', canvas.width),
fg = _rep(canvas.palette[win.getTextColor()], canvas.width),
bg = _rep(canvas.palette[win.getBackgroundColor()], canvas.width),
}
end
while #lines > maxScroll do
table.remove(lines, 1)
end
scrollTo(maxScroll, true)
win.redraw()
end
function win.scrollDown()
scrollTo(scrollPos + 1)
win.redraw()
end
function win.scrollUp()
scrollTo(scrollPos - 1)
win.redraw()
end
function win.setMaxScroll(ms)
maxScroll = ms
end
function win.setParent(p)
parent = p
end
function win.write(str)
str = tostring(str) or ''
local x, y = win.getCursorPos()
scrollTo(#lines - canvas.height)
win.blit(str,
_rep(canvas.palette[win.getTextColor()], #str),
_rep(canvas.palette[win.getBackgroundColor()], #str))
win.setCursorPos(x + #str, y)
end
function win.reposition(x, y, width, height)
win.canvas.x, win.canvas.y = x, y
win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
end
win.clear()
end
return Canvas return Canvas

View File

@ -1,4 +1,3 @@
-------------------------------------------------------------------------------
-- --
-- tek.lib.region -- tek.lib.region
-- Written by Timm S. Mueller <tmueller at schulze-mueller.de> -- Written by Timm S. Mueller <tmueller at schulze-mueller.de>
@ -9,49 +8,11 @@
-- * Franciska Schulze <fschulze at schulze-mueller.de> -- * Franciska Schulze <fschulze at schulze-mueller.de>
-- * Tobias Schwinger <tschwinger at isonews2.com> -- * Tobias Schwinger <tschwinger at isonews2.com>
-- --
-- Permission is hereby granted, free of charge, to any person obtaining -- https://opensource.org/licenses/MIT
-- 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 -- Some comments have been removed to reduce file size, see:
-- included in all copies or substantial portions of the Software. -- https://github.com/technosaurus/tekui/blob/master/etc/region.lua
-- -- for the full source
-- === 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 insert = table.insert
local ipairs = ipairs local ipairs = ipairs
@ -65,24 +26,18 @@ Region._VERSION = "Region 11.3"
Region.__index = Region Region.__index = Region
-------------------------------------------------------------------------------
-- x0, y0, x1, y1 = Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4): -- 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 -- Returns the coordinates of a rectangle where a rectangle specified by
-- the coordinates s1, s2, s3, s4 overlaps with the rectangle specified -- the coordinates s1, s2, s3, s4 overlaps with the rectangle specified
-- by the coordinates d1, d2, d3, d4. The return value is '''nil''' if -- by the coordinates d1, d2, d3, d4. The return value is '''nil''' if
-- the rectangles do not overlap. -- the rectangles do not overlap.
-------------------------------------------------------------------------------
function Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4) function Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4)
if s3 >= d1 and s1 <= d3 and s4 >= d2 and s2 <= d4 then 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) return max(s1, d1), max(s2, d2), min(s3, d3), min(s4, d4)
end end
end end
-------------------------------------------------------------------------------
-- insertrect: insert rect to table, merging with an existing one if possible -- insertrect: insert rect to table, merging with an existing one if possible
-------------------------------------------------------------------------------
local function insertrect(d, s1, s2, s3, s4) local function insertrect(d, s1, s2, s3, s4)
for i = 1, min(4, #d) do for i = 1, min(4, #d) do
local a = d[i] local a = d[i]
@ -108,10 +63,7 @@ local function insertrect(d, s1, s2, s3, s4)
insert(d, 1, { s1, s2, s3, s4 }) insert(d, 1, { s1, s2, s3, s4 })
end end
-------------------------------------------------------------------------------
-- cutrect: cut rect d into table of new rects, using rect s as a punch -- 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) local function cutrect(d1, d2, d3, d4, s1, s2, s3, s4)
if not Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4) then if not Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4) then
return { { d1, d2, d3, d4 } } return { { d1, d2, d3, d4 } }
@ -135,10 +87,7 @@ local function cutrect(d1, d2, d3, d4, s1, s2, s3, s4)
return r return r
end end
-------------------------------------------------------------------------------
-- cutregion: cut region d, using s as a punch -- cutregion: cut region d, using s as a punch
-------------------------------------------------------------------------------
local function cutregion(d, s1, s2, s3, s4) local function cutregion(d, s1, s2, s3, s4)
local r = { } local r = { }
for _, dr in ipairs(d) do for _, dr in ipairs(d) do
@ -150,11 +99,8 @@ local function cutregion(d, s1, s2, s3, s4)
return r return r
end end
-------------------------------------------------------------------------------
-- region = Region.new(r1, r2, r3, r4): Creates a new region from the given -- region = Region.new(r1, r2, r3, r4): Creates a new region from the given
-- coordinates. -- coordinates.
-------------------------------------------------------------------------------
function Region.new(r1, r2, r3, r4) function Region.new(r1, r2, r3, r4)
if r1 then if r1 then
return setmetatable({ region = { { r1, r2, r3, r4 } } }, Region) return setmetatable({ region = { { r1, r2, r3, r4 } } }, Region)
@ -162,39 +108,27 @@ function Region.new(r1, r2, r3, r4)
return setmetatable({ region = { } }, Region) return setmetatable({ region = { } }, Region)
end end
-------------------------------------------------------------------------------
-- self = region:setRect(r1, r2, r3, r4): Resets an existing region -- self = region:setRect(r1, r2, r3, r4): Resets an existing region
-- to the specified rectangle. -- to the specified rectangle.
-------------------------------------------------------------------------------
function Region:setRect(r1, r2, r3, r4) function Region:setRect(r1, r2, r3, r4)
self.region = { { r1, r2, r3, r4 } } self.region = { { r1, r2, r3, r4 } }
return self return self
end end
-------------------------------------------------------------------------------
-- region:orRect(r1, r2, r3, r4): Logical ''or''s a rectangle to a region -- region:orRect(r1, r2, r3, r4): Logical ''or''s a rectangle to a region
-------------------------------------------------------------------------------
function Region:orRect(s1, s2, s3, s4) function Region:orRect(s1, s2, s3, s4)
self.region = cutregion(self.region, s1, s2, s3, s4) self.region = cutregion(self.region, s1, s2, s3, s4)
insertrect(self.region, s1, s2, s3, s4) insertrect(self.region, s1, s2, s3, s4)
end end
-------------------------------------------------------------------------------
-- region:orRegion(region): Logical ''or''s another region to a region -- region:orRegion(region): Logical ''or''s another region to a region
-------------------------------------------------------------------------------
function Region:orRegion(s) function Region:orRegion(s)
for _, r in ipairs(s) do for _, r in ipairs(s) do
self:orRect(r[1], r[2], r[3], r[4]) self:orRect(r[1], r[2], r[3], r[4])
end end
end end
-------------------------------------------------------------------------------
-- region:andRect(r1, r2, r3, r4): Logical ''and''s a rectange to a region -- region:andRect(r1, r2, r3, r4): Logical ''and''s a rectange to a region
-------------------------------------------------------------------------------
function Region:andRect(s1, s2, s3, s4) function Region:andRect(s1, s2, s3, s4)
local r = { } local r = { }
for _, d in ipairs(self.region) do for _, d in ipairs(self.region) do
@ -207,10 +141,7 @@ function Region:andRect(s1, s2, s3, s4)
self.region = r self.region = r
end end
-------------------------------------------------------------------------------
-- region:xorRect(r1, r2, r3, r4): Logical ''xor''s a rectange to a region -- region:xorRect(r1, r2, r3, r4): Logical ''xor''s a rectange to a region
-------------------------------------------------------------------------------
function Region:xorRect(s1, s2, s3, s4) function Region:xorRect(s1, s2, s3, s4)
local r1 = { } local r1 = { }
local r2 = { { s1, s2, s3, s4 } } local r2 = { { s1, s2, s3, s4 } }
@ -225,10 +156,7 @@ function Region:xorRect(s1, s2, s3, s4)
self:orRegion(r2) self:orRegion(r2)
end end
-------------------------------------------------------------------------------
-- self = region:subRect(r1, r2, r3, r4): Subtracts a rectangle from a region -- self = region:subRect(r1, r2, r3, r4): Subtracts a rectangle from a region
-------------------------------------------------------------------------------
function Region:subRect(s1, s2, s3, s4) function Region:subRect(s1, s2, s3, s4)
local r1 = { } local r1 = { }
for _, d in ipairs(self.region) do for _, d in ipairs(self.region) do
@ -241,10 +169,7 @@ function Region:subRect(s1, s2, s3, s4)
return self return self
end end
-------------------------------------------------------------------------------
-- region:getRect - gets an iterator on the rectangles in a region [internal] -- region:getRect - gets an iterator on the rectangles in a region [internal]
-------------------------------------------------------------------------------
function Region:getRects() function Region:getRects()
local index = 0 local index = 0
return function(object) return function(object)
@ -255,12 +180,9 @@ function Region:getRects()
end, self.region end, self.region
end end
-------------------------------------------------------------------------------
-- success = region:checkIntersect(x0, y0, x1, y1): Returns a boolean -- success = region:checkIntersect(x0, y0, x1, y1): Returns a boolean
-- indicating whether a rectangle specified by its coordinates overlaps -- indicating whether a rectangle specified by its coordinates overlaps
-- with a region. -- with a region.
-------------------------------------------------------------------------------
function Region:checkIntersect(s1, s2, s3, s4) function Region:checkIntersect(s1, s2, s3, s4)
for _, d in ipairs(self.region) do for _, d in ipairs(self.region) do
if Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4) then if Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4) then
@ -270,10 +192,7 @@ function Region:checkIntersect(s1, s2, s3, s4)
return false return false
end end
-------------------------------------------------------------------------------
-- region:subRegion(region2): Subtracts {{region2}} from {{region}}. -- region:subRegion(region2): Subtracts {{region2}} from {{region}}.
-------------------------------------------------------------------------------
function Region:subRegion(region) function Region:subRegion(region)
if region then if region then
for r1, r2, r3, r4 in region:getRects() do for r1, r2, r3, r4 in region:getRects() do
@ -282,10 +201,7 @@ function Region:subRegion(region)
end end
end end
-------------------------------------------------------------------------------
-- region:andRegion(r): Logically ''and''s a region to a region -- region:andRegion(r): Logically ''and''s a region to a region
-------------------------------------------------------------------------------
function Region:andRegion(s) function Region:andRegion(s)
local r = { } local r = { }
for _, s in ipairs(s.region) do for _, s in ipairs(s.region) do
@ -301,23 +217,17 @@ function Region:andRegion(s)
self.region = r self.region = r
end end
-------------------------------------------------------------------------------
-- region:forEach(func, obj, ...): For each rectangle in a region, calls the -- region:forEach(func, obj, ...): For each rectangle in a region, calls the
-- specified function according the following scheme: -- specified function according the following scheme:
-- func(obj, x0, y0, x1, y1, ...) -- func(obj, x0, y0, x1, y1, ...)
-- Extra arguments are passed through to the function. -- Extra arguments are passed through to the function.
-------------------------------------------------------------------------------
function Region:forEach(func, obj, ...) function Region:forEach(func, obj, ...)
for x0, y0, x1, y1 in self:getRects() do for x0, y0, x1, y1 in self:getRects() do
func(obj, x0, y0, x1, y1, ...) func(obj, x0, y0, x1, y1, ...)
end end
end end
-------------------------------------------------------------------------------
-- region:shift(dx, dy): Shifts a region by delta x and y. -- region:shift(dx, dy): Shifts a region by delta x and y.
-------------------------------------------------------------------------------
function Region:shift(dx, dy) function Region:shift(dx, dy)
for _, r in ipairs(self.region) do for _, r in ipairs(self.region) do
r[1] = r[1] + dx r[1] = r[1] + dx
@ -327,18 +237,12 @@ function Region:shift(dx, dy)
end end
end end
-------------------------------------------------------------------------------
-- region:isEmpty(): Returns '''true''' if a region is empty. -- region:isEmpty(): Returns '''true''' if a region is empty.
-------------------------------------------------------------------------------
function Region:isEmpty() function Region:isEmpty()
return #self.region == 0 return #self.region == 0
end end
-------------------------------------------------------------------------------
-- minx, miny, maxx, maxy = region:get(): Get region's min/max extents -- minx, miny, maxx, maxy = region:get(): Get region's min/max extents
-------------------------------------------------------------------------------
function Region:get() function Region:get()
if #self.region > 0 then if #self.region > 0 then
local minx = 1000000 -- ui.HUGE local minx = 1000000 -- ui.HUGE

View File

@ -7,24 +7,7 @@ local tween = {
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
Permission is hereby granted, free of charge, to any person obtaining a Licence details: https://opensource.org/licenses/MIT
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.
]] ]]
} }

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local UI = require('ui') local UI = require('ui')
local Util = require('util') local Util = require('util')

456
sys/apps/Installer.lua Normal file
View File

@ -0,0 +1,456 @@
local colors = _G.colors
local fs = _G.fs
local http = _G.http
local install = _ENV.install
local os = _G.os
local requireInjector
if not install.testing then
local url ='https://raw.githubusercontent.com/kepler155c/opus/master/sys/apis/injector.lua'
requireInjector = load(http.get(url).readAll())()
else
requireInjector = _G.requireInjector
end
requireInjector(_ENV)
if not install.testing then
if package then
for _ = 1, 4 do
table.remove(package.loaders, 1)
end
end
end
local Git = require('git')
local UI = require('ui')
local Util = require('util')
local currentFile = ''
local currentProgress = 0
local cancelEvent
local args = { ... }
local steps = install.steps[args[1] or 'install']
if not steps then
error('Invalid install type')
end
local mode = steps[#steps]
if UI.term.width < 32 then
cancelEvent = 'quit'
end
local page = UI.Page {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
event = cancelEvent,
},
wizard = UI.Wizard {
y = 2, ey = -2,
},
notification = UI.Notification(),
accelerators = {
q = 'quit',
},
}
local pages = {
splash = UI.Viewport { },
review = UI.Viewport { },
license = UI.Viewport {
backgroundColor = colors.black,
},
branch = UI.Window {
grid = UI.ScrollingGrid {
ey = -3,
columns = {
{ heading = 'Branch', key = 'branch' },
{ heading = 'Description', key = 'description' },
},
values = install.branches,
autospace = true,
},
},
files = UI.Window {
grid = UI.ScrollingGrid {
ey = -3,
columns = {
{ heading = 'Files', key = 'file' },
},
sortColumn = 'file',
},
},
install = UI.Window {
progressBar = UI.ProgressBar {
y = -1,
},
},
uninstall = UI.Window {
progressBar = UI.ProgressBar {
y = -1,
},
},
}
local function getFileList()
if install.gitRepo then
local gitFiles = Git.list(string.format('%s/%s', install.gitRepo, install.gitBranch or 'master'))
install.files = { }
install.diskspace = 0
for path, entry in pairs(gitFiles) do
install.files[path] = entry.url
install.diskspace = install.diskspace + entry.size
end
end
if not install.files or Util.empty(install.files) then
error('File list is missing or empty')
end
end
--[[ Splash ]]--
function pages.splash:enable()
page.titleBar.title = 'Installer v1.0'
UI.Viewport.enable(self)
end
function pages.splash:draw()
self:clear()
self:setCursorPos(1, 1)
self:print(
string.format('%s v%s\n', install.title, install.version), nil, colors.yellow)
self:print(
string.format('By: %s\n\n%s\n', install.author, install.description))
self.ymax = self.cursorY
end
--[[ License ]]--
function pages.license:enable()
page.titleBar.title = 'License Review'
page.wizard.nextButton.text = 'Accept'
UI.Viewport.enable(self)
end
function pages.license:draw()
self:clear()
self:setCursorPos(1, 1)
self:print(
string.format('Copyright (c) %s %s\n\n', install.copyrightYear,
install.copyrightHolders),
nil, colors.yellow)
self:print(install.license)
self.ymax = self.cursorY + 1
end
--[[ Review ]]--
function pages.review:enable()
if mode == 'uninstall' then
page.nextButton.text = 'Remove'
page.titleBar.title = 'Remove Installed Files'
else
page.wizard.nextButton.text = 'Begin'
page.titleBar.title = 'Download and Install'
end
UI.Viewport.enable(self)
end
function pages.review:draw()
self:clear()
self:setCursorPos(1, 1)
local text = 'Ready to begin installation.\n\nProceeding will download and install the files to the hard drive.'
if mode == 'uninstall' then
text = 'Ready to begin.\n\nProceeding will remove the files previously installed.'
end
self:print(text)
self.ymax = self.cursorY + 1
end
--[[ Files ]]--
function pages.files:enable()
page.titleBar.title = 'Review Files'
self.grid.values = { }
for k,v in pairs(install.files) do
table.insert(self.grid.values, { file = k, code = v })
end
self.grid:update()
UI.Window.enable(self)
end
function pages.files:draw()
self:clear()
local function formatSize(size)
if size >= 1000000 then
return string.format('%dM', math.floor(size/1000000, 2))
elseif size >= 1000 then
return string.format('%dK', math.floor(size/1000, 2))
end
return size
end
if install.diskspace then
local bg = self.backgroundColor
local diskFree = fs.getFreeSpace('/')
if install.diskspace > diskFree then
bg = colors.red
end
local text = string.format('Space Required: %s, Free: %s',
formatSize(install.diskspace), formatSize(diskFree))
if #text > self.width then
text = string.format('Space: %s Free: %s',
formatSize(install.diskspace), formatSize(diskFree))
end
self:write(1, self.height, Util.widthify(text, self.width), bg)
end
self.grid:draw()
end
--[[
function pages.files:view(url)
local s, m = pcall(function()
page.notification:info('Downloading')
page:sync()
Util.download(url, '/.source')
end)
page.notification:disable()
if s then
shell.run('edit /.source')
fs.delete('/.source')
page:draw()
page.notification:cancel()
else
page.notification:error(m:gsub('.*: (.*)', '%1'))
end
end
function pages.files:eventHandler(event)
if event.type == 'grid_select' then
self:view(event.selected.code)
return true
end
end
--]]
local function drawCommon(self)
if currentFile then
self:write(1, 3, 'File:')
self:write(1, 4, Util.widthify(currentFile, self.width))
else
self:write(1, 3, 'Finished')
end
if self.failed then
self:write(1, 5, Util.widthify(self.failed, self.width), colors.red)
end
self:write(1, self.height - 1, 'Progress')
self.progressBar.value = currentProgress
self.progressBar:draw()
self:sync()
end
--[[ Branch ]]--
function pages.branch:enable()
page.titleBar.title = 'Select Branch'
UI.Window.enable(self)
end
function pages.branch:eventHandler(event)
-- user is navigating to next view (not previous)
if event.type == 'enable_view' and event.next then
install.gitBranch = self.grid:getSelected().branch
getFileList()
end
end
--[[ Install ]]--
function pages.install:enable()
page.wizard.cancelButton:disable()
page.wizard.previousButton:disable()
page.wizard.nextButton:disable()
page.titleBar.title = 'Installing...'
page.titleBar.event = nil
UI.Window.enable(self)
page:draw()
page:sync()
local i = 0
local numFiles = Util.size(install.files)
for filename,url in pairs(install.files) do
currentFile = filename
currentProgress = i / numFiles * 100
self:draw(self)
self:sync()
local s, m = pcall(function()
Util.download(url, fs.combine(install.directory or '', filename))
end)
if not s then
self.failed = m:gsub('.*: (.*)', '%1')
break
end
i = i + 1
end
if not self.failed then
currentProgress = 100
currentFile = nil
if install.postInstall then
local s, m = pcall(function() install.postInstall(page, UI) end)
if not s then
self.failed = m:gsub('.*: (.*)', '%1')
end
end
end
page.wizard.nextButton.text = 'Exit'
page.wizard.nextButton.event = 'quit'
if not self.failed and install.rebootAfter then
page.wizard.nextButton.text = 'Reboot'
page.wizard.nextButton.event = 'reboot'
end
page.wizard.nextButton:enable()
page:draw()
page:sync()
if not self.failed and Util.key(args, 'automatic') then
if install.rebootAfter then
os.reboot()
else
UI:exitPullEvents()
end
end
end
function pages.install:draw()
self:clear()
local text = 'The files are being installed'
if #text > self.width then
text = 'Installing files'
end
self:write(1, 1, text, nil, colors.yellow)
drawCommon(self)
end
--[[ Uninstall ]]--
function pages.uninstall:enable()
page.wizard.cancelButton:disable()
page.wizard.previousButton:disable()
page.wizard.nextButton:disable()
page.titleBar.title = 'Uninstalling...'
page.titleBar.event = nil
page:draw()
page:sync()
UI.Window.enable(self)
local function pruneDir(dir)
if #dir > 0 then
if fs.exists(dir) then
local files = fs.list(dir)
if #files == 0 then
fs.delete(dir)
pruneDir(fs.getDir(dir))
end
end
end
end
local i = 0
local numFiles = Util.size(install.files)
for k in pairs(install.files) do
currentFile = k
currentProgress = i / numFiles * 100
self:draw()
self:sync()
fs.delete(k)
pruneDir(fs.getDir(k))
i = i + 1
end
currentProgress = 100
currentFile = nil
page.wizard.nextButton.text = 'Exit'
page.wizard.nextButton.event = 'quit'
page.wizard.nextButton:enable()
page:draw()
page:sync()
end
function pages.uninstall:draw()
self:clear()
self:write(1, 1, 'Uninstalling files', nil, colors.yellow)
drawCommon(self)
end
function page:eventHandler(event)
if event.type == 'cancel' then
UI:exitPullEvents()
elseif event.type == 'reboot' then
os.reboot()
elseif event.type == 'quit' then
UI:exitPullEvents()
else
return UI.Page.eventHandler(self, event)
end
return true
end
function page:enable()
UI.Page.enable(self)
self:setFocus(page.wizard.nextButton)
if UI.term.width < 32 then
page.wizard.cancelButton:disable()
page.wizard.previousButton.x = 2
end
end
getFileList()
local wizardPages = { }
for k,v in ipairs(steps) do
if not pages[v] then
error('Invalid step: ' .. v)
end
wizardPages[k] = pages[v]
wizardPages[k].index = k
wizardPages[k].x = 2
wizardPages[k].y = 2
wizardPages[k].ey = -3
wizardPages[k].ex = -2
end
page.wizard:add(wizardPages)
if Util.key(steps, 'install') and install.preInstall then
install.preInstall(page, UI)
end
UI:setPage(page)
local s, m = pcall(function() UI:pullEvents() end)
if not s then
UI.term:reset()
_G.printError(m)
end

View File

@ -1,6 +1,5 @@
_G.requireInjector(_ENV) _G.requireInjector(_ENV)
local Event = require('event')
local History = require('history') local History = require('history')
local UI = require('ui') local UI = require('ui')
local Util = require('util') local Util = require('util')
@ -10,8 +9,10 @@ local os = _G.os
local textutils = _G.textutils local textutils = _G.textutils
local term = _G.term local term = _G.term
local _exit
local sandboxEnv = setmetatable(Util.shallowCopy(_ENV), { __index = _G }) local sandboxEnv = setmetatable(Util.shallowCopy(_ENV), { __index = _G })
sandboxEnv.exit = function() Event.exitPullEvents() end sandboxEnv.exit = function() _exit = true end
sandboxEnv._echo = function( ... ) return { ... } end sandboxEnv._echo = function( ... ) return { ... } end
_G.requireInjector(sandboxEnv) _G.requireInjector(sandboxEnv)
@ -19,7 +20,6 @@ UI:configure('Lua', ...)
local command = '' local command = ''
local history = History.load('usr/.lua_history', 25) local history = History.load('usr/.lua_history', 25)
local extChars = Util.getVersion() > 1.76
local page = UI.Page { local page = UI.Page {
menuBar = UI.MenuBar { menuBar = UI.MenuBar {
@ -41,10 +41,6 @@ local page = UI.Page {
[ 'control-space' ] = 'autocomplete', [ 'control-space' ] = 'autocomplete',
}, },
}, },
indicator = UI.Text {
backgroundColor = colors.black,
y = 2, x = -1, width = 1,
},
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 3, ey = -2, y = 3, ey = -2,
columns = { columns = {
@ -62,21 +58,10 @@ local page = UI.Page {
}, },
output = UI.Embedded { output = UI.Embedded {
y = -6, y = -6,
backgroundColor = colors.gray,
}, },
--notification = UI.Notification(),
} }
function page.indicator:showResult(s)
local values = {
[ true ] = { c = colors.green, i = (extChars and '\003') or '+' },
[ false ] = { c = colors.red, i = 'x' }
}
self.textColor = values[s].c
self.value = values[s].i
self:draw()
end
function page:setPrompt(value, focus) function page:setPrompt(value, focus)
self.prompt:setValue(value) self.prompt:setValue(value)
self.prompt.scroll = 0 self.prompt.scroll = 0
@ -133,12 +118,12 @@ end
function page:eventHandler(event) function page:eventHandler(event)
if event.type == 'global' then if event.type == 'global' then
self:setPrompt('', true) self:setPrompt('_G', true)
self:executeStatement('_G') self:executeStatement('_G')
command = nil command = nil
elseif event.type == 'local' then elseif event.type == 'local' then
self:setPrompt('', true) self:setPrompt('_ENV', true)
self:executeStatement('_ENV') self:executeStatement('_ENV')
command = nil command = nil
@ -341,8 +326,11 @@ end
function page:executeStatement(statement) function page:executeStatement(statement)
command = statement command = statement
local s, m
local oterm = term.redirect(self.output.win) local oterm = term.redirect(self.output.win)
local s, m = self:rawExecute(command) pcall(function()
s, m = self:rawExecute(command)
end)
if not s then if not s then
_G.printError(m) _G.printError(m)
end end
@ -353,14 +341,14 @@ function page:executeStatement(statement)
else else
self.grid:setValues({ }) self.grid:setValues({ })
self.grid:draw() self.grid:draw()
if m then if m and not self.output.enabled then
if not self.output.enabled then
self:emit({ type = 'show_output' }) self:emit({ type = 'show_output' })
end end
--self.notification:error(m, 5)
end end
if _exit then
UI:exitPullEvents()
end end
self.indicator:showResult(not not s)
end end
local args = { ... } local args = { ... }
@ -371,5 +359,4 @@ if args[1] then
end end
UI:setPage(page) UI:setPage(page)
Event.pullEvents() UI:pullEvents()
UI.term:reset()

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local class = require('class') local class = require('class')
local Config = require('config') local Config = require('config')

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Config = require('config') local Config = require('config')
local Security = require('security') local Security = require('security')
@ -201,9 +201,13 @@ end
if settings then if settings then
local values = { } local values = { }
for _,v in pairs(settings.getNames()) do for _,v in pairs(settings.getNames()) do
local value = settings.get(v)
if not value then
value = false
end
table.insert(values, { table.insert(values, {
name = v, name = v,
value = not not settings.get(v), value = value,
}) })
end end
@ -224,7 +228,9 @@ if settings then
}) })
function systemPage.tabs.settingsTab:eventHandler(event) function systemPage.tabs.settingsTab:eventHandler(event)
if event.type == 'grid_select' then if event.type == 'grid_select' then
if not event.selected.value or type(event.selected.value) == 'boolean' then
event.selected.value = not event.selected.value event.selected.value = not event.selected.value
end
settings.set(event.selected.name, event.selected.value) settings.set(event.selected.name, event.selected.value)
settings.save('.settings') settings.save('.settings')
self.grid:draw() self.grid:draw()

View File

@ -58,11 +58,7 @@ function page.grid:getDisplayValues(row)
else else
row.timestamp = string.format("%sm", math.floor(elapsed/6)/10) row.timestamp = string.format("%sm", math.floor(elapsed/6)/10)
end end
if row.isDead then row.status = row.isDead and 'error' or coroutine.status(row.co)
row.status = 'error'
else
row.status = coroutine.status(row.co)
end
return row return row
end end

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Event = require('event') local Event = require('event')
local Util = require('util') local Util = require('util')

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Security = require('security') local Security = require('security')
local SHA1 = require('sha1') local SHA1 = require('sha1')

View File

@ -11,7 +11,7 @@ for k,v in pairs(_ENV) do
end end
sandboxEnv.shell = shell sandboxEnv.shell = shell
_G.requireInjector() _G.requireInjector(_ENV)
local Util = require('util') local Util = require('util')
@ -118,6 +118,10 @@ function shell.resolveProgram( _sCommand )
_sCommand = tAliases[_sCommand] _sCommand = tAliases[_sCommand]
end end
if _sCommand:match("^(https?:)") then
return _sCommand
end
local path = shell.resolve(_sCommand) local path = shell.resolve(_sCommand)
if fs.exists(path) and not fs.isDir(path) then if fs.exists(path) and not fs.isDir(path) then
return path return path
@ -325,10 +329,8 @@ function shell.openTab( ... )
local sPath = shell.resolveProgram(sCommand) local sPath = shell.resolveProgram(sCommand)
if sPath == "sys/apps/shell" then if sPath == "sys/apps/shell" then
return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), sPath, table.unpack(tWords, 2)) return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), sPath, table.unpack(tWords, 2))
elseif sPath ~= nil then
return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), "sys/apps/shell", sCommand, table.unpack(tWords, 2))
else else
return false, "No such program" return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), "sys/apps/shell", sCommand, table.unpack(tWords, 2))
end end
end end
end end
@ -352,11 +354,12 @@ if #tArgs > 0 then
end end
local Config = require('config') local Config = require('config')
local Entry = require('entry')
local History = require('history') local History = require('history')
local Input = require('input')
local Terminal = require('terminal') local Terminal = require('terminal')
local colors = _G.colors local colors = _G.colors
local keys = _G.keys
local os = _G.os local os = _G.os
local term = _G.term local term = _G.term
local textutils = _G.textutils local textutils = _G.textutils
@ -538,128 +541,81 @@ local function autocomplete(line)
end end
local function shellRead(history) local function shellRead(history)
term.setCursorBlink( true ) local lastLen = 0
local entry = Entry({
local sLine = "" width = term.getSize() - 3
local nPos = 0 })
local w = term.getSize()
local sx = term.getCursorPos()
history:reset() history:reset()
term.setCursorBlink(true)
local function redraw( sReplace ) local function redraw()
local nScroll = 0
if sx + nPos >= w then
nScroll = (sx + nPos) - w
end
local _,cy = term.getCursorPos() local _,cy = term.getCursorPos()
term.setCursorPos( sx, cy ) term.setCursorPos(3, cy)
if sReplace then local filler = #entry.value < lastLen
term.write( string.rep( sReplace, math.max( string.len(sLine) - nScroll, 0 ) ) ) and string.rep(' ', lastLen - #entry.value)
else or ''
term.write( string.sub( sLine, nScroll + 1 ) ) local str = string.sub(entry.value, entry.scroll + 1)
end term.write(string.sub(str, 1, entry.width) .. filler)
term.setCursorPos( sx + nPos - nScroll, cy ) term.setCursorPos(3 + entry.pos - entry.scroll, cy)
lastLen = #entry.value
end end
while true do while true do
local sEvent, param = os.pullEventRaw() local event, p1, p2, p3 = os.pullEventRaw()
if sEvent == "char" then local ie = Input:translate(event, p1, p2, p3)
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 ) if ie then
nPos = nPos + 1 if ie.code == 'scroll_up' then
redraw()
elseif sEvent == "paste" then
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
nPos = nPos + string.len( param )
redraw()
elseif sEvent == 'mouse_click' and param == 2 then
redraw(string.rep(' ', #sLine))
sLine = ''
nPos = 0
redraw()
elseif sEvent == 'mouse_scroll' then
if param == -1 then
terminal.scrollUp() terminal.scrollUp()
else
elseif ie.code == 'scroll_down' then
terminal.scrollDown() terminal.scrollDown()
end
elseif sEvent == 'terminate' then elseif ie.code == 'terminate' then
bExit = true bExit = true
break break
elseif sEvent == "key" then
if param == keys.enter then elseif ie.code == 'enter' then
-- Enter
break break
elseif param == keys.tab then
if nPos == #sLine then elseif ie.code == 'up' or ie.code == 'down' then
local cline = autocomplete(sLine) if ie.code == 'up' then
entry.value = history:back() or ''
else
entry.value = history:forward() or ''
end
entry.pos = string.len(entry.value)
entry.scroll = 0
entry:updateScroll()
redraw()
elseif ie.code == 'tab' then
if entry.pos == #entry.value then
local cline = autocomplete(entry.value)
if cline then if cline then
sLine = cline entry.value = cline
nPos = #sLine entry.pos = #entry.value
redraw() entry:updateScroll()
end
end
elseif param == keys.left then
if nPos > 0 then
nPos = nPos - 1
redraw()
end
elseif param == keys.right then
if nPos < string.len(sLine) then
redraw(" ")
nPos = nPos + 1
redraw()
end
elseif param == keys.up or param == keys.down then
redraw(" ")
if param == keys.up then
sLine = history:back()
else
sLine = history:forward()
end
if sLine then
nPos = string.len(sLine)
else
sLine = ""
nPos = 0
end
redraw()
elseif param == keys.backspace then
if nPos > 0 then
redraw(" ")
sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
nPos = nPos - 1
redraw()
end
elseif param == keys.home then
redraw(" ")
nPos = 0
redraw()
elseif param == keys.delete then
if nPos < string.len(sLine) then
redraw(" ")
sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
redraw()
end
elseif param == keys["end"] then
redraw(" ")
nPos = string.len(sLine)
redraw()
end
elseif sEvent == "term_resize" then
w = term.getSize()
redraw() redraw()
end end
end end
local _, cy = term.getCursorPos() elseif entry:process(ie) then
term.setCursorPos( w + 1, cy ) redraw()
end
elseif event == "term_resize" then
entry.width = term.getSize() - 3
redraw()
end
end
--local _, cy = term.getCursorPos()
--term.setCursorPos( w + 1, cy )
print() print()
term.setCursorBlink( false ) term.setCursorBlink( false )
return sLine return entry.value
end end
local history = History.load('usr/.shell_history', 25) local history = History.load('usr/.shell_history', 25)

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Event = require('event') local Event = require('event')
local Socket = require('socket') local Socket = require('socket')

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Crypto = require('crypto') local Crypto = require('crypto')
local Security = require('security') local Security = require('security')

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Event = require('event') local Event = require('event')
local Socket = require('socket') local Socket = require('socket')

View File

@ -13,13 +13,13 @@ kernel.hook('clipboard_copy', function(_, args)
data = args[1] data = args[1]
end) end)
keyboard.addHotkey('control-shift-paste', function(_, args) keyboard.addHotkey('shift-paste', function()
if type(data) == 'table' then if type(data) == 'table' then
local s, m = pcall(textutils.serialize, data) local s, m = pcall(textutils.serialize, data)
data = (s and m) or Util.tostring(data) data = (s and m) or Util.tostring(data)
end end
-- replace the event paste data with our internal data -- replace the event paste data with our internal data
args[1] = Util.tostring(data or '') -- args[1] = Util.tostring(data or '')
if data then if data then
os.queueEvent('paste', data) os.queueEvent('paste', data)
end end

View File

@ -4,7 +4,7 @@ local turtle = _G.turtle
if turtle and modem then if turtle and modem then
local s, m = turtle.run(function() local s, m = turtle.run(function()
_G.requireInjector() _G.requireInjector(_ENV)
local Config = require('config') local Config = require('config')
local config = { local config = {

View File

@ -1,14 +1,19 @@
if _G.device.wireless_modem then if _G.device.wireless_modem then
_G.requireInjector() _G.requireInjector(_ENV)
local Config = require('config') local Config = require('config')
local kernel = _G.kernel
local config = { } local config = { }
Config.load('gps', config) Config.load('gps', config)
if config.host and type(config.host) == 'table' then if config.host and type(config.host) == 'table' then
_ENV._APP_TITLE = 'GPS Daemon' kernel.run({
os.run(_ENV, '/rom/programs/gps', 'host', config.host.x, config.host.y, config.host.z) title = 'GPS Daemon',
print('GPS daemon stopped') hidden = true,
path = '/rom/programs/gps',
args = { 'host', config.host.x, config.host.y, config.host.z },
})
end end
end end

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Util = require('util') local Util = require('util')
@ -10,7 +10,7 @@ local multishell = _ENV.multishell
keyboard.addHotkey('control-o', function() keyboard.addHotkey('control-o', function()
for _,tab in pairs(multishell.getTabs()) do for _,tab in pairs(multishell.getTabs()) do
if tab.isOverview then if tab.isOverview then
multishell.setFocus(tab.tabId) multishell.setFocus(tab.uid)
end end
end end
end) end)

View File

@ -10,11 +10,8 @@ local multishell = _ENV.multishell
local os = _G.os local os = _G.os
local term = _G.term local term = _G.term
local function systemLog()
local routine = kernel.getCurrent() local routine = kernel.getCurrent()
if multishell then
multishell.setTitle(multishell.getCurrent(), 'System Log')
multishell.hideTab(routine.uid)
end
local w, h = kernel.window.getSize() local w, h = kernel.window.getSize()
kernel.window.reposition(1, 2, w, h - 1) kernel.window.reposition(1, 2, w, h - 1)
@ -49,3 +46,10 @@ end)
os.pullEventRaw('terminate') os.pullEventRaw('terminate')
keyboard.removeHotkey('control-d') keyboard.removeHotkey('control-d')
end
multishell.openTab({
title = 'System Log',
fn = systemLog,
hidden = true,
})

View File

@ -1,3 +0,0 @@
term.clear()
term.setCursorPos(1, 1)
print(os.version())

View File

@ -70,8 +70,8 @@ kernel.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData)
-- and fire hotkeys -- and fire hotkeys
local hotkey = Input:translate(event, eventData[1], eventData[2]) local hotkey = Input:translate(event, eventData[1], eventData[2])
if hotkey and keyboard.hotkeys[hotkey] then if hotkey and keyboard.hotkeys[hotkey.code] then
keyboard.hotkeys[hotkey](event, eventData) keyboard.hotkeys[hotkey.code](event, eventData)
end end
end) end)

View File

@ -2,7 +2,7 @@ if fs.native then
return return
end end
_G.requireInjector() _G.requireInjector(_ENV)
local Util = require('util') local Util = require('util')
local fs = _G.fs local fs = _G.fs

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Util = require('util') local Util = require('util')

View File

@ -2,7 +2,7 @@ if not _G.turtle then
return return
end end
_G.requireInjector() _G.requireInjector(_ENV)
local Pathing = require('turtle.pathfind') local Pathing = require('turtle.pathfind')
local GPS = require('gps') local GPS = require('gps')

View File

@ -1,4 +1,4 @@
_G.requireInjector() _G.requireInjector(_ENV)
local Config = require('config') local Config = require('config')
local Util = require('util') local Util = require('util')
@ -199,6 +199,10 @@ kernel.hook('multishell_terminate', function(_, eventData)
return true return true
end) end)
kernel.hook('terminate', function()
return kernel.getFocused().isOverview
end)
kernel.hook('multishell_redraw', function() kernel.hook('multishell_redraw', function()
tabsDirty = false tabsDirty = false