cancel rumble on disconnect
apple / swift (push) Successful in 56s
ci / rust (push) Successful in 1m37s
ci / web (push) Successful in 28s
ci / docs-site (push) Successful in 33s
android / android (push) Failing after 3m55s
deb / build-publish (push) Successful in 2m27s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
ci / bench (push) Successful in 4m40s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m36s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m28s
docker / deploy-docs (push) Successful in 19s

hide system bar in StreamScreen.kt
This commit is contained in:
2026-06-18 17:20:57 +02:00
parent b8c9f88cfd
commit 02b1be652d
2 changed files with 17 additions and 2 deletions
@@ -12,12 +12,16 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import io.unom.punktfunk.kit.Gamepad import io.unom.punktfunk.kit.Gamepad
import io.unom.punktfunk.kit.GamepadFeedback import io.unom.punktfunk.kit.GamepadFeedback
import io.unom.punktfunk.kit.NativeBridge import io.unom.punktfunk.kit.NativeBridge
@@ -28,6 +32,10 @@ fun StreamScreen(handle: Long, micEnabled: Boolean, onDisconnect: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val activity = context as? MainActivity val activity = context as? MainActivity
val window = activity?.window val window = activity?.window
val controller = remember(window) {
window?.let { WindowCompat.getInsetsController(it, it.decorView) }
}
// Start mic only if the user enabled it AND granted RECORD_AUDIO (else the AAudio input fails). // Start mic only if the user enabled it AND granted RECORD_AUDIO (else the AAudio input fails).
val micWanted = micEnabled && ContextCompat.checkSelfPermission( val micWanted = micEnabled && ContextCompat.checkSelfPermission(
context, context,
@@ -36,6 +44,10 @@ fun StreamScreen(handle: Long, micEnabled: Boolean, onDisconnect: () -> Unit) {
DisposableEffect(handle) { DisposableEffect(handle) {
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
controller?.let {
it.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
it.hide(WindowInsetsCompat.Type.systemBars())
}
activity?.streamHandle = handle // route hardware keys to this session activity?.streamHandle = handle // route hardware keys to this session
activity?.axisMapper = Gamepad.AxisMapper(handle) // route joystick axes activity?.axisMapper = Gamepad.AxisMapper(handle) // route joystick axes
// Host→client feedback (rumble + DualSense lightbar/LEDs); poll threads stopped before close. // Host→client feedback (rumble + DualSense lightbar/LEDs); poll threads stopped before close.
@@ -45,6 +57,7 @@ fun StreamScreen(handle: Long, micEnabled: Boolean, onDisconnect: () -> Unit) {
activity?.axisMapper?.reset() // release-all so nothing sticks on the host activity?.axisMapper?.reset() // release-all so nothing sticks on the host
activity?.axisMapper = null activity?.axisMapper = null
activity?.streamHandle = 0L activity?.streamHandle = 0L
controller?.show(WindowInsetsCompat.Type.systemBars())
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// Leaving the stream: stop the mic + audio + decode threads and tear down the session. // Leaving the stream: stop the mic + audio + decode threads and tear down the session.
NativeBridge.nativeStopMic(handle) NativeBridge.nativeStopMic(handle)
@@ -78,9 +78,11 @@ class GamepadFeedback(private val handle: Long) {
/** Idempotent. Stops + joins the poll threads (must complete before the session handle is freed). */ /** Idempotent. Stops + joins the poll threads (must complete before the session handle is freed). */
fun stop() { fun stop() {
running = false running = false
rumbleThread?.interrupt()
hidoutThread?.interrupt()
runCatching { vm?.cancel() } // drop any held rumble immediately runCatching { vm?.cancel() } // drop any held rumble immediately
runCatching { rumbleThread?.join(500) } runCatching { rumbleThread?.join(200) }
runCatching { hidoutThread?.join(500) } runCatching { hidoutThread?.join(200) }
rumbleThread = null rumbleThread = null
hidoutThread = null hidoutThread = null
runCatching { lightsSession?.close() } runCatching { lightsSession?.close() }