Restore the commits mistakenly deleted

The develop branch history was forcefully overwritten when merging in
the sound card support. This commit restores the commits that were
deleted due to this.

See also !31.

Closes #64.
This commit is contained in:
Fingercomp 2023-04-06 22:14:22 +07:00
commit dc6a8d00a2
No known key found for this signature in database
GPG Key ID: BBC71CEE45D86E37
6 changed files with 62 additions and 44 deletions

View File

@ -24,6 +24,7 @@ import java.util.concurrent.locks.{Lock, ReentrantLock}
import javax.swing.JFileChooser
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Success, Try}
@ -36,9 +37,9 @@ object OcelotDesktop extends Logging {
val ticker = new Ticker
private val TickerIntervalHistorySize = 5
val tickerIntervalHistory = new mutable.Queue[Long](TickerIntervalHistorySize)
val tickerIntervalHistory = new mutable.Queue[Duration](TickerIntervalHistorySize)
def pushToTickerIntervalHistory(interval: Long): Unit = {
def pushToTickerIntervalHistory(interval: Duration): Unit = {
if (tickerIntervalHistory.size >= TickerIntervalHistorySize) tickerIntervalHistory.dequeue()
tickerIntervalHistory.enqueue(interval)
}

View File

@ -19,4 +19,8 @@ class RelayNode(val relay: Relay) extends Node(relay) {
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
override protected val exposeAddress = false
override def shouldReceiveEventsFor(address: String): Boolean = {
ports.exists(port => getNodeByPort(port).address == address)
}
}

View File

@ -24,6 +24,7 @@ import java.nio.channels.Channels
import java.nio.file.{Files, Paths}
import javax.imageio.ImageIO
import scala.collection.mutable
import scala.concurrent.duration.DurationInt
object UiHandler extends Logging {
var root: RootWidget = _
@ -34,7 +35,7 @@ object UiHandler extends Logging {
private val fpsCalculator = new FPSCalculator
private val ticker = new Ticker
ticker.tickInterval = 1000f / 60f
ticker.tickInterval = 1.second / 60
def getHierarchy: Array[Widget] = hierarchy.toArray

View File

@ -4,11 +4,14 @@ import ocelot.desktop.OcelotDesktop
import ocelot.desktop.audio.{SoundSource, SoundSources}
import ocelot.desktop.geometry.Padding2D
import ocelot.desktop.ui.layout.LinearLayout
import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs
import ocelot.desktop.ui.widget.modal.ModalDialog
import ocelot.desktop.util.Orientation
import scala.concurrent.duration.{Duration, DurationLong}
class ChangeSimulationSpeedDialog() extends ModalDialog {
private var tickInterval: Option[Long] = Some(OcelotDesktop.ticker.tickInterval)
private var tickInterval: Option[Duration] = Some(OcelotDesktop.ticker.tickInterval)
private def confirm(): Unit = {
if (tickInterval.isDefined) {
@ -27,53 +30,45 @@ class ChangeSimulationSpeedDialog() extends ModalDialog {
private var inputTPS: TextInput = _
private var inputMSPT: TextInput = _
private def formatMSPT(v: Long) = (v.toFloat / 1000).toString
private def formatTPS(v: Long) = (1000000 / v.toFloat).toString
private def formatMSPT(interval: Duration): String = (interval.toMicros / 1000f).toString
private def formatTPS(interval: Duration): String = (1_000_000f / interval.toMicros).toString
inputMSPT = new TextInput(formatMSPT(OcelotDesktop.ticker.tickInterval)) {
focus()
private def parseInput(text: String): Option[Duration] = try {
validateIntervalUs((text.toFloat * 1000).toLong)
} catch {
case _: NumberFormatException => None
}
override def onInput(text: String): Unit = {
tickInterval = try {
val v = (text.toFloat * 1000).toLong
inputTPS.setInput(formatTPS(v))
Some(v)
} catch {
case _: NumberFormatException => None
tickInterval = parseInput(text).map { interval =>
inputTPS.setInput(formatTPS(interval))
interval
}
}
override def validator(text: String): Boolean = {
try {
text.toFloat
true
} catch {
case _: NumberFormatException => false
}
}
override def validator(text: String): Boolean = parseInput(text).isDefined
override def onConfirm(): Unit = confirm()
}
inputTPS = new TextInput(formatTPS(OcelotDesktop.ticker.tickInterval)) {
private def parseInput(text: String): Option[Duration] = try {
validateIntervalUs((1_000_000 / text.toFloat).toLong)
} catch {
case _: NumberFormatException => None
}
override def onInput(text: String): Unit = {
tickInterval = try {
val v = ((1 / text.toFloat) * 1000000).toLong
inputMSPT.setInput(formatMSPT(v))
Some(v)
} catch {
case _: NumberFormatException => None
tickInterval = parseInput(text).map { interval =>
inputMSPT.setInput(formatMSPT(interval))
interval
}
}
override def validator(text: String): Boolean = {
try {
text.toFloat
true
} catch {
case _: NumberFormatException => false
}
}
override def validator(text: String): Boolean = parseInput(text).isDefined
override def onConfirm(): Unit = confirm()
}
@ -96,6 +91,7 @@ class ChangeSimulationSpeedDialog() extends ModalDialog {
override def onClick(): Unit = close()
}, Padding2D(right = 8))
// TODO: disable the button if tickInterval.isEmpty
children :+= new Button {
override def text: String = "Apply"
override def onClick(): Unit = confirm()
@ -103,3 +99,16 @@ class ChangeSimulationSpeedDialog() extends ModalDialog {
}
}, Padding2D.equal(16))
}
object ChangeSimulationSpeedDialog {
private val MaxUpdateInterval = 1.minute
private val MinUpdateInterval = 1.micro
private def validateIntervalUs(us: Long): Option[Duration] = try {
val interval = us.micros
Option.when(interval >= MinUpdateInterval && interval <= MaxUpdateInterval)(interval)
} catch {
case _: IllegalArgumentException => None
}
}

View File

@ -1,6 +1,5 @@
package ocelot.desktop.ui.widget.statusbar
import ocelot.desktop.audio.Audio
import ocelot.desktop.color.Color
import ocelot.desktop.geometry.{Padding2D, Size2D}
import ocelot.desktop.graphics.Graphics
@ -13,6 +12,8 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.{ColorScheme, OcelotDesktop}
import org.lwjgl.input.Keyboard
import scala.concurrent.duration.DurationInt
class StatusBar extends Widget {
override protected val layout: LinearLayout = new LinearLayout(this, contentAlignment = Alignment.Center) {
contentAlignment = Alignment.Center
@ -59,12 +60,12 @@ class StatusBar extends Widget {
new ChangeSimulationSpeedDialog().show()
}))
menu.addEntry(new ContextMenuEntry("Reset simulation speed", () => {
OcelotDesktop.ticker.tickInterval = 50000
OcelotDesktop.ticker.tickInterval = 50.millis
}))
if (OcelotDesktop.tickerIntervalHistory.nonEmpty)
menu.addSeparator()
for (elem <- OcelotDesktop.tickerIntervalHistory.reverseIterator) {
menu.addEntry(new ContextMenuEntry((1000000 / elem.toFloat).toString, () => {
menu.addEntry(new ContextMenuEntry((1_000_000f / elem.toMicros).toString, () => {
OcelotDesktop.ticker.tickInterval = elem
}))
}

View File

@ -1,24 +1,26 @@
package ocelot.desktop.util
import java.util.concurrent.TimeUnit
import scala.concurrent.duration.{Duration, DurationInt}
class Ticker extends Logging {
var lastTick: Long = System.nanoTime()
var tick: Long = 0
private[this] var _tickInterval: Long = _
private var _tickIntervalNs: Long = _
def tickInterval: Long = _tickInterval / 1000
def tickInterval: Duration = Duration.fromNanos(_tickIntervalNs)
def tickInterval_=(us: Float): Unit = {
logger.info(f"Setting tick interval to $us us (${1000000f / us} s^-1)")
_tickInterval = (us * 1000).toLong
def tickInterval_=(interval: Duration): Unit = {
val us = interval.toMicros
logger.info(f"Setting tick interval to $us μs (${1_000_000f / us} s^-1)")
_tickIntervalNs = interval.toNanos
}
tickInterval = 50000
tickInterval = 1.second / 20
def waitNext(): Unit = {
val deadline = lastTick + _tickInterval
val deadline = lastTick + _tickIntervalNs
var time = System.nanoTime()
while (time < deadline) {
val rem = deadline - time