private static void GUI_OnTick(GUITickEventArgs eventArgs)
        {
            _keys = eventArgs.Keys;

            if (_renderFrameNextTick)
            {
                eventArgs.FrameBuffer  = _frameBuffer;
                eventArgs.ShouldRender = true;
                _renderFrameNextTick   = false;
            }

            if (_playSoundNextTick)
            {
                eventArgs.PlaySound = true;
                _playSoundNextTick  = false;
            }

            if (_emulator.Finished && !_keepOpenWhenDoneEmulating)
            {
                eventArgs.ShouldQuit = true;
            }

            if (_logPerformance)
            {
                _guiTickCounter++;

                if (_guiPerformanceWatch.ElapsedMilliseconds >= 1000)
                {
                    Console.WriteLine("GUI ticks per second: " + _guiTickCounter);
                    _guiTickCounter = 0;
                    _guiPerformanceWatch.Restart();
                }
            }

            if (_debug && eventArgs.KeyDown != null)
            {
                if (eventArgs.KeyDown == SDL2.SDL.SDL_Keycode.SDLK_SPACE)
                {
                    _shouldBreak = true;
                }
                else if (eventArgs.KeyDown == SDL2.SDL.SDL_Keycode.SDLK_F5)
                {
                    _shouldStep  = false;
                    _shouldBreak = false;
                }
                else if (eventArgs.KeyDown == SDL2.SDL.SDL_Keycode.SDLK_F10)
                {
                    _shouldStep  = true;
                    _shouldBreak = false;
                }
            }
        }
Exemple #2
0
        public void StartLoop()
        {
            // Used to keep track of the time elapsed in each loop iteration. This is used to
            // notify the OnTick handlers so they can update their simulation, as well as throttle
            // the update loop to 60hz if needed.
            var stopwatch = new Stopwatch();

            // Structure used to pass data to and from the OnTick handlers. We initialize it once
            // outside of the loop to avoid eating a ton of memory putting GC into a tailspin.
            var tickEventArgs = new GUITickEventArgs();

            tickEventArgs.Keys = GetEmptyKeyDictionary();

            // The SDL event polled for in each iteration of the loop.
            SDL.SDL_Event sdlEvent;

            while (true)
            {
                stopwatch.Restart();

                tickEventArgs.KeyDown = null;

                while (SDL.SDL_PollEvent(out sdlEvent) != 0)
                {
                    switch (sdlEvent.type)
                    {
                    // e.g. Command + Q
                    case SDL.SDL_EventType.SDL_QUIT:
                        // Break out of the SDL event loop, which will close the program.
                        return;

                    case SDL.SDL_EventType.SDL_KEYDOWN:
                        tickEventArgs.KeyDown = sdlEvent.key.keysym.sym;
                        UpdateKeys(tickEventArgs.Keys, sdlEvent.key.keysym.sym, true);
                        break;

                    case SDL.SDL_EventType.SDL_KEYUP:
                        UpdateKeys(tickEventArgs.Keys, sdlEvent.key.keysym.sym, false);
                        break;
                    }
                }

                // Update the event arguments that will be sent with the event handler.

                tickEventArgs.ShouldRender = false;
                tickEventArgs.PlaySound    = false;

                // Delegate out to the event handler so work can be done.
                if (OnTick != null)
                {
                    OnTick(tickEventArgs);
                }

                // We only want to re-render if the frame buffer has changed since last time because
                // the SDL_RenderPresent method is relatively expensive and massively slows down the
                // amount of opcodes (ticks) we can execute.
                if (tickEventArgs.ShouldRender)
                {
                    // Clear the screen.
                    SDL.SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 0);
                    SDL.SDL_RenderClear(_renderer);

                    // Render screen from the updated the frame buffer.

                    SDL.SDL_SetRenderDrawColor(_renderer, 255, 255, 255, 255);

                    var frameBuffer = tickEventArgs.FrameBuffer;

                    // TODO: Build out a 2D array of points so we can do a single call to SDL_RenderDrawPoints here.
                    if (frameBuffer != null)
                    {
                        for (var x = 0; x < frameBuffer.GetLength(0); x++)
                        {
                            for (var y = 0; y < frameBuffer.GetLength(1); y++)
                            {
                                if (frameBuffer[x, y] == 1)
                                {
                                    SDL.SDL_RenderDrawPoint(_renderer, x, y);
                                }
                            }
                        }
                    }

                    SDL.SDL_RenderPresent(_renderer);
                }

                // If the event handler indicated a should needs to be played, do it now.
                if (tickEventArgs.PlaySound)
                {
                    // TODO: Beep
                }

                // See if we need to delay to keep locked to ~ TARGET_FPS FPS.

                if (stopwatch.Elapsed.TotalMilliseconds < (1000 / _targetFPS))
                {
                    var delay = (1000 / _targetFPS) - stopwatch.Elapsed.TotalMilliseconds;
                    SDL.SDL_Delay((uint)delay);
                }

                // If the event handler indicated we should quit, then stop.
                if (tickEventArgs.ShouldQuit)
                {
                    return;
                }
            }
        }