-
Notifications
You must be signed in to change notification settings - Fork 2
/
display.go
131 lines (115 loc) · 3.39 KB
/
display.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
package display
import (
"fmt"
"image"
"runtime"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/glfw/v3.1/glfw"
"github.com/scottyw/tetromino/gameboy/controller"
)
// Display implements the LCD display using GL
type Display struct {
window *glfw.Window
}
// New implements an LCD display in GL
func New(controller *controller.Controller, onInput func(), debug bool) *Display {
if err := glfw.Init(); err != nil {
panic(fmt.Sprintf("Failed to create display: %v", err))
}
var width float32
var height float32
if debug {
width = 256
height = 256
} else {
width = 160
height = 144
}
glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1)
glfw.WindowHint(glfw.Resizable, 0)
window, err := glfw.CreateWindow(int(width*3), int(height*3), "Tetromino", nil, nil)
if err != nil {
panic(fmt.Sprintf("Failed to create display: %v", err))
}
window.MakeContextCurrent()
// Max out speed - framerate is controlled by the audio subsystem
glfw.SwapInterval(0)
if err := gl.Init(); err != nil {
panic(fmt.Sprintf("Failed to create display: %v", err))
}
gl.Enable(gl.TEXTURE_2D)
window.SetKeyCallback(onKeyFunc(controller, onInput))
var texture uint32
gl.GenTextures(1, &texture)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
display := &Display{
window: window,
}
return display
}
// Cleanup returns resources to the OS
func (d *Display) Cleanup() {
glfw.Terminate()
}
// RenderFrame draws a frame to the GL window and returns user input
func (d *Display) RenderFrame(image *image.RGBA) bool {
size := image.Rect.Size()
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(size.X), int32(size.Y), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(image.Pix))
gl.Begin(gl.QUADS)
gl.TexCoord2f(0, 1)
gl.Vertex2f(-1, -1)
gl.TexCoord2f(1, 1)
gl.Vertex2f(1, -1)
gl.TexCoord2f(1, 0)
gl.Vertex2f(1, 1)
gl.TexCoord2f(0, 0)
gl.Vertex2f(-1, 1)
gl.End()
d.window.SwapBuffers()
glfw.PollEvents()
return d.window.ShouldClose()
}
func onKeyFunc(c *controller.Controller, onInput func()) func(*glfw.Window, glfw.Key, int, glfw.Action, glfw.ModifierKey) {
return func(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
if action != glfw.Press && action != glfw.Release {
return
}
switch key {
case glfw.KeyA:
c.ButtonAction(controller.Start, action == glfw.Press)
onInput()
case glfw.KeyS:
c.ButtonAction(controller.Select, action == glfw.Press)
onInput()
case glfw.KeyZ:
c.ButtonAction(controller.B, action == glfw.Press)
onInput()
case glfw.KeyX:
c.ButtonAction(controller.A, action == glfw.Press)
onInput()
case glfw.KeyUp:
c.ButtonAction(controller.Up, action == glfw.Press)
onInput()
case glfw.KeyDown:
c.ButtonAction(controller.Down, action == glfw.Press)
onInput()
case glfw.KeyLeft:
c.ButtonAction(controller.Left, action == glfw.Press)
onInput()
case glfw.KeyRight:
c.ButtonAction(controller.Right, action == glfw.Press)
onInput()
}
}
}
func init() {
// we need a parallel OS thread to avoid audio stuttering
runtime.GOMAXPROCS(2)
// we need to keep OpenGL calls on a single thread
runtime.LockOSThread()
}