Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Compose for Desktop get and listen to Gtk Dark/Light Theme changes
// implementation("org.zeroturnaround:zt-exec:1.12") LIBRARY USAGED
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import kotlin.coroutines.resume
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.suspendCancellableCoroutine
import org.zeroturnaround.exec.ProcessExecutor
import org.zeroturnaround.exec.stream.LogOutputStream
private const val DCONF_THEME_KEY_NAME = "/org/gnome/desktop/interface/gtk-theme"
private const val DCONF_WATCH_THEME_COMMAND = "dconf watch $DCONF_THEME_KEY_NAME"
private const val DCONF_READ_THEME_COMMAND = "dconf read $DCONF_THEME_KEY_NAME"
@Composable
fun isGtkThemeDark(): Boolean {
return gtkThemeChangesFlow().collectAsState("").value
.contains("dark", ignoreCase = true)
}
/** Emits the name of the new theme. */
private fun gtkThemeChangesFlow(): Flow<String> {
val watchThemeCommandFlow = callbackFlow<String> {
val process = ProcessExecutor().commandSplit(DCONF_WATCH_THEME_COMMAND)
.redirectOutput(object : LogOutputStream() {
override fun processLine(line: String) {
this@callbackFlow.offer(line.trim())
}
})
.start()
awaitClose { process.process.destroy() }
}
return flow<String> {
var previousIsKey = false
watchThemeCommandFlow.collect {
if(previousIsKey) {
previousIsKey = false
emit(it)
}
if(it == DCONF_THEME_KEY_NAME) previousIsKey = true
}
}.onStart {
emit(currentlyGtkTheme())
}
}
/** Get the currently system theme */
private suspend fun currentlyGtkTheme(): String {
return suspendCancellableCoroutine<String> { continuation ->
val process = ProcessExecutor().commandSplit(DCONF_READ_THEME_COMMAND)
.redirectOutput(object : LogOutputStream() {
override fun processLine(line: String) {
if(continuation.isActive)
continuation.resume(line)
}
})
.start()
continuation.invokeOnCancellation { process.process.destroy() }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment