All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.joaocosta.minart.backend.SdlAsyncLoopRunner.scala Maven / Gradle / Ivy

The newest version!
package eu.joaocosta.minart.backend

import scala.annotation.tailrec
import scala.concurrent.*
import scala.scalanative.concurrent.NativeExecutionContext.Implicits.queue
import scala.scalanative.libc.stdlib.*
import scala.scalanative.unsafe.{blocking as _, *}
import scala.scalanative.unsigned.*

import sdl2.all.*
import sdl2.enumerations.SDL_EventType.*

import eu.joaocosta.minart.runtime.*

/** Loop Runner for the native backend, backed by SDL.
  *
  *  All Futures run on Scala Native's QueueExecutionContext, as SDL requires events to be pumped in the main thread.
  *  See: https://wiki.libsdl.org/SDL2/SDL_PumpEvents
  */
object SdlAsyncLoopRunner extends LoopRunner[Future] {
  def finiteLoop[S](
      initialState: S,
      operation: S => S,
      terminateWhen: S => Boolean,
      cleanup: () => Unit,
      frequency: LoopFrequency
  ): Future[S] = {
    val event: Ptr[SDL_Event] = malloc(sizeof[SDL_Event]).asInstanceOf[Ptr[SDL_Event]]
    val fullCleanup = () => {
      cleanup()
      free(event.asInstanceOf[Ptr[Byte]])
      SDL_Quit()
    }
    val fullTerminateWhen = (state: S) => {
      SDL_PumpEvents()
      val quit = SDL_PeepEvents(event, 1, SDL_eventaction.SDL_GETEVENT, SDL_QUIT.uint, SDL_QUIT.uint) >= 1
      quit || terminateWhen(state)
    }
    frequency match {
      case LoopFrequency.Never =>
        new NeverLoop(operation, event, fullCleanup).run(initialState)
      case LoopFrequency.Uncapped =>
        new UncappedLoop(operation, fullTerminateWhen, fullCleanup).run(initialState)
      case freq @ LoopFrequency.LoopDuration(_) =>
        new CappedLoop(operation, fullTerminateWhen, freq.millis, fullCleanup).run(initialState)
    }
  }

  final class NeverLoop[S](operation: S => S, event: Ptr[SDL_Event], cleanup: () => Unit) {
    def checkQuit() =
      SDL_WaitEvent(event) == 1 && SDL_EventType.define((!event).`type`) == SDL_QUIT

    @tailrec
    private def finiteLoopAux(): Unit = {
      val quit = checkQuit()
      Thread.`yield`()
      if (quit) ()
      else finiteLoopAux()
    }

    def run(initialState: S): Future[S] = {
      Future(operation(initialState))
        .map { res =>
          finiteLoopAux()
          res
        }
        .map { res =>
          cleanup()
          res
        }
    }
  }

  final class UncappedLoop[S](
      operation: S => S,
      terminateWhen: S => Boolean,
      cleanup: () => Unit
  ) {
    @tailrec
    def finiteLoopAux(state: S): S = {
      val newState = operation(state)
      Thread.`yield`()
      if (!terminateWhen(newState)) finiteLoopAux(newState)
      else newState
    }
    def run(initialState: S): Future[S] = Future {
      val res = finiteLoopAux(initialState)
      cleanup()
      res
    }
  }

  final class CappedLoop[S](
      operation: S => S,
      terminateWhen: S => Boolean,
      iterationMillis: Long,
      cleanup: () => Unit
  ) {
    @tailrec
    def finiteLoopAux(state: S): S = {
      val startTime = SDL_GetTicks().toLong
      val newState  = operation(state)
      Thread.`yield`()
      if (!terminateWhen(newState)) {
        val endTime  = SDL_GetTicks().toLong
        val waitTime = iterationMillis - (endTime - startTime)
        if (waitTime > 0) blocking { SDL_Delay(waitTime.toUInt) }
        finiteLoopAux(newState)
      } else newState
    }

    def run(initialState: S): Future[S] =
      Future {
        val res = finiteLoopAux(initialState)
        cleanup()
        res
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy