Add Device Power State (Windows, Linux, Mac and Android) (#197)

Uses native power state methods to display battery percentage and charging state correctly. Mainly for qlaunch.
Tested on Windows, Linux. Mac and Android

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/197
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
Maufeat 2025-06-18 08:34:54 +00:00 committed by Maufeat
parent 6bf5ae700a
commit 8c33b0bb5d
11 changed files with 269 additions and 14 deletions

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu
@ -489,4 +489,9 @@ object NativeLibrary {
* Checks if all necessary keys are present for decryption
*/
external fun areKeysPresent(): Boolean
/**
* Updates the device power state to global variables
*/
external fun updatePowerState(percentage: Int, isCharging: Boolean, hasBattery: Boolean)
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -13,6 +16,7 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.PowerStateUpdater
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@ -40,6 +44,7 @@ class YuzuApplication : Application() {
GpuDriverHelper.initializeDriverParameters()
NativeInput.reloadInputDevices()
NativeLibrary.logDeviceInfo()
PowerStateUpdater.start()
Log.logDeviceInfo()
createNotificationChannels()

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.activities
@ -58,6 +58,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.ThemeHelper
import org.yuzu.yuzu_emu.utils.PowerStateUtils
import java.text.NumberFormat
import kotlin.math.roundToInt

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.os.Handler
import android.os.Looper
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
object PowerStateUpdater {
private lateinit var handler: Handler
private lateinit var runnable: Runnable
private const val UPDATE_INTERVAL_MS = 1000L
private var isStarted = false
fun start() {
if (isStarted) {
return
}
val context = YuzuApplication.appContext
handler = Handler(Looper.getMainLooper())
runnable = Runnable {
val info = PowerStateUtils.getBatteryInfo(context)
NativeLibrary.updatePowerState(info[0], info[1] == 1, info[2] == 1)
handler.postDelayed(runnable, UPDATE_INTERVAL_MS)
}
handler.post(runnable)
isStarted = true
}
fun stop() {
if (!isStarted) {
return
}
if (::handler.isInitialized) {
handler.removeCallbacks(runnable)
}
isStarted = false
}
}

View file

@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
object PowerStateUtils {
@JvmStatic
fun getBatteryInfo(context: Context?): IntArray {
if (context == null) {
return intArrayOf(0, 0, 0) // Percentage, IsCharging, HasBattery
}
val results = intArrayOf(100, 0, 1)
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatusIntent: Intent? = context.registerReceiver(null, iFilter)
if (batteryStatusIntent != null) {
val present = batteryStatusIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true)
if (!present) {
results[2] = 0; results[0] = 0; results[1] = 0; return results
}
results[2] = 1
val level = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
if (level != -1 && scale != -1 && scale != 0) {
results[0] = (level.toFloat() / scale.toFloat() * 100.0f).toInt()
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager?
results[0] = bm?.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) ?: 100
}
val status = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
results[1] = if (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL) 1 else 0
}
return results
}
}

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <codecvt>
#include <locale>
@ -79,6 +79,11 @@ static EmulationSession s_instance;
std::unique_ptr<AndroidMultiplayer> multiplayer{nullptr};
std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
//Power Status default values
std::atomic<int> g_battery_percentage = {100};
std::atomic<bool> g_is_charging = {false};
std::atomic<bool> g_has_battery = {true};
EmulationSession::EmulationSession() {
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
}
@ -1014,4 +1019,16 @@ JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayUnb
JNIEnv* env, [[maybe_unused]] jobject obj, jstring username) {
multiplayer->NetPlayUnbanUser(Common::Android::GetJString(env, username));
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_updatePowerState(
JNIEnv* env,
jobject,
jint percentage,
jboolean isCharging,
jboolean hasBattery) {
g_battery_percentage.store(percentage, std::memory_order_relaxed);
g_is_charging.store(isCharging, std::memory_order_relaxed);
g_has_battery.store(hasBattery, std::memory_order_relaxed);
}
} // extern "C"