View difference between Paste ID: A8vPDdUi and HG7CQhxH
SHOW: | | - or go back to the newest paste.
1
--------------------------------------------------------------------------------------------------------
2
-- Wojbies API 4.3 - CanvasTerm - Api for drawing window-like term object on Plethora overlay glasses --
3
--------------------------------------------------------------------------------------------------------
4
--   Copyright (c) 2015-2021 Wojbie ([email protected])
5
--   Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
6
--   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
--   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
--   3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9
--   4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
10
--   5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
11
--   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY.
12
13
--### Basic utility functions
14
local function copyTable(...)
15
    local tArgs = {...}
16
    local copy = {}
17
    for _, piece in pairs(tArgs) do
18
        if piece and type(piece) == "table" then
19
            for key, val in pairs(piece) do
20
                if type(val) == "table" then copy[key] = copyTable( copy[key] or {}, val)
21
                else copy[key] = val end
22
            end
23
        end
24
    end
25
    return copy
26
end
27
28
--### Initializing
29
local c = shell and {} or (_ENV or getfenv())
30
c.versionName = "CanvasTerm By Wojbie"
31
c.versionNum = 4.338 --2020-01-15
32
33
local expect = require "cc.expect".expect
34
35
--### Internal palette related tables.
36
local tDefaultPallette = {[ colors.white ] = 0xF0F0F0, [ colors.orange ] = 0xF2B233, [ colors.magenta ] = 0xE57FD8, [ colors.lightBlue ] = 0x99B2F2, [ colors.yellow ] = 0xDEDE6C, [ colors.lime ] = 0x7FCC19, [ colors.pink ] = 0xF2B2CC, [ colors.gray ] = 0x4C4C4C, [ colors.lightGray ] = 0x999999, [ colors.cyan ] = 0x4C99B2, [ colors.purple ] = 0xB266E5, [ colors.blue ] = 0x3366CC, [ colors.brown ] = 0x7F664C, [ colors.green ] = 0x57A64E, [ colors.red ] = 0xCC4C4C, [ colors.black ] = 0x111111}
37
38
local tRGBA = {[ "0" ] = 0xF0F0F0FF, [ "1" ] = 0xF2B233FF, [ "2" ] = 0xE57FD8FF, [ "3" ] = 0x99B2F2FF, [ "4" ] = 0xDEDE6CFF, [ "5" ] = 0x7FCC19FF, [ "6" ] = 0xF2B2CCFF, [ "7" ] = 0x4C4C4CFF, [ "8" ] = 0x999999FF, [ "9" ] = 0x4C99B2FF, [ "a" ] = 0xB266E5FF, [ "b" ] = 0x3366CCFF, [ "c" ] = 0x7F664CFF, [ "d" ] = 0x57A64EFF, [ "e" ] = 0xCC4C4CFF, [ "f" ] = 0x111111FF}
39
40
local tHex = {[ colors.white ] = "0", [ colors.orange ] = "1", [ colors.magenta ] = "2", [ colors.lightBlue ] = "3", [ colors.yellow ] = "4", [ colors.lime ] = "5", [ colors.pink ] = "6", [ colors.gray ] = "7", [ colors.lightGray ] = "8", [ colors.cyan ] = "9", [ colors.purple ] = "a", [ colors.blue ] = "b", [ colors.brown ] = "c", [ colors.green ] = "d", [ colors.red ] = "e", [ colors.black ] = "f"}
41
42
c.dummyTerm = function(nWidth, nHeight, bColor)
43
44
    local isColor = bColor and true or false
45
    local tDefaultPallette = copyTable(tDefaultPallette)
46
47
    local dummy = {}
48
    dummy.getPaletteColour = function( c ) return colors.unpackRGB(tDefaultPallette[c])  end
49
    dummy.isColor = function() return isColor end
50
51
    local buffer = window.create( dummy, 1, 1, nWidth, nHeight, false )
52
    local bufferResize = buffer.reposition
53
54
    buffer.setVisible = nil
55
    buffer.redraw = nil
56
    buffer.restoreCursor = nil
57
    buffer.getPosition = nil
58
    buffer.reposition = nil
59
    buffer.resize = function(nWidth, nHeight) return bufferResize(1, 1, nWidth, nHeight) end
60
61
    return buffer
62
end local dummyTerm = c.dummyTerm
63
64
local textOffsetX = { --How much it goes to right on each symbol
65
    ["!"] = 2, ['"'] = 1, ["'"] = 2, ['('] = 1, [')'] = 1, ['*'] = 1, [","] = 2, ["."] = 2, [":"] = 2, [";"] = 2, ["I"] = 1, ["["] = 1, ["]"] = 1, ["`"] = 2, ["f"] = 1, ["i"] = 2, ["k"] = 1, ["l"] = 2, ["t"] = 1, ["{"] = 1, ["|"] = 2, ["}"] = 1,
66
    ["\161"] = 2, ["\162"] = 1, ["\164"] = 1, ["\165"] = 1, ["\166"] = 3, ["\167"] = 1, ["\168"] = 1, ["\169"] = 1, ["\176"] = -1, ["\181"] = 1, ["\182"] = 1, ["\184"] = 2, ["\195"] = 1, ["\204"] = 1, ["\205"] = 1, ["\206"] = 1, ["\207"] = 1, ["\208"] = 1, ["\210"] = 1, ["\215"] = 1, ["\217"] = 1, ["\219"] = 1, ["\221"] = 1, ["\222"] = 1, ["\236"] = 1, ["\237"] = 2, ["\239"] = 1, ["\240"] = 1, ["\253"] = 1, ["\254"] = 1,
67
}
68
for i = 1, 8 do textOffsetX[string.char(i)] = -1 end
69
for i = 11, 12 do textOffsetX[string.char(i)] = -1 end
70
for i = 14, 31 do textOffsetX[string.char(i)] = -1 end
71
for i = 127, 159 do textOffsetX[string.char(i)] = -1 end
72
textOffsetX["\173"] = -1
73
74
--### Main function
75
c.create = function( parent, nX, nY, nWidth, nHeight, nScale, bVisible, bFrameCursor, bDisableAutoRedraw )
76
77
    expect(1, parent, "table")
78
    if not parent.addGroup then
79
        error( "Invalid parent object, please provide 2d canvas. try canvas() or canvas3d().create().addFrame()", 2 )
80
    end
81
    expect(2, nX, "number")
82
    expect(3, nY, "number")
83
    expect(4, nWidth, "number")
84
    expect(5, nHeight, "number")
85
    expect(6, nScale, "number", "nil")
86
    expect(7, bVisible, "boolean", "nil")
87
    expect(8, bFrameCursor, "boolean", "nil")
88
    expect(9, bDisableAutoRedraw, "boolean", "nil")
89
90
    bVisible = bVisible ~= false
91
    nScale = nScale or 1
92
93
    local group = parent.addGroup({nX, nY})
94
    local buffer = dummyTerm(nWidth, nHeight, true)
95
    local bufferResize = buffer.resize
96
    buffer.resize = nil
97
    local tRGBA = copyTable(tRGBA)
98
    local tHex = copyTable(tHex)
99
    local textOffsetX = copyTable(textOffsetX)
100
    local tFrame, tTexts, tBacks, tCursor
101
    local tText, tFront, tBack, tCursorMeta
102
    local bBlink, bOnScreen = true, true
103
    local nTextScaler = 4 / 6
104
105
    local function build()
106
        tTexts = {}
107
        tBacks = {}
108
        local xSize = 4 * nScale
109
        local ySize = 6 * nScale
110
        tFrame = group.addRectangle( 0 , 0 , xSize * nWidth + 2, ySize * nHeight + 2, 0x111111FF)
111
        for y = 1, nHeight do
112
            tBacks[y] = {}
113
            tTexts[y] = {}
114
            for x = 1, nWidth do
115
                tBacks[y][x] = group.addRectangle( (x - 1) * xSize + 1 , (y - 1) * ySize + 1 , xSize, ySize, 0x7F664CFF)
116
            end
117
            for x = 1, nWidth do
118
                tTexts[y][x] = group.addText({(x - 1) * xSize + 1, (y - 1) * ySize + 1}, "A", 0x57A64EFF, nScale * nTextScaler)
119
                tTexts[y][x].x = (x - 1) * xSize + 1
120
                tTexts[y][x].y = (y - 1) * ySize + 1
121
            end
122
        end
123
            tCursor = group.addGroup({0, 0})
124
            tCursor.cursor = tCursor.addText({1, 1}, "_", 0xF0F0F0FF, nScale * nTextScaler)
125
        if bFrameCursor then
126
            tCursor.lines = tCursor.addLines({1, 1}, {1, ySize + 1}, {xSize + 1, ySize + 1}, {xSize + 1, 1}, 0xF0F0F0FF, 1)
127
        end
128
        bBlink = true
129
        tText = {}
130
        tFront = {}
131
        tBack = {}
132
        tCursorMeta = {}
133
        for i in pairs(tHex) do
134
            tRGBA[tHex[i]] = bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(i)), 8), 0xFF)
135
        end
136
    end
137
138
    local function destroy()
139
        for y = 1, nHeight do
140
            for x = 1, nWidth do tBacks[y][x].remove() end
141
            for x = 1, nWidth do tTexts[y][x].remove() end
142
        end
143
        tFrame.remove()
144
        tCursor.remove()
145
    end
146
    
147
    local function redrawLine(nLine, bForce)
148
        local text, front, back = buffer.getLine(nLine)
149
        if bForce or tBack[nLine] ~= back then --dirty back
150
            local rowBack = tBacks[nLine]
151
            back:gsub("()(.)", function(x, c)
152
                if rowBack[x].color ~= tRGBA[c] then
153
                    rowBack[x].setColor(tRGBA[c])
154
                    rowBack[x].color = tRGBA[c]
155
                end
156
            end)
157
            tBack[nLine] = back
158
        end
159
        if bForce or tText[nLine] ~= text or tFront[nLine] ~= front then --dirty text
160
            local rowText = tTexts[nLine]
161
            front:gsub("()(.)", function(x, c)
162
                if rowText[x].color ~= tRGBA[c] then
163
                    rowText[x].setColor(tRGBA[c])
164
                    rowText[x].color = tRGBA[c]
165
                end
166
            end)
167
            text:gsub("()(.)", function(x, c)
168
                if rowText[x].text ~= c then
169
                    rowText[x].setText(c)
170
                    rowText[x].setPosition(rowText[x].x + (textOffsetX[c] or 0) * nTextScaler * nScale, rowText[x].y)
171
                    rowText[x].text = c
172
                end
173
            end)
174
            tText[nLine] = text
175
            tFront[nLine] = front
176
        end
177
    end
178
179
    local function redraw(bForce)
180
        for y = 1, nHeight do
181
            redrawLine(y, bForce)
182
        end
183
    end
184
185
    local function updateCursor()
186
        local cx, cy = buffer.getCursorPos()
187
        if tCursorMeta.x ~= cx or tCursorMeta.y ~= cy then
188
            bOnScreen = cx > 0 and cx <= nWidth and cy > 0 and cy <= nHeight
189
            tCursor.setPosition((cx - 1) * 4 * nScale, (cy - 1) * 6 * nScale)
190
            tCursorMeta.x, tCursorMeta.y = cx, cy
191
        end
192
        local cColor = tRGBA[tHex[buffer.getTextColor()]]
193
        if tCursorMeta.color ~= cColor then
194
            tCursor.cursor.setColor(cColor)
195
            tCursorMeta.color = cColor
196
        end
197
        local cBlink = buffer.getCursorBlink()
198
        if tCursorMeta.blink ~= cBlink or tCursorMeta.bOnScreen ~= bOnScreen then
199
            tCursor.cursor.setAlpha(bBlink and bOnScreen and cBlink and 0xFF or 0)
200
            tCursorMeta.blink = cBlink
201
            tCursorMeta.bOnScreen = bOnScreen
202
        end
203
    end
204
    
205
    local function updatePaletteColor(c)
206
        if tHex[c] then
207
            local col =  bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(c)), 8), 0xFF)
208
            if tRGBA[tHex[c]] ~= col then
209
                tRGBA[tHex[c]] = col
210
                return true
211
            end   
212
        end
213
        return false
214
    end
215
    
216
    local function updatePalette()
217
        local changed = false
218
        for i in pairs(tHex) do
219
            local col = bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(i)), 8), 0xFF)
220
            if tRGBA[tHex[i]] ~= col then
221
                tRGBA[tHex[i]] = col
222
                changed = true
223
            end
224
        end
225
        return changed
226
    end
227
228
    local function blinker()
229
        if math.floor(os.epoch("utc") / 400) % 2 == 0 ~= bBlink then
230
            bBlink = not bBlink
231
            tCursor.cursor.setAlpha(bBlink and bOnScreen and buffer.getCursorBlink() and 0xFF or 0)
232
        end
233
    end
234
235
    
236
    local redrawDirtiers = {["clear"] = true, ["scroll"] = true }
237
    local redrawLineDirtiers = {["write"] = true, ["blit"] = true, ["clearLine"] = true }
238
    local cursorDirters = {["setCursorPos"] = true, ["setCursorBlink"] = true, ["setTextColor"] = true, ["setTextColour"] = true}
239
    local paletteDirters = {["setPaletteColor"] = true, ["setPaletteColour"] = true}
240
    
241
    local canvasTerm = {}
242
    
243
    for i, k in pairs(buffer) do
244
        if bDisableAutoRedraw then
245
            canvasTerm[i] = k
246
        elseif redrawDirtiers[i] then
247
            canvasTerm[i] = function(...)
248
                local returns = table.pack(k(...))
249
                if bVisible then redraw() updateCursor() end
250
                return table.unpack(returns, 1, returns.n)
251
            end
252
        elseif redrawLineDirtiers[i] then
253
            canvasTerm[i] = function(...)
254
                local returns = table.pack(k(...))
255
                if bVisible then redrawLine(select(2, buffer.getCursorPos())) updateCursor() end
256
                return table.unpack(returns, 1, returns.n)
257
            end
258
        elseif cursorDirters[i] then
259
            canvasTerm[i] = function(...)
260
                local returns = table.pack(k(...))
261
                if bVisible then updateCursor() end
262
                return table.unpack(returns, 1, returns.n)
263
            end
264
        elseif paletteDirters[i] then
265
            canvasTerm[i] = function(c, ...)
266
                local returns = table.pack(k(c, ...))
267
                local changed = updatePaletteColor(c)
268
                if bVisible then redraw(changed) updateCursor() end
269
                return table.unpack(returns, 1, returns.n)
270
            end
271
        else
272
            canvasTerm[i] = k
273
        end
274
    end
275
276
    canvasTerm.setVisible = function( b )
277
        expect(1, b, "boolean")
278
        if bVisible ~= b then
279
            bVisible = b
280
            if bVisible then
281
                build()
282
                redraw()
283
                updateCursor()
284
            else
285
                destroy()
286
            end
287
        end
288
    end
289
290
    if bDisableAutoRedraw then
291
292
        canvasTerm.flush = function()
293
            if bVisible then
294
                local changed = updatePalette()
295
                redraw(changed)
296
                updateCursor()
297
            end
298
        end
299
300
    end
301
    
302
    canvasTerm.loadLines = function(tWindow)
303
        expect(1, tWindow, "table")
304
        assert(tWindow.getLine, "Error. loadLones requires windows table with getLine supported")
305
        local _, inY = tWindow.getSize()
306
        local cX, xY = buffer.getCursorPos()
307
        for i in pairs(tHex) do
308
            buffer.setPaletteColour(i, tWindow.getPaletteColour(i))
309
        end
310
        for y = 1, math.min(nHeight, inY) do
311
            buffer.setCursorPos(1, y)
312
            buffer.blit(tWindow.getLine(y))
313
        end
314
        buffer.setCursorPos(cX, xY)
315
        if bVisible then
316
            local changed = updatePalette()
317
            redraw(changed)
318
            updateCursor()
319
        end
320
    end
321
322
    canvasTerm.blinker = function()
323
        if bVisible then
324
            blinker()
325
        end
326
    end
327
328
    canvasTerm.reposition = function( nNewX, nNewY, nNewWidth, nNewHeight, nNewScale, newParent )
329
        if nNewX ~= nil or nNewY ~= nil then
330
            expect(1, nNewX, "number")
331
            expect(2, nNewY, "number")
332
        end
333
        if nNewWidth ~= nil or nNewHeight ~= nil then
334
            expect(3, nNewWidth, "number")
335
            expect(4, nNewHeight, "number")
336
        end
337
        expect(5, nNewScale, "number", "nil")
338
        expect(6, newParent, "table", "nil")
339
340
        if bVisible then
341
            destroy()
342
        end
343
344
        nX = nNewX or nX
345
        nY = nNewY or nY
346
        nWidth = nNewWidth or nWidth
347
        nHeight = nNewHeight or nHeight
348
        nScale = nNewScale or nScale
349
        parent = newParent or parent
350
351
        group.remove()
352
        group = parent.addGroup({nX, nY})
353
        bufferResize(nWidth, nHeight)
354
355
        if bVisible then
356
            build()
357
            redraw()
358
            updateCursor()
359
        end
360
    end
361
362
    if bVisible then
363
        build()
364
        redraw()
365
        updateCursor()
366
    end
367
368
    return canvasTerm
369
end
370
371
--### Finalizing
372
return c