diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowButtonsConfiguration.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowButtonsConfiguration.kt index 678056c0272c..6e0e0abc5582 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowButtonsConfiguration.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowButtonsConfiguration.kt @@ -1,19 +1,23 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. @file:ApiStatus.Internal + package com.intellij.openapi.wm.impl -import com.intellij.ide.AppLifecycleListener import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.components.StoragePathMacros import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.util.SystemInfoRt -import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.registry.RegistryManager +import com.intellij.util.concurrency.ThreadingAssertions +import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus @@ -22,7 +26,7 @@ import org.jetbrains.annotations.ApiStatus */ @ApiStatus.Internal @State(name = "WindowButtonsConfiguration", storages = [Storage(StoragePathMacros.CACHE_FILE)]) -class WindowButtonsConfiguration(private val scope: CoroutineScope) : PersistentStateComponent { +class WindowButtonsConfiguration(scope: CoroutineScope) : PersistentStateComponent { enum class WindowButton { MINIMIZE, MAXIMIZE, @@ -32,6 +36,9 @@ class WindowButtonsConfiguration(private val scope: CoroutineScope) : Persistent companion object { fun getInstance(): WindowButtonsConfiguration? = if (isSupported()) service() else null + private val log = thisLogger() + private const val WINDOW_BUTTONS_CONFIG_KEY = "ide.linux.window.buttons.config" + private fun isSupported(): Boolean { return SystemInfoRt.isLinux } @@ -40,35 +47,55 @@ class WindowButtonsConfiguration(private val scope: CoroutineScope) : Persistent private var mutableStateFlow = MutableStateFlow(null) val stateFlow: StateFlow = mutableStateFlow.asStateFlow() + init { + if (isSupported()) { + scope.launch(CoroutineName("WindowButtonsConfiguration")) { + val registryManager = RegistryManager.getInstanceAsync() + registryManager.awaitRegistryLoad() + + var state = loadStateFromRegistry(registryManager) ?: loadStateFromOs() + mutableStateFlow.update { state } + } + } + } + override fun getState(): State? { return mutableStateFlow.value } override fun loadState(state: State) { - mutableStateFlow.value = state - scheduleUpdateFromOs() - } - - override fun noStateLoaded() { - scheduleUpdateFromOs() - } - - fun scheduleUpdateFromOs(customConfig: String? = null) { - scope.launch { - loadStateFromOs(customConfig) + mutableStateFlow.update { currentState -> + // The saved state is used only for caching, the actual computed state takes priority. + // Normally the state isn't computed yet, unless loadState is called when the service is already up and running. + // This is possible, according to the API docs, but only if the files are modified when the IDE is running. + // In this case we definitely don't want to pick up the modified value, as the sources of truth are the OS and the registry. + currentState ?: state } } - private fun loadStateFromOs(customConfig: String?) { - var windowButtonsState: State? = null - - if (isSupported()) { - val config = if (customConfig.isNullOrBlank()) X11UiUtil.getWindowButtonsConfig() else customConfig - if (config != null) { - windowButtonsState = parseFromString(config) - } + private fun loadStateFromRegistry(registryManager: RegistryManager): State? { + val value = registryManager.stringValue(WINDOW_BUTTONS_CONFIG_KEY) + if (value.isNullOrEmpty()) { + return null } - mutableStateFlow.value = windowButtonsState + + val result = parseFromString(value) + if (result == null) { + log.warn("Failed to parse '$WINDOW_BUTTONS_CONFIG_KEY' registry value: $value") + } + + return result + } + + private fun loadStateFromOs(): State? { + ThreadingAssertions.assertBackgroundThread() + + val config = X11UiUtil.getWindowButtonsConfig() ?: return null + val result = parseFromString(config) + if (result == null) { + log.warn("Failed to parse OS window buttons config: $config") + } + return result } class State { @@ -113,10 +140,3 @@ private fun stringsToWindowButtons(strings: List): List -