use core::fmt;
use bitflags::bitflags;
use windows::Win32::System::Threading::{
GetCurrentProcess, GetProcessInformation, PROCESS_POWER_THROTTLING_CURRENT_VERSION,
PROCESS_POWER_THROTTLING_EXECUTION_SPEED, PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION,
SetProcessInformation,
};
use winver::WindowsVersion;
use crate::{PROCESS_POWER_THROTTLING, WIN11_22H2};
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PowerThrottlingControlMask: u32 {
const EXECUTION_SPEED = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
const IGNORE_TIMER_RESOLUTION = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PowerThrottlingStateMask: u32 {
const EXECUTION_SPEED = 0x1;
const IGNORE_TIMER_RESOLUTION = 0x4;
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ProcessPowerThrottlingState {
version: u32,
control_mask: u32,
state_mask: u32,
}
const _: () = assert!(core::mem::size_of::<ProcessPowerThrottlingState>() == 12);
impl Default for ProcessPowerThrottlingState {
fn default() -> Self {
Self {
version: PROCESS_POWER_THROTTLING_CURRENT_VERSION,
control_mask: 0,
state_mask: 0,
}
}
}
macro_rules! check_win11_22h2 {
() => {{
let ver = WindowsVersion::from_ntdll_dll()?;
if ver.build < WIN11_22H2 {
return Err(crate::Error::NotAvailable(ver.build));
}
}};
}
pub(crate) fn get_power_throttling_state(
version: u32,
) -> Result<ProcessPowerThrottlingState, crate::Error> {
check_win11_22h2!();
let mut process_power_throttling = ProcessPowerThrottlingState {
version,
..Default::default()
};
let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
unsafe {
GetProcessInformation(
GetCurrentProcess(),
PROCESS_POWER_THROTTLING,
&mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
process_information_size,
)?;
}
Ok(process_power_throttling)
}
pub(crate) fn set_power_throttling_state(
version: u32,
state_mask: u32,
control_mask: u32,
) -> Result<(), crate::Error> {
check_win11_22h2!();
let mut process_power_throttling = ProcessPowerThrottlingState {
version,
state_mask,
control_mask,
..Default::default()
};
let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
unsafe {
SetProcessInformation(
GetCurrentProcess(),
PROCESS_POWER_THROTTLING,
&mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
process_information_size,
)?;
}
Ok(())
}
impl ProcessPowerThrottlingState {
pub fn new(
control_mask: PowerThrottlingControlMask,
state_mask: PowerThrottlingStateMask,
) -> Self {
Self {
control_mask: control_mask.bits(),
state_mask: state_mask.bits(),
..Default::default()
}
}
pub fn from_windows() -> Result<Self, crate::Error> {
get_power_throttling_state(PROCESS_POWER_THROTTLING_CURRENT_VERSION)
}
pub fn apply(&self) -> Result<(), crate::Error> {
set_power_throttling_state(self.version, self.state_mask, self.control_mask)
}
pub fn enable_execution_speed_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
self.state_mask |= PowerThrottlingStateMask::EXECUTION_SPEED.bits();
}
pub fn disable_execution_speed_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
self.state_mask |= PowerThrottlingStateMask::empty().bits();
}
pub fn enable_timer_resolution_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
self.state_mask |= PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION.bits();
}
pub fn disable_timer_resolution_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
self.state_mask |= PowerThrottlingStateMask::empty().bits();
}
pub fn enable_all_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::all().bits();
self.state_mask |= PowerThrottlingStateMask::all().bits();
}
pub fn disable_all_throttling(&mut self) {
self.control_mask |= PowerThrottlingControlMask::all().bits();
self.state_mask |= PowerThrottlingStateMask::empty().bits();
}
pub fn control_flags(&self) -> PowerThrottlingControlMask {
PowerThrottlingControlMask::from_bits_truncate(self.control_mask)
}
pub fn state_flags(&self) -> PowerThrottlingStateMask {
PowerThrottlingStateMask::from_bits_truncate(self.state_mask)
}
pub fn is_execution_speed_throttled(&self) -> bool {
self.state_flags()
.contains(PowerThrottlingStateMask::EXECUTION_SPEED)
}
pub fn is_ignore_timer_resolution_throttled(&self) -> bool {
self.state_flags()
.contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION)
}
pub fn is_execution_speed_controlled(&self) -> bool {
self.control_flags()
.contains(PowerThrottlingControlMask::EXECUTION_SPEED)
}
pub fn is_timer_resolution_controlled(&self) -> bool {
self.control_flags()
.contains(PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION)
}
pub fn version(&self) -> u32 {
self.version
}
pub fn set_version(&mut self, version: u32) {
self.version = version;
}
}
impl fmt::Display for ProcessPowerThrottlingState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state_flags = self.state_flags();
let control_flags = self.control_flags();
if state_flags.is_empty() {
write!(f, "No Throttling Active")
} else {
let mut descriptions = Vec::new();
if state_flags.contains(PowerThrottlingStateMask::EXECUTION_SPEED) {
descriptions.push("Execution Speed Throttled");
}
if state_flags.contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION) {
descriptions.push("Timer Resolution Throttled");
}
write!(f, "{}", descriptions.join(", "))?;
if !control_flags.is_empty()
&& control_flags
!= PowerThrottlingControlMask::from_bits_truncate(state_flags.bits())
{
write!(f, " (Controlled: {})", control_flags)?;
}
Ok(())
}
}
}
impl fmt::Display for PowerThrottlingControlMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
write!(f, "None")
} else {
let mut flags = Vec::new();
if self.contains(Self::EXECUTION_SPEED) {
flags.push("ExecutionSpeed");
}
if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
flags.push("TimerResolution");
}
write!(f, "{}", flags.join("|"))
}
}
}
impl fmt::Display for PowerThrottlingStateMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
write!(f, "None")
} else {
let mut flags = Vec::new();
if self.contains(Self::EXECUTION_SPEED) {
flags.push("ExecutionSpeed");
}
if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
flags.push("TimerResolution");
}
write!(f, "{}", flags.join("|"))
}
}
}