func initSDLSubSystems(app *spectrum.Application) error { if sdl.Init(sdl.INIT_VIDEO|sdl.INIT_AUDIO|sdl.INIT_JOYSTICK) != 0 { return errors.New(sdl.GetError()) } if ttf.Init() != 0 { return errors.New(sdl.GetError()) } if sdl.NumJoysticks() > 0 { // Open joystick joystick = sdl.JoystickOpen(DEFAULT_JOYSTICK_ID) if joystick != nil { if app.Verbose { app.PrintfMsg("Opened Joystick %d", DEFAULT_JOYSTICK_ID) app.PrintfMsg("Name: %s", sdl.JoystickName(DEFAULT_JOYSTICK_ID)) app.PrintfMsg("Number of Axes: %d", joystick.NumAxes()) app.PrintfMsg("Number of Buttons: %d", joystick.NumButtons()) app.PrintfMsg("Number of Balls: %d", joystick.NumBalls()) } } else { return errors.New("Couldn't open Joystick!") } } sdl.WM_SetCaption("GoSpeccy - ZX Spectrum Emulator", "") sdl.EnableUNICODE(1) return nil }
func newSDLSurface(app *spectrum.Application, w, h int) *SDLSurface { surface := sdl.CreateRGBSurface(sdl.SWSURFACE, w, h, 32, 0, 0, 0, 0) if surface == nil { app.PrintfMsg("%s", sdl.GetError()) app.RequestExit() return nil } return &SDLSurface{surface} }
func playbackLoop(app *spectrum.Application, audio *SDLAudio) { for audioData := range audio.playback { audio.bufferRemove() audio.render(audioData) } if app.Verbose { app.PrintfMsg("audio playback loop: exit") } audio.playbackLoopFinished <- 0 }
func NewSDLScreen2x(app *spectrum.Application) *SDLScreen2x { SDL_screen := &SDLScreen2x{ screenChannel: make(chan *spectrum.DisplayData), screenSurface: NewSDLSurface2x(app), unscaledDisplay: newUnscaledDisplay(), updatedRectsCh: make(chan []sdl.Rect), app: app, } go screenRenderLoop(app.NewEventLoop(), SDL_screen.screenChannel, SDL_screen) return SDL_screen }
// The composer's command loop. // This function runs in a separate goroutine. func (composer *SDLSurfaceComposer) commandLoop(app *spectrum.Application) { evtLoop := app.NewEventLoop() shutdown.Add(1) for { select { case <-evtLoop.Pause: evtLoop.Pause <- 0 case <-evtLoop.Terminate: // Terminate this goroutine if app.Verbose { app.PrintfMsg("surface compositing loop: exit") } evtLoop.Terminate <- 0 shutdown.Done() return case untyped_cmd := <-composer.commandChannel: switch cmd := untyped_cmd.(type) { case cmd_add: composer.add(app, cmd.surface, cmd.x, cmd.y, cmd.updatedRectsCh) case cmd_remove: composer.remove(cmd.surface, cmd.done) case cmd_removeAll: composer.removeAll(cmd.done) case cmd_setPosition: composer.setPosition(cmd.surface, cmd.x, cmd.y) case cmd_replaceOutputSurface: composer.output_orNil = cmd.surface_orNil cmd.done <- 0 case cmd_showPaintedRegions: composer.showPaintedRegions = cmd.enable composer.repaintTheWholeOutputSurface() case cmd_update: composer.performCompositing(cmd.surface.x, cmd.surface.y, cmd.rects) } } } }
// Opens SDL audio. // If 'playbackFrequency' is 0, the frequency will be equivalent to PLAYBACK_FREQUENCY. func NewSDLAudio(app *spectrum.Application, playbackFrequency uint, hqAudio bool) (*SDLAudio, error) { if playbackFrequency == 0 { playbackFrequency = PLAYBACK_FREQUENCY } if playbackFrequency < MIN_PLAYBACK_FREQUENCY { return nil, errors.New(fmt.Sprintf("playback frequency of %d Hz is too low", playbackFrequency)) } // Open SDL audio var spec sdl_audio.AudioSpec { spec.Freq = int(playbackFrequency) spec.Format = sdl_audio.AUDIO_S16SYS spec.Channels = 1 spec.Samples = uint16(2048 * float32(playbackFrequency) / PLAYBACK_FREQUENCY) if sdl_audio.OpenAudio(&spec, &spec) != 0 { return nil, errors.New(sdl.GetError()) } if app.Verbose { app.PrintfMsg("%#v", spec) } } audio := &SDLAudio{ data: make(chan *spectrum.AudioData), playback: make(chan *spectrum.AudioData, 2*BUFSIZE_IDEAL), // Use a buffered Go channel playbackLoopFinished: make(chan byte), forwarderLoopFinished: nil, sdlAudioUnpaused: false, bufSize: 0, freq: uint(spec.Freq), virtualFreq: uint(spec.Freq), hqAudio: hqAudio, } go forwarderLoop(app.NewEventLoop(), audio) go playbackLoop(app, audio) return audio, nil }
func (composer *SDLSurfaceComposer) add(app *spectrum.Application, surface *sdl.Surface, x, y int, updatedRectsCh <-chan []sdl.Rect) { newInput := &input_surface_t{ surface: surface, updatedRectsCh: updatedRectsCh, forwarderLoop: app.NewEventLoop(), x: x, y: y, } composer.inputs = append(composer.inputs, newInput) updateRect := sdl.Rect{ X: int16(0), Y: int16(0), W: uint16(newInput.surface.W), H: uint16(newInput.surface.H), } composer.performCompositing(x, y, []sdl.Rect{updateRect}) go composer.forwarderLoop(newInput) }
func newAppSurface(app *spectrum.Application, scale2x, fullscreen bool) SDLSurfaceAccessor { var sdlMode int64 if fullscreen { scale2x = true sdlMode |= sdl.FULLSCREEN sdl.ShowCursor(sdl.DISABLE) } else { sdl.ShowCursor(sdl.ENABLE) sdlMode |= sdl.SWSURFACE } <-composer.ReplaceOutputSurface(nil) surface := sdl.SetVideoMode(int(width(scale2x, fullscreen)), int(height(scale2x, fullscreen)), 32, uint32(sdlMode)) if app.Verbose { app.PrintfMsg("video surface resolution: %dx%d", surface.W, surface.H) } <-composer.ReplaceOutputSurface(surface) return &wrapSurface{surface} }
func Main() { var init_waitGroup *sync.WaitGroup init_waitGroup = env.WaitName("init WaitGroup").(*sync.WaitGroup) init_waitGroup.Add(1) var app *spectrum.Application app = env.Wait(reflect.TypeOf(app)).(*spectrum.Application) var speccy *spectrum.Spectrum48k speccy = env.Wait(reflect.TypeOf(speccy)).(*spectrum.Spectrum48k) if !*enableSDL { return } uiSettings = &InitialSettings{ scale2x: Scale2x, fullscreen: Fullscreen, showPaintedRegions: ShowPaintedRegions, audio: Audio, audioFreq: AudioFreq, hqAudio: HQAudio, } composer = NewSDLSurfaceComposer(app) composer.ShowPaintedRegions(*ShowPaintedRegions) // SDL subsystems init if err := initSDLSubSystems(app); err != nil { app.PrintfMsg("%s", err) app.RequestExit() return } // Setup the display r = NewSDLRenderer(app, speccy, *Scale2x, *Fullscreen, *Audio, *HQAudio, *AudioFreq) setUI(r) initCLI() // Setup the audio if *Audio { audio, err := NewSDLAudio(app, *AudioFreq, *HQAudio) if err == nil { speccy.CommandChannel <- spectrum.Cmd_AddAudioReceiver{audio} } else { app.PrintfMsg("%s", err) } } // Start the SDL event loop go sdlEventLoop(app, speccy, *verboseInput) init_waitGroup.Done() hint := "Hint: Press F10 to invoke the built-in console.\n" hint += " Input an empty line in the console to display available commands.\n" fmt.Print(hint) // Wait for all event loops to terminate, and then call 'sdl.Quit()' shutdown.Wait() if r.app.Verbose { r.app.PrintfMsg("SDL: sdl.Quit()") } sdl.Quit() }
// A Go routine for processing SDL events. func sdlEventLoop(app *spectrum.Application, speccy *spectrum.Spectrum48k, verboseInput bool) { evtLoop := app.NewEventLoop() consoleIsVisible := false shutdown.Add(1) for { select { case <-evtLoop.Pause: evtLoop.Pause <- 0 case <-evtLoop.Terminate: // Terminate this Go routine if app.Verbose { app.PrintfMsg("SDL event loop: exit") } evtLoop.Terminate <- 0 shutdown.Done() return case event := <-sdl.Events: switch e := event.(type) { case sdl.QuitEvent: if app.Verbose { app.PrintfMsg("SDL quit -> request[exit the application]") } app.RequestExit() case sdl.JoyAxisEvent: if verboseInput { app.PrintfMsg("[Joystick] Axis: %d, Value: %d", e.Axis, e.Value) } if e.Axis == 0 { if e.Value > 0 { speccy.Joystick.KempstonDown(spectrum.KEMPSTON_RIGHT) } else if e.Value < 0 { speccy.Joystick.KempstonDown(spectrum.KEMPSTON_LEFT) } else { speccy.Joystick.KempstonUp(spectrum.KEMPSTON_RIGHT) speccy.Joystick.KempstonUp(spectrum.KEMPSTON_LEFT) } } else if e.Axis == 1 { if e.Value > 0 { speccy.Joystick.KempstonDown(spectrum.KEMPSTON_UP) } else if e.Value < 0 { speccy.Joystick.KempstonDown(spectrum.KEMPSTON_DOWN) } else { speccy.Joystick.KempstonUp(spectrum.KEMPSTON_UP) speccy.Joystick.KempstonUp(spectrum.KEMPSTON_DOWN) } } case sdl.JoyButtonEvent: if verboseInput { app.PrintfMsg("[Joystick] Button: %d, State: %d", e.Button, e.State) } if e.Button == 0 { if e.State > 0 { speccy.Joystick.KempstonDown(spectrum.KEMPSTON_FIRE) } else { speccy.Joystick.KempstonUp(spectrum.KEMPSTON_FIRE) } } case sdl.KeyboardEvent: keyName := sdl.GetKeyName(sdl.Key(e.Keysym.Sym)) if verboseInput { app.PrintfMsg("\n") app.PrintfMsg("%v: %v", e.Keysym.Sym, keyName) app.PrintfMsg("Type: %02x Which: %02x State: %02x\n", e.Type, e.Which, e.State) app.PrintfMsg("Scancode: %02x Sym: %08x Mod: %04x Unicode: %04x\n", e.Keysym.Scancode, e.Keysym.Sym, e.Keysym.Mod, e.Keysym.Unicode) } if (keyName == "escape") && (e.Type == sdl.KEYDOWN) { if app.Verbose { app.PrintfMsg("escape key -> request[exit the application]") } app.RequestExit() } else if (keyName == "f10") && (e.Type == sdl.KEYDOWN) { //if app.Verbose { // app.PrintfMsg("f10 key -> toggle console") //} if !r.toggling { r.toggling = true if r.cliSurface_orNil == nil { done := make(chan bool) r.cliSurfaceCh <- cmd_newCliSurface{newCLISurface(r.scale2x, r.fullscreen), done} <-done } anim := clingon.NewSliderAnimation(0.500, 1.0) var targetState int if consoleIsVisible { targetState = HIDE } else { targetState = SHOW } r.sliderCh <- cmd_newSlider{anim, targetState} anim.Start() consoleIsVisible = !consoleIsVisible } } else { if r.cliSurface_orNil != nil { cliSurface := r.cliSurface_orNil if (keyName == "page up") && (e.Type == sdl.KEYDOWN) { cliSurface.EventCh() <- clingon.Cmd_Scroll{clingon.SCROLL_UP} } else if (keyName == "page down") && (e.Type == sdl.KEYDOWN) { cliSurface.EventCh() <- clingon.Cmd_Scroll{clingon.SCROLL_DOWN} } else if (keyName == "up") && (e.Type == sdl.KEYDOWN) { cli.PutReadline(clingon.HISTORY_PREV) } else if (keyName == "down") && (e.Type == sdl.KEYDOWN) { cli.PutReadline(clingon.HISTORY_NEXT) } else if (keyName == "left") && (e.Type == sdl.KEYDOWN) { cli.PutReadline(clingon.CURSOR_LEFT) } else if (keyName == "right") && (e.Type == sdl.KEYDOWN) { cli.PutReadline(clingon.CURSOR_RIGHT) } else { unicode := e.Keysym.Unicode if unicode > 0 { cli.PutUnicode(unicode) } } } else { sequence, haveMapping := spectrum.SDL_KeyMap[keyName] if haveMapping { switch e.Type { case sdl.KEYDOWN: // Normal order for i := 0; i < len(sequence); i++ { speccy.Keyboard.KeyDown(sequence[i]) } case sdl.KEYUP: // Reverse order for i := len(sequence) - 1; i >= 0; i-- { speccy.Keyboard.KeyUp(sequence[i]) } } } } } } } } }