0

I'm implementing a chess clock in Jetpack Compose for Desktop. But it seems I'm facing a strange behaviour : on Linux (at least on Ubuntu 22.04 64 bits), when you switch window so that the clock host is in background process, the clock is paused instead of going on. Whereas on Windows 11 (64 bits) it works perfectly.

My build.gradle.kts

import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose")
}

group = "com.loloof64"
version = "1.0-SNAPSHOT"

repositories {
    google()
    mavenCentral()
    maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}

kotlin {
    jvm {
        jvmToolchain(11)
        withJava()
    }
    sourceSets {
        val jvmMain by getting {
            dependencies {
                implementation(compose.desktop.currentOs)
            }
        }
        val jvmTest by getting
    }
}

compose.desktop {
    application {
        mainClass = "MainKt"
        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "PeerChess"
            packageVersion = "1.0.0"
        }
    }
}

My source file Main.kt

@Composable
fun SimpleClockComponent(
    modifier: Modifier = Modifier,
    whiteTimeInDeciSeconds: Int,
    blackTimeInDeciSeconds: Int,
    whiteTimeActive: Boolean,
) {
    val whiteText = getTimeText(whiteTimeInDeciSeconds)
    val blackText = getTimeText(blackTimeInDeciSeconds)

    var whiteZoneFgColor = Color.Black
    var blackZoneFgColor = Color.White

    var whiteZoneBgColor = Color.White
    var blackZoneBgColor = Color.Black

    if (whiteTimeActive) {
        if (whiteTimeInDeciSeconds < 600) {
            whiteZoneBgColor = Color.Red
            whiteZoneFgColor = Color.White
        } else {
            whiteZoneBgColor = Color.Blue
            whiteZoneFgColor = Color.Yellow
        }
    } else {
        if (blackTimeInDeciSeconds < 600) {
            blackZoneBgColor = Color.Red
            blackZoneFgColor = Color.White
        } else {
            blackZoneBgColor = Color.Blue
            blackZoneFgColor = Color.Yellow
        }
    }

    Row(
        modifier = modifier.fillMaxWidth().fillMaxHeight(0.1f).border(width = 1.dp, color = Color.Black)
    ) {
        Box(
            modifier = Modifier.fillMaxWidth(0.5f).fillMaxHeight().background(whiteZoneBgColor),
            contentAlignment = Alignment.Center,
        ) {
            Text(
                text = whiteText,
                color = whiteZoneFgColor,
                textAlign = TextAlign.Center,
            )
        }
        Box(
            modifier = Modifier.fillMaxWidth().fillMaxHeight().background(blackZoneBgColor),
            contentAlignment = Alignment.Center,
        ) {
            Text(
                text = blackText,
                color = blackZoneFgColor,
                textAlign = TextAlign.Center,
            )
        }
    }
}


fun startClock(
    coroutineScope: CoroutineScope,
    whiteTimeActive: Boolean,
    onWhiteTimeDec: () -> Unit,
    onBlackTimeDec: () -> Unit
) : Job? {
    return coroutineScope.launch {
        while (isActive) {
            delay(100)
            if (whiteTimeActive) {
                onWhiteTimeDec()
            } else {
                onBlackTimeDec()
            }
        }
    }
}

@Preview
@Composable
fun WorkingClockComponentPreview() {
    val allocatedTimeDeciSeconds = 15 * 10
    val coroutineScope = rememberCoroutineScope()
    var whiteTimeInDeciSeconds by remember { mutableStateOf(allocatedTimeDeciSeconds) }
    var blackTimeInDeciSeconds by remember { mutableStateOf(allocatedTimeDeciSeconds) }
    var clockActive by remember { mutableStateOf(false) }
    var clockJob by remember { mutableStateOf<Job?>(null) }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        SimpleClockComponent(
            whiteTimeInDeciSeconds = whiteTimeInDeciSeconds,
            blackTimeInDeciSeconds = blackTimeInDeciSeconds,
            whiteTimeActive = true,
        )
        Button({
            whiteTimeInDeciSeconds = allocatedTimeDeciSeconds
            blackTimeInDeciSeconds = allocatedTimeDeciSeconds
            clockJob = startClock(coroutineScope, true, {
                whiteTimeInDeciSeconds--
                if (whiteTimeInDeciSeconds <= 0) {
                    clockActive = false
                    clockJob?.cancel()
                }
            }, {
                blackTimeInDeciSeconds--
                if (blackTimeInDeciSeconds <= 0) {
                    clockActive = false
                    clockJob?.cancel()
                }
            })
        }) {
            Text("Start")
        }
    }
}

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        window.minimumSize = Dimension(400, 500)
        WorkingClockComponentPreview()
    }
}

I've tested with Intellij Idea Community 2022.3.3

So what's wrong with my code ?

loloof64
  • 5,252
  • 12
  • 41
  • 78

1 Answers1

0

In fact the problem was not platform-related, but it was based on the Dispatcher used to launch the coroutine.

I must set the Dispatcher to Default, and not leaving the coroutine to the Main dispatcher.

coroutineScope.launch {}

is incorrect

coroutineScope.launch(Dispatcher.Default) {}

is better in my case.

loloof64
  • 5,252
  • 12
  • 41
  • 78