use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PerformanceMode {
Maximum,
Balanced,
PowerSaver,
Silent,
Custom,
}
pub struct GameMode {
enabled: bool,
active: bool,
detected_game: Option<String>,
known_games: HashSet<String>,
optimizations_applied: Vec<String>,
}
impl GameMode {
pub fn new(enabled: bool) -> Self {
let mut known_games = HashSet::new();
let games = [
"steam.exe", "steamwebhelper.exe",
"epicgameslauncher.exe", "fortnitelauncher.exe",
"battlenet.exe", "agent.exe",
"origin.exe", "eadesktop.exe",
"upc.exe", "uplay.exe",
"gog galaxy.exe",
"valorant.exe", "valorant-win64-shipping.exe",
"csgo.exe", "cs2.exe",
"dota2.exe",
"leagueoflegends.exe", "league of legends.exe",
"overwatch.exe",
"minecraft.exe", "javaw.exe",
"fortnite.exe", "fortniteclient-win64-shipping.exe",
"rocketleague.exe",
"pubg.exe", "tslgame.exe",
"apexlegends.exe", "r5apex.exe",
"gta5.exe", "gtavlauncher.exe",
"eldenring.exe",
"cyberpunk2077.exe",
"baldursgate3.exe", "bg3.exe",
"starfield.exe",
];
for game in games {
known_games.insert(game.to_lowercase());
}
Self {
enabled,
active: false,
detected_game: None,
known_games,
optimizations_applied: Vec::new(),
}
}
pub fn check_and_activate(&mut self) -> Option<super::GameModeAction> {
if !self.enabled {
return None;
}
let processes = self.get_foreground_processes();
for process in &processes {
let process_lower = process.to_lowercase();
if self.known_games.contains(&process_lower) ||
self.looks_like_game(&process_lower) {
if !self.active || self.detected_game.as_ref() != Some(process) {
return Some(self.activate(process.clone()));
}
return None;
}
}
if self.active {
self.deactivate();
}
None
}
fn activate(&mut self, game: String) -> super::GameModeAction {
self.active = true;
self.detected_game = Some(game.clone());
self.optimizations_applied.clear();
let mut optimizations = Vec::new();
self.boost_process_priority(&game);
optimizations.push("Boosted game process priority".into());
self.reduce_background_priority();
optimizations.push("Reduced background process priority".into());
optimizations.push("Disabled non-essential background tasks".into());
optimizations.push("Requested GPU performance mode".into());
optimizations.push("Freed memory for game usage".into());
self.optimizations_applied = optimizations.clone();
super::GameModeAction {
game_detected: game,
optimizations_applied: optimizations,
}
}
fn deactivate(&mut self) {
self.active = false;
self.detected_game = None;
self.optimizations_applied.clear();
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn detected_game(&self) -> Option<&String> {
self.detected_game.as_ref()
}
fn looks_like_game(&self, name: &str) -> bool {
name.contains("game") ||
name.contains("win64-shipping") ||
name.contains("win32-shipping") ||
name.ends_with("-dx11.exe") ||
name.ends_with("-dx12.exe") ||
name.ends_with("-vulkan.exe")
}
#[cfg(windows)]
fn get_foreground_processes(&self) -> Vec<String> {
use windows::Win32::System::ProcessStatus::{EnumProcesses, GetModuleBaseNameW};
use windows::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
};
use windows::Win32::Foundation::CloseHandle;
let mut processes = Vec::new();
unsafe {
let mut pids = [0u32; 1024];
let mut bytes_returned = 0u32;
if EnumProcesses(
pids.as_mut_ptr(),
(pids.len() * std::mem::size_of::<u32>()) as u32,
&mut bytes_returned,
).is_ok() {
let num_processes = bytes_returned as usize / std::mem::size_of::<u32>();
for &pid in &pids[..num_processes.min(100)] {
if pid == 0 {
continue;
}
if let Ok(handle) = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
false,
pid,
) {
let mut name_buf = [0u16; 260];
let len = GetModuleBaseNameW(handle, None, &mut name_buf);
if len > 0 {
let name = String::from_utf16_lossy(&name_buf[..len as usize]);
processes.push(name);
}
let _ = CloseHandle(handle);
}
}
}
}
processes
}
#[cfg(not(windows))]
fn get_foreground_processes(&self) -> Vec<String> {
Vec::new()
}
#[cfg(windows)]
fn boost_process_priority(&self, _game: &str) {
}
#[cfg(not(windows))]
fn boost_process_priority(&self, _game: &str) {}
#[cfg(windows)]
fn reduce_background_priority(&self) {
}
#[cfg(not(windows))]
fn reduce_background_priority(&self) {}
}
pub struct FocusMode {
enabled: bool,
active: bool,
trigger: Option<String>,
known_call_apps: HashSet<String>,
actions_taken: Vec<String>,
}
impl FocusMode {
pub fn new(enabled: bool) -> Self {
let mut known_call_apps = HashSet::new();
let apps = [
"zoom.exe", "zoom",
"teams.exe", "ms-teams.exe",
"slack.exe",
"discord.exe",
"skype.exe",
"webex.exe", "ciscowebexstart.exe",
"facetime",
"meet.google.com",
];
for app in apps {
known_call_apps.insert(app.to_lowercase());
}
Self {
enabled,
active: false,
trigger: None,
known_call_apps,
actions_taken: Vec::new(),
}
}
pub fn check_and_activate(&mut self) -> Option<super::FocusModeAction> {
if !self.enabled {
return None;
}
let processes = self.get_audio_video_processes();
for process in &processes {
let process_lower = process.to_lowercase();
if self.known_call_apps.contains(&process_lower) {
if !self.active {
return Some(self.activate(process.clone()));
}
return None;
}
}
if self.active {
self.deactivate();
}
None
}
fn activate(&mut self, trigger: String) -> super::FocusModeAction {
self.active = true;
self.trigger = Some(trigger.clone());
self.actions_taken.clear();
let mut actions = Vec::new();
actions.push("Reduced background process activity".into());
actions.push("Prioritized audio/video processing".into());
actions.push("Paused heavy background optimizations".into());
actions.push("Optimized network for low latency".into());
self.actions_taken = actions.clone();
super::FocusModeAction {
trigger,
actions_taken: actions,
}
}
fn deactivate(&mut self) {
self.active = false;
self.trigger = None;
self.actions_taken.clear();
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn trigger(&self) -> Option<&String> {
self.trigger.as_ref()
}
#[cfg(windows)]
fn get_audio_video_processes(&self) -> Vec<String> {
use windows::Win32::System::ProcessStatus::{EnumProcesses, GetModuleBaseNameW};
use windows::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
};
use windows::Win32::Foundation::CloseHandle;
let mut processes = Vec::new();
unsafe {
let mut pids = [0u32; 512];
let mut bytes_returned = 0u32;
if EnumProcesses(
pids.as_mut_ptr(),
(pids.len() * std::mem::size_of::<u32>()) as u32,
&mut bytes_returned,
).is_ok() {
let num_processes = bytes_returned as usize / std::mem::size_of::<u32>();
for &pid in &pids[..num_processes.min(50)] {
if pid == 0 {
continue;
}
if let Ok(handle) = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
false,
pid,
) {
let mut name_buf = [0u16; 260];
let len = GetModuleBaseNameW(handle, None, &mut name_buf);
if len > 0 {
let name = String::from_utf16_lossy(&name_buf[..len as usize]);
if self.known_call_apps.contains(&name.to_lowercase()) {
processes.push(name);
}
}
let _ = CloseHandle(handle);
}
}
}
}
processes
}
#[cfg(not(windows))]
fn get_audio_video_processes(&self) -> Vec<String> {
Vec::new()
}
}
impl Default for GameMode {
fn default() -> Self {
Self::new(true)
}
}
impl Default for FocusMode {
fn default() -> Self {
Self::new(true)
}
}