From 35b78d42546aa718719f9f86ff5197635cc6d7ae Mon Sep 17 00:00:00 2001 From: soywiz Date: Thu, 18 Jul 2024 14:34:57 +0200 Subject: [PATCH 01/21] Prepare for korlibs 6.0.0-beta1 --- gradle/libs.versions.toml | 4 ++-- .../datastructure/_Datastructure_event.kt | 21 ++++++------------- .../datastructure/thread/ThreadAlias.kt | 6 ++++-- .../SyncEventLoopCoroutineDispatcher.kt | 2 +- korge-sandbox/src/samples/MainAudioScene.kt | 11 ++++++---- korge-sandbox/src/samples/MainPolyphonic.kt | 4 ++-- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c10ed52dc1..d890ee57b7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,8 +15,8 @@ kotlinx-coroutines = "1.9.0-RC" kotlinx-serialization = "1.7.0" kotlinx-atomicfu = "0.24.0" -korlibs = "6.0.0-alpha9" -#korlibs = "999.0.0.999" +#korlibs = "6.0.0-alpha9" +korlibs = "999.0.0.999" kotlinx-benchmark = "0.4.7" dokka = "1.9.10" diff --git a/korge-core/src/korlibs/datastructure/_Datastructure_event.kt b/korge-core/src/korlibs/datastructure/_Datastructure_event.kt index 8a022c3b87..1a8303de24 100644 --- a/korge-core/src/korlibs/datastructure/_Datastructure_event.kt +++ b/korge-core/src/korlibs/datastructure/_Datastructure_event.kt @@ -5,15 +5,9 @@ package korlibs.datastructure.event import korlibs.concurrent.lock.* import korlibs.concurrent.thread.* import korlibs.datastructure.* -import korlibs.datastructure.lock.* -import korlibs.datastructure.lock.Lock import korlibs.datastructure.pauseable.* -import korlibs.datastructure.thread.* -import korlibs.datastructure.thread.NativeThread -import korlibs.datastructure.thread.nativeThread import korlibs.logger.* import korlibs.time.* -import kotlinx.atomicfu.locks.* import kotlin.time.* expect fun createPlatformEventLoop(precise: Boolean = true): SyncEventLoop @@ -24,25 +18,22 @@ interface EventLoop : Pauseable, AutoCloseable { fun setImmediate(task: () -> Unit) fun setTimeout(time: Duration, task: () -> Unit): AutoCloseable fun setInterval(time: Duration, task: () -> Unit): AutoCloseable - fun setIntervalFrame(task: () -> Unit): AutoCloseable = setInterval(60.hz.timeSpan, task) + fun setIntervalFrame(task: () -> Unit): AutoCloseable = setInterval(60.hz.duration, task) } -fun EventLoop.setInterval(time: Frequency, task: () -> Unit): AutoCloseable = setInterval(time.timeSpan, task) +fun EventLoop.setInterval(time: Frequency, task: () -> Unit): AutoCloseable = setInterval(time.duration, task) abstract class BaseEventLoop : EventLoop, Pauseable { val runLock = Lock() } open class SyncEventLoop( - /** precise=true will have better precision at the cost of more CPU-usage (busy waiting) */ - //var precise: Boolean = true, - var precise: Boolean = false, /** Execute timers immediately instead of waiting. Useful for testing. */ var immediateRun: Boolean = false, ) : BaseEventLoop(), Pauseable { private val pauseable = SyncPauseable() override var paused: Boolean by pauseable::paused - private val lock = korlibs.concurrent.lock.Lock() + private val lock = Lock() private var running = true protected class TimedTask(val eventLoop: SyncEventLoop, var now: Duration, val time: Duration, var interval: Boolean, val callback: () -> Unit) : @@ -126,7 +117,7 @@ open class SyncEventLoop( protected fun wait(waitTime: Duration) { if (immediateRun) return - lock { lock.wait(waitTime, precise) } + lock { lock.wait(waitTime) } } fun runAvailableNextTasks(runTimers: Boolean = true): Int { @@ -232,12 +223,12 @@ open class SyncEventLoop( open fun start(): Unit { if (thread != null) return thread = nativeThread { - runTasksForever { thread?.threadSuggestRunning == true } + runTasksForever { running } } } open fun stop(): Unit { - thread?.threadSuggestRunning = false + running = false thread = null } } diff --git a/korge-core/src/korlibs/datastructure/thread/ThreadAlias.kt b/korge-core/src/korlibs/datastructure/thread/ThreadAlias.kt index ddb5b6613e..536715bcb9 100644 --- a/korge-core/src/korlibs/datastructure/thread/ThreadAlias.kt +++ b/korge-core/src/korlibs/datastructure/thread/ThreadAlias.kt @@ -1,5 +1,7 @@ package korlibs.datastructure.thread +import korlibs.concurrent.thread.* + @Deprecated("", ReplaceWith("korlibs.concurrent.thread.NativeThread")) typealias NativeThread = korlibs.concurrent.thread.NativeThread @@ -8,6 +10,6 @@ public fun nativeThread( start: Boolean = true, isDaemon: Boolean = false, name: String? = null, - priority: Int = -1, + priority: NativeThreadPriority = NativeThreadPriority.NORMAL, block: (NativeThread) -> Unit -): NativeThread = korlibs.concurrent.thread.nativeThread(start, isDaemon, name, priority, block) +): NativeThread = korlibs.concurrent.thread.nativeThread(isDaemon = isDaemon, name = name, priority = priority, start = start, block = block) diff --git a/korge-core/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt b/korge-core/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt index f8cf3593b6..7dd775318d 100644 --- a/korge-core/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt +++ b/korge-core/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt @@ -8,7 +8,7 @@ import kotlin.coroutines.* @OptIn(InternalCoroutinesApi::class) class SyncEventLoopCoroutineDispatcher(val eventLoop: SyncEventLoop) : CoroutineDispatcher(), Delay, AutoCloseable { - constructor(precise: Boolean = true, immediateRun: Boolean = false) : this(SyncEventLoop(precise, immediateRun)) + constructor(immediateRun: Boolean = false) : this(SyncEventLoop(immediateRun)) override fun close() { eventLoop.close() diff --git a/korge-sandbox/src/samples/MainAudioScene.kt b/korge-sandbox/src/samples/MainAudioScene.kt index 4164e613a2..4316e881e2 100644 --- a/korge-sandbox/src/samples/MainAudioScene.kt +++ b/korge-sandbox/src/samples/MainAudioScene.kt @@ -10,10 +10,13 @@ import korlibs.korge.view.* class MainAudioScene : Scene() { override suspend fun SContainer.sceneMain() { uiVerticalStack(width = 640.0) { - uiButton(label = "MP3 Sound") { onClick { resourcesVfs["sounds/mp3.mp3"].readSound().play() } } - uiButton(label = "MP3 Music") { onClick { resourcesVfs["sounds/mp3.mp3"].readMusic().play() } } - uiButton(label = "WAV Sound") { onClick { resourcesVfs["sounds/wav.wav"].readSound().play() } } - uiButton(label = "WAV Music") { onClick { resourcesVfs["sounds/wav.wav"].readMusic().play() } } + println("coroutineContext=$coroutineContext") + uiButton(label = "Small MP3 Sound") { onClick { resourcesVfs["sounds/mp3.mp3"].readSound().play() } } + uiButton(label = "Small MP3 Music") { onClick { resourcesVfs["sounds/mp3.mp3"].readMusic().play() } } + uiButton(label = "Small WAV Sound") { onClick { resourcesVfs["sounds/wav.wav"].readSound().play() } } + uiButton(label = "Small WAV Music") { onClick { resourcesVfs["sounds/wav.wav"].readMusic().play() } } + uiButton(label = "Long MP3 Sound") { onClick { resourcesVfs["sounds/Snowland.mp3"].readSound().play() } } + uiButton(label = "Long MP3 Music") { onClick { resourcesVfs["sounds/Snowland.mp3"].readMusic().play() } } //uiButton(label = "OGG Sound") { onClick { resourcesVfs["sounds/ogg.ogg"].readSound().play() } } //uiButton(label = "OGG Music") { onClick { resourcesVfs["sounds/ogg.ogg"].readMusic().play() } } } diff --git a/korge-sandbox/src/samples/MainPolyphonic.kt b/korge-sandbox/src/samples/MainPolyphonic.kt index 5a2a852829..9304265909 100644 --- a/korge-sandbox/src/samples/MainPolyphonic.kt +++ b/korge-sandbox/src/samples/MainPolyphonic.kt @@ -376,7 +376,7 @@ class MainPolyphonic : Scene() { return 0.0f } - fun audioOutCallback(channel: Int, buf: ShortArray, reqn: Int = buf.size, bufn: Int = 0, nchannels: Int = 1) { + fun audioOutCallback(channel: Int, buf: AudioSampleArray, reqn: Int = buf.size, bufn: Int = 0, nchannels: Int = 1) { val state = channelStates[channel] var bufn = bufn for (i in 0 until reqn) { @@ -394,7 +394,7 @@ class MainPolyphonic : Scene() { } val rvalue = value.clamp(Short.MIN_VALUE.toFloat(), Short.MAX_VALUE.toInt().toFloat()).toInt().toShort() //for (n in 0 until nchannels) buf[bufn++] = value.toShort() - buf[bufn++] = rvalue + buf[bufn++] = AudioSample(rvalue) //buf[bufn++] = rvalue } } From 4c15b8fb01d617d00e581981245a983083c22bfc Mon Sep 17 00:00:00 2001 From: soywiz Date: Thu, 18 Jul 2024 15:37:28 +0200 Subject: [PATCH 02/21] Do not scope to views.mouse.coroutineContext by default. Create new onClickSuspend*/onMouseStuffSuspend* variants allowing suspending, while making the older ones non-suspending so we don't have problem with the coroutineContext Job scope Unify QView and View mouse events --- korge-sandbox/src/samples/MainAudioScene.kt | 13 ++- korge-sandbox/src/samples/MainEasing.kt | 2 +- korge-sandbox/src/samples/MainScenes.kt | 6 +- korge-sandbox/src/samples/MainUI.kt | 2 +- korge-sandbox/src/samples/pong/MenuScene.kt | 2 +- korge/src/korlibs/korge/input/MouseEvents.kt | 97 ++++++++++++------- korge/src/korlibs/korge/ui/UIWindow.kt | 4 +- korge/src/korlibs/korge/view/QView.kt | 2 - .../korlibs/korge/testing/testing_utils.kt | 2 +- 9 files changed, 75 insertions(+), 55 deletions(-) diff --git a/korge-sandbox/src/samples/MainAudioScene.kt b/korge-sandbox/src/samples/MainAudioScene.kt index 4316e881e2..792e6d6568 100644 --- a/korge-sandbox/src/samples/MainAudioScene.kt +++ b/korge-sandbox/src/samples/MainAudioScene.kt @@ -10,13 +10,12 @@ import korlibs.korge.view.* class MainAudioScene : Scene() { override suspend fun SContainer.sceneMain() { uiVerticalStack(width = 640.0) { - println("coroutineContext=$coroutineContext") - uiButton(label = "Small MP3 Sound") { onClick { resourcesVfs["sounds/mp3.mp3"].readSound().play() } } - uiButton(label = "Small MP3 Music") { onClick { resourcesVfs["sounds/mp3.mp3"].readMusic().play() } } - uiButton(label = "Small WAV Sound") { onClick { resourcesVfs["sounds/wav.wav"].readSound().play() } } - uiButton(label = "Small WAV Music") { onClick { resourcesVfs["sounds/wav.wav"].readMusic().play() } } - uiButton(label = "Long MP3 Sound") { onClick { resourcesVfs["sounds/Snowland.mp3"].readSound().play() } } - uiButton(label = "Long MP3 Music") { onClick { resourcesVfs["sounds/Snowland.mp3"].readMusic().play() } } + uiButton(label = "Small MP3 Sound") { onClickSuspend { resourcesVfs["sounds/mp3.mp3"].readSound().play() } } + uiButton(label = "Small MP3 Music") { onClickSuspend { resourcesVfs["sounds/mp3.mp3"].readMusic().play() } } + uiButton(label = "Small WAV Sound") { onClickSuspend { resourcesVfs["sounds/wav.wav"].readSound().play() } } + uiButton(label = "Small WAV Music") { onClickSuspend { resourcesVfs["sounds/wav.wav"].readMusic().play() } } + uiButton(label = "Long MP3 Sound") { onClickSuspend { resourcesVfs["sounds/Snowland.mp3"].readSound().play() } } + uiButton(label = "Long MP3 Music") { onClickSuspend { resourcesVfs["sounds/Snowland.mp3"].readMusic().play() } } //uiButton(label = "OGG Sound") { onClick { resourcesVfs["sounds/ogg.ogg"].readSound().play() } } //uiButton(label = "OGG Music") { onClick { resourcesVfs["sounds/ogg.ogg"].readMusic().play() } } } diff --git a/korge-sandbox/src/samples/MainEasing.kt b/korge-sandbox/src/samples/MainEasing.kt index 37d76c2da8..b1aff0c78c 100644 --- a/korge-sandbox/src/samples/MainEasing.kt +++ b/korge-sandbox/src/samples/MainEasing.kt @@ -44,7 +44,7 @@ class MainEasing : Scene() { text("$easing", textSize = textSize).xy(0.0, textSize) onOver { bg.color = Colors.BLACK.withAd(1.0) } onOut { bg.color = Colors.BLACK.withAd(0.2) } - onClick { + onClickSuspend(coroutineContext) { ballTween?.cancel() ballTween = ball.tweenAsync(ball::x[64f, 64f + 512f], easing = easing) } diff --git a/korge-sandbox/src/samples/MainScenes.kt b/korge-sandbox/src/samples/MainScenes.kt index 2659ccc962..2d8bebf95f 100644 --- a/korge-sandbox/src/samples/MainScenes.kt +++ b/korge-sandbox/src/samples/MainScenes.kt @@ -39,7 +39,7 @@ class MainScenes : Scene() { alpha = 0.7 onOver { alpha = 1.0 } onOut { alpha = 0.7 } - onClick { + onClickSuspend { sceneContainer.changeTo() } } @@ -48,7 +48,7 @@ class MainScenes : Scene() { alpha = 0.7 onOver { alpha = 1.0 } onOut { alpha = 0.7 } - onClick { + onClickSuspend { sceneContainer.changeTo() } } @@ -64,7 +64,7 @@ class MainScenes : Scene() { val blueSquare = solidRect(100, 100, Colors.BLUE) { position(200, 200) - onClick { + onClickSuspend { sceneContainer.changeTo(MyDependency("From MyScene2")) } } diff --git a/korge-sandbox/src/samples/MainUI.kt b/korge-sandbox/src/samples/MainUI.kt index e2145875c7..99f8bc0475 100644 --- a/korge-sandbox/src/samples/MainUI.kt +++ b/korge-sandbox/src/samples/MainUI.kt @@ -50,7 +50,7 @@ class MainUI : Scene() { } uiButton(size = Size(256.0, 32.0)) { text = "Close Window" - onClick { + onClickSuspend { if (gameWindow.confirm("Are you sure to close the window?")) { gameWindow.close() } diff --git a/korge-sandbox/src/samples/pong/MenuScene.kt b/korge-sandbox/src/samples/pong/MenuScene.kt index c998be46d4..2ca14efbe8 100644 --- a/korge-sandbox/src/samples/pong/MenuScene.kt +++ b/korge-sandbox/src/samples/pong/MenuScene.kt @@ -20,7 +20,7 @@ class MenuScene() : Scene() { var playButton = uiButton(size = Size(256.0, 32.0)) { text = "Play" position(sceneWidth / 2 - 128, sceneHeight / 2 - 64) - onClick { + onClickSuspend { sceneContainer.changeTo() } } diff --git a/korge/src/korlibs/korge/input/MouseEvents.kt b/korge/src/korlibs/korge/input/MouseEvents.kt index 28f1a8c6ee..4107dcca46 100644 --- a/korge/src/korlibs/korge/input/MouseEvents.kt +++ b/korge/src/korlibs/korge/input/MouseEvents.kt @@ -1,6 +1,7 @@ package korlibs.korge.input import korlibs.datastructure.* +import korlibs.datastructure.iterators.* import korlibs.event.* import korlibs.image.bitmap.* import korlibs.image.color.* @@ -13,6 +14,8 @@ import korlibs.korge.view.* import korlibs.math.geom.* import korlibs.render.* import korlibs.time.* +import kotlinx.coroutines.* +import kotlin.coroutines.* import kotlin.math.* import kotlin.native.concurrent.* import kotlin.reflect.* @@ -556,12 +559,26 @@ inline fun T.mouse(callback: MouseEvents.() -> Unit): T { } @PublishedApi -internal inline fun T?.doMouseEvent( +internal inline fun T?.doMouseEventSuspend( prop: KProperty1>, - noinline handler: suspend (MouseEvents) -> Unit + noinline handler: suspend (MouseEvents) -> Unit, + coroutineContext: CoroutineContext? = null, ): T? { - this?.mouse?.let { mouse -> - prop.get(mouse).add { launchImmediately(mouse.coroutineContext) { handler(it) } } + this?.bviewAll?.fastForEach { + it?.mouse?.let { mouse -> + prop.get(mouse).add { launchImmediately(coroutineContext ?: mouse.coroutineContext) { handler(it) } } + } + } + return this +} + +@PublishedApi +internal inline fun T?.doMouseEvent( + prop: KProperty1>, + noinline handler: (MouseEvents) -> Unit, +): T? { + this?.bviewAll?.fastForEach { + it?.mouse?.let { mouse -> prop.get(mouse).add { handler(it) } } } return this } @@ -572,49 +589,55 @@ annotation class EventsDslMarker // @TODO: onOut should happen before onOver (circumvented via onNextFrame) -inline fun T.onOutOnOver( +inline fun T.onOutOnOver( noinline out: @EventsDslMarker (MouseEvents) -> Unit, noinline over: @EventsDslMarker (MouseEvents) -> Unit ): T { - var component: AutoCloseable? = null + var components: List = emptyList() onOut { events -> - component?.close() - component = null + components.fastForEach { it.close() } + components = emptyList() out(events) } - onOver { events -> component = onNextFrame { over(events) } } + onOver { events -> + components = bviewAll.map { it.onNextFrame { over(events) } } + } return this } -inline fun T.onClick(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::click, handler) - -inline fun T.onOver(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::over, handler) - -inline fun T.onOut(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::out, handler) - -inline fun T.onDown(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::down, handler) - -inline fun T.onDownFromOutside(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::downFromOutside, handler) - -inline fun T.onUp(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::up, handler) - -inline fun T.onUpOutside(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::upOutside, handler) - -inline fun T.onUpAnywhere(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::upAnywhere, handler) - -inline fun T.onMove(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::move, handler) +inline fun T.onClickSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::click, handler, coroutineContext) +inline fun T.onOverSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::over, handler, coroutineContext) +inline fun T.onOutSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::out, handler, coroutineContext) +inline fun T.onDownSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::down, handler, coroutineContext) +inline fun T.onDownFromOutsideSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::downFromOutside, handler, coroutineContext) +inline fun T.onUpSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::up, handler, coroutineContext) +inline fun T.onUpOutsideSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::upOutside, handler, coroutineContext) +inline fun T.onUpAnywhereSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::upAnywhere, handler, coroutineContext) +inline fun T.onMoveSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::move, handler, coroutineContext) +inline fun T.onScrollSuspend(coroutineContext: CoroutineContext?, noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::scroll, handler, coroutineContext) + +suspend inline fun T.onClickSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::click, handler, coroutineContext) +suspend inline fun T.onOverSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::over, handler, coroutineContext) +suspend inline fun T.onOutSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::out, handler, coroutineContext) +suspend inline fun T.onDownSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::down, handler, coroutineContext) +suspend inline fun T.onDownFromOutsideSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::downFromOutside, handler, coroutineContext) +suspend inline fun T.onUpSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::up, handler, coroutineContext) +suspend inline fun T.onUpOutsideSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::upOutside, handler, coroutineContext) +suspend inline fun T.onUpAnywhereSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::upAnywhere, handler, coroutineContext) +suspend inline fun T.onMoveSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::move, handler, coroutineContext) +suspend inline fun T.onScrollSuspend(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = doMouseEventSuspend(MouseEvents::scroll, handler, coroutineContext) + +inline fun T.onClick(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::click, handler) +inline fun T.onOver(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::over, handler) +inline fun T.onOut(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::out, handler) +inline fun T.onDown(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::down, handler) +inline fun T.onDownFromOutside(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::downFromOutside, handler) +inline fun T.onUp(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::up, handler) +inline fun T.onUpOutside(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::upOutside, handler) +inline fun T.onUpAnywhere(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::upAnywhere, handler) +inline fun T.onMove(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::move, handler) +inline fun T.onScroll(noinline handler: @EventsDslMarker (MouseEvents) -> Unit) = doMouseEvent(MouseEvents::scroll, handler) -inline fun T.onScroll(noinline handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = - doMouseEvent(MouseEvents::scroll, handler) fun ViewsContainer.installMouseDebugExtensionOnce() = MouseEvents.installDebugExtensionOnce(views) diff --git a/korge/src/korlibs/korge/ui/UIWindow.kt b/korge/src/korlibs/korge/ui/UIWindow.kt index e569b644c7..8708403a80 100644 --- a/korge/src/korlibs/korge/ui/UIWindow.kt +++ b/korge/src/korlibs/korge/ui/UIWindow.kt @@ -15,7 +15,6 @@ import korlibs.math.interpolation.* import korlibs.render.* import korlibs.time.* - @KorgeExperimental inline fun Container.uiWindow( title: String, @@ -59,8 +58,9 @@ class UIWindow(title: String, size: Size = Size(256, 256)) : UIContainer(size) { elevation = false bgColorOut = MaterialColors.RED_600 bgColorOver = MaterialColors.RED_800 - onClick { closeAnimated() } + onClickSuspend(null) { closeAnimated() } } + var title: String by titleView::plainText val container = uiScrollable(Size(width, height - titleHeight)).position(0.0, titleHeight).also { it.backgroundColor = Colors["#161a1d"] diff --git a/korge/src/korlibs/korge/view/QView.kt b/korge/src/korlibs/korge/view/QView.kt index c98849841f..2f4c4420f1 100644 --- a/korge/src/korlibs/korge/view/QView.kt +++ b/korge/src/korlibs/korge/view/QView.kt @@ -2,7 +2,6 @@ package korlibs.korge.view import korlibs.datastructure.iterators.* import korlibs.image.color.* -import korlibs.korge.input.* import korlibs.math.geom.* import kotlin.reflect.* @@ -54,7 +53,6 @@ class QView(val views: List) : List by views, BView { fun QView.visible(value: Boolean) { visible = value } fun QView.alpha(value: Double) { alpha = value } -fun QView.onClick(handler: @EventsDslMarker suspend (MouseEvents) -> Unit) = fastForEach { it.onClick(handler) } inline fun QView.castTo(): T? = firstOrNull as? T? /** Indexer that allows to get a descendant marked with the name [name]. */ diff --git a/korge/src@jvm/korlibs/korge/testing/testing_utils.kt b/korge/src@jvm/korlibs/korge/testing/testing_utils.kt index 99a49638e2..b366bf4f0c 100644 --- a/korge/src@jvm/korlibs/korge/testing/testing_utils.kt +++ b/korge/src@jvm/korlibs/korge/testing/testing_utils.kt @@ -137,7 +137,7 @@ inline fun korgeScreenshotTestV2( viewsToAlign += uiButton("Accept change?") { centerXOn(this@container) - onClick { + onClickSuspend { val goldenFileNameWithExt = context.makeGoldenFileNameWithExtension(testResult.goldenName) if (testResult.newBitmap != null) { From 986f23664a03f8de12da708c54481bf0791f8e95 Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 12:13:10 +0200 Subject: [PATCH 03/21] Fixes SyncEventLoop --- .../kotlin/korlibs/datastructure/_Datastructure_event.native.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/korge-core/src@native/kotlin/korlibs/datastructure/_Datastructure_event.native.kt b/korge-core/src@native/kotlin/korlibs/datastructure/_Datastructure_event.native.kt index 2c99e025d2..6f1730f31b 100644 --- a/korge-core/src@native/kotlin/korlibs/datastructure/_Datastructure_event.native.kt +++ b/korge-core/src@native/kotlin/korlibs/datastructure/_Datastructure_event.native.kt @@ -3,4 +3,4 @@ package korlibs.datastructure.event actual fun createPlatformEventLoop(precise: Boolean): SyncEventLoop = - SyncEventLoop(precise = precise) + SyncEventLoop() From 1842476f58439961fe5e2f9a08875462f4c41b24 Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 12:13:18 +0200 Subject: [PATCH 04/21] Upgrades xcodeGen --- .../main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt index b56504cd3e..385d30599c 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt @@ -9,7 +9,7 @@ val Project.iosXcodegenExt by projectExtension { } class IosXcodegen(val project: Project) { - val xcodeGenGitTag = "2.37.0" + val xcodeGenGitTag = "2.42.0" val korlibsFolder = File(System.getProperty("user.home") + "/.korlibs").apply { mkdirs() } val xcodeGenFolder = File(korlibsFolder, "XcodeGen-$xcodeGenGitTag") val xcodeGenLocalExecutable = File("/usr/local/bin/xcodegen") From 6100a8c622b2a846a8baed68740db1384c6785fc Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 12:14:37 +0200 Subject: [PATCH 05/21] Use .korge folder instead of .korlibs folder --- .../main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt index 385d30599c..0d288b9f8b 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosXcodegen.kt @@ -10,7 +10,7 @@ val Project.iosXcodegenExt by projectExtension { class IosXcodegen(val project: Project) { val xcodeGenGitTag = "2.42.0" - val korlibsFolder = File(System.getProperty("user.home") + "/.korlibs").apply { mkdirs() } + val korlibsFolder = File(System.getProperty("user.home") + "/.korge").apply { mkdirs() } val xcodeGenFolder = File(korlibsFolder, "XcodeGen-$xcodeGenGitTag") val xcodeGenLocalExecutable = File("/usr/local/bin/xcodegen") val xcodeGenExecutable = FileList( From 35238bacc7b2e4dacabaf47674b836eab48e49f5 Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 12:32:16 +0200 Subject: [PATCH 06/21] Some ios fixes --- .../main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt | 8 +++++++- .../korlibs/korge/gradle/targets/ios/IosProjectTools.kt | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt index 1ccf6e8a9c..9fc6f6790d 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt @@ -210,6 +210,10 @@ fun Project.configureNativeIosTvosRun(targetName: String) { simulator -> if (isArm) "SimulatorArm64" else "X64" else -> "Arm64" } + val archNoSim = when { + simulator -> "X64" + else -> "Arm64" + } val arch2 = when { simulator -> if (isArm) "arm64" else "x86_64" else -> "arm64" @@ -229,6 +233,7 @@ fun Project.configureNativeIosTvosRun(targetName: String) { //} workingDir(xcodeProjDir) doFirst { + //commandLine("xcrun", "xcodebuild", "-allowProvisioningUpdates", "-scheme", "app-$archNoSim-$debugSuffix", "-project", ".", "-configuration", debugSuffix, "-derivedDataPath", "build", "-arch", arch2, "-sdk", iosSdkExt.appleFindSdk(sdkName)) commandLine("xcrun", "xcodebuild", "-allowProvisioningUpdates", "-scheme", "app-$arch-$debugSuffix", "-project", ".", "-configuration", debugSuffix, "-derivedDataPath", "build", "-arch", arch2, "-sdk", iosSdkExt.appleFindSdk(sdkName)) println("COMMAND: ${commandLine.joinToString(" ")}") } @@ -275,7 +280,8 @@ fun Project.configureNativeIosTvosRun(targetName: String) { val device = iosSdkExt.appleGetInstallDevice(iphoneVersion) // xcrun simctl launch --console 7F49203A-1F16-4DEE-B9A2-7A1BB153DF70 com.sample.demo.app-X64-Debug //logger.info(params.joinToString(" ")) - execLogger { it.commandLine("xcrun", "simctl", "launch", "--console", device.udid, "${korge.id}.app-X64-$debugSuffix") } + val arch = if (isArm) "SimulatorArm64" else "X64" + execLogger { it.commandLine("xcrun", "simctl", "launch", "--console", device.udid, "${korge.id}.app-$arch-$debugSuffix") } } } diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosProjectTools.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosProjectTools.kt index fc616ff065..b3c2fc546c 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosProjectTools.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/IosProjectTools.kt @@ -243,12 +243,12 @@ object IosProjectTools { indent { for (debug in listOf(false, true)) { val debugSuffix = if (debug) "Debug" else "Release" - for (arch in listOf("X64", "Arm64")) { + for (arch in listOf("X64", "SimulatorArm64", "Arm64")) { line("app-$arch-$debugSuffix:") indent { line("platform: ${if (targetName == "ios") "iOS" else "tvOS"}") line("type: application") - line("deploymentTarget: \"10.0\"") + line("deploymentTarget: \"15.0\"") line("sources:") indent { line("- app") @@ -275,7 +275,7 @@ object IosProjectTools { line(" DEVELOPMENT_TEAM: $team") } line("dependencies:") - line(" - framework: ../../bin/${targetName}$arch/${debugSuffix.toLowerCase()}Framework/GameMain.framework") + line(" - framework: ../../bin/${targetName}$arch/${debugSuffix.lowercase()}Framework/GameMain.framework") } } } From c4bcfdb5d9fa3ed769295ec97cb9ba296e556d64 Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 14:24:31 +0200 Subject: [PATCH 07/21] Send KeyEvent.Type.TYPE events too on iOS --- .../korlibs/render/DefaultGameWindowIos.kt | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt b/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt index 2211559f7b..ff29d6de05 100644 --- a/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt +++ b/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt @@ -152,7 +152,6 @@ class ViewController( @Suppress("RemoveRedundantCallsOfConversionMethods") @OptIn(UnsafeNumber::class) private fun pressesHandler(type: KeyEvent.Type, presses: Set<*>, withEvent: UIPressesEvent?) { - super.pressesBegan(presses, withEvent) for (press in presses) { if (press !is UIPress) continue val uiKey = press.key ?: continue @@ -161,17 +160,16 @@ class ViewController( val key = IosKeyMap.KEY_MAP[keyCode.toInt()] ?: Key.UNKNOWN //println("pressesHandler[$type]: ${keyCode}, ${modifierFlags}, $key, ${uiKey.charactersIgnoringModifiers}") - gameWindow.dispatchKeyEventEx( - type, - 0, - uiKey.charactersIgnoringModifiers.firstOrNull() ?: '\u0000', - key, - keyCode.toInt(), - shift = modifierFlags.hasFlags(UIKeyModifierShift.toInt()), - ctrl = modifierFlags.hasFlags(UIKeyModifierControl.toInt()), - alt = modifierFlags.hasFlags(UIKeyModifierAlternate.toInt()), - meta = modifierFlags.hasFlags(UIKeyModifierCommand.toInt()), - ) + val char = uiKey.charactersIgnoringModifiers.firstOrNull() ?: '\u0000' + + val shift = modifierFlags.hasFlags(UIKeyModifierShift.toInt()) + val ctrl = modifierFlags.hasFlags(UIKeyModifierControl.toInt()) + val alt = modifierFlags.hasFlags(UIKeyModifierAlternate.toInt()) + val meta = modifierFlags.hasFlags(UIKeyModifierCommand.toInt()) + if (type == KeyEvent.Type.DOWN && char >= '\u0020') { + gameWindow.dispatchKeyEventEx(KeyEvent.Type.TYPE, 0, char, key, keyCode.toInt(), shift = shift, ctrl = ctrl, alt = alt, meta = meta,) + } + gameWindow.dispatchKeyEventEx(type, 0, char, key, keyCode.toInt(), shift = shift, ctrl = ctrl, alt = alt, meta = meta,) } } @@ -181,7 +179,7 @@ class ViewController( } override fun pressesEnded(presses: Set<*>, withEvent: UIPressesEvent?) { - super.pressesBegan(presses, withEvent) + super.pressesEnded(presses, withEvent) pressesHandler(KeyEvent.Type.UP, presses, withEvent) } From 3765c7e2858562a70ecc6923b67fb7be3462f93b Mon Sep 17 00:00:00 2001 From: soywiz Date: Fri, 19 Jul 2024 19:04:33 +0200 Subject: [PATCH 08/21] Some iOS Key mapping improvements --- korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt | 5 +++-- korge-core/src@darwin/korlibs/render/IosKeyMap.kt | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt b/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt index ff29d6de05..40196f7047 100644 --- a/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt +++ b/korge-core/src@darwin/korlibs/render/DefaultGameWindowIos.kt @@ -158,15 +158,16 @@ class ViewController( val keyCode = uiKey.keyCode.toInt() val modifierFlags = uiKey.modifierFlags.toInt() val key = IosKeyMap.KEY_MAP[keyCode.toInt()] ?: Key.UNKNOWN - //println("pressesHandler[$type]: ${keyCode}, ${modifierFlags}, $key, ${uiKey.charactersIgnoringModifiers}") val char = uiKey.charactersIgnoringModifiers.firstOrNull() ?: '\u0000' + //println("pressesHandler[$type]: ${keyCode}, ${modifierFlags}, $key, char='$char', char.code=${char.code}, uiKey.characters='${uiKey.characters}'") + val shift = modifierFlags.hasFlags(UIKeyModifierShift.toInt()) val ctrl = modifierFlags.hasFlags(UIKeyModifierControl.toInt()) val alt = modifierFlags.hasFlags(UIKeyModifierAlternate.toInt()) val meta = modifierFlags.hasFlags(UIKeyModifierCommand.toInt()) - if (type == KeyEvent.Type.DOWN && char >= '\u0020') { + if (type == KeyEvent.Type.DOWN && char >= '\u0020' && uiKey.characters.length == 1) { gameWindow.dispatchKeyEventEx(KeyEvent.Type.TYPE, 0, char, key, keyCode.toInt(), shift = shift, ctrl = ctrl, alt = alt, meta = meta,) } gameWindow.dispatchKeyEventEx(type, 0, char, key, keyCode.toInt(), shift = shift, ctrl = ctrl, alt = alt, meta = meta,) diff --git a/korge-core/src@darwin/korlibs/render/IosKeyMap.kt b/korge-core/src@darwin/korlibs/render/IosKeyMap.kt index f693a2b759..dda76ba10c 100644 --- a/korge-core/src@darwin/korlibs/render/IosKeyMap.kt +++ b/korge-core/src@darwin/korlibs/render/IosKeyMap.kt @@ -93,6 +93,7 @@ object IosKeyMap { put(UIKeyboardHIDUsageKeyboardEscape, Key.ESCAPE) put(UIKeyboardHIDUsageKeyboardInsert, Key.INSERT) put(UIKeyboardHIDUsageKeyboardReturn, Key.RETURN) + put(UIKeyboardHIDUsageKeyboardReturnOrEnter, Key.RETURN) put(UIKeyboardHIDUsageKeyboardTab, Key.TAB) // Function Keys From ae937397e678291295c1b44d3b5873904bc30eb4 Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:21:32 +0200 Subject: [PATCH 09/21] Add CoroutineName to several coroutines --- korge-core/src/korlibs/render/GameWindow.kt | 2 +- .../korlibs/render/awt/BaseAwtGameWindow.kt | 2 +- korge/src/korlibs/korge/Korge.kt | 2 +- korge/src/korlibs/korge/scene/Scene.kt | 13 ++++++++++--- korge/src/korlibs/korge/scene/SceneContainer.kt | 14 +++++++++----- korge/src/korlibs/korge/view/Views.kt | 4 ++-- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/korge-core/src/korlibs/render/GameWindow.kt b/korge-core/src/korlibs/render/GameWindow.kt index 10d6a96ce8..0cdff939b7 100644 --- a/korge-core/src/korlibs/render/GameWindow.kt +++ b/korge-core/src/korlibs/render/GameWindow.kt @@ -393,7 +393,7 @@ open class GameWindow : } open suspend fun loop(entry: suspend GameWindow.() -> Unit) { - launchImmediately(getCoroutineDispatcherWithCurrentContext()) { + launchImmediately(getCoroutineDispatcherWithCurrentContext() + CoroutineName("GameWindow.loop")) { entry() } //withContext(getCoroutineDispatcherWithCurrentContext()) { diff --git a/korge-core/src@jvm/korlibs/render/awt/BaseAwtGameWindow.kt b/korge-core/src@jvm/korlibs/render/awt/BaseAwtGameWindow.kt index f25b6162db..14520dce2e 100644 --- a/korge-core/src@jvm/korlibs/render/awt/BaseAwtGameWindow.kt +++ b/korge-core/src@jvm/korlibs/render/awt/BaseAwtGameWindow.kt @@ -274,7 +274,7 @@ abstract class BaseAwtGameWindow( protected var mouseY: Int = 0 override suspend fun loop(entry: suspend GameWindow.() -> Unit) { - launchImmediately(getCoroutineDispatcherWithCurrentContext()) { + launchImmediately(getCoroutineDispatcherWithCurrentContext() + CoroutineName("BaseAwtGameWindow.loop")) { entry() } diff --git a/korge/src/korlibs/korge/Korge.kt b/korge/src/korlibs/korge/Korge.kt index f2c4aa7f20..8074d7408c 100644 --- a/korge/src/korlibs/korge/Korge.kt +++ b/korge/src/korlibs/korge/Korge.kt @@ -217,7 +217,7 @@ object KorgeRunner { // Use this once Korgw is on 1.12.5 //val views = Views(gameWindow.getCoroutineDispatcherWithCurrentContext() + SupervisorJob(), ag, injector, input, timeProvider, stats, gameWindow) val views: Views = Views( - coroutineContext = coroutineContext + gameWindow.coroutineDispatcher + InjectorContext(config.injector) + SupervisorJob(), + coroutineContext = coroutineContext + gameWindow.coroutineDispatcher + InjectorContext(config.injector) + SupervisorJob() + CoroutineName("Views"), ag = if (config.debugAg) AGPrint() else ag, injector = config.injector, input = input, diff --git a/korge/src/korlibs/korge/scene/Scene.kt b/korge/src/korlibs/korge/scene/Scene.kt index b8ee5ec903..fd36fda505 100644 --- a/korge/src/korlibs/korge/scene/Scene.kt +++ b/korge/src/korlibs/korge/scene/Scene.kt @@ -55,7 +55,12 @@ abstract class Scene : InjectorAsyncDependency, ViewsContainer, CoroutineScope, val root get() = _sceneViewContainer protected val cancellables = CancellableGroup() - override val coroutineContext by lazy { views.coroutineContext + InjectorContext(injector) + Job(views.coroutineContext[Job.Key]) } + override val coroutineContext by lazy { + views.coroutineContext + + InjectorContext(injector) + + SupervisorJob(views.coroutineContext[Job.Key]) + + CoroutineName("Scene:$this") + } val sceneView: SContainer by lazy { createSceneView(sceneContainer.size).apply { _sceneViewContainer += this @@ -119,7 +124,7 @@ abstract class Scene : InjectorAsyncDependency, ViewsContainer, CoroutineScope, open suspend fun sceneDestroy() { } - internal suspend fun sceneDestroyInternal() { + internal fun sceneDestroyInternal() { cancellables.cancel() injector.deinit() } @@ -134,10 +139,12 @@ abstract class Scene : InjectorAsyncDependency, ViewsContainer, CoroutineScope, open suspend fun sceneAfterDestroy() { } + class SceneCancellationException(val scene: Scene) : CancellationException("Scene.sceneAfterDestroyInternal:$scene") + internal suspend fun sceneAfterDestroyInternal() { sceneAfterDestroy() try { - coroutineContext.cancel() // cancelAndJoin was being used when hanged on native? + coroutineContext.cancel(SceneCancellationException(this)) // cancelAndJoin was being used when hanged on native? } catch (e: Throwable) { if (e is CancellationException) throw e e.printStackTrace() diff --git a/korge/src/korlibs/korge/scene/SceneContainer.kt b/korge/src/korlibs/korge/scene/SceneContainer.kt index 509649d72f..aa5d9a3b39 100644 --- a/korge/src/korlibs/korge/scene/SceneContainer.kt +++ b/korge/src/korlibs/korge/scene/SceneContainer.kt @@ -14,9 +14,7 @@ import korlibs.logger.* import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred +import kotlinx.coroutines.* import kotlin.reflect.* import kotlin.time.* @@ -281,7 +279,7 @@ class SceneContainer( transitionView.startNewTransition(newScene._sceneViewContainer, transition) - //println("SCENE PREINIT") + //println("&&&&&&&&&&&& SCENE PREINIT: newScene=$newScene") try { newScene.coroutineContext.launchUnscopedAndWait { //println("coroutineContext=$coroutineContext") @@ -292,14 +290,17 @@ class SceneContainer( if (e is CancellationException) throw e //println("WOOOPS!") e.printStackTrace() + } finally { + //println("&&&&&&&&& SCENE POSTINIT newScene=$newScene, oldScene=$oldScene") } - //println("SCENE POSTINIT") + //CoroutineScope(newScene.coroutineContext).launch { newScene.launchUnscoped { newScene.sceneView.apply { newScene.apply { sceneMain() } } } if (oldScene != null) { + //withContext(oldScene.coroutineContext) { oldScene.coroutineContext.launchUnscopedAndWait { oldScene.sceneBeforeLeaving() } @@ -314,12 +315,14 @@ class SceneContainer( transitionView.endTransition() if (oldScene != null) { + //withContext(oldScene.coroutineContext) { oldScene.coroutineContext.launchUnscopedAndWait { //println("sceneDestroy.coroutineContext=$coroutineContext") oldScene.sceneDestroy() oldScene.sceneDestroyInternal() } + //CoroutineScope(oldScene.coroutineContext).launch { oldScene.launchUnscoped { //println("sceneAfterDestroyInternal.coroutineContext=$coroutineContext") @@ -327,6 +330,7 @@ class SceneContainer( } } + //CoroutineScope(newScene.coroutineContext).launch { newScene.coroutineContext.launchUnscoped { newScene.sceneAfterInit() } diff --git a/korge/src/korlibs/korge/view/Views.kt b/korge/src/korlibs/korge/view/Views.kt index 9bc25f3b09..2fe13efe67 100644 --- a/korge/src/korlibs/korge/view/Views.kt +++ b/korge/src/korlibs/korge/view/Views.kt @@ -63,7 +63,7 @@ class Views( DeviceDimensionsProvider by gameWindow { constructor(gameWindow: GameWindow) : this( - gameWindow, gameWindow.ag, gameWindow = gameWindow + gameWindow + CoroutineName("Views"), gameWindow.ag, gameWindow = gameWindow ) var quality by gameWindow::quality @@ -506,7 +506,7 @@ class ViewsLog constructor( val stats: Stats = Stats(), val gameWindow: GameWindow = GameWindowLog() ) : CoroutineScope { - val views: Views = Views(coroutineContext + InjectorContext(injector), ag, injector, input, timeProvider, stats, gameWindow).also { + val views: Views = Views(coroutineContext + InjectorContext(injector) + CoroutineName("Views"), ag, injector, input, timeProvider, stats, gameWindow).also { it.rethrowRenderError = true } val stage: Stage get() = views.stage From 9933ab167d5ceefbe71d69f7309a61a20f2d476a Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:22:11 +0200 Subject: [PATCH 10/21] Avoid double initialization of scenes in korge-sandbox --- korge-sandbox/src/helpers.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/korge-sandbox/src/helpers.kt b/korge-sandbox/src/helpers.kt index c8a1ac0ad8..55706400d6 100644 --- a/korge-sandbox/src/helpers.kt +++ b/korge-sandbox/src/helpers.kt @@ -12,17 +12,21 @@ class Demo(val sceneBuilder: () -> Scene, val name: String = sceneBuilder()::cla suspend fun Stage.demoSelector(default: Demo, all: List) { val container = sceneContainer(size = Size(width, height - 48f)) { }.xy(0, 48) val containerFocus = container.makeFocusable() + var currentDemo: Demo? = null lateinit var comboBox: UIComboBox suspend fun setDemo(demo: Demo?) { + if (currentDemo != demo) currentDemo = demo else return + //container.removeChildren() + //println("setDemo: demo=$demo") if (demo != null) { comboBox.selectedItem = demo views.clearColor = DEFAULT_KORGE_BG_COLOR container.changeTo { containerFocus.focus() - demo.sceneBuilder().also { it.init(this) } + demo.sceneBuilder() } } } @@ -33,6 +37,7 @@ suspend fun Stage.demoSelector(default: Demo, all: List) { this.viewportHeight = 600 this.onSelectionUpdate.add { //println(it) + //CoroutineScope(this@demoSelector.coroutineContext).launchImmediately { setDemo(it.selectedItem!!) } launchImmediately { setDemo(it.selectedItem!!) } } } From 579fd925fc2211c9e895c0eb5ee29e93b77798f3 Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:23:15 +0200 Subject: [PATCH 11/21] Enable corutine debug in korge-sandbox --- korge-sandbox/build.gradle | 1 + korge-sandbox/src/Main.kt | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/korge-sandbox/build.gradle b/korge-sandbox/build.gradle index ed8e88e37a..b3e8c8bec1 100644 --- a/korge-sandbox/build.gradle +++ b/korge-sandbox/build.gradle @@ -8,6 +8,7 @@ korge { dependencies { add("commonMainApi", project(":korge")) + add("commonMainApi", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.9.0-RC") } tasks.register("runJvmAwtSandbox", KorgeJavaExec) { diff --git a/korge-sandbox/src/Main.kt b/korge-sandbox/src/Main.kt index 6de18edd51..48a96c634a 100644 --- a/korge-sandbox/src/Main.kt +++ b/korge-sandbox/src/Main.kt @@ -63,7 +63,11 @@ suspend fun main2() = KorgeCore { } } -suspend fun main() = Korge( +suspend fun main() = run { + kotlinx.coroutines.debug.DebugProbes.enableCreationStackTraces = true + kotlinx.coroutines.debug.DebugProbes.install() + null +} ?: Korge( windowSize = Korge.DEFAULT_WINDOW_SIZE, backgroundColor = DEFAULT_KORGE_BG_COLOR, displayMode = KorgeDisplayMode.CENTER_NO_CLIP, From a8214e21387c4d881c3c5c39c65bf52fbdb82cab Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:41:48 +0200 Subject: [PATCH 12/21] Allow to debug coroutines via flag --- korge-sandbox/build.gradle | 1 - korge-sandbox/src/Main.kt | 7 ++----- korge/build.gradle.kts | 1 + korge/src/korlibs/korge/Korge.kt | 10 +++++++++- korge/src/korlibs/korge/KorgeExt.kt | 1 + korge/src@android/korlibs/korge/KorgeExtAndroid.kt | 2 ++ korge/src@darwin/korlibs/korge/KorgeExtNative.kt | 2 ++ korge/src@js/korlibs/korge/KorgeExtJs.kt | 2 ++ korge/src@jvm/korlibs/korge/KorgeExtJvm.kt | 9 +++++++++ korge/src@wasmJs/korlibs/korge/KorgeExtJs.kt | 4 ++++ 10 files changed, 32 insertions(+), 7 deletions(-) diff --git a/korge-sandbox/build.gradle b/korge-sandbox/build.gradle index b3e8c8bec1..ed8e88e37a 100644 --- a/korge-sandbox/build.gradle +++ b/korge-sandbox/build.gradle @@ -8,7 +8,6 @@ korge { dependencies { add("commonMainApi", project(":korge")) - add("commonMainApi", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.9.0-RC") } tasks.register("runJvmAwtSandbox", KorgeJavaExec) { diff --git a/korge-sandbox/src/Main.kt b/korge-sandbox/src/Main.kt index 48a96c634a..cc147ed9ef 100644 --- a/korge-sandbox/src/Main.kt +++ b/korge-sandbox/src/Main.kt @@ -63,15 +63,12 @@ suspend fun main2() = KorgeCore { } } -suspend fun main() = run { - kotlinx.coroutines.debug.DebugProbes.enableCreationStackTraces = true - kotlinx.coroutines.debug.DebugProbes.install() - null -} ?: Korge( +suspend fun main() = Korge( windowSize = Korge.DEFAULT_WINDOW_SIZE, backgroundColor = DEFAULT_KORGE_BG_COLOR, displayMode = KorgeDisplayMode.CENTER_NO_CLIP, debug = false, + debugCoroutines = true, //forceRenderEveryFrame = false ) { //sceneContainer().changeTo({MainSprites10k()}); return@start diff --git a/korge/build.gradle.kts b/korge/build.gradle.kts index 1eb325ac05..b7dd0d2f45 100644 --- a/korge/build.gradle.kts +++ b/korge/build.gradle.kts @@ -13,4 +13,5 @@ project.extensions.extraProperties.properties.apply { dependencies { commonMainApi(project(":korge-core")) jvmMainApi(project(":korge-ipc")) + add("jvmMainApi", "org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.9.0-RC") } diff --git a/korge/src/korlibs/korge/Korge.kt b/korge/src/korlibs/korge/Korge.kt index 8074d7408c..023c7d9adb 100644 --- a/korge/src/korlibs/korge/Korge.kt +++ b/korge/src/korlibs/korge/Korge.kt @@ -90,6 +90,7 @@ suspend fun Korge( targetFps: Double = 0.0, /** false by default, useful only for debugging on the JVM */ preferSyncIo: Boolean? = null, + debugCoroutines: Boolean? = null, entry: suspend Stage.() -> Unit = {} ): Unit = Korge( args = args, imageFormats = imageFormats, gameWindow = gameWindow, mainSceneClass = mainSceneClass, @@ -109,6 +110,7 @@ suspend fun Korge( unit = Unit, targetFps = targetFps, preferSyncIo = preferSyncIo, + debugCoroutines = debugCoroutines, ).start(entry) data class Korge( @@ -147,8 +149,11 @@ data class Korge( val stageBuilder: (Views) -> Stage = { Stage(it) }, val targetFps: Double = 0.0, val preferSyncIo: Boolean? = null, + val debugCoroutines: Boolean? = null, val unit: Unit = Unit, ) { + val realDebugCoroutines get() = debugCoroutines ?: (korlibs.io.lang.Environment["DEBUG_COROUTINES"] == "true") + companion object { val logger = Logger("Korge") val DEFAULT_GAME_ID = "korlibs.korge.unknown" @@ -174,7 +179,10 @@ suspend fun KorgeWithConfig(config: KorgeConfig, entry: suspend Stage.() -> Unit * You have to call the [Korge] method by either providing some parameters, or a [Korge.Config] object. */ object KorgeRunner { - suspend operator fun invoke(config: Korge) = Worker.init { + suspend operator fun invoke(config: Korge) = run { + beforeStartingKorge(config) + null + } ?: Worker.init { nativeSoundProvider // Ensure web audio hooks are added to avoid first click not working on JS RegisteredImageFormats.register(config.imageFormats) diff --git a/korge/src/korlibs/korge/KorgeExt.kt b/korge/src/korlibs/korge/KorgeExt.kt index a407a05ed2..8cd3831d63 100644 --- a/korge/src/korlibs/korge/KorgeExt.kt +++ b/korge/src/korlibs/korge/KorgeExt.kt @@ -3,3 +3,4 @@ package korlibs.korge import korlibs.korge.view.Views internal expect fun completeViews(views: Views) +internal expect fun beforeStartingKorge(config: Korge) diff --git a/korge/src@android/korlibs/korge/KorgeExtAndroid.kt b/korge/src@android/korlibs/korge/KorgeExtAndroid.kt index 182614b078..e90aec034d 100644 --- a/korge/src@android/korlibs/korge/KorgeExtAndroid.kt +++ b/korge/src@android/korlibs/korge/KorgeExtAndroid.kt @@ -4,3 +4,5 @@ import korlibs.korge.view.* internal actual fun completeViews(views: Views) { } +internal actual fun beforeStartingKorge(config: Korge) { +} diff --git a/korge/src@darwin/korlibs/korge/KorgeExtNative.kt b/korge/src@darwin/korlibs/korge/KorgeExtNative.kt index 182614b078..e90aec034d 100644 --- a/korge/src@darwin/korlibs/korge/KorgeExtNative.kt +++ b/korge/src@darwin/korlibs/korge/KorgeExtNative.kt @@ -4,3 +4,5 @@ import korlibs.korge.view.* internal actual fun completeViews(views: Views) { } +internal actual fun beforeStartingKorge(config: Korge) { +} diff --git a/korge/src@js/korlibs/korge/KorgeExtJs.kt b/korge/src@js/korlibs/korge/KorgeExtJs.kt index e33db94ed1..218a95a379 100644 --- a/korge/src@js/korlibs/korge/KorgeExtJs.kt +++ b/korge/src@js/korlibs/korge/KorgeExtJs.kt @@ -7,3 +7,5 @@ internal actual fun completeViews(views: Views) { // Already performed on Korge start //HtmlSimpleSound.unlock // Tries to unlock audio as soon as possible } +internal actual fun beforeStartingKorge(config: Korge) { +} diff --git a/korge/src@jvm/korlibs/korge/KorgeExtJvm.kt b/korge/src@jvm/korlibs/korge/KorgeExtJvm.kt index 08eb5c3c0f..488b602469 100644 --- a/korge/src@jvm/korlibs/korge/KorgeExtJvm.kt +++ b/korge/src@jvm/korlibs/korge/KorgeExtJvm.kt @@ -1,6 +1,7 @@ package korlibs.korge import korlibs.korge.view.* +import kotlinx.coroutines.* import java.util.* interface ViewsCompleter { @@ -12,3 +13,11 @@ internal actual fun completeViews(views: Views) { completer.completeViews(views) } } + +@OptIn(ExperimentalCoroutinesApi::class) +internal actual fun beforeStartingKorge(config: Korge) { + if (config.realDebugCoroutines) { + kotlinx.coroutines.debug.DebugProbes.enableCreationStackTraces = true + kotlinx.coroutines.debug.DebugProbes.install() + } +} diff --git a/korge/src@wasmJs/korlibs/korge/KorgeExtJs.kt b/korge/src@wasmJs/korlibs/korge/KorgeExtJs.kt index e33db94ed1..fb1ce482f0 100644 --- a/korge/src@wasmJs/korlibs/korge/KorgeExtJs.kt +++ b/korge/src@wasmJs/korlibs/korge/KorgeExtJs.kt @@ -7,3 +7,7 @@ internal actual fun completeViews(views: Views) { // Already performed on Korge start //HtmlSimpleSound.unlock // Tries to unlock audio as soon as possible } + +internal actual fun beforeStartingKorge(config: Korge) { +} + From 2a5b53d150fb28e72e443df0687428feb40f7377 Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:42:23 +0200 Subject: [PATCH 13/21] Update MainAudioScene to test more things --- korge-sandbox/src/Main.kt | 1 + korge-sandbox/src/samples/MainAudioScene.kt | 47 ++++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/korge-sandbox/src/Main.kt b/korge-sandbox/src/Main.kt index cc147ed9ef..1277c92ad9 100644 --- a/korge-sandbox/src/Main.kt +++ b/korge-sandbox/src/Main.kt @@ -134,6 +134,7 @@ suspend fun main() = Korge( //Demo(::MainShape2dScene), //Demo(::MainUIStacks), Demo(::MainAudioScene), + //Demo(::MainAsteroids), //Demo(::MainSprites10k), //Demo(::MainStressMatrixMultiplication), //Demo(::MainSDF), diff --git a/korge-sandbox/src/samples/MainAudioScene.kt b/korge-sandbox/src/samples/MainAudioScene.kt index 792e6d6568..57ae4620af 100644 --- a/korge-sandbox/src/samples/MainAudioScene.kt +++ b/korge-sandbox/src/samples/MainAudioScene.kt @@ -6,19 +6,54 @@ import korlibs.korge.input.* import korlibs.korge.scene.* import korlibs.korge.ui.* import korlibs.korge.view.* +import kotlin.time.Duration.Companion.seconds class MainAudioScene : Scene() { override suspend fun SContainer.sceneMain() { uiVerticalStack(width = 640.0) { - uiButton(label = "Small MP3 Sound") { onClickSuspend { resourcesVfs["sounds/mp3.mp3"].readSound().play() } } - uiButton(label = "Small MP3 Music") { onClickSuspend { resourcesVfs["sounds/mp3.mp3"].readMusic().play() } } - uiButton(label = "Small WAV Sound") { onClickSuspend { resourcesVfs["sounds/wav.wav"].readSound().play() } } - uiButton(label = "Small WAV Music") { onClickSuspend { resourcesVfs["sounds/wav.wav"].readMusic().play() } } - uiButton(label = "Long MP3 Sound") { onClickSuspend { resourcesVfs["sounds/Snowland.mp3"].readSound().play() } } - uiButton(label = "Long MP3 Music") { onClickSuspend { resourcesVfs["sounds/Snowland.mp3"].readMusic().play() } } + var lastChannel: SoundChannel? = null + val len = uiText("Length: 0") + suspend fun play(file: String, music: Boolean) { + val vfs = resourcesVfs[file] + lastChannel = if (music) vfs.readMusic().play() else vfs.readSound().play() + len.text = "Length: ${lastChannel?.total}" + } + fun seekRatio(ratio: Double) { + val computed = lastChannel?.total?.times(ratio) ?: if (ratio >= 1.0) 10000.seconds else 0.seconds + lastChannel?.current = lastChannel?.total?.times(ratio) ?: if (ratio >= 1.0) 10000.seconds else 0.seconds + len.text = "Length: ${lastChannel?.total}, Computed: $computed, Current: ${lastChannel?.current}" + } + + uiButton(label = "Small MP3 Sound") { onClickSuspend { play("sounds/mp3.mp3", music = false) } } + uiButton(label = "Small MP3 Music") { onClickSuspend { play("sounds/mp3.mp3", music = true) } } + uiButton(label = "Small MP3 AudioData.toSound()") { onClickSuspend { lastChannel = resourcesVfs["sounds/mp3.mp3"].readAudioData().toSound().play() } } + uiButton(label = "Small WAV Sound") { onClickSuspend { play("sounds/wav.wav", music = false) } } + uiButton(label = "Small WAV Music") { onClickSuspend { play("sounds/wav.wav", music = true) } } + uiButton(label = "Long MP3 Sound") { onClickSuspend { play("sounds/Snowland.mp3", music = false) } } + uiButton(label = "Long MP3 Music") { onClickSuspend { play("sounds/Snowland.mp3", music = true) } } + uiHorizontalStack { + uiButton(label = "Seek Start") { onClickSuspend { seekRatio(0.001) } } + uiButton(label = "Seek 0.25") { onClickSuspend { seekRatio(0.25) } } + uiButton(label = "Seek Middle") { onClickSuspend { seekRatio(0.5) } } + uiButton(label = "Seek 0.75") { onClickSuspend { seekRatio(0.75) } } + uiButton(label = "Seek End") { onClickSuspend { seekRatio(1.0) } } + } //uiButton(label = "OGG Sound") { onClick { resourcesVfs["sounds/ogg.ogg"].readSound().play() } } //uiButton(label = "OGG Music") { onClick { resourcesVfs["sounds/ogg.ogg"].readMusic().play() } } } + + try { + println("TRYING: coroutineContext=$coroutineContext") + //delay(1.seconds) + println("BYTES: " + resourcesVfs["sounds/mp3.mp3"].readBytes().size) + //localVfs("/tmp/demo.mp3").writeBytes() + //localVfs("/tmp/demo.wav").writeBytes(WAV.encodeToByteArray(resourcesVfs["sounds/mp3.mp3"].readAudioData())) + //println(resourcesVfs["sounds/mp3.mp3"].readAudioData().encodeToFile()) + } catch (e: Throwable) { + println("!!!!!!!!!!!! ERROR") + println(coroutineContext) + e.printStackTrace() + } } } From c7d25669ff47dc12e11d199aa9a50b6971c39b4e Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 21 Jul 2024 11:42:41 +0200 Subject: [PATCH 14/21] Update gradle.properties with configureondemand and configuration-cache --- gradle.properties | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 4272d5e550..f6d144f884 100644 --- a/gradle.properties +++ b/gradle.properties @@ -59,9 +59,6 @@ kotlin.mpp.androidSourceSetLayoutVersion1.nowarn=true org.gradle.daemon=true #org.gradle.caching=true -#org.gradle.configureondemand=true -#org.gradle.unsafe.configuration-cache=true -#org.gradle.unsafe.configuration-cache-problems=warn # https://kotlinlang.org/docs/whatsnew17.html#a-new-approach-to-incremental-compilation kotlin.incremental.useClasspathSnapshot=true @@ -76,3 +73,7 @@ org.gradle.parallel=true enableMFVC=false kotlin.mpp.applyDefaultHierarchyTemplate=false + +org.gradle.configureondemand=true +org.gradle.unsafe.configuration-cache=true +org.gradle.unsafe.configuration-cache-problems=warn From 82d60cfd9bc91643ad0671a5bf433987fb3b717b Mon Sep 17 00:00:00 2001 From: soywiz Date: Mon, 22 Jul 2024 16:40:24 +0200 Subject: [PATCH 15/21] More work --- gradle.properties | 6 +++--- .../korlibs/datastructure/event/JsEventLoop.kt | 2 +- korge-sandbox/src/samples/MainAudioScene.kt | 7 ++++++- korge-sandbox/src/samples/MainPolyphonic.kt | 17 ++++++++++++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/gradle.properties b/gradle.properties index f6d144f884..7f714a9b6e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -74,6 +74,6 @@ enableMFVC=false kotlin.mpp.applyDefaultHierarchyTemplate=false -org.gradle.configureondemand=true -org.gradle.unsafe.configuration-cache=true -org.gradle.unsafe.configuration-cache-problems=warn +#org.gradle.configureondemand=true +#org.gradle.unsafe.configuration-cache=true +#org.gradle.unsafe.configuration-cache-problems=warn diff --git a/korge-core/src@js/korlibs/datastructure/event/JsEventLoop.kt b/korge-core/src@js/korlibs/datastructure/event/JsEventLoop.kt index 6c8734d731..f17af54c12 100644 --- a/korge-core/src@js/korlibs/datastructure/event/JsEventLoop.kt +++ b/korge-core/src@js/korlibs/datastructure/event/JsEventLoop.kt @@ -10,7 +10,7 @@ actual fun createPlatformEventLoop(precise: Boolean): SyncEventLoop = open class LocalJsEventLoop( precise: Boolean = false, immediateRun: Boolean = false, -) : SyncEventLoop(precise, immediateRun) { +) : SyncEventLoop(precise) { private var closeable: AutoCloseable? = null override fun start() { diff --git a/korge-sandbox/src/samples/MainAudioScene.kt b/korge-sandbox/src/samples/MainAudioScene.kt index 57ae4620af..bf4561c6a4 100644 --- a/korge-sandbox/src/samples/MainAudioScene.kt +++ b/korge-sandbox/src/samples/MainAudioScene.kt @@ -1,6 +1,9 @@ package samples +import korlibs.audio.format.* +import korlibs.audio.format.mod.* import korlibs.audio.sound.* +import korlibs.image.format.* import korlibs.io.file.std.* import korlibs.korge.input.* import korlibs.korge.scene.* @@ -10,12 +13,13 @@ import kotlin.time.Duration.Companion.seconds class MainAudioScene : Scene() { override suspend fun SContainer.sceneMain() { + defaultAudioFormats.register(MOD, S3M, XM) uiVerticalStack(width = 640.0) { var lastChannel: SoundChannel? = null val len = uiText("Length: 0") suspend fun play(file: String, music: Boolean) { val vfs = resourcesVfs[file] - lastChannel = if (music) vfs.readMusic().play() else vfs.readSound().play() + lastChannel = if (music) vfs.readMusic(AudioDecodingProps()).play() else vfs.readSound().play() len.text = "Length: ${lastChannel?.total}" } fun seekRatio(ratio: Double) { @@ -31,6 +35,7 @@ class MainAudioScene : Scene() { uiButton(label = "Small WAV Music") { onClickSuspend { play("sounds/wav.wav", music = true) } } uiButton(label = "Long MP3 Sound") { onClickSuspend { play("sounds/Snowland.mp3", music = false) } } uiButton(label = "Long MP3 Music") { onClickSuspend { play("sounds/Snowland.mp3", music = true) } } + uiButton(label = "Long XM Music") { onClickSuspend { play("sounds/poliamber.xm", music = true) } } uiHorizontalStack { uiButton(label = "Seek Start") { onClickSuspend { seekRatio(0.001) } } uiButton(label = "Seek 0.25") { onClickSuspend { seekRatio(0.25) } } diff --git a/korge-sandbox/src/samples/MainPolyphonic.kt b/korge-sandbox/src/samples/MainPolyphonic.kt index 9304265909..bbe0a186c7 100644 --- a/korge-sandbox/src/samples/MainPolyphonic.kt +++ b/korge-sandbox/src/samples/MainPolyphonic.kt @@ -7,6 +7,7 @@ import korlibs.korge.ui.* import korlibs.korge.view.* import korlibs.math.* import kotlinx.atomicfu.* +import kotlinx.coroutines.* import kotlin.math.* class MainPolyphonic : Scene() { @@ -23,12 +24,18 @@ class MainPolyphonic : Scene() { channelStates[0].noteIndex.value = 0; nextNote(0) channelStates[1].noteIndex.value = 0; nextNote(1) - for (nchannel in 0 until 2) { - val stream2 = nativeSoundProvider.createNewPlatformAudioOutput(1, 44100) { samples -> - audioOutCallback(nchannel, samples.data, samples.data.size) - samples.scaleVolume(.05f) + CoroutineScope(coroutineContext).launch { + val channels = (0 until 2).map { ch -> + nativeSoundProvider.createNewPlatformAudioOutput(1, 44100) { samples -> + audioOutCallback(ch, samples.data, samples.data.size) + samples.scaleVolume(.05f) + }.also { it.start() } + } + try { + while (channels.all { it.running }) delay(1L) + } finally { + channels.forEach { it.stop() } } - stream2.start() } } From 42fcce38fd1ee619cdd5e142c3bface4ac285f0b Mon Sep 17 00:00:00 2001 From: soywiz Date: Mon, 22 Jul 2024 16:42:06 +0200 Subject: [PATCH 16/21] Moved AudioChannel to Korge --- korge/src/korlibs/audio/sound/AudioChannel.kt | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 korge/src/korlibs/audio/sound/AudioChannel.kt diff --git a/korge/src/korlibs/audio/sound/AudioChannel.kt b/korge/src/korlibs/audio/sound/AudioChannel.kt new file mode 100644 index 0000000000..f7d3e19a8f --- /dev/null +++ b/korge/src/korlibs/audio/sound/AudioChannel.kt @@ -0,0 +1,70 @@ +package korlibs.audio.sound + +import korlibs.time.seconds +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext +import kotlin.time.* + +class AudioChannel( + val nativeSoundProvider: NativeSoundProvider = korlibs.audio.sound.nativeSoundProvider +) { + private var channel: SoundChannel? = null + + val state get() = channel?.state ?: SoundChannelState.INITIAL + + val playing get() = state.playingOrPaused + val current get() = channel?.current ?: 0.seconds + val total get() = channel?.total ?: 0.seconds + + var volume: Double = 1.0 + set(value) { + field = value + channel?.volume = value + } + + var pitch: Double = 1.0 + set(value) { + field = value + channel?.pitch = value + } + + var panning: Double = 0.0 + set(value) { + field = value + channel?.panning = value + } + + fun volume(value: Double): AudioChannel = this.apply { volume = value } + fun pitch(value: Double): AudioChannel = this.apply { pitch = value } + fun panning(value: Double): AudioChannel = this.apply { panning = value } + + fun play( + sound: Sound, + times: PlaybackTimes = 1.playbackTimes, + startTime: Duration = 0.seconds, + coroutineContext: CoroutineContext = sound.defaultCoroutineContext, + ): AudioChannel { + stop() + channel = sound.play(coroutineContext, PlaybackParameters(times = times, startTime = startTime, volume = volume, pitch = pitch, panning = panning)) + return this + } + + suspend fun play( + sound: AudioStream, + times: PlaybackTimes = 1.playbackTimes, + startTime: Duration = 0.seconds, + ): AudioChannel { + return play(nativeSoundProvider.createStreamingSound(sound, true), times, startTime, coroutineContext) + } + + fun stop(): AudioChannel { + channel?.stop() + channel = null + return this + } + + fun reset(): AudioChannel { + channel?.reset() + return this + } +} From ed40b7bf8c5ed007286818881bc56998df8b09d2 Mon Sep 17 00:00:00 2001 From: soywiz Date: Mon, 22 Jul 2024 16:49:21 +0200 Subject: [PATCH 17/21] Minor --- korge-sandbox/src/samples/MainPolyphonic.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/korge-sandbox/src/samples/MainPolyphonic.kt b/korge-sandbox/src/samples/MainPolyphonic.kt index bbe0a186c7..2b5f999027 100644 --- a/korge-sandbox/src/samples/MainPolyphonic.kt +++ b/korge-sandbox/src/samples/MainPolyphonic.kt @@ -12,6 +12,7 @@ import kotlin.math.* class MainPolyphonic : Scene() { // https://github.com/pspdev/pspsdk/blob/master/src/samples/audio/polyphonic/main.c + @OptIn(ExperimentalStdlibApi::class) override suspend fun SContainer.sceneMain() { uiVerticalStack(adjustSize = false) { text("Polyphonic sample by Shine") From 072a0f9f53e47a5ee664266fe1154de9833b39dd Mon Sep 17 00:00:00 2001 From: soywiz Date: Mon, 22 Jul 2024 16:56:44 +0200 Subject: [PATCH 18/21] Updated to 6.0.0-beta1 --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d890ee57b7..9b348711c4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,8 +15,8 @@ kotlinx-coroutines = "1.9.0-RC" kotlinx-serialization = "1.7.0" kotlinx-atomicfu = "0.24.0" -#korlibs = "6.0.0-alpha9" -korlibs = "999.0.0.999" +korlibs = "6.0.0-beta1" +#korlibs = "999.0.0.999" kotlinx-benchmark = "0.4.7" dokka = "1.9.10" From 8aefbaa34c8c46f86b12aa548228735a640d3d7f Mon Sep 17 00:00:00 2001 From: soywiz Date: Tue, 23 Jul 2024 00:30:51 +0200 Subject: [PATCH 19/21] Fix tests --- .../test/korlibs/datastructure/event/SyncEventLoopTest.kt | 2 +- .../test/korlibs/render/SyncEventLoopCoroutineDispatcherTest.kt | 2 +- .../korlibs/datastructure/event/JvmSyncEventLoopTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/korge-core/test/korlibs/datastructure/event/SyncEventLoopTest.kt b/korge-core/test/korlibs/datastructure/event/SyncEventLoopTest.kt index f3bdc5b216..9e634d1768 100644 --- a/korge-core/test/korlibs/datastructure/event/SyncEventLoopTest.kt +++ b/korge-core/test/korlibs/datastructure/event/SyncEventLoopTest.kt @@ -17,7 +17,7 @@ class SyncEventLoopTest { fun test() = suspendTest({ NativeThread.isSupported }) { //fun test() = suspendTest { repeat(2) { - val ep = SyncEventLoop(precise = true) + val ep = SyncEventLoop() val start = TimeSource.Monotonic.markNow() fun log(msg: String) { println("${start.elapsedNow().milliseconds}: $msg") diff --git a/korge-core/test/korlibs/render/SyncEventLoopCoroutineDispatcherTest.kt b/korge-core/test/korlibs/render/SyncEventLoopCoroutineDispatcherTest.kt index 4598065267..00a3914b01 100644 --- a/korge-core/test/korlibs/render/SyncEventLoopCoroutineDispatcherTest.kt +++ b/korge-core/test/korlibs/render/SyncEventLoopCoroutineDispatcherTest.kt @@ -8,7 +8,7 @@ import kotlin.test.* class SyncEventLoopCoroutineDispatcherTest { @Test fun test() { - val dispatcher = SyncEventLoopCoroutineDispatcher(precise = true, immediateRun = true) + val dispatcher = SyncEventLoopCoroutineDispatcher(immediateRun = true) launchImmediately(dispatcher) { println("${DateTime.now()}: a") delay(1000.milliseconds) diff --git a/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt b/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt index 5f3bbb7cf1..5d92752fad 100644 --- a/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt +++ b/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt @@ -18,7 +18,7 @@ class JvmSyncEventLoopTest { fun test() { //val precise = false val precise = true - val el = SyncEventLoop(precise = precise) + val el = SyncEventLoop() val lock = Lock() var n = 0 From 9dcdccf24251685221a9896c1f7c2e79695aaf9d Mon Sep 17 00:00:00 2001 From: soywiz Date: Tue, 23 Jul 2024 00:51:49 +0200 Subject: [PATCH 20/21] Fix tests --- .../korlibs/datastructure/event/WasmJsEventLoop.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/korge-core/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt b/korge-core/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt index 014dbf8e1b..5211a685c1 100644 --- a/korge-core/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt +++ b/korge-core/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt @@ -1,16 +1,14 @@ package korlibs.datastructure.event -import korlibs.datastructure.closeable.* import korlibs.time.* import kotlinx.browser.* actual fun createPlatformEventLoop(precise: Boolean): SyncEventLoop = - LocalJsEventLoop(precise) + LocalJsEventLoop() open class LocalJsEventLoop( - precise: Boolean = false, immediateRun: Boolean = false, -) : SyncEventLoop(precise, immediateRun) { +) : SyncEventLoop(immediateRun) { private var closeable: AutoCloseable? = null override fun start() { From 05c92452223c81d5565a082514cfe8720e8e3ae0 Mon Sep 17 00:00:00 2001 From: soywiz Date: Tue, 23 Jul 2024 01:02:00 +0200 Subject: [PATCH 21/21] Use Java 21 --- .github/workflows/DEPLOY.yml | 2 +- .github/workflows/TEST.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/DEPLOY.yml b/.github/workflows/DEPLOY.yml index 707bdebc61..90a19a32a4 100644 --- a/.github/workflows/DEPLOY.yml +++ b/.github/workflows/DEPLOY.yml @@ -17,7 +17,7 @@ env: ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }} SONATYPE_USERNAME: ${{ secrets.SONATYPEUSERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPEPASSWORD }} - JAVA_VERSION: 17 + JAVA_VERSION: 21 JAVA_DISTRIBUTION: zulu jobs: diff --git a/.github/workflows/TEST.yml b/.github/workflows/TEST.yml index 6717772321..61c9590dfb 100644 --- a/.github/workflows/TEST.yml +++ b/.github/workflows/TEST.yml @@ -15,7 +15,7 @@ env: CI: true SKIP_KORGE_SAMPLES: true DISPLAY: ":99" - JAVA_VERSION: 17 + JAVA_VERSION: 21 JAVA_DISTRIBUTION: zulu ENABLE_BENCHMARKS: false