diff --git a/.gitignore b/.gitignore index 9777643..7cd9e90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,2 @@ -**~ -main -**.[o86] -.obj/ -**.cgo*.* -**/_obj -**/*.out -test/test -shoot.png -**_cgo* -*.so \ No newline at end of file +sdl-test/sdl-test +sdl-template/sdl-template diff --git a/4s/4s.go b/4s/4s.go deleted file mode 100644 index 9c5415e..0000000 --- a/4s/4s.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// this is SDL version of src/pkg/exp/4s/4s.go -// if you want to run int enable exp/draw support in sdl/Makefile - -package main - -import ( - "sdl" - "log" - "os" - "runtime" -) - - -func main() { - - runtime.LockOSThread() - - args := os.Args - p := pieces4 - if len(args) > 1 && args[1] == "-5" { - p = pieces5 - } - dx, dy := 500, 500 - w, err := sdl.InitContext(dx, dy) - if err != nil { - log.Exit(err) - } - - Play(p, w) -} - -func PlaySound(b []uint16) { - // no audio yet -} - -var whoosh = []uint16{ -// Insert your favorite sound samples here. -} diff --git a/4s/Makefile b/4s/Makefile deleted file mode 100644 index 4d95ad1..0000000 --- a/4s/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -all: 8.out - -KSDIR:=$(GOROOT)/src/pkg/exp/4s/ - -4s.8: 4s.go $(KSDIR)/data.go $(KSDIR)//xs.go - 8g 4s.go $(KSDIR)/data.go $(KSDIR)/xs.go - -8.out: 4s.8 - 8l 4s.8 - -clean: - rm -f *.8 8.out diff --git a/4s/xs.patch b/4s/xs.patch deleted file mode 100644 index 3094e4d..0000000 --- a/4s/xs.patch +++ /dev/null @@ -1,15 +0,0 @@ -# apply this patch if you are geting this error: -# /src/pkg/exp/4s//xs.go:729: cannot use int32((time.Nanoseconds()) % 999999999) (type int32) as type int64 in function argument -# -diff -r 2f32e74ab96e src/pkg/exp/4s/xs.go ---- a/src/pkg/exp/4s/xs.go Sat Nov 21 15:53:03 2009 -0800 -+++ b/src/pkg/exp/4s/xs.go Mon Nov 23 17:39:28 2009 +0100 -@@ -726,7 +726,7 @@ - pieces = pp; - N = len(pieces[0].d); - initPieces(); -- rand.Seed(int32(time.Nanoseconds() % (1e9 - 1))); -+ rand.Seed(int64(time.Nanoseconds() % (1e9 - 1))); - whitemask = draw.White.SetAlpha(0x7F); - tsleep = 50; - timerc = time.Tick(int64(tsleep/2) * 1e6); diff --git a/Makefile b/Makefile deleted file mode 100644 index 28cff24..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -all: install - -install: - make -C sdl install - make -C ttf install - make -C mixer install - make -C gfx install - -clean: - make -C sdl clean - make -C ttf clean - make -C mixer clean - make -C 4s clean - make -C test clean - make -C gfx clean diff --git a/README b/README deleted file mode 100644 index d77bb90..0000000 --- a/README +++ /dev/null @@ -1,5 +0,0 @@ -Make sure you have SDL, SDL_image, SDL_mixer, and SDL_ttf (all in -dev version). - -If the bindings fail to compile, run 'hg pull -u' in $GOROOT and rebuild Go. The release version of Go doesn't have some changes necessary to build the ttf and mixer bindings last I checked. - -Music to test SDL_mixer is by Kevin MacLeod. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5912fba --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Introduction + +This is an improved version of 0xe2-0x9a-0x9b's [Go-SDL](https://github.com/0xe2-0x9a-0x9b/Go-SDL) +currently mantained by neagix. + +The improvements/differences are: + +* audio callback support +* downstreaming support + +There is a nice and fully working PC speaker buzzer example in examples/callback. + +# Known issues + +The re-designed audio system supports only signed 16bit samples, but writing the others is as easy as a copy/paste. + +# Installation + +Make sure you have SDL, SDL-image, SDL-mixer and SDL-ttf (all in -dev version). + +Installing libraries and examples: + + go get -v github.com/neagix/Go-SDL/sdl + go get -v github.com/neagix/Go-SDL/sdl/audio + + +# Credits + +Music to test SDL-mixer is by Kevin MacLeod. diff --git a/TODO b/TODO deleted file mode 100644 index 42f1a03..0000000 --- a/TODO +++ /dev/null @@ -1,26 +0,0 @@ -* permanently fix KeyboardEvent issue - - Go, unlike C, adds pading before nested structures. - - struct A {uint8 n=0x11} - struct B {uint8 n=0x22, A a} - - in c this will result in raw data: - 22 11 - in Go - 22 00 00 00 11 - - This results in misaligned Keysym field. - - This is currently fixed by adding or removing some fields. - Accessor function in C would probably be better solution. - - // static SDL_keysym* KeyboardEvent_GetKeySym(SDL_KeyboardEvent *e){return &(e->keysym);} - import "C" - - func (event *KeyboardEvent) Keysym() *Keysym { return (*Keysym)(cast(C.KeyboardEvent_GetKeySym((*C.SDL_KeyboardEvent)(cast(event))))); } - -* missing functions, threads and file access are useless in Go, but there are probably some usefull functions left -* add joystick support -* other SDL libraries: SGE, gfx ... -* more/better test/example apps diff --git a/doc/template.go b/doc/template.go deleted file mode 100644 index 688f8f7..0000000 --- a/doc/template.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "sdl" -) - -func loadImage(name string) *sdl.Surface { - image := sdl.Load(name) - - if image == nil { - panic(sdl.GetError()) - } - - return image - -} - -func main() { - - if sdl.Init(sdl.INIT_EVERYTHING) != 0 { - panic(sdl.GetError()) - } - - defer sdl.Quit() - - var screen = sdl.SetVideoMode(640, 480, 32, 0) - - if screen == nil { - panic(sdl.GetError()) - } - - sdl.WM_SetCaption("Template", "") - - for true { - - e := &sdl.Event{} - - for e.Poll() { - switch e.Type { - case sdl.QUIT: - return - default: - } - } - - screen.FillRect(nil, 0x000000) - - //screen.Blit(&sdl.Rect{x,y, 0, 0}, image, nil) - - screen.Flip() - sdl.Delay(25) - - } - -} diff --git a/examples/callback/buzzer.go b/examples/callback/buzzer.go new file mode 100644 index 0000000..72f5445 --- /dev/null +++ b/examples/callback/buzzer.go @@ -0,0 +1,149 @@ +/* + * Copyright neagix 2013 + * This sample is part of langwar project + * https://github.com/neagix/langwar + * + * Licensed under GNU/GPL v2 + */ + +package main + +import "github.com/neagix/Go-SDL/sdl/audio" +import "math" +import "time" + +type BeepObject struct { + Freq float64 + SamplesLeft int +} + +var Play chan BeepObject + +const AMPLITUDE = 28000 +const FREQUENCY = 44100 +const AUDIO_SAMPLES = 2048 + +func Beep(freq, duration int) { + bo := BeepObject{ + Freq: float64(freq), + SamplesLeft: duration * FREQUENCY / 1000} + + Play <- bo +} + +func Init() bool { + + desiredSpec := audio.AudioSpec{ + Freq: FREQUENCY, + Format: audio.AUDIO_S16SYS, + Channels: 1, + Samples: AUDIO_SAMPLES, + } + var obtainedSpec audio.AudioSpec + + if audio.OpenAudio(&desiredSpec, &obtainedSpec) != 0 { + return false + } + + Play = make(chan BeepObject) + + // start the playback queue processor + go func() { + for { + // pick next beep object + bo := <-Play + + stream := make([]int16, bo.SamplesLeft) + + v := float64(0) + for i := 0; i < bo.SamplesLeft; i++ { + stream[i] = int16(AMPLITUDE * math.Sin(v*2*math.Pi/FREQUENCY)) + v += bo.Freq + } + + audio.SendAudio_int16(stream) + } + }() + + return true +} + +// to be deferred after corresponding Init() +func Quit() { + defer audio.CloseAudio() +} + +// song by niniel1 as found at http://gendou.com/t/20439 +func PlaySong1() { + Beep(349, 400) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(440, 267) + time.Sleep(time.Millisecond * 33) + Beep(440, 267) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(349, 133) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(440, 133) + time.Sleep(time.Millisecond * 33) + Beep(349, 267) + time.Sleep(time.Millisecond * 33) + Beep(262, 267) + time.Sleep(time.Millisecond * 33) + Beep(349, 400) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(440, 267) + time.Sleep(time.Millisecond * 33) + Beep(440, 267) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(349, 133) + time.Sleep(time.Millisecond * 33) + Beep(392, 133) + time.Sleep(time.Millisecond * 33) + Beep(440, 133) + time.Sleep(time.Millisecond * 33) + Beep(349, 533) + time.Sleep(time.Millisecond * 33) +} + +// find out more songs at https://github.com/binarypearl/beepbeep +func PlaySong3() { + Beep(784, 100) + Beep(784, 100) + Beep(784, 100) + time.Sleep(time.Millisecond * 100) + Beep(784, 600) + Beep(622, 600) + Beep(698, 600) + Beep(784, 200) + time.Sleep(time.Millisecond * 200) + Beep(698, 200) + Beep(784, 800) + +} + +func main() { + if !Init() { + return + } + defer Quit() + + // start playback, yeah! + audio.PauseAudio(false) + + PlaySong1() + + // it's a silence :) + Beep(0, 400) + + PlaySong3() +} diff --git a/test/Fontin Sans.otf b/examples/completeTest/Fontin Sans.otf similarity index 100% rename from test/Fontin Sans.otf rename to examples/completeTest/Fontin Sans.otf diff --git a/examples/completeTest/test.go b/examples/completeTest/test.go new file mode 100644 index 0000000..5ad2fa3 --- /dev/null +++ b/examples/completeTest/test.go @@ -0,0 +1,247 @@ +package main + +import ( + "fmt" + "github.com/neagix/Go-SDL/mixer" + "github.com/neagix/Go-SDL/sdl" + "github.com/neagix/Go-SDL/ttf" + "log" + "math" + "os" + "strings" + "time" +) + +type Point struct { + x int + y int +} + +func (a Point) add(b Point) Point { return Point{a.x + b.x, a.y + b.y} } + +func (a Point) sub(b Point) Point { return Point{a.x - b.x, a.y - b.y} } + +func (a Point) length() float64 { return math.Sqrt(float64(a.x*a.x + a.y*a.y)) } + +func (a Point) mul(b float64) Point { + return Point{int(float64(a.x) * b), int(float64(a.y) * b)} +} + +func worm(in <-chan Point, out chan<- Point, draw chan<- Point) { + + t := Point{0, 0} + + for { + p := (<-in).sub(t) + + if p.length() > 48 { + t = t.add(p.mul(0.1)) + } + + draw <- t + out <- t + } +} + +func main() { + log.SetFlags(0) + + var resourcePath string + { + GOPATH := os.Getenv("GOPATH") + if GOPATH == "" { + log.Fatal("No such environment variable: GOPATH") + } + for _, gopath := range strings.Split(GOPATH, ":") { + a := gopath + "/src/github.com/neagix/Go-SDL/examples/completeTest" + _, err := os.Stat(a) + if err == nil { + resourcePath = a + break + } + } + if resourcePath == "" { + log.Fatal("Failed to find resource directory") + } + } + + var joy *sdl.Joystick + if sdl.Init(sdl.INIT_EVERYTHING) != 0 { + log.Fatal(sdl.GetError()) + } + + if ttf.Init() != 0 { + log.Fatal(sdl.GetError()) + } + + if sdl.NumJoysticks() > 0 { + // Open joystick + joy = sdl.JoystickOpen(0) + + if joy != nil { + println("Opened Joystick 0") + println("Name: ", sdl.JoystickName(0)) + println("Number of Axes: ", joy.NumAxes()) + println("Number of Buttons: ", joy.NumButtons()) + println("Number of Balls: ", joy.NumBalls()) + } else { + println("Couldn't open Joystick!") + } + } + + if mixer.OpenAudio(mixer.DEFAULT_FREQUENCY, mixer.DEFAULT_FORMAT, + mixer.DEFAULT_CHANNELS, 4096) != 0 { + log.Fatal(sdl.GetError()) + } + + var screen = sdl.SetVideoMode(640, 480, 32, sdl.RESIZABLE) + + if screen == nil { + log.Fatal(sdl.GetError()) + } + + var video_info = sdl.GetVideoInfo() + + println("HW_available = ", video_info.HW_available) + println("WM_available = ", video_info.WM_available) + println("Video_mem = ", video_info.Video_mem, "kb") + + sdl.EnableUNICODE(1) + + sdl.WM_SetCaption("Go-SDL SDL Test", "") + + image := sdl.Load(resourcePath + "/test.png") + + if image == nil { + log.Fatal(sdl.GetError()) + } + + sdl.WM_SetIcon(image, nil) + + running := true + + font := ttf.OpenFont(resourcePath+"/Fontin Sans.otf", 72) + + if font == nil { + log.Fatal(sdl.GetError()) + } + + font.SetStyle(ttf.STYLE_UNDERLINE) + white := sdl.Color{255, 255, 255, 0} + text := ttf.RenderText_Blended(font, "Test (with music)", white) + music := mixer.LoadMUS(resourcePath + "/test.ogg") + + if music == nil { + log.Fatal(sdl.GetError()) + } + + music.PlayMusic(-1) + + if sdl.GetKeyName(270) != "[+]" { + log.Fatal("GetKeyName broken") + } + + worm_in := make(chan Point) + draw := make(chan Point, 64) + + var out chan Point + var in chan Point + + out = worm_in + + in = out + out = make(chan Point) + go worm(in, out, draw) + + ticker := time.NewTicker(time.Second / 50) // 50 Hz + + // Note: The following SDL code is highly ineffective. + // It is eating too much CPU. If you intend to use Go-SDL, + // you should to do better than this. + + for running { + select { + case <-ticker.C: + screen.FillRect(nil, 0x302019) + screen.Blit(&sdl.Rect{0, 0, 0, 0}, text, nil) + + loop: + for { + select { + case p := <-draw: + screen.Blit(&sdl.Rect{int16(p.x), int16(p.y), 0, 0}, image, nil) + + case <-out: + default: + break loop + } + } + + var p Point + sdl.GetMouseState(&p.x, &p.y) + worm_in <- p + + screen.Flip() + + case _event := <-sdl.Events: + switch e := _event.(type) { + case sdl.QuitEvent: + running = false + + case sdl.KeyboardEvent: + println("") + println(e.Keysym.Sym, ": ", sdl.GetKeyName(sdl.Key(e.Keysym.Sym))) + + if e.Keysym.Sym == sdl.K_ESCAPE { + running = false + } + + fmt.Printf("%04x ", e.Type) + + for i := 0; i < len(e.Pad0); i++ { + fmt.Printf("%02x ", e.Pad0[i]) + } + println() + + fmt.Printf("Type: %02x Which: %02x State: %02x Pad: %02x\n", e.Type, e.Which, e.State, e.Pad0[0]) + fmt.Printf("Scancode: %02x Sym: %08x Mod: %04x Unicode: %04x\n", e.Keysym.Scancode, e.Keysym.Sym, e.Keysym.Mod, e.Keysym.Unicode) + + case sdl.MouseButtonEvent: + if e.Type == sdl.MOUSEBUTTONDOWN { + println("Click:", e.X, e.Y) + in = out + out = make(chan Point) + go worm(in, out, draw) + } + + case sdl.JoyAxisEvent: + println("Joystick Axis Event ->", "Type", e.Type, "Axis:", e.Axis, " Value:", e.Value, "Which:", e.Which) + + case sdl.JoyButtonEvent: + println("Joystick Button Event ->", e.Button) + println("State of button", e.Button, "->", joy.GetButton(int(e.Button))) + + case sdl.ResizeEvent: + println("resize screen ", e.W, e.H) + + screen = sdl.SetVideoMode(int(e.W), int(e.H), 32, sdl.RESIZABLE) + + if screen == nil { + log.Fatal(sdl.GetError()) + } + } + } + } + + // Close if opened + if sdl.JoystickOpened(0) > 0 { + joy.Close() + } + + image.Free() + music.Free() + font.Close() + + ttf.Quit() + sdl.Quit() +} diff --git a/test/test.ogg b/examples/completeTest/test.ogg similarity index 100% rename from test/test.ogg rename to examples/completeTest/test.ogg diff --git a/test/test.png b/examples/completeTest/test.png similarity index 100% rename from test/test.png rename to examples/completeTest/test.png diff --git a/examples/sound/test.go b/examples/sound/test.go new file mode 100644 index 0000000..4d4f6ff --- /dev/null +++ b/examples/sound/test.go @@ -0,0 +1,72 @@ +package main + +import ( + "github.com/neagix/Go-SDL/sdl" + "github.com/neagix/Go-SDL/sdl/audio" + "github.com/neagix/Go-SDL/sound" + "log" + "time" + "os" + "strings" +) + +func main() { + log.SetFlags(0) + + var resourcePath string + { + GOPATH := os.Getenv("GOPATH") + if GOPATH == "" { + log.Fatal("No such environment variable: GOPATH") + } + for _, gopath := range strings.Split(GOPATH, ":") { + a := gopath + "/src/github.com/neagix/Go-SDL/examples/completeTest" + _, err := os.Stat(a) + if err == nil { + resourcePath = a + break + } + } + if resourcePath == "" { + log.Fatal("Failed to find resource directory") + } + } + + if sdl.Init(sdl.INIT_EVERYTHING) != 0 { + log.Fatal(sdl.GetError()) + } + + sound.Init() + + desiredSpec := audio.AudioSpec{Freq: 44100, Format: audio.AUDIO_S16SYS, Channels: 1, Samples: 4096} + var obtainedSpec audio.AudioSpec + + if audio.OpenAudio(&desiredSpec, &obtainedSpec) != 0 { + log.Fatal(sdl.GetError()) + } + + fmt := sound.AudioInfo{obtainedSpec.Format, obtainedSpec.Channels, uint32(obtainedSpec.Freq)} + /* note: on my machine i always get 2 channels, despite only requesting one */ + + sdl.EnableUNICODE(1) + + sample := sound.NewSampleFromFile(resourcePath+"/test.ogg", &fmt, 1048756) + + sample.DecodeAll() + buf := sample.Buffer_int16() + /* this decodes the entire file at once and loads it into memory. sample.Decode() + will decode in chunks of ~1mb, since that's the buffer size I requested above. */ + + audio.PauseAudio(false) + + audio.SendAudio_int16(buf) + /* note: this sends the entire buffer to the audio system at once, which + is a stupid thing to do in practice because the audio callback will block while + it copies the samples to its internal tail buffer, and won't be able to fill the + current audio frame in a timely manner. this will cause many underruns. + A better approach is to call SendAudio in a loop with small chunks of the + buffer at a time. */ + + time.Sleep(time.Second * 45) + /* we better stick around or we'll exit before making any noise! */ +} diff --git a/gfx/Makefile b/gfx/Makefile deleted file mode 100644 index 415d488..0000000 --- a/gfx/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -TARG=sdl/gfx - -GOFILES:=constants.go\ - framerate.go - -CLEANFILES+=gfx - -include $(GOROOT)/src/Make.pkg diff --git a/gfx/constants.go b/gfx/constants.go index e6be30e..db90ba1 100644 --- a/gfx/constants.go +++ b/gfx/constants.go @@ -1,7 +1,7 @@ package gfx const ( - FPS_UPPER_LIMIT = 200 - FPS_LOWER_LIMIT = 1 - FPS_DEFAULT = 30 + FPS_UPPER_LIMIT = 200 + FPS_LOWER_LIMIT = 1 + FPS_DEFAULT = 30 ) diff --git a/gfx/framerate.go b/gfx/framerate.go index 60420a0..bc3cd03 100644 --- a/gfx/framerate.go +++ b/gfx/framerate.go @@ -5,53 +5,53 @@ A pure Go version of SDL_framerate package gfx import ( - "sdl" + "time" ) type FPSmanager struct { - framecount uint32 - rateticks float - lastticks uint32 - rate uint32 + framecount uint32 + rateticks float64 + lastticks uint64 + rate uint32 } func NewFramerate() *FPSmanager { - return &FPSmanager{ - framecount: 0, - rate: FPS_DEFAULT, - rateticks: (1000.0 / float(FPS_DEFAULT)), - lastticks: sdl.GetTicks(), - } + return &FPSmanager{ + framecount: 0, + rate: FPS_DEFAULT, + rateticks: (1000.0 / float64(FPS_DEFAULT)), + lastticks: uint64(time.Now().UnixNano()) / 1e6, + } } func (manager *FPSmanager) SetFramerate(rate uint32) { - if rate >= FPS_LOWER_LIMIT && rate <= FPS_UPPER_LIMIT { - manager.framecount = 0 - manager.rate = rate - manager.rateticks = 1000.0 / float(rate) - } else { - } + if rate >= FPS_LOWER_LIMIT && rate <= FPS_UPPER_LIMIT { + manager.framecount = 0 + manager.rate = rate + manager.rateticks = 1000.0 / float64(rate) + } else { + } } func (manager *FPSmanager) GetFramerate() uint32 { - return manager.rate + return manager.rate } func (manager *FPSmanager) FramerateDelay() { - var current_ticks, target_ticks, the_delay uint32 - - // next frame - manager.framecount++ - - // get/calc ticks - current_ticks = sdl.GetTicks() - target_ticks = manager.lastticks + uint32(float(manager.framecount) * manager.rateticks) - - if current_ticks <= target_ticks { - the_delay = target_ticks - current_ticks - sdl.Delay(the_delay) - } else { - manager.framecount = 0 - manager.lastticks = sdl.GetTicks() - } + var current_ticks, target_ticks, the_delay uint64 + + // next frame + manager.framecount++ + + // get/calc ticks + current_ticks = uint64(time.Now().UnixNano()) / 1e6 + target_ticks = manager.lastticks + uint64(float64(manager.framecount)*manager.rateticks) + + if current_ticks <= target_ticks { + the_delay = target_ticks - current_ticks + time.Sleep(time.Duration(the_delay * 1e6)) + } else { + manager.framecount = 0 + manager.lastticks = uint64(time.Now().UnixNano()) / 1e6 + } } diff --git a/mixer/Makefile b/mixer/Makefile deleted file mode 100644 index 4476296..0000000 --- a/mixer/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -TARG=sdl/mixer - -GOFILES:=constants.go -CGOFILES:=mixer.go -CGO_LDFLAGS:=-lSDL_mixer - -CLEANFILES+=mixer - -include $(GOROOT)/src/Make.pkg diff --git a/mixer/constants.go b/mixer/constants.go index de7fc68..ee26227 100644 --- a/mixer/constants.go +++ b/mixer/constants.go @@ -1,6 +1,6 @@ package mixer -//TODO: Autogenerate these values and add rest of constants +//TODO: Add rest of constants from SDL include files const ( AUDIO_U8 = 0x0008 AUDIO_S8 = 0x8008 diff --git a/mixer/mixer.go b/mixer/mixer.go index 8c7b96a..59b3195 100644 --- a/mixer/mixer.go +++ b/mixer/mixer.go @@ -7,7 +7,8 @@ functions have been changed to be in a more object-oriented style */ package mixer -// #include +// #cgo pkg-config: SDL_mixer +// #include import "C" import "unsafe" @@ -23,6 +24,18 @@ func OpenAudio(frequency int, format uint16, channels, chunksize int) int { C.int(channels), C.int(chunksize))) } +// Queries the mixer format. Returns (0,0,0) if audio has not been +// opened, and (frequency, format, channels) otherwise +func QuerySpec() (int, uint16, int) { + var frequency C.int + var format C.Uint16 + var channels C.int + if C.Mix_QuerySpec(&frequency, &format, &channels) == 0 { + return 0, 0, 0 + } + return int(frequency), uint16(format), int(channels) +} + // Shuts down SDL_mixer. func CloseAudio() { C.Mix_CloseAudio() } @@ -56,7 +69,7 @@ func (m *Music) FadeInMusic(loops, ms int) int { } // Same as FadeInMusic, only with a specified position to start the music at. -func (m *Music) FadeInMusicPos(loops, ms int, position float) int { +func (m *Music) FadeInMusicPos(loops, ms int, position float64) int { return int(C.Mix_FadeInMusicPos(m.cmusic, C.int(loops), C.int(ms), C.double(position))) } @@ -74,7 +87,7 @@ func ResumeMusic() { C.Mix_ResumeMusic() } func RewindMusic() { C.Mix_RewindMusic() } // Sets the position of the currently playing music. -func SetMusicPosition(position float) int { +func SetMusicPosition(position float64) int { return int(C.Mix_SetMusicPosition(C.double(position))) } @@ -85,7 +98,6 @@ func HaltMusic() { C.Mix_HaltMusic() } // the fade out is completed. func FadeOutMusic(ms int) int { return int(C.Mix_FadeOutMusic(C.int(ms))) } - // Returns the type of the currently playing music. func GetMusicType() int { return int(C.Mix_GetMusicType(nil)) } diff --git a/sdl-template/template.go b/sdl-template/template.go new file mode 100644 index 0000000..7b0453e --- /dev/null +++ b/sdl-template/template.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "github.com/neagix/Go-SDL/sdl" + "math/rand" + "time" +) + +func loadImage(name string) *sdl.Surface { + image := sdl.Load(name) + + if image == nil { + panic(sdl.GetError()) + } + + return image + +} + +func main() { + if sdl.Init(sdl.INIT_VIDEO) != 0 { + panic(sdl.GetError()) + } + + defer sdl.Quit() + + screen := sdl.SetVideoMode(400, 300, 32, 0) + if screen == nil { + panic(sdl.GetError()) + } + + sdl.WM_SetCaption("Template", "") + + ticker := time.NewTicker(1e9 / 2 /*2 Hz*/ ) + +loop: + for { + select { + case <-ticker.C: + // Note: For better efficiency, use UpdateRects instead of Flip + screen.FillRect(nil, /*color*/ rand.Uint32()) + //screen.Blit(&sdl.Rect{x,y, 0, 0}, image, nil) + screen.Flip() + + case event := <-sdl.Events: + fmt.Printf("%#v\n", event) + + switch event.(type) { + case sdl.QuitEvent: + break loop + } + } + } +} diff --git a/sdl/Makefile b/sdl/Makefile deleted file mode 100644 index 5f02895..0000000 --- a/sdl/Makefile +++ /dev/null @@ -1,21 +0,0 @@ - -include $(GOROOT)/src/Make.inc - -TARG=sdl - -GOFILES:=constants.go structs.$(O).go - -CGOFILES:=sdl.go - -CGO_LDFLAGS:=`pkg-config --libs sdl` -lSDL_image - -CGO_CFLAGS:=`pkg-config --cflags sdl` - -CLEANFILES+=sdl - -include $(GOROOT)/src/Make.pkg - -constants.go: constants.c - godefs -g sdl constants.c > constants.go - gofmt -w constants.go - diff --git a/sdl/audio/audio.go b/sdl/audio/audio.go new file mode 100644 index 0000000..824099b --- /dev/null +++ b/sdl/audio/audio.go @@ -0,0 +1,311 @@ +/* + * Copyright neagix - Feb 2013 + * Copyright: ⚛ <0xe2.0x9a.0x9b@gmail.com> 2010 + * + * + * The contents of this file can be used freely, + * except for usages in immoral contexts. + * + */ + +/* +An interface to low-level SDL sound functions +with support for callbacks and rudimentary stream mixing +*/ +package audio + +// #cgo pkg-config: sdl +// #cgo freebsd LDFLAGS: -lrt +// #cgo linux LDFLAGS: -lrt +// #cgo windows LDFLAGS: -lpthread +// #include +// #include "callback.h" +import "C" +import "unsafe" +import "reflect" + +// The version of Go-SDL audio bindings. +// The version descriptor changes into a new unique string +// after a semantically incompatible Go-SDL update. +// +// The returned value can be checked by users of this package +// to make sure they are using a version with the expected semantics. +// +// If Go adds some kind of support for package versioning, this function will go away. +func GoSdlAudioVersion() string { + return "Go-SDL audio 1.2" +} + +var userDefinedCallback func(unsafe.Pointer, int) + +// this is the callback called from the C code +// without any special glue since the CGO threads insanity has been fixed =) +//export streamCallback +func streamCallback(arg unsafe.Pointer) { + ctx := (*C.context)(arg) + + // call the actual Go callback defined by user + //NOTE: here buffer truncation possible with large NumBytes + userDefinedCallback(ctx.Stream, int(ctx.NumBytes)) +} + +// Audio format +const ( + AUDIO_U8 = C.AUDIO_U8 + AUDIO_S8 = C.AUDIO_S8 + AUDIO_U16LSB = C.AUDIO_U16LSB + AUDIO_S16LSB = C.AUDIO_S16LSB + AUDIO_U16MSB = C.AUDIO_U16MSB + AUDIO_S16MSB = C.AUDIO_S16MSB + AUDIO_U16 = C.AUDIO_U16 + AUDIO_S16 = C.AUDIO_S16 +) + +// Native audio byte ordering +const ( + AUDIO_U16SYS = C.AUDIO_U16SYS + AUDIO_S16SYS = C.AUDIO_S16SYS +) + +type AudioSpec struct { + Freq int + Format uint16 // If in doubt, use AUDIO_S16SYS + Channels uint8 // 1 or 2 + Out_Silence uint8 + Samples uint16 // A power of 2, preferrably 2^11 (2048) or more + Out_Size uint32 + UserDefinedCallback func(unsafe.Pointer, int) +} + +var alreadyOpened bool + +const ( + // play concatenating samples + AE_PLAY_CONCAT = iota + //TODO: downmix mode + AE_PAUSE + AE_UNPAUSE +) + +type AudioEvent struct { + Event int + Buffer []int16 + + AudioType int +} + +// Audio status +const ( + SDL_AUDIO_STOPPED = C.SDL_AUDIO_STOPPED + SDL_AUDIO_PLAYING = C.SDL_AUDIO_PLAYING + SDL_AUDIO_PAUSED = C.SDL_AUDIO_PAUSED +) + +var PlayLoop chan AudioEvent +var PlayQueueSize int +var TailBufferS16 []int16 + +//NOTE: we assume this is not going to be called concurrently :) +func DownstreamPlaybackS16(buffer unsafe.Pointer, bufferSize int) { + + // hack to convert C void* pointer to proper Go slice + var stream []int16 + streamLen := bufferSize / 2 + tailBufferLen := len(TailBufferS16) + sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&stream))) + sliceHeader.Cap = streamLen + sliceHeader.Len = streamLen + sliceHeader.Data = uintptr(buffer) + + // in theory we should overmix samples, but for that a double-buffered approach is best + // right now we will just concatenate samples, quirky but serves the purpose of this game + + // first thing, pick any eventual remnants buffer to copy over + dstOffset := 0 + if nil != TailBufferS16 && tailBufferLen > 0 { + for i := range TailBufferS16 { + stream[dstOffset] = TailBufferS16[i] + dstOffset++ + + // we have copied enough + if dstOffset == streamLen { + newTailBufferS16 := make([]int16, tailBufferLen-dstOffset) + copy(newTailBufferS16, TailBufferS16[dstOffset:]) + TailBufferS16 = newTailBufferS16 + return + } + } + + // we have consumed the whole TailBufferS16, reset it + TailBufferS16 = nil + } + + // pick next beep object + for { + // if there's nothing in queue, just play what we got so far + // by default stream is a silence buffer + if 0 == PlayQueueSize { + return + } + + ae := <-PlayLoop + + switch ae.Event { + case AE_UNPAUSE: + { + PauseAudio(false) + continue + } + case AE_PAUSE: + { + PauseAudio(true) + continue + } + } + + // prepare eventual tail buffer + toBeCopied := streamLen - dstOffset + overflowingSamples := len(ae.Buffer) - toBeCopied + if overflowingSamples > 0 { + TailBufferS16 = make([]int16, overflowingSamples) + } + + copy(stream[dstOffset:], ae.Buffer) + if overflowingSamples > 0 { + copy(TailBufferS16, ae.Buffer[toBeCopied:]) + } + + // we have "eaten" a sound object from the queue + PlayQueueSize-- + + // this sound object fully satisfied the buffer + if overflowingSamples > 0 { + return + } + + // in case of perfect boundary match + if streamLen == dstOffset+toBeCopied { + return + } + + // loop again, pick another sound + } +} + +func OpenAudio(desired, obtained_orNil *AudioSpec) int { + if alreadyOpened { + panic("more than 1 audio stream currently not supported") + } + + // copy handle to user-defined callback function, if defined + // it is perfectly supported to not specify the callback function + // in that case you will use default SendAudio semantics + // note that if you specify a callback and use SendAudio, a hangup will instead happen + // when calling SendAudio + + if nil != desired.UserDefinedCallback { + userDefinedCallback = desired.UserDefinedCallback + } else { + // default playback (16-bit signed) + userDefinedCallback = DownstreamPlaybackS16 + PlayLoop = make(chan AudioEvent) + } + + var C_desired, C_obtained *C.SDL_AudioSpec + + C_desired = new(C.SDL_AudioSpec) + C_desired.freq = C.int(desired.Freq) + C_desired.format = C.Uint16(desired.Format) + C_desired.channels = C.Uint8(desired.Channels) + C_desired.samples = C.Uint16(desired.Samples) + // there is an unique C callback acting as proxy to the different Go callbacks + // see streamContext() + C_desired.callback = C.callback_getCallback() + + if obtained_orNil != nil { + if desired != obtained_orNil { + C_obtained = new(C.SDL_AudioSpec) + } else { + C_obtained = C_desired + } + } + + status := C.SDL_OpenAudio(C_desired, C_obtained) + + if status == 0 { + alreadyOpened = true + } + + if obtained_orNil != nil { + obtained := obtained_orNil + + obtained.Freq = int(C_obtained.freq) + obtained.Format = uint16(C_obtained.format) + obtained.Channels = uint8(C_obtained.channels) + obtained.Samples = uint16(C_obtained.samples) + obtained.Out_Silence = uint8(C_obtained.silence) + obtained.Out_Size = uint32(C_obtained.size) + } + + return int(status) +} + +func CloseAudio() { + if !alreadyOpened { + panic("SDL audio not opened") + } + + PauseAudio(true) + + C.SDL_CloseAudio() +} + +func GetAudioStatus() int { + return int(C.SDL_GetAudioStatus()) +} + +// Pause or unpause the audio. +func PauseAudio(pause_on bool) { + if pause_on { + C.SDL_PauseAudio(1) + } else { + C.SDL_PauseAudio(0) + } +} + +// pause or unpause the audio synchronously +func PauseAudioSync(pause_on bool) { + if nil == PlayLoop { + panic("Cannot use PauseAudioSync with custom callback") + } + if pause_on { + PlayLoop <- AudioEvent{Event: AE_PAUSE} + } else { + PlayLoop <- AudioEvent{Event: AE_UNPAUSE} + } +} + +// Send samples to the audio device (AUDIO_S16SYS format). +// This function blocks until all the samples are consumed by the SDL audio thread. +func SendAudio_int16(data []int16) { + PlayQueueSize++ + PlayLoop <- AudioEvent{Event: AE_PLAY_CONCAT, Buffer: data, AudioType: AUDIO_S16} +} + +// Send samples to the audio device (AUDIO_U16SYS format). +// This function blocks until all the samples are consumed by the SDL audio thread. +func SendAudio_uint16(data []uint16) { + panic("Only int16 samples currently supported") +} + +// Send samples to the audio device (AUDIO_S8 format). +// This function blocks until all the samples are consumed by the SDL audio thread. +func SendAudio_int8(data []int8) { + panic("Only int16 samples currently supported") +} + +// Send samples to the audio device (AUDIO_U8 format). +// This function blocks until all the samples are consumed by the SDL audio thread. +func SendAudio_uint8(data []uint8) { + panic("Only int16 samples currently supported") +} diff --git a/sdl/audio/callback.c b/sdl/audio/callback.c new file mode 100644 index 0000000..c213dc3 --- /dev/null +++ b/sdl/audio/callback.c @@ -0,0 +1,34 @@ +/* + * Copyright neagix - Feb 2013 + * Copyright: ⚛ <0xe2.0x9a.0x9b@gmail.com> 2010 + * + * + * The contents of this file can be used freely, + * except for usages in immoral contexts. + * + */ + +/* +An interface to low-level SDL sound functions +with support for callbacks and rudimentary stream mixing +*/ + +#include "callback.h" +#include +#include +#include +#include "_cgo_export.h" + +static void SDLCALL callback(void *userdata, Uint8 *_stream, int len) { + + SDL_LockAudio(); + + context context = { _stream, len }; + streamCallback(&context); + + SDL_UnlockAudio(); +} + +callback_t callback_getCallback() { + return &callback; +} diff --git a/sdl/audio/callback.h b/sdl/audio/callback.h new file mode 100644 index 0000000..b2874a7 --- /dev/null +++ b/sdl/audio/callback.h @@ -0,0 +1,26 @@ +/* + * Copyright neagix - Feb 2013 + * Copyright: ⚛ <0xe2.0x9a.0x9b@gmail.com> 2010 + * + * + * The contents of this file can be used freely, + * except for usages in immoral contexts. + * + */ + +#ifndef __CALLBACK_H +#define __CALLBACK_H + +#include + +typedef struct context context; +struct context { + void *Stream; + int NumBytes; +}; + +typedef void (SDLCALL *callback_t)(void *userdata, Uint8 *stream, int len); + +extern callback_t callback_getCallback(); + +#endif //__CALLBACK_H diff --git a/sdl/constants.c b/sdl/constants.c deleted file mode 100644 index 621b9e5..0000000 --- a/sdl/constants.c +++ /dev/null @@ -1,350 +0,0 @@ - -#include - -enum -{ - -// init flags - - $INIT_AUDIO = SDL_INIT_AUDIO, - $INIT_VIDEO = SDL_INIT_VIDEO, - $INIT_CDROM = SDL_INIT_CDROM, - $INIT_TIMER = SDL_INIT_TIMER, - $INIT_JOYSTICK = SDL_INIT_JOYSTICK, - $INIT_NOPARACHUTE = SDL_INIT_NOPARACHUTE, - $INIT_EVENTTHREAD = SDL_INIT_EVENTTHREAD, - $INIT_EVERYTHING = SDL_INIT_EVERYTHING, - -// setvideo flags - - $SWSURFACE = SDL_SWSURFACE, - $HWSURFACE = SDL_HWSURFACE, - $ASYNCBLIT = SDL_ASYNCBLIT, - $ANYFORMAT = SDL_ANYFORMAT, - $HWPALETTE = SDL_HWPALETTE, - $DOUBLEBUF = SDL_DOUBLEBUF, - $FULLSCREEN = SDL_FULLSCREEN, - $OPENGL = SDL_OPENGL, - $OPENGLBLIT = SDL_OPENGLBLIT, - $RESIZABLE = SDL_RESIZABLE, - $NOFRAME = SDL_NOFRAME, - $HWACCEL = SDL_HWACCEL, - $SRCCOLORKEY = SDL_SRCCOLORKEY, - $RLEACCELOK = SDL_RLEACCELOK, - $RLEACCEL = SDL_RLEACCEL, - $SRCALPHA = SDL_SRCALPHA, - $PREALLOC = SDL_PREALLOC, - $YV12_OVERLAY = SDL_YV12_OVERLAY, - $IYUV_OVERLAY = SDL_IYUV_OVERLAY, - $YUY2_OVERLAY = SDL_YUY2_OVERLAY, - $UYVY_OVERLAY = SDL_UYVY_OVERLAY, - $YVYU_OVERLAY = SDL_YVYU_OVERLAY, - $LOGPAL = SDL_LOGPAL, - $PHYSPAL = SDL_PHYSPAL, - -// event types - - $NOEVENT = SDL_NOEVENT, - $ACTIVEEVENT = SDL_ACTIVEEVENT, - $KEYDOWN = SDL_KEYDOWN, - $KEYUP = SDL_KEYUP, - $MOUSEMOTION = SDL_MOUSEMOTION, - $MOUSEBUTTONDOWN = SDL_MOUSEBUTTONDOWN, - $MOUSEBUTTONUP = SDL_MOUSEBUTTONUP, - $JOYAXISMOTION = SDL_JOYAXISMOTION, - $JOYBALLMOTION = SDL_JOYBALLMOTION, - $JOYHATMOTION = SDL_JOYHATMOTION, - $JOYBUTTONDOWN = SDL_JOYBUTTONDOWN, - $JOYBUTTONUP = SDL_JOYBUTTONUP, - $QUIT = SDL_QUIT, - $SYSWMEVENT = SDL_SYSWMEVENT, - $EVENT_RESERVEDA = SDL_EVENT_RESERVEDA, - $EVENT_RESERVEDB = SDL_EVENT_RESERVEDB, - $VIDEORESIZE = SDL_VIDEORESIZE, - $VIDEOEXPOSE = SDL_VIDEOEXPOSE, - $EVENT_RESERVED2 = SDL_EVENT_RESERVED2, - $EVENT_RESERVED3 = SDL_EVENT_RESERVED3, - $EVENT_RESERVED4 = SDL_EVENT_RESERVED4, - $EVENT_RESERVED5 = SDL_EVENT_RESERVED5, - $EVENT_RESERVED6 = SDL_EVENT_RESERVED6, - $EVENT_RESERVED7 = SDL_EVENT_RESERVED7, - - $USEREVENT = SDL_USEREVENT, - - $NUMEVENTS = SDL_NUMEVENTS, - -// event masks - - $ACTIVEEVENTMASK = SDL_ACTIVEEVENTMASK, - $KEYDOWNMASK = SDL_KEYDOWNMASK, - $KEYUPMASK = SDL_KEYUPMASK, - $KEYEVENTMASK = SDL_KEYEVENTMASK, - $MOUSEMOTIONMASK = SDL_MOUSEMOTIONMASK, - $MOUSEBUTTONDOWNMASK = SDL_MOUSEBUTTONDOWNMASK, - $MOUSEBUTTONUPMASK = SDL_MOUSEBUTTONUPMASK, - $MOUSEEVENTMASK = SDL_MOUSEEVENTMASK, - $JOYAXISMOTIONMASK = SDL_JOYAXISMOTIONMASK, - $JOYBALLMOTIONMASK = SDL_JOYBALLMOTIONMASK, - $JOYHATMOTIONMASK = SDL_JOYHATMOTIONMASK, - $JOYBUTTONDOWNMASK = SDL_JOYBUTTONDOWNMASK, - $JOYBUTTONUPMASK = SDL_JOYBUTTONUPMASK, - $JOYEVENTMASK = SDL_JOYEVENTMASK, - $VIDEORESIZEMASK = SDL_VIDEORESIZEMASK, - $VIDEOEXPOSEMASK = SDL_VIDEOEXPOSEMASK, - $QUITMASK = SDL_QUITMASK, - $SYSWMEVENTMASK = SDL_SYSWMEVENTMASK, - -// keys - $K_UNKNOWN = SDLK_UNKNOWN, - $K_FIRST = SDLK_FIRST, - $K_BACKSPACE = SDLK_BACKSPACE, - $K_TAB = SDLK_TAB, - $K_CLEAR = SDLK_CLEAR, - $K_RETURN = SDLK_RETURN, - $K_PAUSE = SDLK_PAUSE, - $K_ESCAPE = SDLK_ESCAPE, - $K_SPACE = SDLK_SPACE, - $K_EXCLAIM = SDLK_EXCLAIM, - $K_QUOTEDBL = SDLK_QUOTEDBL, - $K_HASH = SDLK_HASH, - $K_DOLLAR = SDLK_DOLLAR, - $K_AMPERSAND = SDLK_AMPERSAND, - $K_QUOTE = SDLK_QUOTE, - $K_LEFTPAREN = SDLK_LEFTPAREN, - $K_RIGHTPAREN = SDLK_RIGHTPAREN, - $K_ASTERISK = SDLK_ASTERISK, - $K_PLUS = SDLK_PLUS, - $K_COMMA = SDLK_COMMA, - $K_MINUS = SDLK_MINUS, - $K_PERIOD = SDLK_PERIOD, - $K_SLASH = SDLK_SLASH, - $K_0 = SDLK_0, - $K_1 = SDLK_1, - $K_2 = SDLK_2, - $K_3 = SDLK_3, - $K_4 = SDLK_4, - $K_5 = SDLK_5, - $K_6 = SDLK_6, - $K_7 = SDLK_7, - $K_8 = SDLK_8, - $K_9 = SDLK_9, - $K_COLON = SDLK_COLON, - $K_SEMICOLON = SDLK_SEMICOLON, - $K_LESS = SDLK_LESS, - $K_EQUALS = SDLK_EQUALS, - $K_GREATER = SDLK_GREATER, - $K_QUESTION = SDLK_QUESTION, - $K_AT = SDLK_AT, - $K_LEFTBRACKET = SDLK_LEFTBRACKET, - $K_BACKSLASH = SDLK_BACKSLASH, - $K_RIGHTBRACKET = SDLK_RIGHTBRACKET, - $K_CARET = SDLK_CARET, - $K_UNDERSCORE = SDLK_UNDERSCORE, - $K_BACKQUOTE = SDLK_BACKQUOTE, - $K_a = SDLK_a, - $K_b = SDLK_b, - $K_c = SDLK_c, - $K_d = SDLK_d, - $K_e = SDLK_e, - $K_f = SDLK_f, - $K_g = SDLK_g, - $K_h = SDLK_h, - $K_i = SDLK_i, - $K_j = SDLK_j, - $K_k = SDLK_k, - $K_l = SDLK_l, - $K_m = SDLK_m, - $K_n = SDLK_n, - $K_o = SDLK_o, - $K_p = SDLK_p, - $K_q = SDLK_q, - $K_r = SDLK_r, - $K_s = SDLK_s, - $K_t = SDLK_t, - $K_u = SDLK_u, - $K_v = SDLK_v, - $K_w = SDLK_w, - $K_x = SDLK_x, - $K_y = SDLK_y, - $K_z = SDLK_z, - $K_DELETE = SDLK_DELETE, - $K_WORLD_0 = SDLK_WORLD_0, - $K_WORLD_1 = SDLK_WORLD_1, - $K_WORLD_2 = SDLK_WORLD_2, - $K_WORLD_3 = SDLK_WORLD_3, - $K_WORLD_4 = SDLK_WORLD_4, - $K_WORLD_5 = SDLK_WORLD_5, - $K_WORLD_6 = SDLK_WORLD_6, - $K_WORLD_7 = SDLK_WORLD_7, - $K_WORLD_8 = SDLK_WORLD_8, - $K_WORLD_9 = SDLK_WORLD_9, - $K_WORLD_10 = SDLK_WORLD_10, - $K_WORLD_11 = SDLK_WORLD_11, - $K_WORLD_12 = SDLK_WORLD_12, - $K_WORLD_13 = SDLK_WORLD_13, - $K_WORLD_14 = SDLK_WORLD_14, - $K_WORLD_15 = SDLK_WORLD_15, - $K_WORLD_16 = SDLK_WORLD_16, - $K_WORLD_17 = SDLK_WORLD_17, - $K_WORLD_18 = SDLK_WORLD_18, - $K_WORLD_19 = SDLK_WORLD_19, - $K_WORLD_20 = SDLK_WORLD_20, - $K_WORLD_21 = SDLK_WORLD_21, - $K_WORLD_22 = SDLK_WORLD_22, - $K_WORLD_23 = SDLK_WORLD_23, - $K_WORLD_24 = SDLK_WORLD_24, - $K_WORLD_25 = SDLK_WORLD_25, - $K_WORLD_26 = SDLK_WORLD_26, - $K_WORLD_27 = SDLK_WORLD_27, - $K_WORLD_28 = SDLK_WORLD_28, - $K_WORLD_29 = SDLK_WORLD_29, - $K_WORLD_30 = SDLK_WORLD_30, - $K_WORLD_31 = SDLK_WORLD_31, - $K_WORLD_32 = SDLK_WORLD_32, - $K_WORLD_33 = SDLK_WORLD_33, - $K_WORLD_34 = SDLK_WORLD_34, - $K_WORLD_35 = SDLK_WORLD_35, - $K_WORLD_36 = SDLK_WORLD_36, - $K_WORLD_37 = SDLK_WORLD_37, - $K_WORLD_38 = SDLK_WORLD_38, - $K_WORLD_39 = SDLK_WORLD_39, - $K_WORLD_40 = SDLK_WORLD_40, - $K_WORLD_41 = SDLK_WORLD_41, - $K_WORLD_42 = SDLK_WORLD_42, - $K_WORLD_43 = SDLK_WORLD_43, - $K_WORLD_44 = SDLK_WORLD_44, - $K_WORLD_45 = SDLK_WORLD_45, - $K_WORLD_46 = SDLK_WORLD_46, - $K_WORLD_47 = SDLK_WORLD_47, - $K_WORLD_48 = SDLK_WORLD_48, - $K_WORLD_49 = SDLK_WORLD_49, - $K_WORLD_50 = SDLK_WORLD_50, - $K_WORLD_51 = SDLK_WORLD_51, - $K_WORLD_52 = SDLK_WORLD_52, - $K_WORLD_53 = SDLK_WORLD_53, - $K_WORLD_54 = SDLK_WORLD_54, - $K_WORLD_55 = SDLK_WORLD_55, - $K_WORLD_56 = SDLK_WORLD_56, - $K_WORLD_57 = SDLK_WORLD_57, - $K_WORLD_58 = SDLK_WORLD_58, - $K_WORLD_59 = SDLK_WORLD_59, - $K_WORLD_60 = SDLK_WORLD_60, - $K_WORLD_61 = SDLK_WORLD_61, - $K_WORLD_62 = SDLK_WORLD_62, - $K_WORLD_63 = SDLK_WORLD_63, - $K_WORLD_64 = SDLK_WORLD_64, - $K_WORLD_65 = SDLK_WORLD_65, - $K_WORLD_66 = SDLK_WORLD_66, - $K_WORLD_67 = SDLK_WORLD_67, - $K_WORLD_68 = SDLK_WORLD_68, - $K_WORLD_69 = SDLK_WORLD_69, - $K_WORLD_70 = SDLK_WORLD_70, - $K_WORLD_71 = SDLK_WORLD_71, - $K_WORLD_72 = SDLK_WORLD_72, - $K_WORLD_73 = SDLK_WORLD_73, - $K_WORLD_74 = SDLK_WORLD_74, - $K_WORLD_75 = SDLK_WORLD_75, - $K_WORLD_76 = SDLK_WORLD_76, - $K_WORLD_77 = SDLK_WORLD_77, - $K_WORLD_78 = SDLK_WORLD_78, - $K_WORLD_79 = SDLK_WORLD_79, - $K_WORLD_80 = SDLK_WORLD_80, - $K_WORLD_81 = SDLK_WORLD_81, - $K_WORLD_82 = SDLK_WORLD_82, - $K_WORLD_83 = SDLK_WORLD_83, - $K_WORLD_84 = SDLK_WORLD_84, - $K_WORLD_85 = SDLK_WORLD_85, - $K_WORLD_86 = SDLK_WORLD_86, - $K_WORLD_87 = SDLK_WORLD_87, - $K_WORLD_88 = SDLK_WORLD_88, - $K_WORLD_89 = SDLK_WORLD_89, - $K_WORLD_90 = SDLK_WORLD_90, - $K_WORLD_91 = SDLK_WORLD_91, - $K_WORLD_92 = SDLK_WORLD_92, - $K_WORLD_93 = SDLK_WORLD_93, - $K_WORLD_94 = SDLK_WORLD_94, - $K_WORLD_95 = SDLK_WORLD_95, - $K_KP0 = SDLK_KP0, - $K_KP1 = SDLK_KP1, - $K_KP2 = SDLK_KP2, - $K_KP3 = SDLK_KP3, - $K_KP4 = SDLK_KP4, - $K_KP5 = SDLK_KP5, - $K_KP6 = SDLK_KP6, - $K_KP7 = SDLK_KP7, - $K_KP8 = SDLK_KP8, - $K_KP9 = SDLK_KP9, - $K_KP_PERIOD = SDLK_KP_PERIOD, - $K_KP_DIVIDE = SDLK_KP_DIVIDE, - $K_KP_MULTIPLY = SDLK_KP_MULTIPLY, - $K_KP_MINUS = SDLK_KP_MINUS, - $K_KP_PLUS = SDLK_KP_PLUS, - $K_KP_ENTER = SDLK_KP_ENTER, - $K_KP_EQUALS = SDLK_KP_EQUALS, - $K_UP = SDLK_UP, - $K_DOWN = SDLK_DOWN, - $K_RIGHT = SDLK_RIGHT, - $K_LEFT = SDLK_LEFT, - $K_INSERT = SDLK_INSERT, - $K_HOME = SDLK_HOME, - $K_END = SDLK_END, - $K_PAGEUP = SDLK_PAGEUP, - $K_PAGEDOWN = SDLK_PAGEDOWN, - $K_F1 = SDLK_F1, - $K_F2 = SDLK_F2, - $K_F3 = SDLK_F3, - $K_F4 = SDLK_F4, - $K_F5 = SDLK_F5, - $K_F6 = SDLK_F6, - $K_F7 = SDLK_F7, - $K_F8 = SDLK_F8, - $K_F9 = SDLK_F9, - $K_F10 = SDLK_F10, - $K_F11 = SDLK_F11, - $K_F12 = SDLK_F12, - $K_F13 = SDLK_F13, - $K_F14 = SDLK_F14, - $K_F15 = SDLK_F15, - $K_NUMLOCK = SDLK_NUMLOCK, - $K_CAPSLOCK = SDLK_CAPSLOCK, - $K_SCROLLOCK = SDLK_SCROLLOCK, - $K_RSHIFT = SDLK_RSHIFT, - $K_LSHIFT = SDLK_LSHIFT, - $K_RCTRL = SDLK_RCTRL, - $K_LCTRL = SDLK_LCTRL, - $K_RALT = SDLK_RALT, - $K_LALT = SDLK_LALT, - $K_RMETA = SDLK_RMETA, - $K_LMETA = SDLK_LMETA, - $K_LSUPER = SDLK_LSUPER, - $K_RSUPER = SDLK_RSUPER, - $K_MODE = SDLK_MODE, - $K_COMPOSE = SDLK_COMPOSE, - $K_HELP = SDLK_HELP, - $K_PRINT = SDLK_PRINT, - $K_SYSREQ = SDLK_SYSREQ, - $K_BREAK = SDLK_BREAK, - $K_MENU = SDLK_MENU, - $K_POWER = SDLK_POWER, - $K_EURO = SDLK_EURO, - $K_UNDO = SDLK_UNDO, - -// key mods - - $KMOD_NONE = KMOD_NONE, - $KMOD_LSHIFT = KMOD_LSHIFT, - $KMOD_RSHIFT = KMOD_RSHIFT, - $KMOD_LCTRL = KMOD_LCTRL, - $KMOD_RCTRL = KMOD_RCTRL, - $KMOD_LALT = KMOD_LALT, - $KMOD_RALT = KMOD_RALT, - $KMOD_LMETA = KMOD_LMETA, - $KMOD_RMETA = KMOD_RMETA, - $KMOD_NUM = KMOD_NUM, - $KMOD_CAPS = KMOD_CAPS, - $KMOD_MODE = KMOD_MODE, - $KMOD_RESERVED = KMOD_RESERVED, - - -}; - diff --git a/sdl/constants.go b/sdl/constants.go index 39106a9..c1031b8 100644 --- a/sdl/constants.go +++ b/sdl/constants.go @@ -1,333 +1,417 @@ -// godefs -g sdl constants.c - -// MACHINE GENERATED - DO NOT EDIT. - package sdl -// Constants +// #cgo pkg-config: sdl +// #include +import "C" + const ( - INIT_AUDIO = 0x10 - INIT_VIDEO = 0x20 - INIT_CDROM = 0x100 - INIT_TIMER = 0x1 - INIT_JOYSTICK = 0x200 - INIT_NOPARACHUTE = 0x100000 - INIT_EVENTTHREAD = 0x1000000 - INIT_EVERYTHING = 0xffff - SWSURFACE = 0 - HWSURFACE = 0x1 - ASYNCBLIT = 0x4 - ANYFORMAT = 0x10000000 - HWPALETTE = 0x20000000 - DOUBLEBUF = 0x40000000 - FULLSCREEN = 0x80000000 - OPENGL = 0x2 - OPENGLBLIT = 0xa - RESIZABLE = 0x10 - NOFRAME = 0x20 - HWACCEL = 0x100 - SRCCOLORKEY = 0x1000 - RLEACCELOK = 0x2000 - RLEACCEL = 0x4000 - SRCALPHA = 0x10000 - PREALLOC = 0x1000000 - YV12_OVERLAY = 0x32315659 - IYUV_OVERLAY = 0x56555949 - YUY2_OVERLAY = 0x32595559 - UYVY_OVERLAY = 0x59565955 - YVYU_OVERLAY = 0x55595659 - LOGPAL = 0x1 - PHYSPAL = 0x2 - NOEVENT = 0 - ACTIVEEVENT = 0x1 - KEYDOWN = 0x2 - KEYUP = 0x3 - MOUSEMOTION = 0x4 - MOUSEBUTTONDOWN = 0x5 - MOUSEBUTTONUP = 0x6 - JOYAXISMOTION = 0x7 - JOYBALLMOTION = 0x8 - JOYHATMOTION = 0x9 - JOYBUTTONDOWN = 0xa - JOYBUTTONUP = 0xb - QUIT = 0xc - SYSWMEVENT = 0xd - EVENT_RESERVEDA = 0xe - EVENT_RESERVEDB = 0xf - VIDEORESIZE = 0x10 - VIDEOEXPOSE = 0x11 - EVENT_RESERVED2 = 0x12 - EVENT_RESERVED3 = 0x13 - EVENT_RESERVED4 = 0x14 - EVENT_RESERVED5 = 0x15 - EVENT_RESERVED6 = 0x16 - EVENT_RESERVED7 = 0x17 - USEREVENT = 0x18 - NUMEVENTS = 0x20 - ACTIVEEVENTMASK = 0x2 - KEYDOWNMASK = 0x4 - KEYUPMASK = 0x8 - KEYEVENTMASK = 0xc - MOUSEMOTIONMASK = 0x10 - MOUSEBUTTONDOWNMASK = 0x20 - MOUSEBUTTONUPMASK = 0x40 - MOUSEEVENTMASK = 0x70 - JOYAXISMOTIONMASK = 0x80 - JOYBALLMOTIONMASK = 0x100 - JOYHATMOTIONMASK = 0x200 - JOYBUTTONDOWNMASK = 0x400 - JOYBUTTONUPMASK = 0x800 - JOYEVENTMASK = 0xf80 - VIDEORESIZEMASK = 0x10000 - VIDEOEXPOSEMASK = 0x20000 - QUITMASK = 0x1000 - SYSWMEVENTMASK = 0x2000 - K_UNKNOWN = 0 - K_FIRST = 0 - K_BACKSPACE = 0x8 - K_TAB = 0x9 - K_CLEAR = 0xc - K_RETURN = 0xd - K_PAUSE = 0x13 - K_ESCAPE = 0x1b - K_SPACE = 0x20 - K_EXCLAIM = 0x21 - K_QUOTEDBL = 0x22 - K_HASH = 0x23 - K_DOLLAR = 0x24 - K_AMPERSAND = 0x26 - K_QUOTE = 0x27 - K_LEFTPAREN = 0x28 - K_RIGHTPAREN = 0x29 - K_ASTERISK = 0x2a - K_PLUS = 0x2b - K_COMMA = 0x2c - K_MINUS = 0x2d - K_PERIOD = 0x2e - K_SLASH = 0x2f - K_0 = 0x30 - K_1 = 0x31 - K_2 = 0x32 - K_3 = 0x33 - K_4 = 0x34 - K_5 = 0x35 - K_6 = 0x36 - K_7 = 0x37 - K_8 = 0x38 - K_9 = 0x39 - K_COLON = 0x3a - K_SEMICOLON = 0x3b - K_LESS = 0x3c - K_EQUALS = 0x3d - K_GREATER = 0x3e - K_QUESTION = 0x3f - K_AT = 0x40 - K_LEFTBRACKET = 0x5b - K_BACKSLASH = 0x5c - K_RIGHTBRACKET = 0x5d - K_CARET = 0x5e - K_UNDERSCORE = 0x5f - K_BACKQUOTE = 0x60 - K_a = 0x61 - K_b = 0x62 - K_c = 0x63 - K_d = 0x64 - K_e = 0x65 - K_f = 0x66 - K_g = 0x67 - K_h = 0x68 - K_i = 0x69 - K_j = 0x6a - K_k = 0x6b - K_l = 0x6c - K_m = 0x6d - K_n = 0x6e - K_o = 0x6f - K_p = 0x70 - K_q = 0x71 - K_r = 0x72 - K_s = 0x73 - K_t = 0x74 - K_u = 0x75 - K_v = 0x76 - K_w = 0x77 - K_x = 0x78 - K_y = 0x79 - K_z = 0x7a - K_DELETE = 0x7f - K_WORLD_0 = 0xa0 - K_WORLD_1 = 0xa1 - K_WORLD_2 = 0xa2 - K_WORLD_3 = 0xa3 - K_WORLD_4 = 0xa4 - K_WORLD_5 = 0xa5 - K_WORLD_6 = 0xa6 - K_WORLD_7 = 0xa7 - K_WORLD_8 = 0xa8 - K_WORLD_9 = 0xa9 - K_WORLD_10 = 0xaa - K_WORLD_11 = 0xab - K_WORLD_12 = 0xac - K_WORLD_13 = 0xad - K_WORLD_14 = 0xae - K_WORLD_15 = 0xaf - K_WORLD_16 = 0xb0 - K_WORLD_17 = 0xb1 - K_WORLD_18 = 0xb2 - K_WORLD_19 = 0xb3 - K_WORLD_20 = 0xb4 - K_WORLD_21 = 0xb5 - K_WORLD_22 = 0xb6 - K_WORLD_23 = 0xb7 - K_WORLD_24 = 0xb8 - K_WORLD_25 = 0xb9 - K_WORLD_26 = 0xba - K_WORLD_27 = 0xbb - K_WORLD_28 = 0xbc - K_WORLD_29 = 0xbd - K_WORLD_30 = 0xbe - K_WORLD_31 = 0xbf - K_WORLD_32 = 0xc0 - K_WORLD_33 = 0xc1 - K_WORLD_34 = 0xc2 - K_WORLD_35 = 0xc3 - K_WORLD_36 = 0xc4 - K_WORLD_37 = 0xc5 - K_WORLD_38 = 0xc6 - K_WORLD_39 = 0xc7 - K_WORLD_40 = 0xc8 - K_WORLD_41 = 0xc9 - K_WORLD_42 = 0xca - K_WORLD_43 = 0xcb - K_WORLD_44 = 0xcc - K_WORLD_45 = 0xcd - K_WORLD_46 = 0xce - K_WORLD_47 = 0xcf - K_WORLD_48 = 0xd0 - K_WORLD_49 = 0xd1 - K_WORLD_50 = 0xd2 - K_WORLD_51 = 0xd3 - K_WORLD_52 = 0xd4 - K_WORLD_53 = 0xd5 - K_WORLD_54 = 0xd6 - K_WORLD_55 = 0xd7 - K_WORLD_56 = 0xd8 - K_WORLD_57 = 0xd9 - K_WORLD_58 = 0xda - K_WORLD_59 = 0xdb - K_WORLD_60 = 0xdc - K_WORLD_61 = 0xdd - K_WORLD_62 = 0xde - K_WORLD_63 = 0xdf - K_WORLD_64 = 0xe0 - K_WORLD_65 = 0xe1 - K_WORLD_66 = 0xe2 - K_WORLD_67 = 0xe3 - K_WORLD_68 = 0xe4 - K_WORLD_69 = 0xe5 - K_WORLD_70 = 0xe6 - K_WORLD_71 = 0xe7 - K_WORLD_72 = 0xe8 - K_WORLD_73 = 0xe9 - K_WORLD_74 = 0xea - K_WORLD_75 = 0xeb - K_WORLD_76 = 0xec - K_WORLD_77 = 0xed - K_WORLD_78 = 0xee - K_WORLD_79 = 0xef - K_WORLD_80 = 0xf0 - K_WORLD_81 = 0xf1 - K_WORLD_82 = 0xf2 - K_WORLD_83 = 0xf3 - K_WORLD_84 = 0xf4 - K_WORLD_85 = 0xf5 - K_WORLD_86 = 0xf6 - K_WORLD_87 = 0xf7 - K_WORLD_88 = 0xf8 - K_WORLD_89 = 0xf9 - K_WORLD_90 = 0xfa - K_WORLD_91 = 0xfb - K_WORLD_92 = 0xfc - K_WORLD_93 = 0xfd - K_WORLD_94 = 0xfe - K_WORLD_95 = 0xff - K_KP0 = 0x100 - K_KP1 = 0x101 - K_KP2 = 0x102 - K_KP3 = 0x103 - K_KP4 = 0x104 - K_KP5 = 0x105 - K_KP6 = 0x106 - K_KP7 = 0x107 - K_KP8 = 0x108 - K_KP9 = 0x109 - K_KP_PERIOD = 0x10a - K_KP_DIVIDE = 0x10b - K_KP_MULTIPLY = 0x10c - K_KP_MINUS = 0x10d - K_KP_PLUS = 0x10e - K_KP_ENTER = 0x10f - K_KP_EQUALS = 0x110 - K_UP = 0x111 - K_DOWN = 0x112 - K_RIGHT = 0x113 - K_LEFT = 0x114 - K_INSERT = 0x115 - K_HOME = 0x116 - K_END = 0x117 - K_PAGEUP = 0x118 - K_PAGEDOWN = 0x119 - K_F1 = 0x11a - K_F2 = 0x11b - K_F3 = 0x11c - K_F4 = 0x11d - K_F5 = 0x11e - K_F6 = 0x11f - K_F7 = 0x120 - K_F8 = 0x121 - K_F9 = 0x122 - K_F10 = 0x123 - K_F11 = 0x124 - K_F12 = 0x125 - K_F13 = 0x126 - K_F14 = 0x127 - K_F15 = 0x128 - K_NUMLOCK = 0x12c - K_CAPSLOCK = 0x12d - K_SCROLLOCK = 0x12e - K_RSHIFT = 0x12f - K_LSHIFT = 0x130 - K_RCTRL = 0x131 - K_LCTRL = 0x132 - K_RALT = 0x133 - K_LALT = 0x134 - K_RMETA = 0x135 - K_LMETA = 0x136 - K_LSUPER = 0x137 - K_RSUPER = 0x138 - K_MODE = 0x139 - K_COMPOSE = 0x13a - K_HELP = 0x13b - K_PRINT = 0x13c - K_SYSREQ = 0x13d - K_BREAK = 0x13e - K_MENU = 0x13f - K_POWER = 0x140 - K_EURO = 0x141 - K_UNDO = 0x142 - KMOD_NONE = 0 - KMOD_LSHIFT = 0x1 - KMOD_RSHIFT = 0x2 - KMOD_LCTRL = 0x40 - KMOD_RCTRL = 0x80 - KMOD_LALT = 0x100 - KMOD_RALT = 0x200 - KMOD_LMETA = 0x400 - KMOD_RMETA = 0x800 - KMOD_NUM = 0x1000 - KMOD_CAPS = 0x2000 - KMOD_MODE = 0x4000 - KMOD_RESERVED = 0x8000 -) + // init flags + + INIT_AUDIO = C.SDL_INIT_AUDIO + INIT_VIDEO = C.SDL_INIT_VIDEO + INIT_CDROM = C.SDL_INIT_CDROM + INIT_TIMER = C.SDL_INIT_TIMER + INIT_JOYSTICK = C.SDL_INIT_JOYSTICK + INIT_NOPARACHUTE = C.SDL_INIT_NOPARACHUTE + INIT_EVENTTHREAD = C.SDL_INIT_EVENTTHREAD + INIT_EVERYTHING = C.SDL_INIT_EVERYTHING + + // application states + + APPMOUSEFOCUS = C.SDL_APPMOUSEFOCUS + APPINPUTFOCUS = C.SDL_APPINPUTFOCUS + APPACTIVE = C.SDL_APPACTIVE + + // setvideo flags + + SWSURFACE = C.SDL_SWSURFACE + HWSURFACE = C.SDL_HWSURFACE + ASYNCBLIT = C.SDL_ASYNCBLIT + ANYFORMAT = C.SDL_ANYFORMAT + HWPALETTE = C.SDL_HWPALETTE + DOUBLEBUF = C.SDL_DOUBLEBUF + FULLSCREEN = C.SDL_FULLSCREEN + OPENGL = C.SDL_OPENGL + OPENGLBLIT = C.SDL_OPENGLBLIT + RESIZABLE = C.SDL_RESIZABLE + NOFRAME = C.SDL_NOFRAME + HWACCEL = C.SDL_HWACCEL + SRCCOLORKEY = C.SDL_SRCCOLORKEY + RLEACCELOK = C.SDL_RLEACCELOK + RLEACCEL = C.SDL_RLEACCEL + SRCALPHA = C.SDL_SRCALPHA + PREALLOC = C.SDL_PREALLOC + YV12_OVERLAY = C.SDL_YV12_OVERLAY + IYUV_OVERLAY = C.SDL_IYUV_OVERLAY + YUY2_OVERLAY = C.SDL_YUY2_OVERLAY + UYVY_OVERLAY = C.SDL_UYVY_OVERLAY + YVYU_OVERLAY = C.SDL_YVYU_OVERLAY + LOGPAL = C.SDL_LOGPAL + PHYSPAL = C.SDL_PHYSPAL + + // More setvideo flags: GLattr enumeration + + GL_RED_SIZE = C.SDL_GL_RED_SIZE + GL_GREEN_SIZE = C.SDL_GL_GREEN_SIZE + GL_BLUE_SIZE = C.SDL_GL_BLUE_SIZE + GL_ALPHA_SIZE = C.SDL_GL_ALPHA_SIZE + GL_BUFFER_SIZE = C.SDL_GL_BUFFER_SIZE + GL_DOUBLEBUFFER = C.SDL_GL_DOUBLEBUFFER + GL_DEPTH_SIZE = C.SDL_GL_DEPTH_SIZE + GL_STENCIL_SIZE = C.SDL_GL_STENCIL_SIZE + GL_ACCUM_RED_SIZE = C.SDL_GL_ACCUM_RED_SIZE + GL_ACCUM_GREEN_SIZE = C.SDL_GL_ACCUM_GREEN_SIZE + GL_ACCUM_BLUE_SIZE = C.SDL_GL_ACCUM_BLUE_SIZE + GL_ACCUM_ALPHA_SIZE = C.SDL_GL_ACCUM_ALPHA_SIZE + GL_STEREO = C.SDL_GL_STEREO + GL_MULTISAMPLEBUFFERS = C.SDL_GL_MULTISAMPLEBUFFERS + GL_MULTISAMPLESAMPLES = C.SDL_GL_MULTISAMPLESAMPLES + GL_ACCELERATED_VISUAL = C.SDL_GL_ACCELERATED_VISUAL + GL_SWAP_CONTROL = C.SDL_GL_SWAP_CONTROL + + // event types + + NOEVENT = C.SDL_NOEVENT + ACTIVEEVENT = C.SDL_ACTIVEEVENT + KEYDOWN = C.SDL_KEYDOWN + KEYUP = C.SDL_KEYUP + MOUSEMOTION = C.SDL_MOUSEMOTION + MOUSEBUTTONDOWN = C.SDL_MOUSEBUTTONDOWN + MOUSEBUTTONUP = C.SDL_MOUSEBUTTONUP + JOYAXISMOTION = C.SDL_JOYAXISMOTION + JOYBALLMOTION = C.SDL_JOYBALLMOTION + JOYHATMOTION = C.SDL_JOYHATMOTION + JOYBUTTONDOWN = C.SDL_JOYBUTTONDOWN + JOYBUTTONUP = C.SDL_JOYBUTTONUP + QUIT = C.SDL_QUIT + SYSWMEVENT = C.SDL_SYSWMEVENT + EVENT_RESERVEDA = C.SDL_EVENT_RESERVEDA + EVENT_RESERVEDB = C.SDL_EVENT_RESERVEDB + VIDEORESIZE = C.SDL_VIDEORESIZE + VIDEOEXPOSE = C.SDL_VIDEOEXPOSE + EVENT_RESERVED2 = C.SDL_EVENT_RESERVED2 + EVENT_RESERVED3 = C.SDL_EVENT_RESERVED3 + EVENT_RESERVED4 = C.SDL_EVENT_RESERVED4 + EVENT_RESERVED5 = C.SDL_EVENT_RESERVED5 + EVENT_RESERVED6 = C.SDL_EVENT_RESERVED6 + EVENT_RESERVED7 = C.SDL_EVENT_RESERVED7 + + USEREVENT = C.SDL_USEREVENT + + NUMEVENTS = C.SDL_NUMEVENTS -// Types + // event masks + + ACTIVEEVENTMASK = C.SDL_ACTIVEEVENTMASK + KEYDOWNMASK = C.SDL_KEYDOWNMASK + KEYUPMASK = C.SDL_KEYUPMASK + KEYEVENTMASK = C.SDL_KEYEVENTMASK + MOUSEMOTIONMASK = C.SDL_MOUSEMOTIONMASK + MOUSEBUTTONDOWNMASK = C.SDL_MOUSEBUTTONDOWNMASK + MOUSEBUTTONUPMASK = C.SDL_MOUSEBUTTONUPMASK + MOUSEEVENTMASK = C.SDL_MOUSEEVENTMASK + JOYAXISMOTIONMASK = C.SDL_JOYAXISMOTIONMASK + JOYBALLMOTIONMASK = C.SDL_JOYBALLMOTIONMASK + JOYHATMOTIONMASK = C.SDL_JOYHATMOTIONMASK + JOYBUTTONDOWNMASK = C.SDL_JOYBUTTONDOWNMASK + JOYBUTTONUPMASK = C.SDL_JOYBUTTONUPMASK + JOYEVENTMASK = C.SDL_JOYEVENTMASK + VIDEORESIZEMASK = C.SDL_VIDEORESIZEMASK + VIDEOEXPOSEMASK = C.SDL_VIDEOEXPOSEMASK + QUITMASK = C.SDL_QUITMASK + SYSWMEVENTMASK = C.SDL_SYSWMEVENTMASK + + // event state + + QUERY = C.SDL_QUERY + DISABLE = C.SDL_DISABLE + ENABLE = C.SDL_ENABLE + + // keys + K_UNKNOWN = C.SDLK_UNKNOWN + K_FIRST = C.SDLK_FIRST + K_BACKSPACE = C.SDLK_BACKSPACE + K_TAB = C.SDLK_TAB + K_CLEAR = C.SDLK_CLEAR + K_RETURN = C.SDLK_RETURN + K_PAUSE = C.SDLK_PAUSE + K_ESCAPE = C.SDLK_ESCAPE + K_SPACE = C.SDLK_SPACE + K_EXCLAIM = C.SDLK_EXCLAIM + K_QUOTEDBL = C.SDLK_QUOTEDBL + K_HASH = C.SDLK_HASH + K_DOLLAR = C.SDLK_DOLLAR + K_AMPERSAND = C.SDLK_AMPERSAND + K_QUOTE = C.SDLK_QUOTE + K_LEFTPAREN = C.SDLK_LEFTPAREN + K_RIGHTPAREN = C.SDLK_RIGHTPAREN + K_ASTERISK = C.SDLK_ASTERISK + K_PLUS = C.SDLK_PLUS + K_COMMA = C.SDLK_COMMA + K_MINUS = C.SDLK_MINUS + K_PERIOD = C.SDLK_PERIOD + K_SLASH = C.SDLK_SLASH + K_0 = C.SDLK_0 + K_1 = C.SDLK_1 + K_2 = C.SDLK_2 + K_3 = C.SDLK_3 + K_4 = C.SDLK_4 + K_5 = C.SDLK_5 + K_6 = C.SDLK_6 + K_7 = C.SDLK_7 + K_8 = C.SDLK_8 + K_9 = C.SDLK_9 + K_COLON = C.SDLK_COLON + K_SEMICOLON = C.SDLK_SEMICOLON + K_LESS = C.SDLK_LESS + K_EQUALS = C.SDLK_EQUALS + K_GREATER = C.SDLK_GREATER + K_QUESTION = C.SDLK_QUESTION + K_AT = C.SDLK_AT + K_LEFTBRACKET = C.SDLK_LEFTBRACKET + K_BACKSLASH = C.SDLK_BACKSLASH + K_RIGHTBRACKET = C.SDLK_RIGHTBRACKET + K_CARET = C.SDLK_CARET + K_UNDERSCORE = C.SDLK_UNDERSCORE + K_BACKQUOTE = C.SDLK_BACKQUOTE + K_a = C.SDLK_a + K_b = C.SDLK_b + K_c = C.SDLK_c + K_d = C.SDLK_d + K_e = C.SDLK_e + K_f = C.SDLK_f + K_g = C.SDLK_g + K_h = C.SDLK_h + K_i = C.SDLK_i + K_j = C.SDLK_j + K_k = C.SDLK_k + K_l = C.SDLK_l + K_m = C.SDLK_m + K_n = C.SDLK_n + K_o = C.SDLK_o + K_p = C.SDLK_p + K_q = C.SDLK_q + K_r = C.SDLK_r + K_s = C.SDLK_s + K_t = C.SDLK_t + K_u = C.SDLK_u + K_v = C.SDLK_v + K_w = C.SDLK_w + K_x = C.SDLK_x + K_y = C.SDLK_y + K_z = C.SDLK_z + K_DELETE = C.SDLK_DELETE + K_WORLD_0 = C.SDLK_WORLD_0 + K_WORLD_1 = C.SDLK_WORLD_1 + K_WORLD_2 = C.SDLK_WORLD_2 + K_WORLD_3 = C.SDLK_WORLD_3 + K_WORLD_4 = C.SDLK_WORLD_4 + K_WORLD_5 = C.SDLK_WORLD_5 + K_WORLD_6 = C.SDLK_WORLD_6 + K_WORLD_7 = C.SDLK_WORLD_7 + K_WORLD_8 = C.SDLK_WORLD_8 + K_WORLD_9 = C.SDLK_WORLD_9 + K_WORLD_10 = C.SDLK_WORLD_10 + K_WORLD_11 = C.SDLK_WORLD_11 + K_WORLD_12 = C.SDLK_WORLD_12 + K_WORLD_13 = C.SDLK_WORLD_13 + K_WORLD_14 = C.SDLK_WORLD_14 + K_WORLD_15 = C.SDLK_WORLD_15 + K_WORLD_16 = C.SDLK_WORLD_16 + K_WORLD_17 = C.SDLK_WORLD_17 + K_WORLD_18 = C.SDLK_WORLD_18 + K_WORLD_19 = C.SDLK_WORLD_19 + K_WORLD_20 = C.SDLK_WORLD_20 + K_WORLD_21 = C.SDLK_WORLD_21 + K_WORLD_22 = C.SDLK_WORLD_22 + K_WORLD_23 = C.SDLK_WORLD_23 + K_WORLD_24 = C.SDLK_WORLD_24 + K_WORLD_25 = C.SDLK_WORLD_25 + K_WORLD_26 = C.SDLK_WORLD_26 + K_WORLD_27 = C.SDLK_WORLD_27 + K_WORLD_28 = C.SDLK_WORLD_28 + K_WORLD_29 = C.SDLK_WORLD_29 + K_WORLD_30 = C.SDLK_WORLD_30 + K_WORLD_31 = C.SDLK_WORLD_31 + K_WORLD_32 = C.SDLK_WORLD_32 + K_WORLD_33 = C.SDLK_WORLD_33 + K_WORLD_34 = C.SDLK_WORLD_34 + K_WORLD_35 = C.SDLK_WORLD_35 + K_WORLD_36 = C.SDLK_WORLD_36 + K_WORLD_37 = C.SDLK_WORLD_37 + K_WORLD_38 = C.SDLK_WORLD_38 + K_WORLD_39 = C.SDLK_WORLD_39 + K_WORLD_40 = C.SDLK_WORLD_40 + K_WORLD_41 = C.SDLK_WORLD_41 + K_WORLD_42 = C.SDLK_WORLD_42 + K_WORLD_43 = C.SDLK_WORLD_43 + K_WORLD_44 = C.SDLK_WORLD_44 + K_WORLD_45 = C.SDLK_WORLD_45 + K_WORLD_46 = C.SDLK_WORLD_46 + K_WORLD_47 = C.SDLK_WORLD_47 + K_WORLD_48 = C.SDLK_WORLD_48 + K_WORLD_49 = C.SDLK_WORLD_49 + K_WORLD_50 = C.SDLK_WORLD_50 + K_WORLD_51 = C.SDLK_WORLD_51 + K_WORLD_52 = C.SDLK_WORLD_52 + K_WORLD_53 = C.SDLK_WORLD_53 + K_WORLD_54 = C.SDLK_WORLD_54 + K_WORLD_55 = C.SDLK_WORLD_55 + K_WORLD_56 = C.SDLK_WORLD_56 + K_WORLD_57 = C.SDLK_WORLD_57 + K_WORLD_58 = C.SDLK_WORLD_58 + K_WORLD_59 = C.SDLK_WORLD_59 + K_WORLD_60 = C.SDLK_WORLD_60 + K_WORLD_61 = C.SDLK_WORLD_61 + K_WORLD_62 = C.SDLK_WORLD_62 + K_WORLD_63 = C.SDLK_WORLD_63 + K_WORLD_64 = C.SDLK_WORLD_64 + K_WORLD_65 = C.SDLK_WORLD_65 + K_WORLD_66 = C.SDLK_WORLD_66 + K_WORLD_67 = C.SDLK_WORLD_67 + K_WORLD_68 = C.SDLK_WORLD_68 + K_WORLD_69 = C.SDLK_WORLD_69 + K_WORLD_70 = C.SDLK_WORLD_70 + K_WORLD_71 = C.SDLK_WORLD_71 + K_WORLD_72 = C.SDLK_WORLD_72 + K_WORLD_73 = C.SDLK_WORLD_73 + K_WORLD_74 = C.SDLK_WORLD_74 + K_WORLD_75 = C.SDLK_WORLD_75 + K_WORLD_76 = C.SDLK_WORLD_76 + K_WORLD_77 = C.SDLK_WORLD_77 + K_WORLD_78 = C.SDLK_WORLD_78 + K_WORLD_79 = C.SDLK_WORLD_79 + K_WORLD_80 = C.SDLK_WORLD_80 + K_WORLD_81 = C.SDLK_WORLD_81 + K_WORLD_82 = C.SDLK_WORLD_82 + K_WORLD_83 = C.SDLK_WORLD_83 + K_WORLD_84 = C.SDLK_WORLD_84 + K_WORLD_85 = C.SDLK_WORLD_85 + K_WORLD_86 = C.SDLK_WORLD_86 + K_WORLD_87 = C.SDLK_WORLD_87 + K_WORLD_88 = C.SDLK_WORLD_88 + K_WORLD_89 = C.SDLK_WORLD_89 + K_WORLD_90 = C.SDLK_WORLD_90 + K_WORLD_91 = C.SDLK_WORLD_91 + K_WORLD_92 = C.SDLK_WORLD_92 + K_WORLD_93 = C.SDLK_WORLD_93 + K_WORLD_94 = C.SDLK_WORLD_94 + K_WORLD_95 = C.SDLK_WORLD_95 + K_KP0 = C.SDLK_KP0 + K_KP1 = C.SDLK_KP1 + K_KP2 = C.SDLK_KP2 + K_KP3 = C.SDLK_KP3 + K_KP4 = C.SDLK_KP4 + K_KP5 = C.SDLK_KP5 + K_KP6 = C.SDLK_KP6 + K_KP7 = C.SDLK_KP7 + K_KP8 = C.SDLK_KP8 + K_KP9 = C.SDLK_KP9 + K_KP_PERIOD = C.SDLK_KP_PERIOD + K_KP_DIVIDE = C.SDLK_KP_DIVIDE + K_KP_MULTIPLY = C.SDLK_KP_MULTIPLY + K_KP_MINUS = C.SDLK_KP_MINUS + K_KP_PLUS = C.SDLK_KP_PLUS + K_KP_ENTER = C.SDLK_KP_ENTER + K_KP_EQUALS = C.SDLK_KP_EQUALS + K_UP = C.SDLK_UP + K_DOWN = C.SDLK_DOWN + K_RIGHT = C.SDLK_RIGHT + K_LEFT = C.SDLK_LEFT + K_INSERT = C.SDLK_INSERT + K_HOME = C.SDLK_HOME + K_END = C.SDLK_END + K_PAGEUP = C.SDLK_PAGEUP + K_PAGEDOWN = C.SDLK_PAGEDOWN + K_F1 = C.SDLK_F1 + K_F2 = C.SDLK_F2 + K_F3 = C.SDLK_F3 + K_F4 = C.SDLK_F4 + K_F5 = C.SDLK_F5 + K_F6 = C.SDLK_F6 + K_F7 = C.SDLK_F7 + K_F8 = C.SDLK_F8 + K_F9 = C.SDLK_F9 + K_F10 = C.SDLK_F10 + K_F11 = C.SDLK_F11 + K_F12 = C.SDLK_F12 + K_F13 = C.SDLK_F13 + K_F14 = C.SDLK_F14 + K_F15 = C.SDLK_F15 + K_NUMLOCK = C.SDLK_NUMLOCK + K_CAPSLOCK = C.SDLK_CAPSLOCK + K_SCROLLOCK = C.SDLK_SCROLLOCK + K_RSHIFT = C.SDLK_RSHIFT + K_LSHIFT = C.SDLK_LSHIFT + K_RCTRL = C.SDLK_RCTRL + K_LCTRL = C.SDLK_LCTRL + K_RALT = C.SDLK_RALT + K_LALT = C.SDLK_LALT + K_RMETA = C.SDLK_RMETA + K_LMETA = C.SDLK_LMETA + K_LSUPER = C.SDLK_LSUPER + K_RSUPER = C.SDLK_RSUPER + K_MODE = C.SDLK_MODE + K_COMPOSE = C.SDLK_COMPOSE + K_HELP = C.SDLK_HELP + K_PRINT = C.SDLK_PRINT + K_SYSREQ = C.SDLK_SYSREQ + K_BREAK = C.SDLK_BREAK + K_MENU = C.SDLK_MENU + K_POWER = C.SDLK_POWER + K_EURO = C.SDLK_EURO + K_UNDO = C.SDLK_UNDO + + // key mods + + KMOD_NONE = C.KMOD_NONE + KMOD_LSHIFT = C.KMOD_LSHIFT + KMOD_RSHIFT = C.KMOD_RSHIFT + KMOD_LCTRL = C.KMOD_LCTRL + KMOD_RCTRL = C.KMOD_RCTRL + KMOD_LALT = C.KMOD_LALT + KMOD_RALT = C.KMOD_RALT + KMOD_LMETA = C.KMOD_LMETA + KMOD_RMETA = C.KMOD_RMETA + KMOD_NUM = C.KMOD_NUM + KMOD_CAPS = C.KMOD_CAPS + KMOD_MODE = C.KMOD_MODE + KMOD_RESERVED = C.KMOD_RESERVED + + // hat states + + HAT_CENTERED = C.SDL_HAT_CENTERED + HAT_UP = C.SDL_HAT_UP + HAT_RIGHT = C.SDL_HAT_RIGHT + HAT_DOWN = C.SDL_HAT_DOWN + HAT_LEFT = C.SDL_HAT_LEFT + HAT_RIGHTUP = C.SDL_HAT_RIGHTUP + HAT_RIGHTDOWN = C.SDL_HAT_RIGHTDOWN + HAT_LEFTUP = C.SDL_HAT_LEFTUP + HAT_LEFTDOWN = C.SDL_HAT_LEFTDOWN + + // keyboard/mouse state + + RELEASED = C.SDL_RELEASED + PRESSED = C.SDL_PRESSED + + // mouse button constants + + BUTTON_LEFT = C.SDL_BUTTON_LEFT + BUTTON_MIDDLE = C.SDL_BUTTON_MIDDLE + BUTTON_RIGHT = C.SDL_BUTTON_RIGHT + BUTTON_WHEELUP = C.SDL_BUTTON_WHEELUP + BUTTON_WHEELDOWN = C.SDL_BUTTON_WHEELDOWN + BUTTON_X1 = C.SDL_BUTTON_X1 + BUTTON_X2 = C.SDL_BUTTON_X2 + + // mouse button masks + + BUTTON_LMASK = 1 << (BUTTON_LEFT - 1) + BUTTON_MMASK = 1 << (BUTTON_MIDDLE - 1) + BUTTON_RMASK = 1 << (BUTTON_RIGHT - 1) + BUTTON_WHEELUPMASK = 1 << (BUTTON_WHEELUP - 1) + BUTTON_WHEELDOWNMASK = 1 << (BUTTON_WHEELDOWN - 1) + BUTTON_X1MASK = 1 << (BUTTON_X1 - 1) + BUTTON_X2MASK = 1 << (BUTTON_X2 - 1) +) diff --git a/sdl/event.go b/sdl/event.go new file mode 100644 index 0000000..a21e2ca --- /dev/null +++ b/sdl/event.go @@ -0,0 +1,65 @@ +package sdl + +import "time" + +var events chan interface{} = make(chan interface{}) + +// This channel delivers SDL events. Each object received from this channel +// has one of the following types: sdl.QuitEvent, sdl.KeyboardEvent, +// sdl.MouseButtonEvent, sdl.MouseMotionEvent, sdl.ActiveEvent, +// sdl.ResizeEvent, sdl.JoyAxisEvent, sdl.JoyButtonEvent, sdl.JoyHatEvent, +// sdl.JoyBallEvent +var Events <-chan interface{} = events + +// Polling interval, in milliseconds +const poll_interval_ms = 10 + +// Polls SDL events in periodic intervals. +// This function does not return. +func pollEvents() { + // It is more efficient to create the event-object here once, + // rather than multiple times within the loop + event := &Event{} + + for { + for event.poll() { + switch event.Type { + case QUIT: + events <- *(*QuitEvent)(cast(event)) + + case KEYDOWN, KEYUP: + events <- *(*KeyboardEvent)(cast(event)) + + case MOUSEBUTTONDOWN, MOUSEBUTTONUP: + events <- *(*MouseButtonEvent)(cast(event)) + + case MOUSEMOTION: + events <- *(*MouseMotionEvent)(cast(event)) + + case JOYAXISMOTION: + events <- *(*JoyAxisEvent)(cast(event)) + + case JOYBUTTONDOWN, JOYBUTTONUP: + events <- *(*JoyButtonEvent)(cast(event)) + + case JOYHATMOTION: + events <- *(*JoyHatEvent)(cast(event)) + + case JOYBALLMOTION: + events <- *(*JoyBallEvent)(cast(event)) + + case ACTIVEEVENT: + events <- *(*ActiveEvent)(cast(event)) + + case VIDEORESIZE: + events <- *(*ResizeEvent)(cast(event)) + } + } + + time.Sleep(poll_interval_ms * 1e6) + } +} + +func init() { + go pollEvents() +} diff --git a/sdl/rwops.go b/sdl/rwops.go new file mode 100644 index 0000000..f4c825e --- /dev/null +++ b/sdl/rwops.go @@ -0,0 +1,42 @@ +package sdl + +import ( + "unsafe" +) + +// #cgo pkg-config: sdl +// #include +import "C" + +type RWops struct { + cRWops *C.SDL_RWops + + gcBytes []byte // Prevents garbage collection of memory passed to RWFromMem +} + +func (s *RWops) destroy() { + s.cRWops = nil + s.gcBytes = nil +} + +// Creates an RWops from memory. +func RWFromMem(buf []byte) *RWops { + GlobalMutex.Lock() + defer GlobalMutex.Unlock() + + p := C.SDL_RWFromMem(unsafe.Pointer(&buf[0]), C.int(len(buf))) + var rwops RWops + rwops.cRWops = (*C.SDL_RWops)(p) + rwops.gcBytes = buf + return &rwops + +} + +func (self *RWops) Free() { + GlobalMutex.Lock() + defer GlobalMutex.Unlock() + + C.SDL_FreeRW(self.cRWops) + self.cRWops = nil + self.gcBytes = nil +} diff --git a/sdl/sdl.go b/sdl/sdl.go index b6881ac..aceeb47 100644 --- a/sdl/sdl.go +++ b/sdl/sdl.go @@ -1,97 +1,278 @@ /* A binding of SDL and SDL_image. -The binding is works in pretty much the same way as it does in C, although +The binding works in pretty much the same way as it does in C, although some of the functions have been altered to give them an object-oriented flavor (eg. Rather than sdl.Flip(surface) it's surface.Flip() ) */ package sdl - +// #cgo pkg-config: sdl SDL_image +// // struct private_hwdata{}; // struct SDL_BlitMap{}; // #define map _map // -// #include -// #include +// #include +// #include // static void SetError(const char* description){SDL_SetError("%s",description);} +// static int __SDL_SaveBMP(SDL_Surface *surface, const char *file) { return SDL_SaveBMP(surface, file); } import "C" -import "unsafe" + +import ( + "os" + "reflect" + "runtime" + "sync" + "time" + "unsafe" +) type cast unsafe.Pointer +// Mutex for serialization of access to certain SDL functions. +// +// There is no need to use this in application code, the mutex is a public variable +// just because it needs to be accessible from other parts of Go-SDL (such as package "sdl/ttf"). +// +// Surface-level functions (such as 'Surface.Blit') are not using this mutex, +// so it is possible to modify multiple surfaces concurrently. +// There is no dependency between 'Surface.Lock' and the global mutex. +var GlobalMutex sync.Mutex + +type Surface struct { + cSurface *C.SDL_Surface + mutex sync.RWMutex + + Flags uint32 + Format *PixelFormat + W int32 + H int32 + Pitch uint16 + Pixels unsafe.Pointer + Offset int32 + + gcPixels interface{} // Prevents garbage collection of pixels passed to func CreateRGBSurfaceFrom +} + +func wrap(cSurface *C.SDL_Surface) *Surface { + var s *Surface + + if cSurface != nil { + var surface Surface + surface.SetCSurface(unsafe.Pointer(cSurface)) + s = &surface + } else { + s = nil + } + + return s +} + +// FIXME: Ideally, this should NOT be a public function, but it is needed in the package "ttf" ... +func (s *Surface) SetCSurface(cSurface unsafe.Pointer) { + s.cSurface = (*C.SDL_Surface)(cSurface) + s.reload() +} + +// Pull data from C.SDL_Surface. +// Make sure to use this when the C surface might have been changed. +func (s *Surface) reload() { + s.Flags = uint32(s.cSurface.flags) + s.Format = (*PixelFormat)(cast(s.cSurface.format)) + s.W = int32(s.cSurface.w) + s.H = int32(s.cSurface.h) + s.Pitch = uint16(s.cSurface.pitch) + s.Pixels = s.cSurface.pixels + s.Offset = int32(s.cSurface.offset) +} + +func (s *Surface) destroy() { + s.cSurface = nil + s.Format = nil + s.Pixels = nil + s.gcPixels = nil +} + +// ======= // General +// ======= + +// The version of Go-SDL bindings. +// The version descriptor changes into a new unique string +// after a semantically incompatible Go-SDL update. +// +// The returned value can be checked by users of this package +// to make sure they are using a version with the expected semantics. +// +// If Go adds some kind of support for package versioning, this function will go away. +func GoSdlVersion() string { + return "⚛SDL bindings 1.0" +} // Initializes SDL. -func Init(flags uint32) int { return int(C.SDL_Init(C.Uint32(flags))) } +func Init(flags uint32) int { + GlobalMutex.Lock() + status := int(C.SDL_Init(C.Uint32(flags))) + if (status != 0) && (runtime.GOOS == "darwin") && (flags&INIT_VIDEO != 0) { + if os.Getenv("SDL_VIDEODRIVER") == "" { + os.Setenv("SDL_VIDEODRIVER", "x11") + status = int(C.SDL_Init(C.Uint32(flags))) + if status != 0 { + os.Setenv("SDL_VIDEODRIVER", "") + } + } + } + + GlobalMutex.Unlock() + return status +} // Shuts down SDL -func Quit() { C.SDL_Quit() } +func Quit() { + GlobalMutex.Lock() + + if currentVideoSurface != nil { + currentVideoSurface.destroy() + currentVideoSurface = nil + } + + C.SDL_Quit() + + GlobalMutex.Unlock() +} // Initializes subsystems. -func InitSubSystem(flags uint32) int { return int(C.SDL_InitSubSystem(C.Uint32(flags))) } +func InitSubSystem(flags uint32) int { + GlobalMutex.Lock() + status := int(C.SDL_InitSubSystem(C.Uint32(flags))) + if (status != 0) && (runtime.GOOS == "darwin") && (flags&INIT_VIDEO != 0) { + if os.Getenv("SDL_VIDEODRIVER") == "" { + os.Setenv("SDL_VIDEODRIVER", "x11") + status = int(C.SDL_InitSubSystem(C.Uint32(flags))) + if status != 0 { + os.Setenv("SDL_VIDEODRIVER", "") + } + } + } + GlobalMutex.Unlock() + return status +} // Shuts down a subsystem. -func QuitSubSystem(flags uint32) { C.SDL_QuitSubSystem(C.Uint32(flags)) } +func QuitSubSystem(flags uint32) { + GlobalMutex.Lock() + C.SDL_QuitSubSystem(C.Uint32(flags)) + GlobalMutex.Unlock() +} // Checks which subsystems are initialized. -func WasInit(flags uint32) int { return int(C.SDL_WasInit(C.Uint32(flags))) } +func WasInit(flags uint32) int { + GlobalMutex.Lock() + status := int(C.SDL_WasInit(C.Uint32(flags))) + GlobalMutex.Unlock() + return status +} +// ============== // Error Handling +// ============== // Gets SDL error string -func GetError() string { return C.GoString(C.SDL_GetError()) } +func GetError() string { + GlobalMutex.Lock() + s := C.GoString(C.SDL_GetError()) + GlobalMutex.Unlock() + return s +} // Set a string describing an error to be submitted to the SDL Error system. func SetError(description string) { + GlobalMutex.Lock() + cdescription := C.CString(description) C.SetError(cdescription) C.free(unsafe.Pointer(cdescription)) -} -// TODO SDL_Error + GlobalMutex.Unlock() +} // Clear the current SDL error -func ClearError() { C.SDL_ClearError() } +func ClearError() { + GlobalMutex.Lock() + C.SDL_ClearError() + GlobalMutex.Unlock() +} +// ====== // Video +// ====== + +var currentVideoSurface *Surface = nil // Sets up a video mode with the specified width, height, bits-per-pixel and // returns a corresponding surface. You don't need to call the Free method // of the returned surface, as it will be done automatically by sdl.Quit. func SetVideoMode(w int, h int, bpp int, flags uint32) *Surface { + GlobalMutex.Lock() var screen = C.SDL_SetVideoMode(C.int(w), C.int(h), C.int(bpp), C.Uint32(flags)) - return (*Surface)(cast(screen)) + currentVideoSurface = wrap(screen) + GlobalMutex.Unlock() + return currentVideoSurface } // Returns a pointer to the current display surface. -func GetVideoSurface() *Surface { return (*Surface)(cast(C.SDL_GetVideoSurface())) } +func GetVideoSurface() *Surface { + GlobalMutex.Lock() + surface := currentVideoSurface + GlobalMutex.Unlock() + return surface +} // Checks to see if a particular video mode is supported. Returns 0 if not // supported, or the bits-per-pixel of the closest available mode. func VideoModeOK(width int, height int, bpp int, flags uint32) int { - return int(C.SDL_VideoModeOK(C.int(width), C.int(height), C.int(bpp), C.Uint32(flags))) + GlobalMutex.Lock() + status := int(C.SDL_VideoModeOK(C.int(width), C.int(height), C.int(bpp), C.Uint32(flags))) + GlobalMutex.Unlock() + return status } +// Returns the list of available screen dimensions for the given format. +// +// NOTE: The result of this function uses a different encoding than the underlying C function. +// It returns an empty array if no modes are available, +// and nil if any dimension is okay for the given format. func ListModes(format *PixelFormat, flags uint32) []Rect { modes := C.SDL_ListModes((*C.SDL_PixelFormat)(cast(format)), C.Uint32(flags)) - if modes == nil { //no modes available + + // No modes available + if modes == nil { return make([]Rect, 0) } - var any int - *((***C.SDL_Rect)(unsafe.Pointer(&any))) = modes - if any == -1 { //any dimension is ok + + // (modes == -1) --> Any dimension is ok + if uintptr(unsafe.Pointer(modes))+1 == uintptr(0) { return nil } - var count int + count := 0 ptr := *modes //first element in the list - for count = 0; ptr != nil; count++ { - ptr = *(**C.SDL_Rect)(unsafe.Pointer(uintptr(unsafe.Pointer(modes)) + uintptr(count*unsafe.Sizeof(ptr)))) + for ptr != nil { + count++ + ptr = *(**C.SDL_Rect)(unsafe.Pointer(uintptr(unsafe.Pointer(modes)) + uintptr(count*int(unsafe.Sizeof(ptr))))) + } + + ret := make([]Rect, count) + for i := 0; i < count; i++ { + ptr := (**C.SDL_Rect)(unsafe.Pointer(uintptr(unsafe.Pointer(modes)) + uintptr(i*int(unsafe.Sizeof(*modes))))) + var r *C.SDL_Rect = *ptr + ret[i].X = int16(r.x) + ret[i].Y = int16(r.y) + ret[i].W = uint16(r.w) + ret[i].H = uint16(r.h) } - var ret = make([]Rect, count-1) - *((***C.SDL_Rect)(unsafe.Pointer(&ret))) = modes // TODO return ret } @@ -112,7 +293,9 @@ type VideoInfo struct { } func GetVideoInfo() *VideoInfo { + GlobalMutex.Lock() vinfo := (*internalVideoInfo)(cast(C.SDL_GetVideoInfo())) + GlobalMutex.Unlock() flags := vinfo.Flags @@ -136,22 +319,39 @@ func GetVideoInfo() *VideoInfo { // Makes sure the given area is updated on the given screen. If x, y, w, and // h are all 0, the whole screen will be updated. func (screen *Surface) UpdateRect(x int32, y int32, w uint32, h uint32) { - C.SDL_UpdateRect((*C.SDL_Surface)(cast(screen)), C.Sint32(x), C.Sint32(y), C.Uint32(w), C.Uint32(h)) + GlobalMutex.Lock() + screen.mutex.Lock() + + C.SDL_UpdateRect(screen.cSurface, C.Sint32(x), C.Sint32(y), C.Uint32(w), C.Uint32(h)) + + screen.mutex.Unlock() + GlobalMutex.Unlock() } func (screen *Surface) UpdateRects(rects []Rect) { if len(rects) > 0 { - C.SDL_UpdateRects((*C.SDL_Surface)(cast(screen)), C.int(len(rects)), (*C.SDL_Rect)(cast(&rects[0]))) + GlobalMutex.Lock() + screen.mutex.Lock() + + C.SDL_UpdateRects(screen.cSurface, C.int(len(rects)), (*C.SDL_Rect)(cast(&rects[0]))) + + screen.mutex.Unlock() + GlobalMutex.Unlock() } } // Gets the window title and icon name. func WM_GetCaption() (title, icon string) { - //SDL seems to free these strings. TODO: Check to see if that's the case + GlobalMutex.Lock() + + // SDL seems to free these strings. TODO: Check to see if that's the case var ctitle, cicon *C.char C.SDL_WM_GetCaption(&ctitle, &cicon) title = C.GoString(ctitle) icon = C.GoString(cicon) + + GlobalMutex.Unlock() + return } @@ -159,87 +359,172 @@ func WM_GetCaption() (title, icon string) { func WM_SetCaption(title, icon string) { ctitle := C.CString(title) cicon := C.CString(icon) + + GlobalMutex.Lock() C.SDL_WM_SetCaption(ctitle, cicon) + GlobalMutex.Unlock() + C.free(unsafe.Pointer(ctitle)) C.free(unsafe.Pointer(cicon)) } // Sets the icon for the display window. func WM_SetIcon(icon *Surface, mask *uint8) { - C.SDL_WM_SetIcon((*C.SDL_Surface)(cast(icon)), (*C.Uint8)(mask)) + GlobalMutex.Lock() + C.SDL_WM_SetIcon(icon.cSurface, (*C.Uint8)(mask)) + GlobalMutex.Unlock() } // Minimizes the window -func WM_IconifyWindow() int { return int(C.SDL_WM_IconifyWindow()) } +func WM_IconifyWindow() int { + GlobalMutex.Lock() + status := int(C.SDL_WM_IconifyWindow()) + GlobalMutex.Unlock() + return status +} // Toggles fullscreen mode func WM_ToggleFullScreen(surface *Surface) int { - return int(C.SDL_WM_ToggleFullScreen((*C.SDL_Surface)(cast(surface)))) + GlobalMutex.Lock() + status := int(C.SDL_WM_ToggleFullScreen(surface.cSurface)) + GlobalMutex.Unlock() + return status } // Swaps OpenGL framebuffers/Update Display. -func GL_SwapBuffers() { C.SDL_GL_SwapBuffers() } +func GL_SwapBuffers() { + GlobalMutex.Lock() + C.SDL_GL_SwapBuffers() + GlobalMutex.Unlock() +} func GL_SetAttribute(attr int, value int) int { - return int(C.SDL_GL_SetAttribute(C.SDL_GLattr(attr), C.int(value))) + GlobalMutex.Lock() + status := int(C.SDL_GL_SetAttribute(C.SDL_GLattr(attr), C.int(value))) + GlobalMutex.Unlock() + return status } // Swaps screen buffers. -func (screen *Surface) Flip() int { return int(C.SDL_Flip((*C.SDL_Surface)(cast(screen)))) } +func (screen *Surface) Flip() int { + GlobalMutex.Lock() + screen.mutex.Lock() + + status := int(C.SDL_Flip(screen.cSurface)) + + screen.mutex.Unlock() + GlobalMutex.Unlock() + + return status +} // Frees (deletes) a Surface -func (screen *Surface) Free() { C.SDL_FreeSurface((*C.SDL_Surface)(cast(screen))) } +func (screen *Surface) Free() { + GlobalMutex.Lock() + screen.mutex.Lock() + + C.SDL_FreeSurface(screen.cSurface) + + screen.destroy() + if screen == currentVideoSurface { + currentVideoSurface = nil + } + + screen.mutex.Unlock() + GlobalMutex.Unlock() +} // Locks a surface for direct access. func (screen *Surface) Lock() int { - return int(C.SDL_LockSurface((*C.SDL_Surface)(cast(screen)))) + screen.mutex.Lock() + status := int(C.SDL_LockSurface(screen.cSurface)) + screen.mutex.Unlock() + return status } // Unlocks a previously locked surface. -func (screen *Surface) Unlock() int { return int(C.SDL_Flip((*C.SDL_Surface)(cast(screen)))) } +func (screen *Surface) Unlock() { + screen.mutex.Lock() + C.SDL_UnlockSurface(screen.cSurface) + screen.mutex.Unlock() +} func (dst *Surface) Blit(dstrect *Rect, src *Surface, srcrect *Rect) int { - var ret = C.SDL_UpperBlit( - (*C.SDL_Surface)(cast(src)), - (*C.SDL_Rect)(cast(srcrect)), - (*C.SDL_Surface)(cast(dst)), - (*C.SDL_Rect)(cast(dstrect))) + GlobalMutex.Lock() + global := true + if (src != currentVideoSurface) && (dst != currentVideoSurface) { + GlobalMutex.Unlock() + global = false + } + + // At this point: GlobalMutex is locked only if at least one of 'src' or 'dst' + // was identical to 'currentVideoSurface' + + var ret C.int + { + src.mutex.RLock() + dst.mutex.Lock() + + ret = C.SDL_UpperBlit( + src.cSurface, + (*C.SDL_Rect)(cast(srcrect)), + dst.cSurface, + (*C.SDL_Rect)(cast(dstrect))) + + dst.mutex.Unlock() + src.mutex.RUnlock() + } + + if global { + GlobalMutex.Unlock() + } return int(ret) } // This function performs a fast fill of the given rectangle with some color. func (dst *Surface) FillRect(dstrect *Rect, color uint32) int { + dst.mutex.Lock() + var ret = C.SDL_FillRect( - (*C.SDL_Surface)(cast(dst)), + dst.cSurface, (*C.SDL_Rect)(cast(dstrect)), C.Uint32(color)) + dst.mutex.Unlock() + return int(ret) } // Adjusts the alpha properties of a Surface. func (s *Surface) SetAlpha(flags uint32, alpha uint8) int { - return int(C.SDL_SetAlpha((*C.SDL_Surface)(cast(s)), C.Uint32(flags), C.Uint8(alpha))) + s.mutex.Lock() + status := int(C.SDL_SetAlpha(s.cSurface, C.Uint32(flags), C.Uint8(alpha))) + s.mutex.Unlock() + return status } // Sets the color key (transparent pixel) in a blittable surface and // enables or disables RLE blit acceleration. func (s *Surface) SetColorKey(flags uint32, ColorKey uint32) int { - return int(C.SDL_SetColorKey((*C.SDL_Surface)(cast(s)), - C.Uint32(flags), C.Uint32(ColorKey))) + s.mutex.Lock() + status := int(C.SDL_SetColorKey(s.cSurface, C.Uint32(flags), C.Uint32(ColorKey))) + s.mutex.Unlock() + return status } // Gets the clipping rectangle for a surface. func (s *Surface) GetClipRect(r *Rect) { - C.SDL_GetClipRect((*C.SDL_Surface)(cast(s)), (*C.SDL_Rect)(cast(r))) - return + s.mutex.RLock() + C.SDL_GetClipRect(s.cSurface, (*C.SDL_Rect)(cast(r))) + s.mutex.RUnlock() } // Sets the clipping rectangle for a surface. func (s *Surface) SetClipRect(r *Rect) { - C.SDL_SetClipRect((*C.SDL_Surface)(cast(s)), (*C.SDL_Rect)(cast(r))) - return + s.mutex.Lock() + C.SDL_SetClipRect(s.cSurface, (*C.SDL_Rect)(cast(r))) + s.mutex.Unlock() } // Map a RGBA color value to a pixel format. @@ -254,48 +539,123 @@ func GetRGBA(color uint32, format *PixelFormat, r, g, b, a *uint8) { // Loads Surface from file (using IMG_Load). func Load(file string) *Surface { + GlobalMutex.Lock() + cfile := C.CString(file) var screen = C.IMG_Load(cfile) C.free(unsafe.Pointer(cfile)) - return (*Surface)(cast(screen)) + + GlobalMutex.Unlock() + + return wrap(screen) +} + +// Loads Surface from RWops (using IMG_Load_RW). +func Load_RW(rwOps *RWops) *Surface { + GlobalMutex.Lock() + + var screen = C.IMG_Load_RW((*C.SDL_RWops)(cast(rwOps.cRWops)), 0) + + GlobalMutex.Unlock() + + return wrap(screen) +} + +// SaveBMP saves the src surface as a Windows BMP to file. +func (src *Surface) SaveBMP(file string) int { + GlobalMutex.Lock() + cfile := C.CString(file) + // SDL_SaveBMP is a macro. + res := int(C.__SDL_SaveBMP(src.cSurface, cfile)) + C.free(unsafe.Pointer(cfile)) + GlobalMutex.Unlock() + return res } // Creates an empty Surface. func CreateRGBSurface(flags uint32, width int, height int, bpp int, Rmask uint32, Gmask uint32, Bmask uint32, Amask uint32) *Surface { + GlobalMutex.Lock() + p := C.SDL_CreateRGBSurface(C.Uint32(flags), C.int(width), C.int(height), C.int(bpp), C.Uint32(Rmask), C.Uint32(Gmask), C.Uint32(Bmask), C.Uint32(Amask)) - return (*Surface)(cast(p)) + + GlobalMutex.Unlock() + + return wrap(p) +} + +// Creates a Surface from existing pixel data. It expects pixels to be a slice, pointer or unsafe.Pointer. +func CreateRGBSurfaceFrom(pixels interface{}, width, height, bpp, pitch int, Rmask, Gmask, Bmask, Amask uint32) *Surface { + var ptr unsafe.Pointer + switch v := reflect.ValueOf(pixels); v.Kind() { + case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: + ptr = unsafe.Pointer(v.Pointer()) + default: + panic("Don't know how to handle type: " + v.Kind().String()) + } + + GlobalMutex.Lock() + p := C.SDL_CreateRGBSurfaceFrom(ptr, C.int(width), C.int(height), C.int(bpp), C.int(pitch), + C.Uint32(Rmask), C.Uint32(Gmask), C.Uint32(Bmask), C.Uint32(Amask)) + GlobalMutex.Unlock() + + s := wrap(p) + s.gcPixels = pixels + return s } // Converts a surface to the display format -func DisplayFormat(src *Surface) *Surface { - p := C.SDL_DisplayFormat((*C.SDL_Surface)(cast(src))) - return (*Surface)(cast(p)) +func (s *Surface) DisplayFormat() *Surface { + s.mutex.RLock() + p := C.SDL_DisplayFormat(s.cSurface) + s.mutex.RUnlock() + return wrap(p) } -// Events +// Converts a surface to the display format with alpha +func (s *Surface) DisplayFormatAlpha() *Surface { + s.mutex.RLock() + p := C.SDL_DisplayFormatAlpha(s.cSurface) + s.mutex.RUnlock() + return wrap(p) +} + +// ======== +// Keyboard +// ======== // Enables UNICODE translation. -func EnableUNICODE(enable int) int { return int(C.SDL_EnableUNICODE(C.int(enable))) } +func EnableUNICODE(enable int) int { + GlobalMutex.Lock() + previous := int(C.SDL_EnableUNICODE(C.int(enable))) + GlobalMutex.Unlock() + return previous +} // Sets keyboard repeat rate. func EnableKeyRepeat(delay, interval int) int { - return int(C.SDL_EnableKeyRepeat(C.int(delay), C.int(interval))) + GlobalMutex.Lock() + status := int(C.SDL_EnableKeyRepeat(C.int(delay), C.int(interval))) + GlobalMutex.Unlock() + return status } // Gets keyboard repeat rate. func GetKeyRepeat() (int, int) { - var delay int var interval int + GlobalMutex.Lock() C.SDL_GetKeyRepeat((*C.int)(cast(&delay)), (*C.int)(cast(&interval))) + GlobalMutex.Unlock() return delay, interval } // Gets a snapshot of the current keyboard state func GetKeyState() []uint8 { + GlobalMutex.Lock() + var numkeys C.int array := C.SDL_GetKeyState(&numkeys) @@ -303,6 +663,8 @@ func GetKeyState() []uint8 { *((**C.Uint8)(unsafe.Pointer(&ptr))) = array // TODO + GlobalMutex.Unlock() + return ptr } @@ -313,89 +675,228 @@ type Mod C.int // Key type Key C.int +// Gets the state of modifier keys +func GetModState() Mod { + GlobalMutex.Lock() + state := Mod(C.SDL_GetModState()) + GlobalMutex.Unlock() + return state +} + +// Sets the state of modifier keys +func SetModState(modstate Mod) { + GlobalMutex.Lock() + C.SDL_SetModState(C.SDLMod(modstate)) + GlobalMutex.Unlock() +} + +// Gets the name of an SDL virtual keysym +func GetKeyName(key Key) string { + GlobalMutex.Lock() + name := C.GoString(C.SDL_GetKeyName(C.SDLKey(key))) + GlobalMutex.Unlock() + return name +} + +// ====== +// Events +// ====== + +// Polls for currently pending events +func (event *Event) poll() bool { + GlobalMutex.Lock() + + var ret = C.SDL_PollEvent((*C.SDL_Event)(cast(event))) + + if ret != 0 { + if (event.Type == VIDEORESIZE) && (currentVideoSurface != nil) { + currentVideoSurface.reload() + } + } + + GlobalMutex.Unlock() + + return ret != 0 +} + +// ===== +// Mouse +// ===== + // Retrieves the current state of the mouse. func GetMouseState(x, y *int) uint8 { - return uint8(C.SDL_GetMouseState((*C.int)(cast(x)), (*C.int)(cast(y)))) + GlobalMutex.Lock() + state := uint8(C.SDL_GetMouseState((*C.int)(cast(x)), (*C.int)(cast(y)))) + GlobalMutex.Unlock() + return state } // Retrieves the current state of the mouse relative to the last time this // function was called. func GetRelativeMouseState(x, y *int) uint8 { - return uint8(C.SDL_GetRelativeMouseState((*C.int)(cast(x)), (*C.int)(cast(y)))) + GlobalMutex.Lock() + state := uint8(C.SDL_GetRelativeMouseState((*C.int)(cast(x)), (*C.int)(cast(y)))) + GlobalMutex.Unlock() + return state } -// Gets the state of modifier keys -func GetModState() Mod { return Mod(C.SDL_GetModState()) } +// Toggle whether or not the cursor is shown on the screen. +func ShowCursor(toggle int) int { + GlobalMutex.Lock() + state := int(C.SDL_ShowCursor((C.int)(toggle))) + GlobalMutex.Unlock() + return state +} -// Sets the state of modifier keys -func SetModState(modstate Mod) { C.SDL_SetModState(C.SDLMod(modstate)) } +// ======== +// Joystick +// ======== -// Gets the name of an SDL virtual keysym -func GetKeyName(key Key) string { return C.GoString(C.SDL_GetKeyName(C.SDLKey(key))) } +type Joystick struct { + cJoystick *C.SDL_Joystick +} -// Events +func wrapJoystick(cJoystick *C.SDL_Joystick) *Joystick { + var j *Joystick + if cJoystick != nil { + var joystick Joystick + joystick.cJoystick = (*C.SDL_Joystick)(unsafe.Pointer(cJoystick)) + j = &joystick + } else { + j = nil + } + return j +} -// Waits indefinitely for the next available event -func (event *Event) Wait() bool { - var ret = C.SDL_WaitEvent((*C.SDL_Event)(cast(event))) - return ret != 0 +// Count the number of joysticks attached to the system +func NumJoysticks() int { + GlobalMutex.Lock() + num := int(C.SDL_NumJoysticks()) + GlobalMutex.Unlock() + return num } -// Polls for currently pending events -func (event *Event) Poll() bool { - var ret = C.SDL_PollEvent((*C.SDL_Event)(cast(event))) - return ret != 0 +// Get the implementation dependent name of a joystick. +// This can be called before any joysticks are opened. +// If no name can be found, this function returns NULL. +func JoystickName(deviceIndex int) string { + GlobalMutex.Lock() + name := C.GoString(C.SDL_JoystickName(C.int(deviceIndex))) + GlobalMutex.Unlock() + return name } -// Returns KeyboardEvent or nil if event has other type -func (event *Event) Keyboard() *KeyboardEvent { - if event.Type == KEYUP || event.Type == KEYDOWN { - return (*KeyboardEvent)(cast(event)) - } +// Open a joystick for use The index passed as an argument refers to +// the N'th joystick on the system. This index is the value which will +// identify this joystick in future joystick events. This function +// returns a joystick identifier, or NULL if an error occurred. +func JoystickOpen(deviceIndex int) *Joystick { + GlobalMutex.Lock() + joystick := C.SDL_JoystickOpen(C.int(deviceIndex)) + GlobalMutex.Unlock() + return wrapJoystick(joystick) +} - return nil +// Returns 1 if the joystick has been opened, or 0 if it has not. +func JoystickOpened(deviceIndex int) int { + GlobalMutex.Lock() + opened := int(C.SDL_JoystickOpened(C.int(deviceIndex))) + GlobalMutex.Unlock() + return opened } -// Returns MouseButtonEvent or nil if event has other type -func (event *Event) MouseButton() *MouseButtonEvent { - if event.Type == MOUSEBUTTONDOWN || event.Type == MOUSEBUTTONUP { - return (*MouseButtonEvent)(cast(event)) - } +// Update the current state of the open joysticks. This is called +// automatically by the event loop if any joystick events are enabled. +func JoystickUpdate() { + GlobalMutex.Lock() + C.SDL_JoystickUpdate() + GlobalMutex.Unlock() +} - return nil +// Enable/disable joystick event polling. If joystick events are +// disabled, you must call SDL_JoystickUpdate() yourself and check the +// state of the joystick when you want joystick information. The state +// can be one of SDL_QUERY, SDL_ENABLE or SDL_IGNORE. +func JoystickEventState(state int) int { + GlobalMutex.Lock() + result := int(C.SDL_JoystickEventState(C.int(state))) + GlobalMutex.Unlock() + return result } -// Returns MouseMotion or nil if event has other type -func (event *Event) MouseMotion() *MouseMotionEvent { - if event.Type == MOUSEMOTION { - return (*MouseMotionEvent)(cast(event)) - } +// Close a joystick previously opened with SDL_JoystickOpen() +func (joystick *Joystick) Close() { + GlobalMutex.Lock() + C.SDL_JoystickClose(joystick.cJoystick) + GlobalMutex.Unlock() +} - return nil +// Get the number of general axis controls on a joystick +func (joystick *Joystick) NumAxes() int { + return int(C.SDL_JoystickNumAxes(joystick.cJoystick)) } -// Returns ActiveEvent or nil if event has other type -func (event *Event) Active() *ActiveEvent { - if event.Type == ACTIVEEVENT { - return (*ActiveEvent)(cast(event)) - } +// Get the device index of an opened joystick. +func (joystick *Joystick) Index() int { + return int(C.SDL_JoystickIndex(joystick.cJoystick)) +} - return nil +// Get the number of buttons on a joystick +func (joystick *Joystick) NumButtons() int { + return int(C.SDL_JoystickNumButtons(joystick.cJoystick)) } -// Returns ResizeEvent or nil if event has other type -func (event *Event) Resize() *ResizeEvent { - if event.Type == VIDEORESIZE { - return (*ResizeEvent)(cast(event)) - } +// Get the number of trackballs on a Joystick trackballs have only +// relative motion events associated with them and their state cannot +// be polled. +func (joystick *Joystick) NumBalls() int { + return int(C.SDL_JoystickNumBalls(joystick.cJoystick)) +} - return nil +// Get the number of POV hats on a joystick +func (joystick *Joystick) NumHats() int { + return int(C.SDL_JoystickNumHats(joystick.cJoystick)) } +// Get the current state of a POV hat on a joystick +// The hat indices start at index 0. +func (joystick *Joystick) GetHat(hat int) uint8 { + return uint8(C.SDL_JoystickGetHat(joystick.cJoystick, C.int(hat))) +} + +// Get the current state of a button on a joystick. The button indices +// start at index 0. +func (joystick *Joystick) GetButton(button int) uint8 { + return uint8(C.SDL_JoystickGetButton(joystick.cJoystick, C.int(button))) +} + +// Get the ball axis change since the last poll. The ball indices +// start at index 0. This returns 0, or -1 if you passed it invalid +// parameters. +func (joystick *Joystick) GetBall(ball int, dx, dy *int) int { + return int(C.SDL_JoystickGetBall(joystick.cJoystick, C.int(ball), (*C.int)(cast(dx)), (*C.int)(cast(dy)))) +} + +// Get the current state of an axis control on a joystick. The axis +// indices start at index 0. The state is a value ranging from -32768 +// to 32767. +func (joystick *Joystick) GetAxis(axis int) int16 { + return int16(C.SDL_JoystickGetAxis(joystick.cJoystick, C.int(axis))) +} + +// ==== // Time +// ==== // Gets the number of milliseconds since the SDL library initialization. -func GetTicks() uint32 { return uint32(C.SDL_GetTicks()) } +func GetTicks() uint32 { + GlobalMutex.Lock() + t := uint32(C.SDL_GetTicks()) + GlobalMutex.Unlock() + return t +} // Waits a specified number of milliseconds before returning. -func Delay(ms uint32) { C.SDL_Delay(C.Uint32(ms)) } +func Delay(ms uint32) { + time.Sleep(time.Duration(ms) * time.Millisecond) +} diff --git a/sdl/sdl_darwin.go b/sdl/sdl_darwin.go new file mode 100644 index 0000000..50ce7da --- /dev/null +++ b/sdl/sdl_darwin.go @@ -0,0 +1,11 @@ +package sdl + +import ( + //"os" +) + +func init() { + //if os.Getenv("SDL_VIDEODRIVER") == "" { + // os.Setenv("SDL_VIDEODRIVER", "x11") + //} +} diff --git a/sdl/sdldraw.go b/sdl/sdldraw.go deleted file mode 100644 index fd1d182..0000000 --- a/sdl/sdldraw.go +++ /dev/null @@ -1,140 +0,0 @@ -package sdl - -//implementation of draw.Context and draw.Image interfaces - -import "exp/draw" -import "os" -import "image" -import "unsafe" -import "time" - -type Context struct { - screen *Surface - mouse_chan chan draw.Mouse - key_chan chan int - resize_chan chan bool - quit_chan chan bool -} - -func InitContext(w int, h int) (*Context, os.Error) { - - var this = new(Context) - - Init(INIT_VIDEO) - - this.screen = SetVideoMode(w, h, 32, SWSURFACE) - - this.screen.Lock() - - // TODO \/ - - this.resize_chan = make(chan bool, 64) - this.key_chan = make(chan int, 64) - this.mouse_chan = make(chan draw.Mouse, 1024) - this.quit_chan = make(chan bool) - - return this, nil - -} - -func (this *Context) Screen() draw.Image { return this.screen } - -func (this *Context) FlushImage() { - - this.screen.Unlock() - this.screen.Flip() - this.screen.Lock() - - e := &Event{} - - for e.Poll() { - switch e.Type { - case QUIT: - this.quit_chan <- true - break - case KEYDOWN: - this.key_chan <- int(e.Keyboard().Keysym.Sym) - break - case MOUSEBUTTONDOWN, MOUSEBUTTONUP: - m := e.MouseButton() - this.mouse_chan <- draw.Mouse{int(GetMouseState(nil, nil)), draw.Point{int(m.X), int(m.Y)}, time.Nanoseconds()} - break - case MOUSEMOTION: - m := e.MouseMotion() - this.mouse_chan <- draw.Mouse{int(GetMouseState(nil, nil)), draw.Point{int(m.X), int(m.Y)}, time.Nanoseconds()} - break - case VIDEORESIZE: - this.resize_chan <- true - break - default: - break - } - } - -} - -func (this *Context) KeyboardChan() <-chan int { - //TODO conversion - return this.key_chan -} - - -func (this *Context) MouseChan() <-chan draw.Mouse { - return this.mouse_chan -} - -func (this *Context) ResizeChan() <-chan bool { return this.resize_chan } -func (this *Context) QuitChan() <-chan bool { return this.quit_chan } - -// surface --> Image - -func (surface *Surface) ColorModel() image.ColorModel { - //TODO - return nil -} - -func (surface *Surface) Width() int { return int(surface.W) } - -func (surface *Surface) Height() int { return int(surface.H) } - -func (surface *Surface) Set(x, y int, c image.Color) { - //TODO endianess, bpp, alpha, etc - - var bpp = int(surface.Format.BytesPerPixel) - - var pixel = uintptr(unsafe.Pointer(surface.Pixels)) - - pixel += uintptr(y*int(surface.Pitch) + x*bpp) - - var p = (*image.RGBAColor)(unsafe.Pointer(pixel)) - - var r, g, b, a = c.RGBA() - - p.R = uint8(r) - p.G = uint8(g) - p.R = uint8(b) - p.A = uint8(255 - a) - -} - - -func (surface *Surface) At(x, y int) image.Color { - - var bpp = int(surface.Format.BytesPerPixel) - - var pixel = uintptr(unsafe.Pointer(surface.Pixels)) - - pixel += uintptr(y*int(surface.Pitch) + x*bpp) - - var color = *((*uint32)(unsafe.Pointer(pixel))) - - var r uint8 - var g uint8 - var b uint8 - var a uint8 - - GetRGBA(color, surface.Format, &r, &g, &b, &a) - - return image.RGBAColor{uint8(r), uint8(g), uint8(b), uint8(a)} - -} diff --git a/sdl/structs.8.go b/sdl/structs_386.go similarity index 84% rename from sdl/structs.8.go rename to sdl/structs_386.go index b62ec39..647e810 100644 --- a/sdl/structs.8.go +++ b/sdl/structs_386.go @@ -1,23 +1,5 @@ package sdl -type Surface struct { - Flags uint32 - Format *PixelFormat - W int32 - H int32 - Pitch uint16 - Pad0 [2]byte - Pixels *byte - Offset int32 - Hwdata *[0]byte /* sprivate_hwdata */ - Clip_rect Rect - Unused1 uint32 - Locked uint32 - Map *[0]byte /* sSDL_BlitMap */ - Format_version uint32 - Refcount int32 -} - type PixelFormat struct { Palette *Palette BitsPerPixel uint8 diff --git a/sdl/structs.6.go b/sdl/structs_amd64.go similarity index 80% rename from sdl/structs.6.go rename to sdl/structs_amd64.go index 79b7cd8..0c7a48a 100644 --- a/sdl/structs.6.go +++ b/sdl/structs_amd64.go @@ -1,25 +1,5 @@ package sdl -type Surface struct { - Flags uint32 - Pad0 [4]byte - Format *PixelFormat - W int32 - H int32 - Pitch uint16 - Pad1 [6]byte - Pixels *byte - Offset int32 - Pad2 [4]byte - Hwdata *[0]byte /* sprivate_hwdata */ - Clip_rect Rect - Unused1 uint32 - Locked uint32 - Map *[0]byte /* sSDL_BlitMap */ - Format_version uint32 - Refcount int32 -} - type PixelFormat struct { Palette *Palette BitsPerPixel uint8 @@ -184,9 +164,9 @@ type Event struct { } type Keysym struct { - Sym uint32 - Mod uint32 - Unicode uint16 - - Scancode uint8 // broken + Scancode uint8 + Pad0 [3]byte + Sym uint32 + Mod uint32 + Unicode uint16 } diff --git a/sdl/structs_arm.go b/sdl/structs_arm.go new file mode 100644 index 0000000..647e810 --- /dev/null +++ b/sdl/structs_arm.go @@ -0,0 +1,171 @@ +package sdl + +type PixelFormat struct { + Palette *Palette + BitsPerPixel uint8 + BytesPerPixel uint8 + Rloss uint8 + Gloss uint8 + Bloss uint8 + Aloss uint8 + Rshift uint8 + Gshift uint8 + Bshift uint8 + Ashift uint8 + Pad0 [2]byte + Rmask uint32 + Gmask uint32 + Bmask uint32 + Amask uint32 + Colorkey uint32 + Alpha uint8 + Pad1 [3]byte +} + +type Rect struct { + X int16 + Y int16 + W uint16 + H uint16 +} + +type Color struct { + R uint8 + G uint8 + B uint8 + Unused uint8 +} + +type Palette struct { + Ncolors int32 + Colors *Color +} + +type internalVideoInfo struct { + Flags uint32 + Video_mem uint32 + Vfmt *PixelFormat + Current_w int32 + Current_h int32 +} + +type Overlay struct { + Format uint32 + W int32 + H int32 + Planes int32 + Pitches *uint16 + Pixels **uint8 + Hwfuncs *[0]byte /* sprivate_yuvhwfuncs */ + Hwdata *[0]byte /* sprivate_yuvhwdata */ + Pad0 [4]byte +} + +type ActiveEvent struct { + Type uint8 + Gain uint8 + State uint8 +} + +type KeyboardEvent struct { + Type uint8 + Which uint8 + State uint8 + Pad0 [1]byte + Keysym Keysym +} + +type MouseMotionEvent struct { + Type uint8 + Which uint8 + State uint8 + Pad0 [1]byte + X uint16 + Y uint16 + Xrel int16 + Yrel int16 +} + +type MouseButtonEvent struct { + Type uint8 + Which uint8 + Button uint8 + State uint8 + X uint16 + Y uint16 +} + +type JoyAxisEvent struct { + Type uint8 + Which uint8 + Axis uint8 + Pad0 [1]byte + Value int16 +} + +type JoyBallEvent struct { + Type uint8 + Which uint8 + Ball uint8 + Pad0 [1]byte + Xrel int16 + Yrel int16 +} + +type JoyHatEvent struct { + Type uint8 + Which uint8 + Hat uint8 + Value uint8 +} + +type JoyButtonEvent struct { + Type uint8 + Which uint8 + Button uint8 + State uint8 +} + +type ResizeEvent struct { + Type uint8 + Pad0 [3]byte + W int32 + H int32 +} + +type ExposeEvent struct { + Type uint8 +} + +type QuitEvent struct { + Type uint8 +} + +type UserEvent struct { + Type uint8 + Pad0 [3]byte + Code int32 + Data1 *byte + Data2 *byte +} + +type SysWMmsg struct{} + +type SysWMEvent struct { + Type uint8 + Pad0 [3]byte + Msg *SysWMmsg +} + +type Event struct { + Type uint8 + Pad0 [19]byte +} + +type Keysym struct { + Scancode uint8 + Pad0 [3]byte + Sym uint32 + Mod uint32 + Unicode uint16 +} diff --git a/sound/constants.go b/sound/constants.go new file mode 100644 index 0000000..b2a3335 --- /dev/null +++ b/sound/constants.go @@ -0,0 +1,9 @@ +package sound + +const ( + SAMPLEFLAG_NONE = 0 + SAMPLEFLAG_CANSEEK = 1 + SAMPLEFLAG_EOF = 1 << 29 + SAMPLEFLAG_ERROR = 1 << 30 // unrecoverable error + SAMPLEFLAG_EAGAIN = 1 << 31 // function would block or temp error +) diff --git a/sound/sound.go b/sound/sound.go new file mode 100644 index 0000000..00a6569 --- /dev/null +++ b/sound/sound.go @@ -0,0 +1,180 @@ +/* +A binding of SDL_sound. + +Currently NewSampleFromFile() is the only way to create a Sample struct, the +core focus of the API. From there you can call Sample.Decode()/DecodeAll() +followed by Sample.Buffer_int16() to get at the decoded sound samples. + +Signed 16-bit ints is the only supported audio format at the moment. +*/ +package sound + +// #cgo pkg-config: sdl +// #include +// #cgo LDFLAGS: -lSDL_sound +import "C" +import "unsafe" + +import "errors" +import "sync" +import "fmt" +import "github.com/neagix/Go-SDL/sdl/audio" + +type AudioInfo struct { + Format uint16 + Channels uint8 + Rate uint32 +} + +type DecoderInfo struct { + Extensions []string // a list of valid extensions + Description string // human readable description + Author string + Url string +} + +type Sample struct { + sync.Mutex + csample *C.Sound_Sample + Decoder *DecoderInfo + Desired *AudioInfo + Actual *AudioInfo + + nbytes uint32 // number of bytes read in last Decode() +} + +func GetError() error { + errstr := C.GoString(C.Sound_GetError()) + C.Sound_ClearError() + return errors.New(errstr) +} + +func Init() int { + i, _ := C.Sound_Init() + return int(i) +} + +func Quit() int { + i, _ := C.Sound_Quit() + return int(i) +} + +func fromCDecoderInfo(cinfo *C.Sound_DecoderInfo) *DecoderInfo { + if cinfo == nil { + return nil + } + info := DecoderInfo{} + extptr := uintptr(unsafe.Pointer((*cinfo).extensions)) + for { + ext := (**C.char)(unsafe.Pointer(extptr)) + if *ext == nil { + break + } + info.Extensions = append(info.Extensions, C.GoString(*ext)) + extptr += unsafe.Sizeof(extptr) + } + info.Description = C.GoString((*cinfo).description) + info.Author = C.GoString((*cinfo).author) + info.Url = C.GoString((*cinfo).url) + return &info +} + +func AvailableDecoders() []DecoderInfo { + cinfo, _ := C.Sound_AvailableDecoders() + infos := make([]DecoderInfo, 0, 16) + infptr := uintptr(unsafe.Pointer(cinfo)) + for { + cinfo = (**C.Sound_DecoderInfo)(unsafe.Pointer(infptr)) + if *cinfo == nil { + break + } + infos = append(infos, *fromCDecoderInfo(*cinfo)) + infptr += unsafe.Sizeof(infptr) + } + return infos +} + +func fromCAudioInfo(cinfo *C.Sound_AudioInfo) *AudioInfo { + if cinfo == nil { + return nil + } + return &AudioInfo{uint16(cinfo.format), uint8(cinfo.channels), uint32(cinfo.rate)} +} + +func cAudioInfo(info *AudioInfo) *C.Sound_AudioInfo { + if info == nil { + return nil + } + cinfo := new(C.Sound_AudioInfo) + cinfo.format = C.Uint16(info.Format) + cinfo.channels = C.Uint8(info.Channels) + cinfo.rate = C.Uint32(info.Rate) + return cinfo +} + +func NewSampleFromFile(filename string, desired *AudioInfo, size uint) (*Sample, error) { + sample := new(Sample) + cfile := C.CString(filename) + defer C.free(unsafe.Pointer(cfile)) + sample.csample = C.Sound_NewSampleFromFile(cfile, cAudioInfo(desired), C.Uint32(size)) + if sample.csample == nil { + return nil, GetError() + } + sample.Decoder = fromCDecoderInfo(sample.csample.decoder) + sample.Desired = fromCAudioInfo(&sample.csample.desired) + sample.Actual = fromCAudioInfo(&sample.csample.actual) + return sample, nil +} + +func FreeSample(sample Sample) { + C.Sound_FreeSample(sample.csample) +} + +func (sample *Sample) SetBufferSize(size uint32) int { + ret := C.Sound_SetBufferSize(sample.csample, C.Uint32(size)) + return int(ret) +} + +/* Decodes as many samples as will fit in the buffer. + * Returns the number of BYTES read (zero at EOF). */ +func (sample *Sample) Decode() uint32 { + sample.Lock() + defer sample.Unlock() + sample.nbytes = uint32(C.Sound_Decode(sample.csample)) + return sample.nbytes +} + +func (sample *Sample) DecodeAll() uint32 { + sample.Lock() + defer sample.Unlock() + sample.nbytes = uint32(C.Sound_DecodeAll(sample.csample)) + return sample.nbytes +} + +func (sample *Sample) Rewind() int { + ret := C.Sound_Rewind(sample.csample) + return int(ret) +} + +func (sample *Sample) Seek(ms uint32) int { + ret := C.Sound_Seek(sample.csample, C.Uint32(ms)) + return int(ret) +} + +func (sample *Sample) Flags() uint { + return uint(sample.csample.flags) +} + +func (sample *Sample) Buffer_int16() []int16 { + sample.Lock() + defer sample.Unlock() + if sample.Desired.Format != audio.AUDIO_S16SYS { + panic(fmt.Sprintf("wrong format requested %d", sample.Desired.Format)) + } + + buf := make([]int16, int(sample.nbytes)/2) + for i := 0; i < len(buf); i++ { + buf[i] = int16(*((*C.int16_t)(unsafe.Pointer((uintptr(sample.csample.buffer) + uintptr(i*2)))))) + } + return buf +} diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 4670fb3..0000000 --- a/test/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -TARG=test -GOFILES=test.go - -include $(GOROOT)/src/Make.cmd diff --git a/test/test.go b/test/test.go deleted file mode 100644 index 97e694e..0000000 --- a/test/test.go +++ /dev/null @@ -1,193 +0,0 @@ -package main - -import ( - "sdl" - "sdl/ttf" - "sdl/mixer" - "math" - "fmt" -) - -type Point struct { - x int - y int -} - -func (a Point) add(b Point) Point { return Point{a.x + b.x, a.y + b.y} } - -func (a Point) sub(b Point) Point { return Point{a.x - b.x, a.y - b.y} } - -func (a Point) length() float64 { return math.Sqrt(float64(a.x*a.x + a.y*a.y)) } - -func (a Point) mul(b float64) Point { - return Point{int(float64(a.x) * b), int(float64(a.y) * b)} -} - -func worm(in <-chan Point, out chan<- Point, draw chan<- Point) { - - t := Point{0, 0} - - for { - - p := (<-in).sub(t) - - if p.length() > 48 { - t = t.add(p.mul(0.1)) - } - - draw <- t - out <- t - } -} - -func main() { - - if sdl.Init(sdl.INIT_EVERYTHING) != 0 { - panic(sdl.GetError()) - } - - if ttf.Init() != 0 { - panic(sdl.GetError()) - } - - if mixer.OpenAudio(mixer.DEFAULT_FREQUENCY, mixer.DEFAULT_FORMAT, - mixer.DEFAULT_CHANNELS, 4096) != 0 { - panic(sdl.GetError()) - } - - var screen = sdl.SetVideoMode(640, 480, 32, sdl.RESIZABLE) - - if screen == nil { - panic(sdl.GetError()) - } - - var video_info = sdl.GetVideoInfo() - - println("HW_available = ", video_info.HW_available) - println("WM_available = ", video_info.WM_available) - println("Video_mem = ", video_info.Video_mem, "kb") - - sdl.EnableUNICODE(1) - - sdl.WM_SetCaption("Go-SDL SDL Test", "") - - image := sdl.Load("test.png") - - if image == nil { - panic(sdl.GetError()) - } - - sdl.WM_SetIcon(image, nil) - - running := true - - font := ttf.OpenFont("Fontin Sans.otf", 72) - - if font == nil { - panic(sdl.GetError()) - } - - font.SetStyle(ttf.STYLE_UNDERLINE) - white := sdl.Color{255, 255, 255, 0} - text := ttf.RenderText_Blended(font, "Test (with music)", white) - music := mixer.LoadMUS("test.ogg") - - if music == nil { - panic(sdl.GetError()) - } - - music.PlayMusic(-1) - - if sdl.GetKeyName(270) != "[+]" { - panic("GetKeyName broken") - } - - worm_in := make(chan Point) - draw := make(chan Point, 64) - - var out chan Point - var in chan Point - - out = worm_in - - in = out - out = make(chan Point) - go worm(in, out, draw) - - for running { - - e := &sdl.Event{} - - for e.Poll() { - switch e.Type { - case sdl.QUIT: - running = false - break - case sdl.KEYDOWN, sdl.KEYUP: - println("") - println(e.Keyboard().Keysym.Sym, ": ", sdl.GetKeyName(sdl.Key(e.Keyboard().Keysym.Sym))) - - if e.Keyboard().Keysym.Sym == 27 { - running = false - } - - fmt.Printf("%04x ", e.Type) - - for i := 0; i < len(e.Pad0); i++ { - fmt.Printf("%02x ", e.Pad0[i]) - } - println() - - k := e.Keyboard() - - fmt.Printf("Type: %02x Which: %02x State: %02x Pad: %02x\n", k.Type, k.Which, k.State, k.Pad0[0]) - fmt.Printf("Scancode: %02x Sym: %08x Mod: %04x Unicode: %04x\n", k.Keysym.Scancode, k.Keysym.Sym, k.Keysym.Mod, k.Keysym.Unicode) - case sdl.MOUSEBUTTONDOWN: - println("Click:", e.MouseButton().X, e.MouseButton().Y) - in = out - out = make(chan Point) - go worm(in, out, draw) - case sdl.VIDEORESIZE: - println("resize screen ", e.Resize().W, e.Resize().H) - - screen = sdl.SetVideoMode(int(e.Resize().W), int(e.Resize().H), 32, sdl.RESIZABLE) - - if screen == nil { - panic(sdl.GetError()) - } - default: - } - } - - screen.FillRect(nil, 0x302019) - screen.Blit(&sdl.Rect{0, 0, 0, 0}, text, nil) - - loop := true - - for loop { - - select { - case p := <-draw: - screen.Blit(&sdl.Rect{int16(p.x), int16(p.y), 0, 0}, image, nil) - case <-out: - default: - loop = false - } - - } - - var p Point - sdl.GetMouseState(&p.x, &p.y) - worm_in <- p - - screen.Flip() - sdl.Delay(25) - } - - image.Free() - music.Free() - font.Close() - - ttf.Quit() - sdl.Quit() -} diff --git a/ttf/Makefile b/ttf/Makefile deleted file mode 100644 index b9602a8..0000000 --- a/ttf/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -TARG=sdl/ttf - -GOFILES:=constants.go -CGOFILES:=ttf.go -CGO_LDFLAGS:=-lSDL_ttf - -CLEANFILES+=ttf - -include $(GOROOT)/src/Make.pkg diff --git a/ttf/constants.go b/ttf/constants.go index 157f774..9c0d01b 100644 --- a/ttf/constants.go +++ b/ttf/constants.go @@ -1,6 +1,5 @@ package ttf - const ( STYLE_NORMAL = 0 STYLE_BOLD = 0x1 diff --git a/ttf/ttf.go b/ttf/ttf.go index fd1232a..b6892b7 100644 --- a/ttf/ttf.go +++ b/ttf/ttf.go @@ -7,173 +7,336 @@ that work with loaded fonts are changed to have a more object-oriented feel. */ package ttf -// #include +// #cgo pkg-config: SDL_ttf +// #include import "C" -import "sdl" -import "unsafe" + +import ( + "github.com/neagix/Go-SDL/sdl" + "sync" + "unsafe" +) + +// The version of Go-SDL TTF bindings. +// The version descriptor changes into a new unique string +// after a semantically incompatible Go-SDL update. +// +// The returned value can be checked by users of this package +// to make sure they are using a version with the expected semantics. +// +// If Go adds some kind of support for package versioning, this function will go away. +func GoSdlVersion() string { + return "⚛SDL TTF bindings 1.0" +} + +func wrap(cSurface *C.SDL_Surface) *sdl.Surface { + var s *sdl.Surface + + if cSurface != nil { + var surface sdl.Surface + surface.SetCSurface(unsafe.Pointer(cSurface)) + s = &surface + } else { + s = nil + } + + return s +} // A ttf or otf font. type Font struct { cfont *C.TTF_Font + mutex sync.RWMutex } // Initializes SDL_ttf. -func Init() int { return int(C.TTF_Init()) } +func Init() int { + sdl.GlobalMutex.Lock() + status := int(C.TTF_Init()) + sdl.GlobalMutex.Unlock() + return status +} // Checks to see if SDL_ttf is initialized. Returns 1 if true, 0 if false. -func WasInit() int { return int(C.TTF_WasInit()) } +func WasInit() int { + sdl.GlobalMutex.Lock() + status := int(C.TTF_WasInit()) + sdl.GlobalMutex.Unlock() + return status +} // Shuts down SDL_ttf. -func Quit() { C.TTF_Quit() } +func Quit() { + sdl.GlobalMutex.Lock() + C.TTF_Quit() + sdl.GlobalMutex.Unlock() +} // Loads a font from a file at the specified point size. func OpenFont(file string, ptsize int) *Font { + sdl.GlobalMutex.Lock() + cfile := C.CString(file) cfont := C.TTF_OpenFont(cfile, C.int(ptsize)) C.free(unsafe.Pointer(cfile)) + sdl.GlobalMutex.Unlock() + if cfont == nil { return nil } - return &Font{cfont} + return &Font{cfont: cfont} } // Loads a font from a file containing multiple font faces at the specified // point size. func OpenFontIndex(file string, ptsize, index int) *Font { + sdl.GlobalMutex.Lock() + cfile := C.CString(file) cfont := C.TTF_OpenFontIndex(cfile, C.int(ptsize), C.long(index)) C.free(unsafe.Pointer(cfile)) + sdl.GlobalMutex.Unlock() + if cfont == nil { return nil } - return &Font{cfont} + return &Font{cfont: cfont} } // Frees the pointer to the font. -func (f *Font) Close() { C.TTF_CloseFont(f.cfont) } +func (f *Font) Close() { + sdl.GlobalMutex.Lock() + f.mutex.Lock() + + C.TTF_CloseFont(f.cfont) + + f.mutex.Unlock() + sdl.GlobalMutex.Unlock() +} // Renders Latin-1 text in the specified color and returns an SDL surface. Solid // rendering is quick, although not as smooth as the other rendering types. func RenderText_Solid(font *Font, text string, color sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} surface := C.TTF_RenderText_Solid(font.cfont, ctext, ccol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Renders UTF-8 text in the specified color and returns an SDL surface. Solid // rendering is quick, although not as smooth as the other rendering types. func RenderUTF8_Solid(font *Font, text string, color sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} surface := C.TTF_RenderUTF8_Solid(font.cfont, ctext, ccol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Renders Latin-1 text in the specified color (and with the specified background // color) and returns an SDL surface. Shaded rendering is slower than solid // rendering and the text is in a solid box, but it's better looking. func RenderText_Shaded(font *Font, text string, color, bgcolor sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} cbgcol := C.SDL_Color{C.Uint8(bgcolor.R), C.Uint8(bgcolor.G), C.Uint8(bgcolor.B), C.Uint8(bgcolor.Unused)} surface := C.TTF_RenderText_Shaded(font.cfont, ctext, ccol, cbgcol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Renders UTF-8 text in the specified color (and with the specified background // color) and returns an SDL surface. Shaded rendering is slower than solid // rendering and the text is in a solid box, but it's better looking. func RenderUTF8_Shaded(font *Font, text string, color, bgcolor sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} cbgcol := C.SDL_Color{C.Uint8(bgcolor.R), C.Uint8(bgcolor.G), C.Uint8(bgcolor.B), C.Uint8(bgcolor.Unused)} surface := C.TTF_RenderUTF8_Shaded(font.cfont, ctext, ccol, cbgcol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Renders Latin-1 text in the specified color and returns an SDL surface. // Blended rendering is the slowest of the three methods, although it produces // the best results, especially when blitted over another image. func RenderText_Blended(font *Font, text string, color sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} surface := C.TTF_RenderText_Blended(font.cfont, ctext, ccol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Renders UTF-8 text in the specified color and returns an SDL surface. // Blended rendering is the slowest of the three methods, although it produces // the best results, especially when blitted over another image. func RenderUTF8_Blended(font *Font, text string, color sdl.Color) *sdl.Surface { + sdl.GlobalMutex.Lock() // Because 'C.TTF_Render*' uses 'C.SDL_CreateRGBSurface' + font.mutex.Lock() // Use a write lock, because 'C.TTF_Render*' may update font's internal caches + ctext := C.CString(text) ccol := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.Unused)} surface := C.TTF_RenderUTF8_Blended(font.cfont, ctext, ccol) C.free(unsafe.Pointer(ctext)) - return (*sdl.Surface)(unsafe.Pointer(surface)) + + font.mutex.Unlock() + sdl.GlobalMutex.Unlock() + + return wrap(surface) } // Returns the rendering style of the font. -func (f *Font) GetStyle() int { return int(C.TTF_GetFontStyle(f.cfont)) } +func (f *Font) GetStyle() int { + f.mutex.RLock() + result := int(C.TTF_GetFontStyle(f.cfont)) + f.mutex.RUnlock() + return result +} // Sets the rendering style of the font. -func (f *Font) SetStyle(style int) { C.TTF_SetFontStyle(f.cfont, C.int(style)) } +func (f *Font) SetStyle(style int) { + sdl.GlobalMutex.Lock() + f.mutex.Lock() + + C.TTF_SetFontStyle(f.cfont, C.int(style)) + + f.mutex.Unlock() + sdl.GlobalMutex.Unlock() +} // Returns the maximum height of all the glyphs of the font. -func (f *Font) Height() int { return int(C.TTF_FontHeight(f.cfont)) } +func (f *Font) Height() int { + f.mutex.RLock() + result := int(C.TTF_FontHeight(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns the maximum pixel ascent (from the baseline) of all the glyphs // of the font. -func (f *Font) Ascent() int { return int(C.TTF_FontAscent(f.cfont)) } +func (f *Font) Ascent() int { + f.mutex.RLock() + result := int(C.TTF_FontAscent(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns the maximum pixel descent (from the baseline) of all the glyphs // of the font. -func (f *Font) Descent() int { return int(C.TTF_FontDescent(f.cfont)) } +func (f *Font) Descent() int { + f.mutex.RLock() + result := int(C.TTF_FontDescent(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns the recommended pixel height of a rendered line of text. -func (f *Font) LineSkip() int { return int(C.TTF_FontLineSkip(f.cfont)) } +func (f *Font) LineSkip() int { + f.mutex.RLock() + result := int(C.TTF_FontLineSkip(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns the number of available faces (sub-fonts) in the font. -func (f *Font) Faces() int { return int(C.TTF_FontFaces(f.cfont)) } +func (f *Font) Faces() int { + f.mutex.RLock() + result := int(C.TTF_FontFaces(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns >0 if the font's currently selected face is fixed width // (i.e. monospace), 0 if not. -func (f *Font) IsFixedWidth() int { return int(C.TTF_FontFaceIsFixedWidth(f.cfont)) } +func (f *Font) IsFixedWidth() int { + f.mutex.RLock() + result := int(C.TTF_FontFaceIsFixedWidth(f.cfont)) + f.mutex.RUnlock() + return result +} // Returns the family name of the font's currently selected face, // or a blank string if unavailable. func (f *Font) FamilyName() string { - if f == nil { - return "nil" + var s string + if f != nil { + f.mutex.RLock() + + p := C.TTF_FontFaceFamilyName(f.cfont) + if p != nil { + s = C.GoString(p) + } else { + s = "" + } + + f.mutex.RUnlock() + } else { + s = "nil" } - p := C.TTF_FontFaceFamilyName(f.cfont) - if p == nil { - return "" - } - s := C.GoString(p) + return s } // Returns the style name of the font's currently selected face, // or a blank string if unavailable. func (f *Font) StyleName() string { - if f == nil { - return "nil" + var s string + if f != nil { + f.mutex.RLock() + + p := C.TTF_FontFaceStyleName(f.cfont) + if p != nil { + s = C.GoString(p) + } else { + s = "" + } + + f.mutex.RUnlock() + } else { + s = "nil" } - p := C.TTF_FontFaceStyleName(f.cfont) - if p == nil { - return "" - } - s := C.GoString(p) + return s } @@ -188,12 +351,19 @@ func (f *Font) StyleName() string { // For more information on glyph metrics, visit // http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html func (f *Font) GlyphMetrics(ch uint16) (int, int, int, int, int, int) { + sdl.GlobalMutex.Lock() // Because the underlying C code is fairly complex + f.mutex.Lock() // Use a write lock, because 'C.TTF_GlyphMetrics' may update font's internal caches + minx := C.int(0) maxx := C.int(0) miny := C.int(0) maxy := C.int(0) advance := C.int(0) err := C.TTF_GlyphMetrics(f.cfont, C.Uint16(ch), &minx, &maxx, &miny, &maxy, &advance) + + sdl.GlobalMutex.Unlock() + f.mutex.Unlock() + return int(minx), int(maxx), int(miny), int(maxy), int(advance), int(err) } @@ -201,10 +371,17 @@ func (f *Font) GlyphMetrics(ch uint16) (int, int, int, int, int, int) { // // Return values are (width, height, err) where err is 0 for success, -1 on any error. func (f *Font) SizeText(text string) (int, int, int) { + sdl.GlobalMutex.Lock() // Because the underlying C code is fairly complex + f.mutex.Lock() // Use a write lock, because 'C.TTF_Size*' may update font's internal cache + w := C.int(0) h := C.int(0) s := C.CString(text) err := C.TTF_SizeText(f.cfont, s, &w, &h) + + sdl.GlobalMutex.Unlock() + f.mutex.Unlock() + return int(w), int(h), int(err) } @@ -212,9 +389,16 @@ func (f *Font) SizeText(text string) (int, int, int) { // // Return values are (width, height, err) where err is 0 for success, -1 on any error. func (f *Font) SizeUTF8(text string) (int, int, int) { + sdl.GlobalMutex.Lock() // Because the underlying C code is fairly complex + f.mutex.Lock() // Use a write lock, because 'C.TTF_Size*' may update font's internal caches + w := C.int(0) h := C.int(0) s := C.CString(text) err := C.TTF_SizeUTF8(f.cfont, s, &w, &h) + + sdl.GlobalMutex.Unlock() + f.mutex.Unlock() + return int(w), int(h), int(err) }