Browse Source
Add Device Power State (Windows, Linux, Mac and Android) (#197)
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>pull/21/head
committed by
Maufeat
11 changed files with 269 additions and 14 deletions
-
11src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
-
5src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
-
7src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
-
46src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUpdater.kt
-
45src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUtils.kt
-
23src/android/app/src/main/jni/native.cpp
-
2src/common/CMakeLists.txt
-
102src/common/device_power_state.cpp
-
14src/common/device_power_state.h
-
22src/core/hle/service/ptm/psm.cpp
-
6src/core/hle/service/ptm/psm.h
@ -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 |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "device_power_state.h"
|
||||
|
|
||||
|
#if defined(_WIN32)
|
||||
|
#include <windows.h>
|
||||
|
|
||||
|
#elif defined(__ANDROID__)
|
||||
|
#include <atomic>
|
||||
|
extern std::atomic<int> g_battery_percentage; |
||||
|
extern std::atomic<bool> g_is_charging; |
||||
|
extern std::atomic<bool> g_has_battery; |
||||
|
|
||||
|
#elif defined(__APPLE__)
|
||||
|
#include <TargetConditionals.h>
|
||||
|
#if TARGET_OS_MAC
|
||||
|
#include <IOKit/ps/IOPSKeys.h>
|
||||
|
#include <IOKit/ps/IOPowerSources.h>
|
||||
|
#endif
|
||||
|
|
||||
|
#elif defined(__linux__)
|
||||
|
#include <fstream>
|
||||
|
#include <string>
|
||||
|
#include <dirent.h>
|
||||
|
#endif
|
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
PowerStatus GetPowerStatus() |
||||
|
{ |
||||
|
PowerStatus info; |
||||
|
|
||||
|
#if defined(_WIN32)
|
||||
|
SYSTEM_POWER_STATUS status; |
||||
|
if (GetSystemPowerStatus(&status)) { |
||||
|
if (status.BatteryFlag == 128) { |
||||
|
info.has_battery = false; |
||||
|
} else { |
||||
|
info.percentage = status.BatteryLifePercent; |
||||
|
info.charging = (status.BatteryFlag & 8) != 0; |
||||
|
} |
||||
|
} else { |
||||
|
info.has_battery = false; |
||||
|
} |
||||
|
|
||||
|
#elif defined(__ANDROID__)
|
||||
|
info.percentage = g_battery_percentage.load(std::memory_order_relaxed); |
||||
|
info.charging = g_is_charging.load(std::memory_order_relaxed); |
||||
|
info.has_battery = g_has_battery.load(std::memory_order_relaxed); |
||||
|
|
||||
|
#elif defined(__APPLE__) && TARGET_OS_MAC
|
||||
|
CFTypeRef info_ref = IOPSCopyPowerSourcesInfo(); |
||||
|
CFArrayRef sources = IOPSCopyPowerSourcesList(info_ref); |
||||
|
if (CFArrayGetCount(sources) > 0) { |
||||
|
CFDictionaryRef battery = |
||||
|
IOPSGetPowerSourceDescription(info_ref, CFArrayGetValueAtIndex(sources, 0)); |
||||
|
|
||||
|
CFNumberRef curNum = |
||||
|
(CFNumberRef)CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)); |
||||
|
CFNumberRef maxNum = |
||||
|
(CFNumberRef)CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)); |
||||
|
int cur = 0, max = 0; |
||||
|
CFNumberGetValue(curNum, kCFNumberIntType, &cur); |
||||
|
CFNumberGetValue(maxNum, kCFNumberIntType, &max); |
||||
|
|
||||
|
if (max > 0) |
||||
|
info.percentage = (cur * 100) / max; |
||||
|
|
||||
|
CFBooleanRef isCharging = |
||||
|
(CFBooleanRef)CFDictionaryGetValue(battery, CFSTR(kIOPSIsChargingKey)); |
||||
|
info.charging = CFBooleanGetValue(isCharging); |
||||
|
} else { |
||||
|
info.has_battery = false; |
||||
|
} |
||||
|
CFRelease(sources); |
||||
|
CFRelease(info_ref); |
||||
|
|
||||
|
#elif defined(__linux__)
|
||||
|
const char* battery_path = "/sys/class/power_supply/BAT0/"; |
||||
|
|
||||
|
std::ifstream capFile(std::string(battery_path) + "capacity"); |
||||
|
if (capFile) { |
||||
|
capFile >> info.percentage; |
||||
|
} |
||||
|
else { |
||||
|
info.has_battery = false; |
||||
|
} |
||||
|
|
||||
|
std::ifstream statFile(std::string(battery_path) + "status"); |
||||
|
if (statFile) { |
||||
|
std::string status; |
||||
|
std::getline(statFile, status); |
||||
|
info.charging = (status == "Charging"); |
||||
|
} |
||||
|
#else
|
||||
|
info.has_battery = false; |
||||
|
#endif
|
||||
|
|
||||
|
return info; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
namespace Common { |
||||
|
struct PowerStatus { |
||||
|
int percentage = -1; |
||||
|
bool charging = false; |
||||
|
bool has_battery = true; |
||||
|
}; |
||||
|
|
||||
|
PowerStatus GetPowerStatus(); |
||||
|
} // namespace Common |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue