2019-02-19 15:26:23 +03:00

361 lines
12 KiB
Lua

local color = require("Color")
local screen = require("Screen")
local GUI = require("GUI")
local event = require("Event")
local filesystem = require("Filesystem")
local system = require("System")
local paths = require("Paths")
--------------------------------------------------------------------------------
local configPath = paths.user.applicationData .. "MultiScreen/Config.cfg"
local elementWidth = 48
local baseResolutionWidth = 146
local baseResolutionHeight = 54
local GPUProxy = screen.getGPUProxy()
local mainScreenAddress = GPUProxy.getScreen()
local config = {
backgroundColor = 0x0,
}
--------------------------------------------------------------------------------
if filesystem.exists(configPath) then
config = filesystem.readTable(configPath)
end
local function saveConfig()
filesystem.writeTable(configPath, config, true)
end
local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 68, 37, 0x2D2D2D))
local layout = window:addChild(GUI.layout(1, 1, window.width, window.height, 1, 1))
local function clearScreens()
for address in component.list("screen") do
if address ~= mainScreenAddress then
GPUProxy.bind(address, false)
GPUProxy.setDepth(8)
GPUProxy.setResolution(baseResolutionWidth, baseResolutionHeight)
GPUProxy.setBackground(config.backgroundColor)
GPUProxy.fill(1, 1, baseResolutionWidth, baseResolutionHeight, " ")
end
end
GPUProxy.bind(mainScreenAddress, false)
end
local function addButton(text)
local button = layout:addChild(GUI.button(1, 1, elementWidth, 3, 0x4B4B4B, 0xD2D2D2, 0xD2D2D2, 0x4B4B4B, text))
button.colors.disabled.background = 0x3C3C3C
button.colors.disabled.text = 0x5A5A5A
return button
end
local function addTextBox(lines)
local textBox = layout:addChild(GUI.textBox(1, 1, elementWidth, 16, nil, 0x969696, lines, 1, 0, 0, true, true))
textBox.eventHandler = nil
return textBox
end
local function mainMenu(force)
layout:removeChildren()
local lines = {
{color = 0xE1E1E1, text = "Welcome to MultiScreen software"},
" ",
"You can combine multiple screens into a single cluster and draw huge images saved in OCIF5 format. Image converter converter can be downloaded from it's repository:",
"github.com/IgorTimofeev/OCIFImageConverter",
" ",
"There's some useful tips for best experience:",
"• Use maximum size constructions for each screen (8x6 blocks)",
"• Connect more power sources to screens if they're blinking",
" "
}
if config.map then
table.insert(lines, {color = 0xE1E1E1, text = "Current cluster properties:"})
table.insert(lines, " ")
local width, height = #config.map[1], #config.map
table.insert(lines, width .. "x" .. height .. " screen blocks")
table.insert(lines, width * baseResolutionWidth .. "x" .. height * baseResolutionHeight .. " OpenComputers pixels")
-- table.insert(lines, width * baseResolutionWidth * 2 .. "x" .. height * baseResolutionHeight * 4 .. " pixels using Braille font")
else
table.insert(lines, {color = 0xFF4940, text = "Calibrate your system before starting"})
end
addTextBox(lines)
local actionComboBox = layout:addChild(GUI.comboBox(1, 1, elementWidth, 3, 0xEEEEEE, 0x2D2D2D, 0x3C3C3C, 0x888888))
actionComboBox:addItem("Draw image")
actionComboBox:addItem("Clear screens")
actionComboBox:addItem("Calibrate")
local filesystemChooser = layout:addChild(GUI.filesystemChooser(1, 1, elementWidth, 3, 0xE1E1E1, 0x888888, 0x3C3C3C, 0x888888, nil, "Open", "Cancel", "Choose file", "/"))
filesystemChooser:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE)
filesystemChooser:addExtensionFilter(".pic")
local colorSelector = layout:addChild(GUI.colorSelector(2, 2, elementWidth, 3, config.backgroundColor, "Choose color"))
local actionButton = addButton("Next")
actionComboBox.onItemSelected = function()
filesystemChooser.hidden = actionComboBox.selectedItem ~= 1
colorSelector.hidden = actionComboBox.selectedItem ~= 2
actionButton.disabled = actionComboBox.selectedItem == 1 and (not filesystemChooser.path or not config.map)
end
filesystemChooser.onSubmit = function()
actionComboBox.onItemSelected()
workspace:draw()
end
actionButton.onTouch = function()
if actionComboBox.selectedItem == 1 then
local file = filesystem.open(filesystemChooser.path, "rb")
local signature = file:readString(4)
if signature == "OCIF" then
local encodingMethod = file:readBytes(1)
if encodingMethod == 5 then
local width, height, maxWidth, maxHeight, widthLimit, heightLimit, monitorCornerImageX, monitorCornerImageY, background, foreground, symbol = file:readBytes(2), file:readBytes(2), baseResolutionWidth * #config.map[1], baseResolutionHeight * #config.map
local function skipPixels(count)
for i = 1, count do
file:readString(3)
file:readUnicodeChar()
-- Иначе может впасть в TLWY на тормознутых серваках
if i % 1000 == 0 then
computer.pullSignal(0)
end
end
end
-- Чистим дерьмо вилочкой
clearScreens()
for yMonitor = 1, #config.map do
for xMonitor = 1, #config.map[yMonitor] do
GPUProxy.bind(mainScreenAddress, false)
-- GUI.alert("Monitor", xMonitor, yMonitor)
-- Координаты левого верхнего угла моника в пикселях на пикче
monitorCornerImageX, monitorCornerImageY =
(xMonitor - 1) * baseResolutionWidth,
(yMonitor - 1) * baseResolutionHeight
-- Если размеры моника вообще дотягивают до рассматриваемого моника
if monitorCornerImageX < width and monitorCornerImageY < height then
-- Число пикселей, которые мы можем прочесть до окончания пикчи на данном монике (вдруг она заканчивается посередине?)
widthLimit, heightLimit =
math.min(width - monitorCornerImageX, baseResolutionWidth),
math.min(height - monitorCornerImageY, baseResolutionHeight)
-- GUI.alert("Limits", xMonitor, yMonitor, maxWidth, maxHeight, widthLimit, heightLimit)
-- Тута будет храниться сгруппированная по цветам пикча
local grouped = {}
-- Пиздуем на начало файла, т.к. мы в душе не можем ебать, какой там размер юникод-символов далее в пикче
file:seek("set", 4 + 1 + 2 + 2)
skipPixels(monitorCornerImageY * width + monitorCornerImageX)
-- Читаем ебучие пиксели
local yImageLine = monitorCornerImageY
for yImage = 1, heightLimit do
for xImage = 1, widthLimit do
background, foreground = color.to24Bit(file:readBytes(1)), color.to24Bit(file:readBytes(1))
file:readString(1)
symbol = file:readUnicodeChar()
grouped[background] = grouped[background] or {}
grouped[background][foreground] = grouped[background][foreground] or {}
table.insert(grouped[background][foreground], xImage)
table.insert(grouped[background][foreground], yImage)
table.insert(grouped[background][foreground], symbol)
end
-- Скипаем пиксели вплоть до начала следующего сканлайна на ДАННОМ монике
skipPixels(width - widthLimit)
yImageLine = yImageLine + 1
end
-- Биндим гпуху к выбранному монику и рисуем сгруппированную пикчу
GPUProxy.bind(config.map[yMonitor][xMonitor], false)
local currentForeground
for background in pairs(grouped) do
GPUProxy.setBackground(background)
for foreground in pairs(grouped[background]) do
if currentForeground ~= foreground then
GPUProxy.setForeground(foreground)
currentForeground = foreground
end
for i = 1, #grouped[background][foreground], 3 do
GPUProxy.set(
grouped[background][foreground][i],
grouped[background][foreground][i + 1],
grouped[background][foreground][i + 2]
)
end
end
end
end
end
end
file:close()
GPUProxy.bind(mainScreenAddress, false)
else
file:close()
GUI.alert("Wrong encodingMethod: " .. tostring(encodingMethod))
end
else
file:close()
GUI.alert("Wrong signature: " .. tostring(signature))
end
elseif actionComboBox.selectedItem == 2 then
config.backgroundColor = colorSelector.color
saveConfig()
clearScreens()
else
layout:removeChildren()
addTextBox({
{color = 0xE1E1E1, text = "Screen cluster calibration"},
" ",
"Specify required count of screens (not screen blocks, screens!) by horizontal and vertical"
})
local hSlider = layout:addChild(GUI.slider(1, 1, elementWidth, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 1, 10, 5, false, "Screens by horizontal: ", ""))
hSlider.roundValues = true
hSlider.height = 2
local vSlider = layout:addChild(GUI.slider(1, 1, elementWidth, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 1, 10, 4, false, "Screens by vertical: ", ""))
vSlider.roundValues = true
vSlider.height = 2
addButton("Next").onTouch = function()
local connectedCount = -1
for address in component.list("screen") do
connectedCount = connectedCount + 1
end
hSlider.value, vSlider.value = math.floor(hSlider.value), math.floor(vSlider.value)
local specifiedCount = hSlider.value * vSlider.value
if specifiedCount <= connectedCount then
layout:removeChildren()
addTextBox({
{color = 0xE1E1E1, text = "Screen cluster calibration"},
" ",
"Touch highlighted screen with your hand once. After touching all of screens calibration will be finished"
})
local SSX, SSY = 1, 1
local function screenObjectDraw(object)
screen.drawRectangle(object.x, object.y, object.width, object.height, (SSX == object.SX and SSY == object.SY) and 0x22FF22 or 0xE1E1E1, 0x0, " ")
end
local function newScreen(SX, SY)
local object = GUI.object(1, 1, 8, 3)
object.draw = screenObjectDraw
object.SX = SX
object.SY = SY
return object
end
local function newScreenLine(SY)
local lineLayout = GUI.layout(1, 1, layout.width, 3, 1, 1)
lineLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
lineLayout:setSpacing(1, 1, 2)
for SX = 1, hSlider.value do
lineLayout:addChild(newScreen(SX, SY))
end
return lineLayout
end
for SY = 1, vSlider.value do
layout:addChild(newScreenLine(SY))
end
workspace:draw()
config.map = {}
local hue, hueStep = 0, 360 / specifiedCount
while true do
local e1, e2 = event.pull()
if e1 == "touch" then
if e2 ~= mainScreenAddress then
GPUProxy.bind(e2, false)
GPUProxy.setDepth(8)
GPUProxy.setResolution(baseResolutionWidth, baseResolutionHeight)
GPUProxy.setBackground(color.HSBToInteger(hue, 1, 1))
GPUProxy.setForeground(0x0)
GPUProxy.fill(1, 1, baseResolutionWidth, baseResolutionHeight, " ")
local text = "Screen " .. SSX .. "x" .. SSY .. " has been calibrated"
GPUProxy.set(math.floor(baseResolutionWidth / 2 - unicode.len(text) / 2), math.floor(baseResolutionHeight / 2), text)
GPUProxy.bind(mainScreenAddress, false)
config.map[SSY] = config.map[SSY] or {}
config.map[SSY][SSX] = e2
SSX, hue = SSX + 1, hue + hueStep
if SSX > hSlider.value then
SSX, SSY = 1, SSY + 1
if SSY > vSlider.value then
saveConfig()
break
end
end
workspace:draw()
end
end
end
GUI.alert("All screens has been successfully calibrated")
mainMenu()
else
GUI.alert("Invalid count of connected screens. You're specified " .. specifiedCount .. " of screens, but there's " .. connectedCount .. " connected screens")
end
end
workspace:draw()
end
end
actionComboBox.onItemSelected()
workspace:draw(force)
end
window.onResize = function(width, height)
window.backgroundPanel.width = width
window.backgroundPanel.height = height
layout.width = width
layout.height = height
end
--------------------------------------------------------------------------------
window:resize(window.width, window.height)
mainMenu(true)
workspace:draw()