From 67e90cd926c9ce744a9d37a398abc5456b0f4c07 Mon Sep 17 00:00:00 2001 From: Igor Timofeev Date: Sun, 26 Aug 2018 18:54:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B0=D0=BA,=20=D0=B2=D1=80=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=20=D0=B2=D1=81=D0=B5.=20=D0=9C=D1=83=D0=BB=D1=8C=D1=82?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D1=80=D0=B8=D0=BD=D1=8B-=D1=85=D1=83=D0=B8?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=80=D1=8C=20=D1=85=D0=BE=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=B0=20=D0=B3=D1=83=D0=B9=D0=BA=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=88=D1=83=D1=82.=20=D0=9D=D1=83=20=D0=B8=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=85=D1=83=D0=B9=20=D0=BE=D0=BD=D0=BE=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B4=D0=BE=3F=20-=20=D1=81=D0=BF=D1=80=D0=BE=D1=81=D0=B8?= =?UTF-8?q?=D1=88=D1=8C=20=D1=82=D1=8B.=20=D0=90=20=D1=8D=D1=82=D0=BE=20?= =?UTF-8?q?=D0=B2=D1=81=D0=B5=20=D0=A1=D0=B5=D0=BC=D0=B5=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B4=D0=B5=D0=BB,=20=D0=BC=D0=BE=D0=BB,=20=D0=A7?= =?UTF-8?q?=D0=9E=D0=97=D0=90=20=D0=9D=D0=98=D0=93=D0=A0=D0=90=D0=A4=D0=9E?= =?UTF-8?q?=D0=9D=D0=9D=D0=9D.=20=D0=9A=20=D0=BD=D0=B5=D0=BC=D1=83=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D1=8A=D1=8F=D0=B2=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Applications/MultiScreen/MultiScreen.lua | 552 +++++++++++------------ Applications/MultiScreenGUI.lua | 295 ------------ 2 files changed, 267 insertions(+), 580 deletions(-) delete mode 100644 Applications/MultiScreenGUI.lua diff --git a/Applications/MultiScreen/MultiScreen.lua b/Applications/MultiScreen/MultiScreen.lua index f50c75ce..fa0ae31e 100644 --- a/Applications/MultiScreen/MultiScreen.lua +++ b/Applications/MultiScreen/MultiScreen.lua @@ -1,313 +1,295 @@ -require("advancedLua") -local ecs = require("ECSAPI") -local components = require("component") -local serialization = require("serialization") -local fs = require("filesystem") +local component = require("component") +local color = require("color") +local buffer = require("doubleBuffering") +local GUI = require("GUI") +local bit32 = require("bit32") local event = require("event") local unicode = require("unicode") -local bit32 = require("bit32") -local color = require("color") -local gpu = components.gpu +local fs = require("filesystem") --------------------------------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- -local mainScreenAddress = gpu.getScreen() -local pathToConfigFile = "/MultiScreen.cfg" +local configPath = "/MultiScreen.cfg" +local elementWidth = 48 +local baseResolutionWidth = 146 +local baseResolutionHeight = 54 +local GPUProxy = buffer.getGPUProxy() +local mainScreenAddress = GPUProxy.getScreen() -local colors = { - background = 0x262626, - foreground = 0xDDDDDD, - currentScreen = ecs.colors.green, - screen = 0xDDDDDD, +local config = { + backgroundColor = 0x0, } -local baseResolution = { - width = 146, - height = 54, -} +-------------------------------------------------------------------------------- -local monitors = {} - --------------------------------------------------------------------------------------------------------------------------------------------- - -local currentBackground, currentForeground, currentAddress = 0x000000, 0xffffff, "" - -local function multiScreenSet(x, y, background, foreground, text) - local xMonitor = math.ceil(x / monitors.screenResolutionByWidth) - local yMonitor = math.ceil(y / monitors.screenResolutionByHeight) - - if monitors[xMonitor] and monitors[xMonitor][yMonitor] then - if currentAddress ~= monitors[xMonitor][yMonitor].address then - gpu.bind(monitors[xMonitor][yMonitor].address, false) - gpu.setBackground(background) - gpu.setForeground(foreground) - - currentBackground, currentForeground = background, foreground - - currentAddress = monitors[xMonitor][yMonitor].address - end - - if currentBackground ~= background then - gpu.setBackground(background) - currentBackground = background - end - - if currentForeground ~= foreground then - gpu.setForeground(foreground) - currentForeground = foreground - end - - gpu.set(x - (xMonitor - 1) * monitors.screenResolutionByWidth, y - (yMonitor - 1) * monitors.screenResolutionByHeight, text) - end -end - -local function multiScreenClear(color) - for address in components.list("screen") do - if address ~= mainScreenAddress then - gpu.bind(address, false) - gpu.setResolution(baseResolution.width, baseResolution.height) - gpu.setDepth(8) - gpu.setBackground(0x0) - gpu.setForeground(0xffffff) - gpu.fill(1, 1, baseResolution.width, baseResolution.height, " ") - end - end - - gpu.bind(mainScreenAddress, false) -end - --------------------------------------------------------------------------------------------------------------------------------------------- - -local function getAllConnectedScreens() - local massiv = {} - for address in pairs(components.list("screen")) do - table.insert(massiv, address) - end - return massiv -end - -local function configurator() - fs.makeDirectory(fs.path(pathToConfigFile)) - - local data = ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, "Здорово, ебана!"}, {"EmptyLine"}, {"WrappedText", 0x262626, "Добро пожаловать в программу конфигурации мультимонитора. Вам необходимо указать количество мониторов по ширине и высоте, которые вы желаете объединить, а также выбрать желаемый масштаб."}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Ширина"}, {"Input", 0x262626, 0x880000, "Высота"}, {"Slider", 0x262626, 0x880000, 1, 100, 100, "Масштаб: ", "%"}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Подтвердить"}, {0x777777, 0xffffff, "Отмена"}}) - local width, height, scale = tonumber(data[1]), tonumber(data[2]), tonumber(data[3]) / 100 - if data[4] == "Отмена" then - ecs.prepareToExit() - print("Калибровка отменена!") - os.exit() - end - - baseResolution.width, baseResolution.height = math.floor(baseResolution.width * scale), math.floor(baseResolution.height * scale) - - -- ecs.error(baseResolution.width .. "x" ..baseResolution.height .. " ccale = " ..scale) - - local countOfConnectedScreens = #getAllConnectedScreens() - - while ((countOfConnectedScreens - 1) < width * height) do - data = ecs.universalWindow("auto", "auto", 44, 0xeeeeee, true, {"EmptyLine"}, {"WrappedText", 0x262626, "Теперь вам необходимо подключить внешние мониторы. Вы указали, что собираетесь сделать мультимонитор из " .. width*height .. " мониторов, но на данный момент вы подключили " .. countOfConnectedScreens - 1 .. " мониторов. Так что подключайте все так, как указали, и жмите \"Далее\"."}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Далее"}, {0x777777, 0xffffff, "Отмена"}}) - if data[1] == "Отмена" then - ecs.prepareToExit() - print("Калибровка отменена!") - os.exit() - end - countOfConnectedScreens = #getAllConnectedScreens() - end - - ---- - - local w, h = 8, 3 - local xC, yC = 1, 1 - local xSize, ySize = gpu.getResolution() - - local function drawMonitors() - ecs.clearScreen(colors.background) - local x, y = 3, 2 - local xPos, yPos = x, y - for j = 1, height do - for i = 1, width do - if j == yC and i == xC then - ecs.square(xPos, yPos, w, h, colors.currentScreen) - else - ecs.square(xPos, yPos, w, h, colors.screen) - end - xPos = xPos + w + 2 - end - yPos = yPos + h + 1 - xPos = x - end - - gpu.setBackground(colors.background) - gpu.setForeground(colors.foreground) - ecs.centerText("x", ySize - 5, "Начинаем процесс калибровки. Коснитесь монитора, подсвеченного зеленым цветом.") - ecs.centerText("x", ySize - 4, "Не нарушайте порядок прокосновений!") - end - - ecs.prepareToExit() - print("Идет подготовка мониторов...") - multiScreenClear(0x0) - - monitors = {} - local monitorCount = width * height - local counter = 1 - while counter <= monitorCount do - drawMonitors() - local e = {event.pull("touch")} - if e[2] ~= mainScreenAddress then - local exists = false - for x = 1, #monitors do - for y = 1, #monitors[x] do - if monitors[x][y].address == e[2] then - ecs.error("Ты уже кликал на этот монитор. Совсем уебок штоле?") - exists = true - end - end - end - - if not exists then - gpu.bind(e[2], false) - gpu.setResolution(baseResolution.width, baseResolution.height) - gpu.setDepth(8) - - local color = color.HSBToInteger(counter / monitorCount * 360, 1, 1) - gpu.setBackground(color) - gpu.setForeground(0xffffff - color) - gpu.fill(1, 1, baseResolution.width, baseResolution.height, " ") - - ecs.centerText("xy", 0, "Монитор " .. xC .. "x" .. yC .. " откалиброван!") - - gpu.bind(mainScreenAddress, false) - - monitors[xC] = monitors[xC] or {} - monitors[xC][yC] = {address = e[2]} - - xC = xC + 1 - if xC > width and yC < height then - xC, yC = 1, yC + 1 - end - end - else - ecs.error("Ну что ты за мудак криворукий! Сказано же, каких мониторов касаться. Не трогай этот монитор.") - end - - counter = counter + 1 - end - - monitors.countOfScreensByWidth = width - monitors.countOfScreensByHeight = height - monitors.screenResolutionByWidth = baseResolution.width - monitors.screenResolutionByHeight = baseResolution.height - monitors.totalResolutionByWidth = baseResolution.width * width - monitors.totalResolutionByHeight = baseResolution.height * height - - ecs.prepareToExit() - ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x262626, "Калибровка успешно завершена!"}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Отлично"}}) - ecs.prepareToExit() +if fs.exists(configPath) then + config = table.fromFile(configPath) end local function saveConfig() - local file = io.open(pathToConfigFile, "w") - file:write(serialization.serialize(monitors)) - file:close() + table.toFile(configPath, config, true) end -local function loadConfig() - if fs.exists(pathToConfigFile) then - local file = io.open(pathToConfigFile, "r") - monitors = serialization.unserialize(file:read("*a")) - file:close() - print(" ") - print("Файл конфигурации мультимонитора загружен") - print(" ") - print("Количество экранов: " .. monitors.countOfScreensByWidth .. "x" .. monitors.countOfScreensByHeight .. " шт") - print("Разрешение каждого экрана: " .. monitors.screenResolutionByWidth .. "x" .. monitors.screenResolutionByHeight .. " px") - print("Суммарное разрешение кластера: " .. monitors.totalResolutionByWidth .. "x" .. monitors.totalResolutionByHeight .. " px") - -- print("Суммарное разрешение кластера через шрифт Брайля: ".. monitors.totalResolutionByWidth * 2 .. "x" .. monitors.totalResolutionByHeight * 4 .. " px") - print(" ") - else - configurator() - saveConfig() - loadConfig() - end -end +local mainContainer = GUI.fullScreenContainer() +mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D)) --------------------------------------------------------------------------------------------------------------------------------------------- +local layout = mainContainer:addChild(GUI.layout(1, 1, mainContainer.width, mainContainer.height, 1, 1)) ---Прочитать n байтов из файла, возвращает прочитанные байты как число, если не удалось прочитать, то возвращает 0 -local function readNumber(file, count) - return bit32.byteArrayToNumber({string.byte(file:read(count), 1, count)}) -end - -local function drawBigImageFromOCIFRawFile(x, y, path) - local file = io.open(path, "rb") - print("Открываем файл " .. path) - local signature = file:read(4) - print("Читаем сигнатуру файла: " .. signature) - local encodingMethod = string.byte(file:read(1)) - print("Читаем метод кодирования: " .. tostring(encodingMethod)) - - if encodingMethod ~= 5 then - print("Неподдерживаемый метод кодирования. Откройте конвертер, измените формат на OCIF5 (Multiscreen) и повторите попытку") - file:close() - end - - local width = readNumber(file, 2) - local height = readNumber(file, 2) - - print("Ширина пикчи: " .. tostring(width)) - print("Высота пикчи: " .. tostring(height)) - - print("Начинаю отросовку пикчи...") - - for j = 1, height do - for i = 1, width do - local background = color.to24Bit(string.byte(file:read(1))) - local foreground = color.to24Bit(string.byte(file:read(1))) - file:read(1) - local symbol = fs.readUnicodeChar(file) - - multiScreenSet(x + i - 1, y + j - 1, background, foreground, symbol) +local function clearScreens() + for address in component.list("screen") do + if address ~= mainScreenAddress then + GPUProxy.bind(address, false) + GPUProxy.setResolution(baseResolutionWidth, baseResolutionHeight) + GPUProxy.setBackground(config.backgroundColor) + GPUProxy.fill(1, 1, baseResolutionWidth, baseResolutionHeight, " ") end end - file:close() - - gpu.bind(mainScreenAddress, false) - print("Отрисовка пикчи завершена") + GPUProxy.bind(mainScreenAddress, false) end --------------------------------------------------------------------------------------------------------------------------------------------- +local function addButton(text) + return layout:addChild(GUI.button(1, 1, elementWidth, 3, 0x4B4B4B, 0xD2D2D2, 0xD2D2D2, 0x4B4B4B, text)) +end -local args = {...} +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 -if args[1] == "draw" and args[2] then - loadConfig() - print("Идет очистка мониторов...") - multiScreenClear(0x000000) - if fs.exists(args[2]) then - drawBigImageFromOCIFRawFile(1, 1, args[2]) + return textBox +end + +local function mainMenu(force) + layout:removeChildren() + + local lines = { + {color = 0xE1E1E1, text = "Welcome to MultiScreen software"}, + " ", + "Here you can combine multiple screens into a single cluster and draw huge images saved in OCIF5 format. 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 - print("Файл " .. tostring(args[2]) .. " не найден. Используйте абсолютный путь к файлу, добавив / в начало") + table.insert(lines, {color = 0xE1E1E1, text = "Calibrate your system before starting"}) end -elseif args[1] == "calibrate" then - fs.remove(pathToConfigFile) - loadConfig() -elseif args[1] == "clear" then - loadConfig() - multiScreenClear(tonumber(args[2] or 0x000000)) -else - loadConfig() - print("Использование программы:") - print(" MultiScreen calibrate - перекалибровать мониторы") - print(" MultiScreen draw <путь к изображению> - отобразить изображение из файла на мониторах") - print(" MultiScreen clear <цвет> - очистить мониторы с указанным цветом (черным по умолчанию)") + + 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() + mainContainer:drawOnScreen() + end + + actionButton.onTouch = function() + if actionComboBox.selectedItem == 1 then + local file = io.open(filesystemChooser.path, "rb") + + local signature = file:read(4) + if signature == "OCIF" then + local encodingMethod = string.byte(file:read(1)) + if encodingMethod == 5 then + local width = bit32.byteArrayToNumber({string.byte(file:read(2), 1, 2)}) + local height = bit32.byteArrayToNumber({string.byte(file:read(2), 1, 2)}) + + clearScreens() + + local background, foreground, currentBackground, currentForeground, currentAddress + for y = 1, height do + for x = 1, width do + background = color.to24Bit(string.byte(file:read(1))) + foreground = color.to24Bit(string.byte(file:read(1))) + file:read(1) + + local xMonitor = math.ceil(x / baseResolutionWidth) + local yMonitor = math.ceil(y / baseResolutionHeight) + + if config.map[yMonitor] and config.map[yMonitor][xMonitor] then + if currentAddress ~= config.map[yMonitor][xMonitor] then + GPUProxy.bind(config.map[yMonitor][xMonitor], false) + GPUProxy.setBackground(background) + GPUProxy.setForeground(foreground) + + currentAddress, currentBackground, currentForeground = config.map[yMonitor][xMonitor], background, foreground + end + + if currentBackground ~= background then + GPUProxy.setBackground(background) + currentBackground = background + end + + if currentForeground ~= foreground then + GPUProxy.setForeground(foreground) + currentForeground = foreground + end + + GPUProxy.set(x - (xMonitor - 1) * baseResolutionWidth, y - (yMonitor - 1) * baseResolutionHeight, fs.readUnicodeChar(file)) + end + end + end + + file:close() + + GPUProxy.bind(mainScreenAddress, false) + GUI.alert("Done.") + 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) + buffer.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 + + mainContainer:drawOnScreen() + + clearScreens(0x0) + + config.map = {} + local hue, hueStep = 0, 360 / specifiedCount + while true do + local e1, e2 = event.pull("touch") + if e2 ~= mainScreenAddress then + GPUProxy.bind(e2, false) + + 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 + + mainContainer:drawOnScreen() + 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 + + mainContainer:drawOnScreen() + end + end + + actionComboBox.onItemSelected() + mainContainer:drawOnScreen(force) end --------------------------------------------------------------------------------------------------------------------------------------------- - -return multiScreen - - - - +-------------------------------------------------------------------------------- +mainMenu(true) +mainContainer:startEventHandling() \ No newline at end of file diff --git a/Applications/MultiScreenGUI.lua b/Applications/MultiScreenGUI.lua deleted file mode 100644 index fa0ae31e..00000000 --- a/Applications/MultiScreenGUI.lua +++ /dev/null @@ -1,295 +0,0 @@ - -local component = require("component") -local color = require("color") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local bit32 = require("bit32") -local event = require("event") -local unicode = require("unicode") -local fs = require("filesystem") - --------------------------------------------------------------------------------- - -local configPath = "/MultiScreen.cfg" -local elementWidth = 48 -local baseResolutionWidth = 146 -local baseResolutionHeight = 54 -local GPUProxy = buffer.getGPUProxy() -local mainScreenAddress = GPUProxy.getScreen() - -local config = { - backgroundColor = 0x0, -} - --------------------------------------------------------------------------------- - -if fs.exists(configPath) then - config = table.fromFile(configPath) -end - -local function saveConfig() - table.toFile(configPath, config, true) -end - -local mainContainer = GUI.fullScreenContainer() -mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D)) - -local layout = mainContainer:addChild(GUI.layout(1, 1, mainContainer.width, mainContainer.height, 1, 1)) - -local function clearScreens() - for address in component.list("screen") do - if address ~= mainScreenAddress then - GPUProxy.bind(address, false) - 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) - return layout:addChild(GUI.button(1, 1, elementWidth, 3, 0x4B4B4B, 0xD2D2D2, 0xD2D2D2, 0x4B4B4B, text)) -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"}, - " ", - "Here you can combine multiple screens into a single cluster and draw huge images saved in OCIF5 format. 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 = 0xE1E1E1, 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() - mainContainer:drawOnScreen() - end - - actionButton.onTouch = function() - if actionComboBox.selectedItem == 1 then - local file = io.open(filesystemChooser.path, "rb") - - local signature = file:read(4) - if signature == "OCIF" then - local encodingMethod = string.byte(file:read(1)) - if encodingMethod == 5 then - local width = bit32.byteArrayToNumber({string.byte(file:read(2), 1, 2)}) - local height = bit32.byteArrayToNumber({string.byte(file:read(2), 1, 2)}) - - clearScreens() - - local background, foreground, currentBackground, currentForeground, currentAddress - for y = 1, height do - for x = 1, width do - background = color.to24Bit(string.byte(file:read(1))) - foreground = color.to24Bit(string.byte(file:read(1))) - file:read(1) - - local xMonitor = math.ceil(x / baseResolutionWidth) - local yMonitor = math.ceil(y / baseResolutionHeight) - - if config.map[yMonitor] and config.map[yMonitor][xMonitor] then - if currentAddress ~= config.map[yMonitor][xMonitor] then - GPUProxy.bind(config.map[yMonitor][xMonitor], false) - GPUProxy.setBackground(background) - GPUProxy.setForeground(foreground) - - currentAddress, currentBackground, currentForeground = config.map[yMonitor][xMonitor], background, foreground - end - - if currentBackground ~= background then - GPUProxy.setBackground(background) - currentBackground = background - end - - if currentForeground ~= foreground then - GPUProxy.setForeground(foreground) - currentForeground = foreground - end - - GPUProxy.set(x - (xMonitor - 1) * baseResolutionWidth, y - (yMonitor - 1) * baseResolutionHeight, fs.readUnicodeChar(file)) - end - end - end - - file:close() - - GPUProxy.bind(mainScreenAddress, false) - GUI.alert("Done.") - 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) - buffer.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 - - mainContainer:drawOnScreen() - - clearScreens(0x0) - - config.map = {} - local hue, hueStep = 0, 360 / specifiedCount - while true do - local e1, e2 = event.pull("touch") - if e2 ~= mainScreenAddress then - GPUProxy.bind(e2, false) - - 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 - - mainContainer:drawOnScreen() - 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 - - mainContainer:drawOnScreen() - end - end - - actionComboBox.onItemSelected() - mainContainer:drawOnScreen(force) -end - --------------------------------------------------------------------------------- - -mainMenu(true) -mainContainer:startEventHandling() \ No newline at end of file