1use core::fmt;
2
3use bitflags::bitflags;
4use windows::Win32::System::Threading::{
5 GetCurrentProcess, GetProcessInformation, PROCESS_POWER_THROTTLING_CURRENT_VERSION,
6 PROCESS_POWER_THROTTLING_EXECUTION_SPEED, PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION,
7 SetProcessInformation,
8};
9use winver::WindowsVersion;
10
11use crate::{PROCESS_POWER_THROTTLING, WIN11_22H2};
12
13bitflags! {
14 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16 pub struct PowerThrottlingControlMask: u32 {
17 const EXECUTION_SPEED = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
19 const IGNORE_TIMER_RESOLUTION = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
21 }
22}
23
24bitflags! {
25 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27 pub struct PowerThrottlingStateMask: u32 {
28 const EXECUTION_SPEED = 0x1;
30 const IGNORE_TIMER_RESOLUTION = 0x4;
32 }
33}
34
35#[repr(C)]
37#[derive(Debug, Clone, Copy)]
38pub struct ProcessPowerThrottlingState {
39 version: u32,
43 control_mask: u32,
45 state_mask: u32,
47}
48
49const _: () = assert!(core::mem::size_of::<ProcessPowerThrottlingState>() == 12);
50
51impl Default for ProcessPowerThrottlingState {
52 fn default() -> Self {
53 Self {
54 version: PROCESS_POWER_THROTTLING_CURRENT_VERSION,
55 control_mask: 0,
56 state_mask: 0,
57 }
58 }
59}
60
61macro_rules! check_win11_22h2 {
62 () => {{
63 let ver = WindowsVersion::from_ntdll_dll()?;
64 if ver.build < WIN11_22H2 {
65 return Err(crate::Error::NotAvailable(ver.build));
66 }
67 }};
68}
69
70pub(crate) fn get_power_throttling_state(
72 version: u32,
73) -> Result<ProcessPowerThrottlingState, crate::Error> {
74 check_win11_22h2!();
75
76 let mut process_power_throttling = ProcessPowerThrottlingState {
77 version,
78 ..Default::default()
79 };
80 let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
81
82 unsafe {
83 GetProcessInformation(
84 GetCurrentProcess(),
85 PROCESS_POWER_THROTTLING,
86 &mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
87 process_information_size,
88 )?;
89 }
90
91 Ok(process_power_throttling)
92}
93
94pub(crate) fn set_power_throttling_state(
96 version: u32,
97 state_mask: u32,
98 control_mask: u32,
99) -> Result<(), crate::Error> {
100 check_win11_22h2!();
101
102 let mut process_power_throttling = ProcessPowerThrottlingState {
103 version,
104 state_mask,
105 control_mask,
106 ..Default::default()
107 };
108 let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
109
110 unsafe {
111 SetProcessInformation(
112 GetCurrentProcess(),
113 PROCESS_POWER_THROTTLING,
114 &mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
115 process_information_size,
116 )?;
117 }
118
119 Ok(())
120}
121
122impl ProcessPowerThrottlingState {
123 pub fn new(
125 control_mask: PowerThrottlingControlMask,
126 state_mask: PowerThrottlingStateMask,
127 ) -> Self {
128 Self {
129 control_mask: control_mask.bits(),
130 state_mask: state_mask.bits(),
131 ..Default::default()
132 }
133 }
134
135 pub fn from_windows() -> Result<Self, crate::Error> {
137 get_power_throttling_state(PROCESS_POWER_THROTTLING_CURRENT_VERSION)
138 }
139
140 pub fn apply(&self) -> Result<(), crate::Error> {
142 set_power_throttling_state(self.version, self.state_mask, self.control_mask)
143 }
144
145 pub fn enable_execution_speed_throttling(&mut self) {
147 self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
148 self.state_mask |= PowerThrottlingStateMask::EXECUTION_SPEED.bits();
149 }
150
151 pub fn disable_execution_speed_throttling(&mut self) {
153 self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
154 self.state_mask |= PowerThrottlingStateMask::empty().bits();
155 }
156
157 pub fn enable_timer_resolution_throttling(&mut self) {
159 self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
160 self.state_mask |= PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION.bits();
161 }
162
163 pub fn disable_timer_resolution_throttling(&mut self) {
165 self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
166 self.state_mask |= PowerThrottlingStateMask::empty().bits();
167 }
168
169 pub fn enable_all_throttling(&mut self) {
171 self.control_mask |= PowerThrottlingControlMask::all().bits();
172 self.state_mask |= PowerThrottlingStateMask::all().bits();
173 }
174
175 pub fn disable_all_throttling(&mut self) {
177 self.control_mask |= PowerThrottlingControlMask::all().bits();
178 self.state_mask |= PowerThrottlingStateMask::empty().bits();
179 }
180
181 pub fn control_flags(&self) -> PowerThrottlingControlMask {
183 PowerThrottlingControlMask::from_bits_truncate(self.control_mask)
184 }
185
186 pub fn state_flags(&self) -> PowerThrottlingStateMask {
188 PowerThrottlingStateMask::from_bits_truncate(self.state_mask)
189 }
190
191 pub fn is_execution_speed_throttled(&self) -> bool {
193 self.state_flags()
194 .contains(PowerThrottlingStateMask::EXECUTION_SPEED)
195 }
196
197 pub fn is_ignore_timer_resolution_throttled(&self) -> bool {
199 self.state_flags()
200 .contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION)
201 }
202
203 pub fn is_execution_speed_controlled(&self) -> bool {
205 self.control_flags()
206 .contains(PowerThrottlingControlMask::EXECUTION_SPEED)
207 }
208
209 pub fn is_timer_resolution_controlled(&self) -> bool {
211 self.control_flags()
212 .contains(PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION)
213 }
214
215 pub fn version(&self) -> u32 {
217 self.version
218 }
219
220 pub fn set_version(&mut self, version: u32) {
222 self.version = version;
223 }
224}
225
226impl fmt::Display for ProcessPowerThrottlingState {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 let state_flags = self.state_flags();
229 let control_flags = self.control_flags();
230
231 if state_flags.is_empty() {
232 write!(f, "No Throttling Active")
233 } else {
234 let mut descriptions = Vec::new();
235
236 if state_flags.contains(PowerThrottlingStateMask::EXECUTION_SPEED) {
237 descriptions.push("Execution Speed Throttled");
238 }
239
240 if state_flags.contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION) {
241 descriptions.push("Timer Resolution Throttled");
242 }
243
244 write!(f, "{}", descriptions.join(", "))?;
245
246 if !control_flags.is_empty()
248 && control_flags
249 != PowerThrottlingControlMask::from_bits_truncate(state_flags.bits())
250 {
251 write!(f, " (Controlled: {})", control_flags)?;
252 }
253
254 Ok(())
255 }
256 }
257}
258
259impl fmt::Display for PowerThrottlingControlMask {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 if self.is_empty() {
262 write!(f, "None")
263 } else {
264 let mut flags = Vec::new();
265 if self.contains(Self::EXECUTION_SPEED) {
266 flags.push("ExecutionSpeed");
267 }
268 if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
269 flags.push("TimerResolution");
270 }
271 write!(f, "{}", flags.join("|"))
272 }
273 }
274}
275
276impl fmt::Display for PowerThrottlingStateMask {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 if self.is_empty() {
279 write!(f, "None")
280 } else {
281 let mut flags = Vec::new();
282 if self.contains(Self::EXECUTION_SPEED) {
283 flags.push("ExecutionSpeed");
284 }
285 if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
286 flags.push("TimerResolution");
287 }
288 write!(f, "{}", flags.join("|"))
289 }
290 }
291}