Nucleus
Lifecycle

Energy manager

Abaissez la priorité d'ordonnancement OS de votre app et empêchez l'écran de s'éteindre, depuis Kotlin.

Le module energy-manager vous permet de contrôler deux comportements d'énergie depuis Kotlin : abaisser la priorité CPU, I/O et timers de votre app quand elle tourne en arrière-plan, et empêcher l'écran et le système de se mettre en veille pendant une tâche longue. Les deux fonctionnent sur Windows, macOS et Linux via l'objet EnergyManager.

Ajouter la dépendance

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.energy-manager:2.5.15")
}

Throttler une fenêtre en arrière-plan

Le mode efficacité a deux niveaux. Le mode light déprioritise seulement l'ordonnancement CPU ; le mode full throttle aussi les I/O et, sur macOS, le réseau. Associez-les à l'état de la fenêtre — mode light à la perte de focus, mode full à la minimisation :

import dev.nucleusframework.energymanager.EnergyManager

LaunchedEffect(state.isMinimized, isWindowFocused) {
    when {
        state.isMinimized -> {
            EnergyManager.disableLightEfficiencyMode()
            EnergyManager.enableEfficiencyMode()       // full — CPU et I/O throttlés
        }
        !isWindowFocused -> {
            EnergyManager.disableEfficiencyMode()
            EnergyManager.enableLightEfficiencyMode()  // CPU seul — I/O reste normal
        }
        else -> {
            EnergyManager.disableEfficiencyMode()
            EnergyManager.disableLightEfficiencyMode()
        }
    }
}

Exécuter une coroutine à priorité réduite

withEfficiencyMode exécute un bloc sur un thread dédié à priorité arrière-plan et libère le thread au retour du bloc. withLightEfficiencyMode applique une QoS light au niveau process pour la durée du bloc, sans l'épingler à un thread :

// Thread dédié à basse priorité (efficacité thread), I/O non throttlés
val report = EnergyManager.withEfficiencyMode {
    computeHeavyReport()
}

// QoS light au niveau process pour le bloc — utile pour du travail I/O par lots
EnergyManager.withLightEfficiencyMode {
    syncDataFromServer()
    writeToDatabase()
}

Les deux helpers restaurent le mode précédent au retour du bloc, y compris s'il lève une exception. Les appels enable/disable manuels ne sont pas équilibrés pour vous : à vous de les appairer.

Garder le système éveillé

Empêchez la mise en veille pendant une tâche ou une présentation, puis relâchez la demande à la fin. Choisissez si l'écran doit rester allumé :

import dev.nucleusframework.energymanager.AwakeMode
import dev.nucleusframework.energymanager.EnergyManager

// Présentation / kiosque — l'écran reste allumé (défaut)
EnergyManager.keepAwake(AwakeMode.SYSTEM_AND_DISPLAY)

// Export / sync long — la machine reste éveillée ; l'économiseur d'écran et la veille
// d'affichage se comportent normalement
EnergyManager.keepAwake(AwakeMode.SYSTEM_ONLY)

// ... travail terminé
EnergyManager.releaseAwake()
ModeVeille systèmeÉcran / économiseur
SYSTEM_AND_DISPLAY (défaut)BloquéeBloquée — l'écran reste allumé
SYSTEM_ONLYBloquéeAutorisée — l'écran peut s'éteindre ou se verrouiller

Un appel à keepAwake alors qu'une demande est déjà active la remplace par le nouveau mode. keepScreenAwake() / releaseScreenAwake() / isScreenAwakeActive() restent des forwarders dépréciés vers keepAwake() / releaseAwake() / isAwakeActive().

Tenir plusieurs demandes d'éveil à la fois

keepAwake occupe un slot unique : le dernier appelant gagne, et un seul releaseAwake() annule la demande pour tout le monde. Quand plusieurs fonctionnalités indépendantes ont besoin de la machine éveillée — un lecteur vidéo, un téléchargement, une vue présentation — prenez un handle par fonctionnalité :

import dev.nucleusframework.energymanager.AwakeMode
import dev.nucleusframework.energymanager.EnergyManager

val playback = EnergyManager.acquireAwake(AwakeMode.SYSTEM_AND_DISPLAY)
val download = EnergyManager.acquireAwake(AwakeMode.SYSTEM_ONLY)

playback.close() // la machine reste éveillée : `download` est toujours tenu
download.close() // dernier handle libéré — la demande OS est relâchée

AwakeHandle implémente AutoCloseable, donc use { } borne une demande à un bloc :

EnergyManager.acquireAwake(AwakeMode.SYSTEM_ONLY).use {
    exportProject()
}

Les handles coexistent entre eux et avec keepAwake. La demande envoyée à l'OS retient le mode le plus fort réclamé par un handle vivant, et n'est relâchée qu'une fois tous les handles fermés et sans keepAwake pendant. close() est idempotent et thread-safe.

Garder l'écran allumé depuis un composable

Modifier.keepScreenOn() est le modifier de Compose Multiplatform lui-même et, sur le backend Tao, il prend désormais effet : tant que le modifier est attaché, l'écran reste allumé.

import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Modifier
import androidx.compose.ui.keepScreenOn

@Composable
fun VideoSurface() {
    Box(Modifier.keepScreenOn()) {
        Player()
    }
}

Préférez-le aux appels impératifs pour tout ce qui est lié à un morceau d'UI : la demande vit exactement le temps où le composable reste dans la composition, il n'y a donc aucun relâchement à oublier.

En interne, il est décompté à l'échelle du processus et adossé à un seul handle acquireAwake(AwakeMode.SYSTEM_AND_DISPLAY) : plusieurs fenêtres ou popups peuvent donc le demander en même temps sans se relâcher mutuellement, et aucune ne perturbe un slot keepAwake que votre application détient. Si la demande ne peut pas être honorée, le modifier se dégrade silencieusement et journalise en FINE.

Les backends AWT n'implémentent pas le hook plateforme : le modifier s'y compose mais reste sans effet.

Fonctionnement

Le mode efficacité a deux niveaux. Le mode light laisse l'app pleinement fonctionnelle en arrière-plan : l'ordonnancement CPU est déprioritisé via des hints QoS, mais les I/O et le réseau tournent à vitesse normale, donc les jobs de synchronisation et les téléchargements continuent d'avancer. Le mode full vise une fenêtre minimisée, sans UI à rendre — il ajoute le throttling des I/O et fait passer le process dans la classe de priorité la plus basse. Les deux sont réversibles immédiatement.

L'efficacité au niveau thread applique le même mécanisme au seul thread appelant. Elle confine une coroutine lourde à la voie lente sans ralentir le reste du process, ce qui garde une UI réactive à côté du travail de fond. Le helper withEfficiencyMode s'appuie dessus en exécutant son bloc sur un thread unique épinglé.

Chaque plateforme mappe ces niveaux sur des fonctions natives de l'OS :

OSMode fullMode lightSYSTEM_AND_DISPLAYSYSTEM_ONLY
Windows 11+SetProcessInformation EcoQoS + IDLE_PRIORITY_CLASS (feuille verte dans Task Manager 22H2+)EcoQoS uniquementSetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)ES_SYSTEM_REQUIRED uniquement
macOSsetpriority(PRIO_DARWIN_BG) + task_policy_set QoS Tier 5 (confinement E-cores, throttling I/O et réseau sur Apple Silicon)task_policy_set Tier 5 uniquement — CPU déprioritisé, I/O intactIOPMAssertion(kIOPMAssertPreventUserIdleDisplaySleep)kIOPMAssertPreventUserIdleSystemSleep (comme caffeinate -i ; fermer le capot endort toujours)
Linuxnice +19 + ioprio IDLE + timerslack 100msnice +10 uniquementGNOME SessionManager / PowerManagement / logind / X11Inhibiteur de session suspend-only (pas de fallback X11 screen-saver)

Le mode thread utilise la forme par-thread de ces primitives sur Linux, pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND) sur macOS, et SetThreadInformation/THREAD_PRIORITY_IDLE sur Windows.

Référence de l'API

EnergyManager

MéthodeDescription
isAvailable(): BooleanSi le bridge natif a été chargé sur cette plateforme.
enableEfficiencyMode() / disableEfficiencyMode()Mode full au niveau process.
enableLightEfficiencyMode() / disableLightEfficiencyMode()Mode light au niveau process.
enableThreadEfficiencyMode() / disableThreadEfficiencyMode()Par thread ; n'affecte que le thread appelant.
withEfficiencyMode { } (suspend)Exécute le bloc sur un thread dédié à efficacité thread ; le thread est libéré au retour du bloc.
withLightEfficiencyMode { } (suspend)Applique une QoS light au niveau process pour la durée du bloc ; pas d'épinglage de thread.
keepAwake(mode) / releaseAwake()Empêche puis restaure la veille système (et éventuellement l'écran). Slot partagé unique : le dernier mode gagne.
acquireAwake(mode): AwakeHandleDemande d'éveil indépendante, relâchée par AwakeHandle.close().
isAwakeActive(): BooleanSi une demande d'éveil est active.
keepScreenAwake() / releaseScreenAwake() / isScreenAwakeActive()Forwarders dépréciés vers keepAwake / releaseAwake / isAwakeActive.

AwakeMode

ValeurDescription
SYSTEM_AND_DISPLAYBloque la veille système et l'écran (défaut).
SYSTEM_ONLYBloque uniquement la veille système ; l'écran et l'économiseur se comportent normalement.

AwakeHandle

MembreDescription
mode: AwakeModeLe mode demandé par ce handle.
isActive: Booleanfalse après close().
close()Relâche cette demande. Idempotent et thread-safe.

Chaque appel enable/disable et keepAwake/releaseAwake renvoie un EnergyManager.Result avec success: Boolean, errorCode: Int et message: String. Sur une plateforme non supportée, le résultat est success = false avec errorCode = -1.

Notes

  • Sur Linux, libdbus-1, libX11 et libXss sont chargées via dlopen() au runtime. Les bibliothèques manquantes dégradent proprement, et une connexion DBus privée évite que le module interfère avec le bus d'accessibilité de la JVM. SYSTEM_ONLY exige un bus de session (GNOME, PowerManagement) ou systemd-logind ; le fallback X11 screen-saver ne peut pas servir ce mode.
  • Sur Windows 10 1709+, EcoQoS ne s'applique que sur batterie ; Windows 11+ le respecte aussi sur secteur.
  • Pour une tâche longue qui doit aussi réveiller la machine, maintenez keepAwake(…) en parallèle d'un job du scheduler.

Et ensuite

  • Scheduler — exécute du travail de fond différé ou périodique.
  • Taskbar progress — affiche la progression d'une tâche longue dans la barre des tâches.
  • Auto-launch — lance votre app au login.