-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathterm.go
348 lines (284 loc) · 8.2 KB
/
term.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package gterm
import (
"errors"
"fmt"
"log"
"golang.org/x/text/encoding/charmap"
"github.com/veandco/go-sdl2/img"
"github.com/veandco/go-sdl2/sdl"
)
var White = sdl.Color{R: 225, G: 225, B: 225, A: 255}
var CP437 = charmap.CodePage437
// Window represents the base window object
type Window struct {
Columns int
Rows int
FontSize int
FontHPixel int
FontWPixel int
DisplayHPixel int
DisplayWPixel int
HeightPixel int
WidthPixel int
fontPath string
fontSheet *sdl.Texture
spritesPerRow int
SdlWindow *sdl.Window
SdlRenderer *sdl.Renderer
backgroundColor sdl.Color
cells []cell
fps fpsCounter
vsync bool
}
type cell struct {
bgColor sdl.Color
renderItems []renderItem
}
type renderItem struct {
FColor sdl.Color
Glyph rune
}
// NewWindow constructs a window
func NewWindow(columns int, rows int, fontPath string, fontX int, fontY int, vsync bool) *Window {
numCells := columns * rows
cells := make([]cell, numCells, numCells)
window := &Window{
Columns: columns,
Rows: rows,
fontPath: fontPath,
cells: cells,
vsync: vsync,
FontHPixel: fontX,
FontWPixel: fontY,
DisplayHPixel: fontX,
DisplayWPixel: fontY,
WidthPixel: columns * fontX,
HeightPixel: rows * fontY,
}
return window
}
func (window *Window) SetTitle(title string) {
window.SdlWindow.SetTitle(title)
}
func (window *Window) ChangeFont(fontPath string, w, h int) error {
window.fontPath = fontPath
newFont, err := window.loadFont(w)
if err != nil {
return err
}
oldFont := window.fontSheet
oldFont.Destroy()
window.fontSheet = newFont
window.FontWPixel = w
window.FontHPixel = h
window.SdlWindow.SetSize(window.Columns*w, window.Rows*h)
return nil
}
func (window *Window) updateSize() {
actualW, actualH := window.SdlWindow.GetSize()
window.DisplayWPixel = actualW / window.Columns
window.DisplayHPixel = actualH / window.Rows
}
func (window *Window) loadFont(w int) (*sdl.Texture, error) {
rwops := sdl.RWFromFile(window.fontPath, "rb")
if rwops == nil {
return nil, fmt.Errorf("Failed to load image from %s", window.fontPath)
}
surface, err := img.LoadPNG_RW(rwops)
if err != nil {
return nil, err
}
defer surface.Free()
if err := surface.SetColorKey(sdl.ENABLE, 0); err != nil {
return nil, err
}
window.spritesPerRow = int(surface.W) / w
texture, err := window.SdlRenderer.CreateTextureFromSurface(surface)
if err != nil {
return nil, err
}
if err := texture.SetBlendMode(sdl.BLENDMODE_BLEND); err != nil {
return nil, err
}
return texture, nil
}
// Init initialized the window for drawing
func (window *Window) Init() error {
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
return err
}
if flags := img.Init(img.INIT_PNG); flags&img.INIT_PNG == 0 {
return errors.New("Failed to initialize sdl2_img for PNG")
}
sdlWindow, err := sdl.CreateWindow("", sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, window.WidthPixel, window.HeightPixel, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
if err != nil {
return err
}
var flags uint32 = sdl.RENDERER_ACCELERATED
if window.vsync {
flags = sdl.RENDERER_PRESENTVSYNC
}
sdlRenderer, err := sdl.CreateRenderer(sdlWindow, -1, flags)
if err != nil {
return err
}
if err := sdlRenderer.SetDrawBlendMode(sdl.BLENDMODE_BLEND); err != nil {
return err
}
window.SdlWindow = sdlWindow
window.SdlRenderer = sdlRenderer
window.fontSheet, err = window.loadFont(window.FontWPixel)
if err != nil {
return err
}
err = sdlRenderer.SetDrawColor(0, 0, 0, 0)
if err != nil {
log.Fatalln("Could not set render color", err)
}
window.fps = newFpsCounter()
return nil
}
func (window *Window) SetBackgroundColor(color sdl.Color) {
window.backgroundColor = color
}
func (window *Window) cellIndex(col int, row int) (int, error) {
if col >= window.Columns || col < 0 || row >= window.Rows || row < 0 {
return 0, fmt.Errorf("Requested invalid position (%v,%v) on board of dimensions %vx%v", col, row, window.Columns, window.Rows)
}
return col + window.Columns*row, nil
}
func (window *Window) renderCell(cellCol int, cellRow int) error {
idx, err := window.cellIndex(cellCol, cellRow)
if err != nil {
return err
}
destX := cellCol * window.DisplayWPixel
destY := cellRow * window.DisplayHPixel
destRect := sdl.Rect{X: int32(destX), Y: int32(destY), W: int32(window.DisplayWPixel), H: int32(window.DisplayHPixel)}
cell := window.cells[idx]
for _, item := range cell.renderItems {
runeByte, ok := CP437.EncodeRune(item.Glyph)
if !ok {
log.Println("Could not encode rune", item.Glyph)
}
row := int(runeByte) / window.spritesPerRow
col := int(runeByte) % window.spritesPerRow
sX := col * window.FontWPixel
sY := row * window.FontHPixel
sourceRect := sdl.Rect{X: int32(sX), Y: int32(sY), W: int32(window.FontWPixel), H: int32(window.FontHPixel)}
if cell.bgColor != NoColor {
color := cell.bgColor
r, g, b, a := uint8(color.R), uint8(color.G), uint8(color.B), uint8(color.A)
window.SdlRenderer.SetDrawColor(r, g, b, a)
window.SdlRenderer.FillRect(&destRect)
}
color := item.FColor
r, g, b := uint8(color.R), uint8(color.G), uint8(color.B)
window.fontSheet.SetColorMod(r, g, b)
if err := window.SdlRenderer.Copy(window.fontSheet, &sourceRect, &destRect); err != nil {
return err
}
}
return nil
}
func (window *Window) renderCells() error {
for col := 0; col < window.Columns; col++ {
for row := 0; row < window.Rows; row++ {
if err := window.renderCell(col, row); err != nil {
return err
}
}
}
return nil
}
// NoColor is used to represent no background color
var NoColor = sdl.Color{R: 0, G: 0, B: 0, A: 0}
func (window *Window) PutRune(col int, row int, glyph rune, fColor sdl.Color, bColor sdl.Color) error {
renderItem := renderItem{Glyph: glyph, FColor: fColor}
index, err := window.cellIndex(col, row)
if err != nil {
return err
}
window.cells[index].renderItems = append(window.cells[index].renderItems, renderItem)
window.cells[index].bgColor = bColor
return nil
}
func (window *Window) PutStringBg(col int, row int, content string, fColor sdl.Color, bColor sdl.Color) error {
step := 0
for _, rune := range content {
if err := window.PutRune(col+step, row, rune, fColor, bColor); err != nil {
return err
}
step++
}
return nil
}
func (window *Window) PutString(col int, row int, content string, fColor sdl.Color) error {
return window.PutStringBg(col, row, content, fColor, NoColor)
}
func (window *Window) ClearRegion(col int, row int, width int, height int) error {
for y := row; y < row+height; y++ {
for x := col; x < col+width; x++ {
if err := window.ClearCell(x, y); err != nil {
return err
}
}
}
return nil
}
func (window *Window) ClearCell(col int, row int) error {
index, err := window.cellIndex(col, row)
if err != nil {
return err
}
window.cells[index] = cell{}
return nil
}
func (window *Window) ClearWindow() {
window.cells = make([]cell, window.Columns*window.Rows, window.Columns*window.Rows)
}
func (window *Window) ShouldRenderFps(shouldRender bool) {
window.fps.shouldRender(shouldRender)
}
// Render updates the display based on new information since last Render
type fpsCounter struct {
renderFps bool
framesElapsed int
currentFps int
lastTicks uint32
color sdl.Color
}
func newFpsCounter() fpsCounter {
return fpsCounter{
lastTicks: sdl.GetTicks(),
color: sdl.Color{R: 0, G: 255, B: 0, A: 255},
}
}
func (fps *fpsCounter) shouldRender(shouldRender bool) {
fps.renderFps = shouldRender
}
func min(a uint32, b uint32) uint32 {
if a < b {
return a
}
return b
}
func (window *Window) DebugDrawSpriteSheet() error {
_, _, w, h, err := window.fontSheet.Query()
if err != nil {
return err
}
return window.SdlRenderer.Copy(window.fontSheet, nil, &sdl.Rect{X: 0, Y: 0, W: w, H: h})
}
func (window *Window) Refresh() {
window.updateSize()
err := window.SdlRenderer.SetDrawColor(window.backgroundColor.R, window.backgroundColor.G, window.backgroundColor.B, window.backgroundColor.A)
if err != nil {
log.Fatal(err)
}
window.SdlRenderer.Clear()
if err := window.renderCells(); err != nil {
log.Println("Failed to render cells", err)
}
window.SdlRenderer.Present()
}