Noah Petherbridge
11df6cbda9
These properties will be globally useful to all sorts of Widgets and have been moved from the Button up into the Common widget, and its interface extended to configure these: * Padding int32 * Background color * Foreground color * Border size, color and style (default solid; raised; sunken) * Outline size and color The button adjusts its border style from "raised" to "sunken" for MouseDown events and its Background color for MouseOver events. Other widgets such as Labels and Frames will be able to have borders, paddings and outlines too, but they will be off by default.
217 lines
4.8 KiB
Go
217 lines
4.8 KiB
Go
// Package sdl provides an SDL2 renderer for Doodle.
|
|
package sdl
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"git.kirsle.net/apps/doodle/events"
|
|
"git.kirsle.net/apps/doodle/render"
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
"github.com/veandco/go-sdl2/ttf"
|
|
)
|
|
|
|
// Renderer manages the SDL state.
|
|
type Renderer struct {
|
|
// Configurable fields.
|
|
title string
|
|
width int32
|
|
height int32
|
|
startTime time.Time
|
|
|
|
// Private fields.
|
|
events *events.State
|
|
window *sdl.Window
|
|
renderer *sdl.Renderer
|
|
running bool
|
|
ticks uint64
|
|
|
|
// Optimizations to minimize SDL calls.
|
|
lastColor render.Color
|
|
}
|
|
|
|
// New creates the SDL renderer.
|
|
func New(title string, width, height int32) *Renderer {
|
|
return &Renderer{
|
|
events: events.New(),
|
|
title: title,
|
|
width: width,
|
|
height: height,
|
|
}
|
|
}
|
|
|
|
// Teardown tasks when exiting the program.
|
|
func (r *Renderer) Teardown() {
|
|
r.renderer.Destroy()
|
|
r.window.Destroy()
|
|
sdl.Quit()
|
|
}
|
|
|
|
// Setup the renderer.
|
|
func (r *Renderer) Setup() error {
|
|
// Initialize SDL.
|
|
log.Info("Initializing SDL")
|
|
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Initialize SDL_TTF.
|
|
log.Info("Initializing SDL_TTF")
|
|
if err := ttf.Init(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create our window.
|
|
log.Info("Creating the Main Window")
|
|
window, err := sdl.CreateWindow(
|
|
r.title,
|
|
sdl.WINDOWPOS_CENTERED,
|
|
sdl.WINDOWPOS_CENTERED,
|
|
r.width,
|
|
r.height,
|
|
sdl.WINDOW_SHOWN,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.window = window
|
|
|
|
// Blank out the window in white.
|
|
log.Info("Creating the SDL Renderer")
|
|
renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
r.renderer = renderer
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetTicks gets SDL's current tick count.
|
|
func (r *Renderer) GetTicks() uint32 {
|
|
return sdl.GetTicks()
|
|
}
|
|
|
|
// Poll for events.
|
|
func (r *Renderer) Poll() (*events.State, error) {
|
|
s := r.events
|
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
|
switch t := event.(type) {
|
|
case *sdl.QuitEvent:
|
|
return s, errors.New("quit")
|
|
case *sdl.MouseMotionEvent:
|
|
if DebugMouseEvents {
|
|
log.Debug("[%d ms] tick:%d MouseMotion type:%d id:%d x:%d y:%d xrel:%d yrel:%d",
|
|
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y, t.XRel, t.YRel,
|
|
)
|
|
}
|
|
|
|
// Push the cursor position.
|
|
s.CursorX.Push(t.X)
|
|
s.CursorY.Push(t.Y)
|
|
s.Button1.Push(t.State == 1)
|
|
case *sdl.MouseButtonEvent:
|
|
if DebugClickEvents {
|
|
log.Debug("[%d ms] tick:%d MouseButton type:%d id:%d x:%d y:%d button:%d state:%d",
|
|
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y, t.Button, t.State,
|
|
)
|
|
}
|
|
|
|
// Push the cursor position.
|
|
s.CursorX.Push(t.X)
|
|
s.CursorY.Push(t.Y)
|
|
|
|
// Is a mouse button pressed down?
|
|
if t.Button == 1 {
|
|
var eventName string
|
|
if t.State == 1 && s.Button1.Now == false {
|
|
eventName = "DOWN"
|
|
} else if t.State == 0 && s.Button1.Now == true {
|
|
eventName = "UP"
|
|
}
|
|
|
|
if eventName != "" {
|
|
s.Button1.Push(eventName == "DOWN")
|
|
|
|
// Return the event immediately.
|
|
return s, nil
|
|
}
|
|
}
|
|
|
|
// s.Button2.Push(t.Button == 3 && t.State == 1)
|
|
case *sdl.MouseWheelEvent:
|
|
if DebugMouseEvents {
|
|
log.Debug("[%d ms] tick:%d MouseWheel type:%d id:%d x:%d y:%d",
|
|
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y,
|
|
)
|
|
}
|
|
case *sdl.KeyboardEvent:
|
|
if DebugKeyEvents {
|
|
log.Debug("[%d ms] tick:%d Keyboard type:%d sym:%c modifiers:%d state:%d repeat:%d\n",
|
|
t.Timestamp, r.ticks, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat,
|
|
)
|
|
}
|
|
|
|
switch t.Keysym.Scancode {
|
|
case sdl.SCANCODE_ESCAPE:
|
|
if t.Repeat == 1 {
|
|
continue
|
|
}
|
|
s.EscapeKey.Push(t.State == 1)
|
|
case sdl.SCANCODE_RETURN:
|
|
if t.Repeat == 1 {
|
|
continue
|
|
}
|
|
s.EnterKey.Push(t.State == 1)
|
|
case sdl.SCANCODE_F12:
|
|
s.ScreenshotKey.Push(t.State == 1)
|
|
case sdl.SCANCODE_UP:
|
|
s.Up.Push(t.State == 1)
|
|
case sdl.SCANCODE_LEFT:
|
|
s.Left.Push(t.State == 1)
|
|
case sdl.SCANCODE_RIGHT:
|
|
s.Right.Push(t.State == 1)
|
|
case sdl.SCANCODE_DOWN:
|
|
s.Down.Push(t.State == 1)
|
|
case sdl.SCANCODE_LSHIFT:
|
|
case sdl.SCANCODE_RSHIFT:
|
|
s.ShiftActive.Push(t.State == 1)
|
|
continue
|
|
case sdl.SCANCODE_LALT:
|
|
case sdl.SCANCODE_RALT:
|
|
case sdl.SCANCODE_LCTRL:
|
|
case sdl.SCANCODE_RCTRL:
|
|
continue
|
|
case sdl.SCANCODE_BACKSPACE:
|
|
// Make it a key event with "\b" as the sequence.
|
|
if t.State == 1 || t.Repeat == 1 {
|
|
s.KeyName.Push(`\b`)
|
|
}
|
|
default:
|
|
// Push the string value of the key.
|
|
if t.State == 1 {
|
|
s.KeyName.Push(string(t.Keysym.Sym))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// Present the current frame.
|
|
func (r *Renderer) Present() error {
|
|
r.renderer.Present()
|
|
return nil
|
|
}
|
|
|
|
// Delay using sdl.Delay
|
|
func (r *Renderer) Delay(time uint32) {
|
|
sdl.Delay(time)
|
|
}
|
|
|
|
// Loop is the main loop.
|
|
func (r *Renderer) Loop() error {
|
|
return nil
|
|
}
|