@@ -56,6 +56,7 @@ import com.jankinwu.fntv.client.viewmodel.UiState
5656import com.jankinwu.fntv.client.viewmodel.UserInfoViewModel
5757import com.jankinwu.fntv.client.viewmodel.viewModelModule
5858import com.jankinwu.fntv.client.window.WindowFrame
59+ import com.sun.jna.platform.win32.Kernel32
5960import dev.datlag.kcef.KCEF
6061import fntv_client_multiplatform.composeapp.generated.resources.Res
6162import fntv_client_multiplatform.composeapp.generated.resources.icon
@@ -67,6 +68,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
6768import kotlinx.coroutines.flow.debounce
6869import kotlinx.coroutines.launch
6970import org.jetbrains.compose.resources.painterResource
71+ import org.openani.mediamp.PlaybackState
7072import org.koin.compose.KoinApplication
7173import org.koin.compose.viewmodel.koinViewModel
7274import org.openani.mediamp.compose.rememberMediampPlayer
@@ -75,6 +77,31 @@ import java.awt.Frame
7577import java.io.File
7678import java.util.concurrent.atomic.AtomicBoolean
7779
80+ private object WindowsDisplaySleepBlocker {
81+ private const val ES_SYSTEM_REQUIRED = 0x00000001
82+ private const val ES_DISPLAY_REQUIRED = 0x00000002
83+ private const val ES_CONTINUOUS = 0x80000000 .toInt()
84+
85+ private val logger = Logger .withTag(" WindowsDisplaySleepBlocker" )
86+
87+ fun setEnabled (enabled : Boolean ) {
88+ if (! currentPlatform().isWindows()) return
89+ try {
90+ val flags = if (enabled) {
91+ ES_CONTINUOUS or ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED
92+ } else {
93+ ES_CONTINUOUS
94+ }
95+ val previous = Kernel32 .INSTANCE .SetThreadExecutionState (flags)
96+ if (previous == 0 ) {
97+ logger.w { " SetThreadExecutionState returned 0 (failed), enabled=$enabled " }
98+ }
99+ } catch (t: Throwable ) {
100+ logger.w(t) { " Failed to set execution state, enabled=$enabled " }
101+ }
102+ }
103+ }
104+
78105@OptIn(FlowPreview ::class )
79106fun main () {
80107 val logDir = initializeLoggingDirectory()
@@ -253,6 +280,16 @@ fun main() {
253280 icon = icon,
254281 undecorated = false
255282 ) {
283+ val playState by player.playbackState.collectAsState()
284+ val shouldBlockDisplaySleep = playState == PlaybackState .PLAYING
285+
286+ DisposableEffect (shouldBlockDisplaySleep) {
287+ WindowsDisplaySleepBlocker .setEnabled(shouldBlockDisplaySleep)
288+ onDispose {
289+ WindowsDisplaySleepBlocker .setEnabled(false )
290+ }
291+ }
292+
256293 LaunchedEffect (Unit ) {
257294 val baseWidth = 600
258295 val baseHeight = 400
0 commit comments