diff --git a/README.md b/README.md index 5912fba..c315cb3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,11 @@ # 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. +This is an improved version of neagix's improved version of 0xe2-0x9a-0x9b's [Go-SDL](https://github.com/0xe2-0x9a-0x9b/Go-SDL). -The improvements/differences are: +The improvements over/differences to neagix's version 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. +* working event loop under Windows +* support for RWops # Installation @@ -20,8 +13,9 @@ 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 + go get -v github.com/asig/Go-SDL/sdl + go get -v github.com/asig/Go-SDL/ttf + go get -v github.com/asig/Go-SDL/sdl/audio # Credits diff --git a/mixer/mixer.go b/mixer/mixer.go index 9b71ba4..4190d76 100644 --- a/mixer/mixer.go +++ b/mixer/mixer.go @@ -12,11 +12,15 @@ package mixer import "C" import "unsafe" +import "github.com/asig/Go-SDL/sdl" + // A music file. type Music struct { cmusic *C.Mix_Music } +type cast unsafe.Pointer + // Initializes SDL_mixer. Return 0 if successful and -1 if there were // initialization errors. func OpenAudio(frequency int, format uint16, channels, chunksize int) int { @@ -40,6 +44,15 @@ func LoadMUS(file string) *Music { return &Music{cmusic} } +// Loads a music file to use. +func LoadMUS_RW(rwOps *sdl.RWops) *Music { + cmusic := C.Mix_LoadMUS_RW((*C.SDL_RWops)(cast(rwOps.CRWops))) + if cmusic == nil { + return nil + } + return &Music{cmusic} +} + // Frees the loaded music file. func (m *Music) Free() { C.Mix_FreeMusic(m.cmusic) } diff --git a/sdl/event.go b/sdl/event.go index a21e2ca..fcdc9e7 100644 --- a/sdl/event.go +++ b/sdl/event.go @@ -1,65 +1,46 @@ 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 +// Polls all SDL events that are currently available. +func PollEvents() []interface{} { + events := make([]interface{}, 0) event := &Event{} + for event.Poll() { + var cookedEvent interface{} + switch event.Type { + case QUIT: + cookedEvent = *(*QuitEvent)(cast(event)) - for { - for event.poll() { - switch event.Type { - case QUIT: - events <- *(*QuitEvent)(cast(event)) - - case KEYDOWN, KEYUP: - events <- *(*KeyboardEvent)(cast(event)) + case KEYDOWN, KEYUP: + cookedEvent = *(*KeyboardEvent)(cast(event)) - case MOUSEBUTTONDOWN, MOUSEBUTTONUP: - events <- *(*MouseButtonEvent)(cast(event)) + case MOUSEBUTTONDOWN, MOUSEBUTTONUP: + cookedEvent = *(*MouseButtonEvent)(cast(event)) - case MOUSEMOTION: - events <- *(*MouseMotionEvent)(cast(event)) + case MOUSEMOTION: + cookedEvent = *(*MouseMotionEvent)(cast(event)) - case JOYAXISMOTION: - events <- *(*JoyAxisEvent)(cast(event)) + case JOYAXISMOTION: + cookedEvent = *(*JoyAxisEvent)(cast(event)) - case JOYBUTTONDOWN, JOYBUTTONUP: - events <- *(*JoyButtonEvent)(cast(event)) + case JOYBUTTONDOWN, JOYBUTTONUP: + cookedEvent = *(*JoyButtonEvent)(cast(event)) - case JOYHATMOTION: - events <- *(*JoyHatEvent)(cast(event)) + case JOYHATMOTION: + cookedEvent = *(*JoyHatEvent)(cast(event)) - case JOYBALLMOTION: - events <- *(*JoyBallEvent)(cast(event)) + case JOYBALLMOTION: + cookedEvent = *(*JoyBallEvent)(cast(event)) - case ACTIVEEVENT: - events <- *(*ActiveEvent)(cast(event)) + case ACTIVEEVENT: + cookedEvent = *(*ActiveEvent)(cast(event)) - case VIDEORESIZE: - events <- *(*ResizeEvent)(cast(event)) - } + case VIDEORESIZE: + cookedEvent = *(*ResizeEvent)(cast(event)) } - - time.Sleep(poll_interval_ms * 1e6) + if cookedEvent != nil { + events = append(events, cookedEvent) + } + event = &Event{} } -} - -func init() { - go pollEvents() + return events } diff --git a/sdl/event_generic.go b/sdl/event_generic.go new file mode 100644 index 0000000..bca7d47 --- /dev/null +++ b/sdl/event_generic.go @@ -0,0 +1,38 @@ +// +build !windows + +// On Windows, events must be handled in the thread that created +// the window, which is most probably the thread that called sdl.Init(). +// Therefore. the Events channel is not provided on Windows, and you +// need to set up your own ticker and call sdl.PollEvents() from the +// main thread. + +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() { + for { + for _, event := range PollEvents() { + events <- event + } + time.Sleep(poll_interval_ms * 1e6) + } +} + +func init() { + go pollEvents() +} diff --git a/sdl/rwops.go b/sdl/rwops.go index f4c825e..b79fc7a 100644 --- a/sdl/rwops.go +++ b/sdl/rwops.go @@ -9,13 +9,13 @@ import ( import "C" type RWops struct { - cRWops *C.SDL_RWops + CRWops *C.SDL_RWops gcBytes []byte // Prevents garbage collection of memory passed to RWFromMem } func (s *RWops) destroy() { - s.cRWops = nil + s.CRWops = nil s.gcBytes = nil } @@ -26,7 +26,7 @@ func RWFromMem(buf []byte) *RWops { p := C.SDL_RWFromMem(unsafe.Pointer(&buf[0]), C.int(len(buf))) var rwops RWops - rwops.cRWops = (*C.SDL_RWops)(p) + rwops.CRWops = (*C.SDL_RWops)(p) rwops.gcBytes = buf return &rwops @@ -36,7 +36,7 @@ func (self *RWops) Free() { GlobalMutex.Lock() defer GlobalMutex.Unlock() - C.SDL_FreeRW(self.cRWops) - self.cRWops = nil + C.SDL_FreeRW(self.CRWops) + self.CRWops = nil self.gcBytes = nil } diff --git a/sdl/sdl.go b/sdl/sdl.go index aceeb47..26a6915 100644 --- a/sdl/sdl.go +++ b/sdl/sdl.go @@ -554,7 +554,7 @@ func Load(file string) *Surface { func Load_RW(rwOps *RWops) *Surface { GlobalMutex.Lock() - var screen = C.IMG_Load_RW((*C.SDL_RWops)(cast(rwOps.cRWops)), 0) + var screen = C.IMG_Load_RW((*C.SDL_RWops)(cast(rwOps.CRWops)), 0) GlobalMutex.Unlock() @@ -703,7 +703,7 @@ func GetKeyName(key Key) string { // ====== // Polls for currently pending events -func (event *Event) poll() bool { +func (event *Event) Poll() bool { GlobalMutex.Lock() var ret = C.SDL_PollEvent((*C.SDL_Event)(cast(event))) diff --git a/ttf/ttf.go b/ttf/ttf.go index b6892b7..1347306 100644 --- a/ttf/ttf.go +++ b/ttf/ttf.go @@ -12,11 +12,13 @@ package ttf import "C" import ( - "github.com/neagix/Go-SDL/sdl" + "github.com/asig/Go-SDL/sdl" "sync" "unsafe" ) +type cast unsafe.Pointer + // The version of Go-SDL TTF bindings. // The version descriptor changes into a new unique string // after a semantically incompatible Go-SDL update. @@ -43,6 +45,13 @@ func wrap(cSurface *C.SDL_Surface) *sdl.Surface { return s } +func wrapFont(cfont *C.TTF_Font) *Font { + if cfont == nil { + return nil + } + return &Font{cfont: cfont} +} + // A ttf or otf font. type Font struct { cfont *C.TTF_Font @@ -75,47 +84,53 @@ func Quit() { // Loads a font from a file at the specified point size. func OpenFont(file string, ptsize int) *Font { sdl.GlobalMutex.Lock() + defer sdl.GlobalMutex.Unlock() cfile := C.CString(file) cfont := C.TTF_OpenFont(cfile, C.int(ptsize)) C.free(unsafe.Pointer(cfile)) - sdl.GlobalMutex.Unlock() + return wrapFont(cfont) +} - if cfont == nil { - return nil - } +// Loads a font from an RWops at the specified point size. +func OpenFontRW(rwOps *sdl.RWops, ptsize int) *Font { + sdl.GlobalMutex.Lock() + defer sdl.GlobalMutex.Unlock() - return &Font{cfont: cfont} + return wrapFont(C.TTF_OpenFontRW((*C.SDL_RWops)(cast(rwOps.CRWops)), 0, C.int(ptsize))) } // 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() + defer sdl.GlobalMutex.Unlock() cfile := C.CString(file) cfont := C.TTF_OpenFontIndex(cfile, C.int(ptsize), C.long(index)) C.free(unsafe.Pointer(cfile)) - sdl.GlobalMutex.Unlock() + return wrapFont(cfont) +} - if cfont == nil { - return nil - } +// Loads a font from an RWops containing multiple font faces at the specified +// point size. +func OpenFontIndexRW(rwOps *sdl.RWops, ptsize, index int) *Font { + sdl.GlobalMutex.Lock() + defer sdl.GlobalMutex.Unlock() - return &Font{cfont: cfont} + return wrapFont(C.TTF_OpenFontIndexRW((*C.SDL_RWops)(cast(rwOps.CRWops)), 0, C.int(ptsize), C.long(index))) } // Frees the pointer to the font. func (f *Font) Close() { sdl.GlobalMutex.Lock() + defer sdl.GlobalMutex.Unlock() f.mutex.Lock() + defer f.mutex.Unlock() 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