git.haldean.org e / 60fa98d
scrolling working haldean 1 year, 6 months ago
3 changed file(s) with 96 addition(s) and 124 deletion(s). Raw diff Collapse all Expand all
2323 invalid: Rect
2424
2525 Comp* = ref object
26 size*: Size
27 blit*: Blit
26 size: Size
2827 tb*: illwill.TerminalBuffer
2928 layers: seq[Layer]
3029 srclayer: RectBuf[int]
4544 p0.x += p1.x
4645 p0.y += p1.y
4746
47 ## RECTS
48
49 proc width*(r: Rect): int =
50 if r.hi.x < r.lo.x:
51 return 0
52 return r.hi.x - r.lo.x + 1
53
54 proc height*(r: Rect): int =
55 if r.hi.y < r.lo.y:
56 return 0
57 return r.hi.y - r.lo.y + 1
58
59 proc isempty*(r: Rect): bool = r.width() <= 0 or r.height() <= 0
60
61 proc width*[T](b: RectBuf[T]): int = b.stride
62
63 proc height*[T](b: RectBuf[T]): int =
64 int(b.c.len() / int(b.stride))
65
66 proc size*[T](b: RectBuf[T]): Size =
67 Size(w: b.width(), h: b.height())
68
69 proc size*(c: Comp): Size = c.size
70
71 proc contains*(r: Rect, p: Pt): bool =
72 r.lo.x <= p.x and p.x <= r.hi.x and
73 r.lo.y <= p.y and p.y <= r.hi.y
74
75 proc contains*[T](b: RectBuf[T], p: Pt): bool =
76 0 <= p.x and p.x < b.width() and
77 0 <= p.y and p.y < b.height()
78
79 proc contains*(l: Layer, p: Pt): bool =
80 l.dst.contains(p) and l.blit.contains(p - l.src)
81
82 proc `+=`*(r0: var Rect, r1: Rect) =
83 if r1.isempty():
84 return
85 if r0.isempty():
86 r0.lo = r1.lo
87 r0.hi = r1.hi
88 else:
89 r0.lo = Pt(x: min(r0.lo.x, r1.lo.x), y: min(r0.lo.y, r1.lo.y))
90 r0.hi = Pt(x: max(r0.hi.x, r1.hi.x), y: max(r0.hi.y, r1.hi.y))
91
4892 ## BLIT INDEXING
4993
5094 proc index(b: RectBuf, p: Pt): int =
57101 proc set*[T](b: RectBuf[T], p: Pt, c: T): void =
58102 b.c[b.index(p)] = c
59103
60 proc get*[T](b: RectBuf[T], p: Pt): T = b.c[b.index(p)]
104 proc get*[T](b: RectBuf[T], p: Pt): T =
105 b.c[b.index(p)]
61106
62107 proc get*(l: Layer, dst: Pt): Char =
63108 var src = Pt(
64109 x: l.src.x + (dst.x - l.dst.lo.x),
65110 y: l.src.y + (dst.y - l.dst.lo.y))
66 return l.blit.get(src)
67
68 proc get*(b: Comp, p: Pt): Char =
69 return b.blit.get(p)
111 if l.blit.contains(src):
112 return l.blit.get(src)
113 return NO_CHAR
114
115 proc get*(c: Comp, p: Pt): Char =
116 let layerindex = c.srclayer.get(p)
117 if layerindex == NO_LAYER:
118 return NO_CHAR
119 return c.layers[layerindex].get(p)
70120
71121 proc resize*[T](b: var RectBuf[T], size: Size) =
72122 let s = size.w * size.h
73123 b.c = newSeq[T](s)
74124 b.stride = size.w
75125
76 ## RECTS
77
78 proc width*(r: Rect): int =
79 if r.hi.x < r.lo.x:
80 return 0
81 return r.hi.x - r.lo.x + 1
82
83 proc height*(r: Rect): int =
84 if r.hi.y < r.lo.y:
85 return 0
86 return r.hi.y - r.lo.y + 1
87
88 proc isempty*(r: Rect): bool = r.width() > 0 and r.height() > 0
89
90 proc width*[T](b: RectBuf[T]): int = b.stride
91
92 proc height*[T](b: RectBuf[T]): int =
93 int(b.c.len() / int(b.stride))
94
95 proc size*[T](b: RectBuf[T]): Size =
96 Size(w: b.width(), h: b.height())
97
98 proc contains*(r: Rect, p: Pt): bool =
99 r.lo.x <= p.x and p.x <= r.hi.x and
100 r.lo.y <= p.y and p.y <= r.hi.y
101
102 proc contains*[T](b: RectBuf[T], p: Pt): bool =
103 0 <= p.x and p.x < b.width() and
104 0 <= p.y and p.y < b.height()
105
106 proc contains*(l: Layer, p: Pt): bool =
107 l.dst.contains(p) and l.blit.contains(p - l.src)
108
109 proc `|`*(r0, r1: Rect): Rect =
110 if r0.isempty():
111 r1
112 elif r1.isempty():
113 r0
114 else:
115 Rect(lo: Pt(x: min(r0.lo.x, r1.lo.x), y: min(r0.lo.y, r1.lo.y)),
116 hi: Pt(x: max(r0.hi.x, r1.hi.x), y: max(r0.hi.y, r1.hi.y)))
117
118126 ## CONSTRUCTORS
119127
120128 proc newPt*(x, y: int): Pt =
123131 proc emptyRect(): Rect =
124132 Rect(lo: newPt(0, 0), hi: newPt(-1, -1))
125133
126 proc rectFromSize(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect =
134 proc rectFromSize*(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect =
127135 Rect(lo: lo, hi: Pt(x: lo.x + s.w - 1, y: lo.y + s.h - 1))
128136
129137 proc newRectBuf[T](size: Size): RectBuf[T] =
144152
145153 proc newComp*(size: Size): Comp =
146154 Comp(size: size,
147 blit: newBlit(size),
148155 tb: newTerminalBuffer(size.w, size.h),
149156 layers: newSeq[Layer](),
150157 fullrepaint: true,
155162 iterator pointsinside(rects: seq[Rect]): Pt =
156163 var domain = emptyRect()
157164 for r in rects:
158 domain = domain | r
159 for x in domain.lo.x..domain.hi.x:
160 for y in domain.lo.y..domain.hi.y:
165 domain += r
166 for y in domain.lo.y..domain.hi.y:
167 for x in domain.lo.x..domain.hi.x:
161168 let p = newPt(x, y)
162169 for r in rects:
163170 if r.contains(p):
164171 yield p
165172 break
166173
167 proc layout(c: var Comp) =
168 c.srclayer.resize(c.size)
169 c.srclayer.fill(NO_LAYER)
170 # build srclayer cache
171 for l in 0..(len(c.layers) - 1):
172 let layer = c.layers[l]
173 var full = true
174 for i in 0..(c.size.w - 1):
175 for j in 0..(c.size.h - 1):
176 let d = Pt(x: i, y: j)
177 if c.srclayer.get(d) != NO_LAYER:
178 continue
179 full = false
180 if layer.dst.contains(d):
181 c.srclayer.set(d, l)
182 if full:
183 break
184 c.fullrepaint = true
185
186 proc repaint(c: var Comp, invalid: seq[Rect]) =
187 echo "repaint:", repr(invalid)
174 proc paint*(c: Comp) =
188175 c.tb.clear()
189 for point in pointsinside(invalid):
190 let layerindex = c.srclayer.get(point)
191 if layerindex == NO_LAYER:
192 continue
193 let layer = c.layers[layerindex]
194
195 # the layer's dst rect determines where it's in control, but
196 # it's possible for a pixel in the layer's rect to not have any
197 # data in the layer's source blit; this containment query
198 # handles that case.
199 if layer.contains(point):
200 let ch = layer.get(point)
201 c.tb.write(point.x, point.y, ch[0].toUTF8)
202
203 proc paint*(c: var Comp) =
204 var invalid = newSeq[Rect]()
205 if c.fullrepaint:
206 invalid.add(rectFromSize(c.size))
207 else:
208 for l in c.layers:
209 if not l.invalid.isempty():
210 invalid.add(l.invalid)
211 l.invalid = emptyRect()
212 c.fullrepaint = false
213
214 if invalid.len() > 0:
215 c.repaint(invalid)
176 for layer in c.layers:
177 for point in pointsinside(@[layer.dst]):
178 c.tb.write(point.x, point.y, layer.get(point)[0].toUTF8)
216179 c.tb.display()
217180
218181 ## LAYER MANAGEMENT
219182
220183 proc pushlayer*(c: var Comp, l: Layer) =
221 c.layers.insert(l, 0)
222 c.layout()
184 c.layers.add(l)
223185
224186 proc poplayer*(c: var Comp) =
225 c.layers.delete(0)
226 c.layout()
187 c.layers.delete(c.layers.len - 1)
227188
228189 proc `size=`*(c: var Comp, s: Size) =
229 if c.size != s:
230 c.tb = newTerminalBuffer(s.w, s.h)
231 c.size = s
232 c.layout()
190 c.tb = newTerminalBuffer(s.w, s.h)
191 c.size = s
233192
234193 proc translate*(l: var Layer, amt: Pt) =
235194 l.src += amt
236 # scrolling invalidates the entire dest rect
237 l.invalid = l.dst
238195
239196 proc `dst=`*(l: var Layer, r: Rect) =
240 l.invalid = l.dst | r
197 l.invalid += l.dst
198 l.invalid += r
241199 l.dst = r
242200
243201 ## DEBUG
258216 var c = b.srclayer.get(newPt(i, j))
259217 stdout.write(if c == NO_LAYER: 'X' else: char(int('1') + c))
260218 stdout.write('\n')
219
220 proc printdebug*(c: Comp) =
221 echo c.layers[0].src, " -> ", c.layers[0].dst
55 buf: string
66 blit*: Blit
77
8 proc skip(r: Rune): bool =
9 if r == Rune('\r'):
10 return true
11 if r.toUTF8 == "\xEF\xBB\xBF":
12 return true
13 return false
14
815 proc paint(buf: var Buf) =
916 var
1017 w = 0
1118 h = 0
1219 cur = 0
1320 for r in runes(buf.buf):
21 if r.skip():
22 continue
1423 if r == Rune('\n'):
1524 h += 1
1625 cur = 0
2433
2534 var p = Pt(x: 0, y: 0)
2635 for r in runes(buf.buf):
36 if r.skip():
37 continue
2738 if r == Rune('\n'):
2839 p.y += 1
2940 p.x = 0
30 elif r == Rune('\r'):
31 continue
3241 else:
3342 buf.blit.set(p, (r, 0.uint8))
3443 p.x += 1
77 bx.load("test/pg844.txt")
88
99 var lx = blit.newLayer(bx.blit)
10 lx.dst = Rect(lo: newPt(0, 0),
11 hi: newPt(comp.size.w - 1, comp.size.h - 1))
10 lx.dst = rectFromSize(comp.size())
1211 comp.pushlayer(lx)
1312
1413 proc exitProc() {.noconv.} =
2019 if e != nil:
2120 echo "exception", repr(e), "raised:", msg
2221 writeFile("err.txt", msg)
23 quit(10)
22 quit(0)
2423
2524 proc main() =
2625 illwillInit(fullscreen=false)
2827 hideCursor()
2928
3029 try:
31 comp.size = Size(w: terminalWidth(), h: terminalHeight())
32
3330 while true:
3431 var size = Size(w: terminalWidth(), h: terminalHeight())
35 if (size != comp.size):
32 if size != comp.size:
3633 comp.size = size
3734
3835 var k = illwill.getKey()
4340 lx.translate(newPt(0, 1))
4441 of Key.K:
4542 lx.translate(newPt(0, -1))
43 of Key.H:
44 lx.translate(newPt(-1, 0))
45 of Key.L:
46 lx.translate(newPt(1, 0))
4647 of Key.None:
4748 discard
4849 else:
5354 except:
5455 exitProc()
5556
56 comp.print()
57 comp.printsrc()
58 #main()
57 #comp.size = Size(w: 50, h: 30)
58 #comp.composite()
59 #comp.printsrc()
60 main()