Skip to content

Commit af5bed9

Browse files
committed
feat(player): Fix an issue where the screen would freeze when playing a video on Windows
1 parent 8e96832 commit af5bed9

File tree

1 file changed

+37
-0
lines changed
  • composeApp/src/jvmMain/kotlin/com/jankinwu/fntv/client

1 file changed

+37
-0
lines changed

composeApp/src/jvmMain/kotlin/com/jankinwu/fntv/client/main.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import com.jankinwu.fntv.client.viewmodel.UiState
5656
import com.jankinwu.fntv.client.viewmodel.UserInfoViewModel
5757
import com.jankinwu.fntv.client.viewmodel.viewModelModule
5858
import com.jankinwu.fntv.client.window.WindowFrame
59+
import com.sun.jna.platform.win32.Kernel32
5960
import dev.datlag.kcef.KCEF
6061
import fntv_client_multiplatform.composeapp.generated.resources.Res
6162
import fntv_client_multiplatform.composeapp.generated.resources.icon
@@ -67,6 +68,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
6768
import kotlinx.coroutines.flow.debounce
6869
import kotlinx.coroutines.launch
6970
import org.jetbrains.compose.resources.painterResource
71+
import org.openani.mediamp.PlaybackState
7072
import org.koin.compose.KoinApplication
7173
import org.koin.compose.viewmodel.koinViewModel
7274
import org.openani.mediamp.compose.rememberMediampPlayer
@@ -75,6 +77,31 @@ import java.awt.Frame
7577
import java.io.File
7678
import 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)
79106
fun 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

Comments
 (0)