Nucleus
Lifecycle

Energy manager

Lower your app's OS scheduling priority and keep the display awake from Kotlin.

The energy-manager module lets you control two forms of power behavior from Kotlin: lowering your app's CPU, I/O, and timer priority when it runs in the background, and preventing the display and system from sleeping during a long task. Both work on Windows, macOS, and Linux through the EnergyManager object.

Add the dependency

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

Throttle a background window

Efficiency mode comes in two levels. Light mode deprioritizes CPU scheduling only; full mode also throttles I/O and, on macOS, network. Tie them to window state — light mode when the window loses focus, full mode when it is minimized:

import dev.nucleusframework.energymanager.EnergyManager

LaunchedEffect(state.isMinimized, isWindowFocused) {
    when {
        state.isMinimized -> {
            EnergyManager.disableLightEfficiencyMode()
            EnergyManager.enableEfficiencyMode()       // full — CPU and I/O throttled
        }
        !isWindowFocused -> {
            EnergyManager.disableEfficiencyMode()
            EnergyManager.enableLightEfficiencyMode()  // CPU only — I/O stays normal
        }
        else -> {
            EnergyManager.disableEfficiencyMode()
            EnergyManager.disableLightEfficiencyMode()
        }
    }
}

Run a coroutine with reduced priority

withEfficiencyMode runs a block on a dedicated background-priority thread and disposes the thread when the block returns. withLightEfficiencyMode applies process-level light QoS for the duration of the block, without pinning it to a thread:

// Dedicated low-priority thread (thread-level efficiency), I/O not throttled
val report = EnergyManager.withEfficiencyMode {
    computeHeavyReport()
}

// Process-level light QoS for the block — useful for batch I/O work
EnergyManager.withLightEfficiencyMode {
    syncDataFromServer()
    writeToDatabase()
}

Both helpers restore the previous mode when the block returns, including when it throws. Manual enable/disable calls are not balanced for you, so pair them yourself.

Keep the system awake

Prevent sleep while a task or presentation runs, then release the request when it finishes. Choose whether the display must stay on:

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

// Presentation / kiosk — screen stays on (default)
EnergyManager.keepAwake(AwakeMode.SYSTEM_AND_DISPLAY)

// Long export / sync — machine stays awake; screen saver and display sleep behave normally
EnergyManager.keepAwake(AwakeMode.SYSTEM_ONLY)

// ... work completes
EnergyManager.releaseAwake()
ModeSystem sleepDisplay / screen saver
SYSTEM_AND_DISPLAY (default)BlockedBlocked — screen stays on
SYSTEM_ONLYBlockedAllowed — screen can blank or lock

Calling keepAwake while a request is already active replaces it with the new mode. keepScreenAwake() / releaseScreenAwake() / isScreenAwakeActive() remain as deprecated forwarders to keepAwake() / releaseAwake() / isAwakeActive().

Hold several awake requests at once

keepAwake owns a single slot: the last caller wins, and one releaseAwake() drops the request for everybody. When independent features each need the machine awake — a video player, a download, a presentation view — acquire a handle per feature instead:

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() // the machine stays awake: `download` is still held
download.close() // last handle gone — the OS request is released

AwakeHandle implements AutoCloseable, so use { } scopes a request to a block:

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

Handles coexist with each other and with keepAwake. The OS request uses the strongest mode any live handle asked for, and is released only once every handle is closed and no unmatched keepAwake is left. close() is idempotent and thread-safe.

Keep the screen on from a composable

Modifier.keepScreenOn() is Compose Multiplatform's own modifier, and on the Tao backend it now takes effect: while the modifier is attached, the display stays awake.

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

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

Prefer it over the imperative calls for anything tied to a piece of UI: the request lives exactly as long as the composable is in the composition, so there is no release to forget.

Under the hood it is refcounted process-wide and backed by one acquireAwake(AwakeMode.SYSTEM_AND_DISPLAY) handle, so several windows or popups can ask at the same time without releasing each other, and none of them disturbs a keepAwake slot your app owns. If the request cannot be honored the modifier degrades silently and logs at FINE.

The AWT backends do not implement the platform hook, so the modifier composes but has no effect there.

How it works

Efficiency mode has two levels. Light mode leaves the app fully functional in the background: CPU scheduling is deprioritized through QoS hints, but I/O and network run at normal speed, so sync jobs and downloads still make progress. Full mode is meant for a minimized window where there is no UI to render — it adds I/O throttling and drops the process to the lowest priority class. Both are reversible immediately.

Thread-level efficiency applies the same mechanism to the calling thread only. It confines one heavy coroutine to the slow lane without slowing the rest of the process, which keeps a responsive UI alongside background work. The withEfficiencyMode helper builds on this by running its block on a single pinned thread.

Each platform maps these levels to native OS facilities:

OSFull modeLight modeSYSTEM_AND_DISPLAYSYSTEM_ONLY
Windows 11+SetProcessInformation EcoQoS + IDLE_PRIORITY_CLASS (green leaf in Task Manager 22H2+)EcoQoS onlySetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)ES_SYSTEM_REQUIRED only
macOSsetpriority(PRIO_DARWIN_BG) + task_policy_set QoS Tier 5 (E-core confinement, I/O and network throttling on Apple Silicon)task_policy_set Tier 5 only — CPU deprioritized, I/O unaffectedIOPMAssertion(kIOPMAssertPreventUserIdleDisplaySleep)kIOPMAssertPreventUserIdleSystemSleep (like caffeinate -i; lid close still sleeps)
Linuxnice +19 + ioprio IDLE + timerslack 100msnice +10 onlyGNOME SessionManager / PowerManagement / logind / X11Suspend-only session inhibitor (no X11 screen-saver fallback)

Thread-level mode uses the per-thread form of these primitives on Linux, pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND) on macOS, and SetThreadInformation/THREAD_PRIORITY_IDLE on Windows.

API reference

EnergyManager

MethodDescription
isAvailable(): BooleanWhether the native bridge loaded on this platform.
enableEfficiencyMode() / disableEfficiencyMode()Full process mode.
enableLightEfficiencyMode() / disableLightEfficiencyMode()Light process mode.
enableThreadEfficiencyMode() / disableThreadEfficiencyMode()Per-thread; affects the calling thread only.
withEfficiencyMode { } (suspend)Runs the block on a dedicated thread with thread-level efficiency; the thread is disposed when the block returns.
withLightEfficiencyMode { } (suspend)Applies process-level light QoS for the block's lifetime; no thread pinning.
keepAwake(mode) / releaseAwake()Prevent and restore system (and optionally display) sleep. One shared slot; the last mode wins.
acquireAwake(mode): AwakeHandleIndependent awake request released by AwakeHandle.close().
isAwakeActive(): BooleanWhether an awake request is currently held.
keepScreenAwake() / releaseScreenAwake() / isScreenAwakeActive()Deprecated forwarders to the keepAwake / releaseAwake / isAwakeActive APIs.

AwakeMode

ValueDescription
SYSTEM_AND_DISPLAYBlock system and display sleep (default).
SYSTEM_ONLYBlock system sleep only; display and screen saver behave normally.

AwakeHandle

MemberDescription
mode: AwakeModeThe mode this handle requested.
isActive: Booleanfalse after close().
close()Drops this request. Idempotent and thread-safe.

Every enable/disable call and keepAwake/releaseAwake returns an EnergyManager.Result with success: Boolean, errorCode: Int, and message: String. On an unsupported platform the result is success = false with errorCode = -1.

Notes

  • On Linux, libdbus-1, libX11, and libXss are loaded through dlopen() at runtime. Missing libraries degrade gracefully, and a private DBus connection is used so the module does not interfere with the JVM's accessibility bus. SYSTEM_ONLY needs a session bus (GNOME, PowerManagement) or systemd-logind; the X11 screen-saver fallback cannot serve that mode.
  • On Windows 10 1709+, EcoQoS applies only on battery; Windows 11+ honors it on AC power too.
  • For a long task that must also wake the device, hold keepAwake(…) alongside a job from the scheduler.

What's next