Files
aego/platform/headless/headless.go

271 lines
5.4 KiB
Go

package headless
import (
"runtime"
"strconv"
"aego/core/clock"
"aego/core/errs"
"aego/core/mathx"
"aego/platform"
)
type Options struct {
FrameTime float64
Displays []platform.Display
}
type Platform struct {
opts Options
clock *clock.Clock
sim float64
win *Window
queue []platform.Event
events []platform.Event
clipText string
clipData map[string][]byte
audio *Audio
}
func New(opts Options) *Platform {
return &Platform{
opts: opts,
clock: clock.New(),
queue: make([]platform.Event, 0, 16),
events: make([]platform.Event, 0, 16),
clipData: map[string][]byte{},
}
}
func (p *Platform) CreateWindow(desc platform.WindowDesc) (platform.Window, error) {
if p.win != nil {
return nil, errs.New(errs.PlatformWindow, "headless platform supports a single window")
}
w, h := desc.Width, desc.Height
if w <= 0 {
w = 1280
}
if h <= 0 {
h = 720
}
p.win = &Window{
p: p,
vsync: desc.VSync,
metrics: platform.WindowMetrics{
LogicalW: w, LogicalH: h,
PixelW: w, PixelH: h,
PixelDensity: 1, DisplayScale: 1,
},
}
return p.win, nil
}
func (p *Platform) Push(ev platform.Event) {
if ev.Window == (platform.WindowMetrics{}) && p.win != nil {
ev.Window = p.win.metrics
}
if ev.Kind == platform.EvWindowResized && p.win != nil {
p.win.metrics = ev.Window
}
p.queue = append(p.queue, ev)
}
func (p *Platform) PollEvents(fn func(platform.Event)) {
if p.opts.FrameTime > 0 {
p.sim += p.opts.FrameTime
}
if p.audio != nil {
p.audio.advance()
}
if len(p.queue) == 0 {
return
}
p.events, p.queue = p.queue, p.events[:0]
for _, ev := range p.events {
fn(ev)
}
p.events = p.events[:0]
}
func (p *Platform) Now() float64 {
if p.opts.FrameTime > 0 {
return p.sim
}
return p.clock.Now()
}
func (p *Platform) Info() platform.Info {
return platform.Info{
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Backend: "headless",
Version: "-",
VideoDriver: "none",
}
}
func (p *Platform) Shutdown() {
p.win = nil
}
type Window struct {
p *Platform
metrics platform.WindowMetrics
vsync platform.VSyncMode
fullscreen bool
textInput bool
textArea mathx.Rect
cursor platform.CursorShape
captured bool
mode platform.WindowMode
display int
}
func (w *Window) Metrics() platform.WindowMetrics {
return w.metrics
}
func (w *Window) SetTitle(string) {}
func (w *Window) SetVSync(m platform.VSyncMode) platform.VSyncMode {
w.vsync = m
return m
}
func (w *Window) SetFullscreen(on bool) {
w.fullscreen = on
}
func (w *Window) Fullscreen() bool {
return w.fullscreen
}
func (w *Window) StartTextInput() {
w.textInput = true
}
func (w *Window) StopTextInput() {
w.textInput = false
}
func (w *Window) TextInputActive() bool {
return w.textInput
}
func (w *Window) SetTextInputArea(r mathx.Rect, cursor int) {
w.textArea = r
}
func (w *Window) ClipboardText() string {
if w.p == nil {
return ""
}
return w.p.clipText
}
func (w *Window) SetClipboardText(s string) {
if w.p == nil {
return
}
w.p.clipText = s
clear(w.p.clipData)
}
func (w *Window) SetClipboardData(mime string, data []byte, text string) bool {
if w.p == nil {
return false
}
clear(w.p.clipData)
w.p.clipData[mime] = append([]byte(nil), data...)
w.p.clipText = text
return true
}
func (w *Window) ClipboardData(mime string) ([]byte, bool) {
if w.p == nil {
return nil, false
}
d, ok := w.p.clipData[mime]
if !ok {
return nil, false
}
return append([]byte(nil), d...), true
}
func (w *Window) SetCursor(s platform.CursorShape) {
w.cursor = s
}
func (w *Window) Cursor() platform.CursorShape {
return w.cursor
}
func (w *Window) CaptureMouse(on bool) {
w.captured = on
}
func (w *Window) SwapBuffers() {}
func (w *Window) Close() {
if w.p != nil && w.p.win == w {
w.p.win = nil
}
}
var (
_ platform.TextInputer = (*Window)(nil)
_ platform.Clipboard = (*Window)(nil)
_ platform.Cursorer = (*Window)(nil)
_ platform.MouseCapturer = (*Window)(nil)
)
func defaultDisplays() []platform.Display {
return []platform.Display{{
ID: 1, Name: "Headless", Current: platform.DisplayMode{W: 1920, H: 1080, Hz: 60},
Modes: []platform.DisplayMode{{W: 1920, H: 1080, Hz: 60}, {W: 1280, H: 720, Hz: 60}},
}}
}
func (w *Window) Displays() []platform.Display {
if w.p != nil && len(w.p.opts.Displays) > 0 {
return w.p.opts.Displays
}
return defaultDisplays()
}
func (w *Window) WindowDisplay() int {
return w.display
}
func (w *Window) WindowMode() platform.WindowMode {
return w.mode
}
func (w *Window) SetWindowMode(m platform.WindowMode, display int, size platform.DisplayMode) error {
ds := w.Displays()
if display < 0 || display >= len(ds) {
return errs.New(errs.PlatformWindow, "there is no display "+strconv.Itoa(display))
}
d := &ds[display]
switch m {
case platform.Fullscreen:
if size.W <= 0 {
size = d.Current
}
if !d.Has(size) {
return errs.New(errs.PlatformWindow, "display "+d.Name+" has no mode "+strconv.Itoa(size.W)+"x"+strconv.Itoa(size.H))
}
case platform.Borderless:
size = d.Current
}
w.mode, w.display, w.fullscreen = m, display, m != platform.Windowed
if size.W > 0 && size.H > 0 && w.p != nil {
mt := w.metrics
mt.LogicalW, mt.LogicalH, mt.PixelW, mt.PixelH = size.W, size.H, size.W, size.H
w.p.Push(platform.Event{Kind: platform.EvWindowResized, Window: mt})
}
return nil
}
var _ platform.Displayer = (*Window)(nil)