2018-07-22 00:12:22 +00:00
|
|
|
// 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)
|
|
|
|
}
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
renderer.SetDrawBlendMode(sdl.BLENDMODE_BLEND)
|
2018-07-22 00:12:22 +00:00
|
|
|
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
|
2018-07-26 03:25:02 +00:00
|
|
|
if t.State == 1 && s.Button1.Now == false {
|
|
|
|
eventName = "DOWN"
|
|
|
|
} else if t.State == 0 && s.Button1.Now == true {
|
|
|
|
eventName = "UP"
|
2018-07-22 00:12:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2018-07-22 03:43:01 +00:00
|
|
|
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,
|
|
|
|
)
|
2018-07-22 00:12:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch t.Keysym.Scancode {
|
|
|
|
case sdl.SCANCODE_ESCAPE:
|
2018-07-22 03:43:01 +00:00
|
|
|
if t.Repeat == 1 {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-22 00:12:22 +00:00
|
|
|
s.EscapeKey.Push(t.State == 1)
|
2018-07-22 03:43:01 +00:00
|
|
|
case sdl.SCANCODE_RETURN:
|
|
|
|
if t.Repeat == 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
s.EnterKey.Push(t.State == 1)
|
2018-07-22 00:12:22 +00:00
|
|
|
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)
|
2018-07-22 03:43:01 +00:00
|
|
|
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))
|
|
|
|
}
|
2018-07-22 00:12:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
2018-07-22 03:43:01 +00:00
|
|
|
// Present the current frame.
|
|
|
|
func (r *Renderer) Present() error {
|
2018-07-22 00:12:22 +00:00
|
|
|
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
|
|
|
|
}
|