diff --git a/src/features/anti_afk.rs b/src/features/anti_afk.rs index 1f08b3a..32ed6b6 100644 --- a/src/features/anti_afk.rs +++ b/src/features/anti_afk.rs @@ -7,6 +7,7 @@ use windows::Win32::UI::Input::KeyboardAndMouse::{ pub const INTERVAL: Duration = Duration::from_secs(60); const VK_NUMPAD4: u8 = 0x64; const VK_NUMPAD6: u8 = 0x66; +const PRESS_KEYS: [u8; 2] = [VK_NUMPAD4, VK_NUMPAD6]; #[derive(Debug)] pub struct AntiAfk { @@ -25,30 +26,28 @@ impl Default for AntiAfk { impl AntiAfk { pub fn activate(&mut self) { - if util::is_window_focused(GTA_WINDOW_TITLE) - && !util::is_key_pressed(VK_NUMPAD4 as i32) - && !util::is_key_pressed(VK_NUMPAD6 as i32) + if util::win::is_window_focused(GTA_WINDOW_TITLE) + && !util::win::is_any_key_pressed(&PRESS_KEYS) { - send(VK_NUMPAD4); - send(VK_NUMPAD6); + send(&PRESS_KEYS); } self.interval = Instant::now(); } } -pub fn send(vk_code: u8) { - unsafe { +pub fn send(vk_codes: &[u8]) { + vk_codes.iter().for_each(|vk_code: &u8| unsafe { keybd_event( - vk_code, - u8::try_from(MapVirtualKeyW(u32::from(vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(), + *vk_code, + u8::try_from(MapVirtualKeyW(u32::from(*vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(), KEYBD_EVENT_FLAGS(0), 0, ); keybd_event( - vk_code, - u8::try_from(MapVirtualKeyW(u32::from(vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(), + *vk_code, + u8::try_from(MapVirtualKeyW(u32::from(*vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(), KEYBD_EVENT_FLAGS(2), 0, ); - } + }); } diff --git a/src/gui.rs b/src/gui.rs index 112db97..cf1adad 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -9,7 +9,6 @@ use crate::{ util::{ self, consts::{ENHANCED, GTA_WINDOW_TITLE, LEGACY}, - elevation, }, }; use eframe::egui; @@ -21,8 +20,6 @@ use std::{ sync::LazyLock, time::{Duration, Instant}, }; -use sysinfo::System; -use windows::Win32::Foundation::HANDLE; const THEME: catppuccin_egui::Theme = catppuccin_egui::MOCHA; const WINDOW_SIZE: [f32; 2] = [240.0, 240.0]; @@ -46,16 +43,21 @@ pub enum Stage { } #[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Default)] +pub struct Flags { + initialized: bool, + elevated: bool, + debug: bool, + closing: bool, + current_frame: bool, +} + #[derive(Debug)] pub struct App { stage: Stage, - closing: bool, - debug: bool, - initialized: bool, - elevated: bool, - current_frame: bool, - pub sysinfo: System, - pub game_handle: HANDLE, + flags: Flags, + pub sysinfo: sysinfo::System, + pub game_handle: windows::Win32::Foundation::HANDLE, launch: Launch, force_close: ForceClose, empty_session: EmptySession, @@ -66,13 +68,9 @@ impl Default for App { fn default() -> Self { Self { stage: Stage::default(), - closing: false, - initialized: false, - debug: false, - elevated: elevation::is_elevated(), - current_frame: false, - sysinfo: System::new_all(), - game_handle: HANDLE::default(), + flags: Flags::default(), + sysinfo: sysinfo::System::new_all(), + game_handle: windows::Win32::Foundation::HANDLE::default(), launch: Launch::default(), force_close: ForceClose::default(), empty_session: EmptySession::default(), @@ -83,7 +81,7 @@ impl Default for App { impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - if !self.initialized { + if !self.flags.initialized { catppuccin_egui::set_theme(ctx, THEME); egui_extras::install_image_loaders(ctx); if let Ok(config) = fs::read_to_string(CONFIG_PATH.as_path()) { @@ -95,7 +93,8 @@ impl eframe::App for App { style.spacing.item_spacing = egui::vec2(4.0, 4.0); style.interaction.selectable_labels = false; }); - self.initialized = true; + self.flags.elevated = util::win::is_elevated(); + self.flags.initialized = true; } self.run_timers(); egui::TopBottomPanel::bottom("bottom_panel") @@ -106,11 +105,11 @@ impl eframe::App for App { ui.selectable_value(&mut self.stage, Stage::About, "About"); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { let button = ui - .add_enabled(!self.elevated, egui::Button::new("Elevate")) + .add_enabled(!self.flags.elevated, egui::Button::new("Elevate")) .on_hover_text("Relaunch ourselves as administrator.") .on_disabled_hover_text("We are already running elevated."); if button.clicked() { - elevation::elevate(&mut self.closing); + util::win::elevate(&mut self.flags.closing); } }); }); @@ -127,12 +126,12 @@ impl eframe::App for App { Stage::About => self.show_about(ctx, ui), }); if check_debug_keycombo_pressed(ctx) { - self.debug = !self.debug; + self.flags.debug = !self.flags.debug; } if check_debug_viewport_close_button_pressed(ctx) { - self.debug = false; + self.flags.debug = false; } - if self.debug { + if self.flags.debug { let main_rect = ctx.input(|i| { i.viewport() .clone() @@ -151,7 +150,7 @@ impl eframe::App for App { .with_icon(load_icon()), |ctx, _class| { if check_debug_keycombo_pressed(ctx) { - self.debug = !self.debug; + self.flags.debug = !self.flags.debug; } egui::CentralPanel::default().show(ctx, |ui| { egui::ScrollArea::both() @@ -164,7 +163,7 @@ impl eframe::App for App { ); } ctx.request_repaint_after(Duration::from_millis(100)); - if self.closing { + if self.flags.closing { ctx.send_viewport_cmd(egui::ViewportCommand::Close); } } @@ -211,20 +210,20 @@ impl App { ); if force_close_button.clicked() && !self.force_close.prompting { self.force_close.prompting(); - self.current_frame = true; + self.flags.current_frame = true; }; if self.force_close.prompting && self.force_close.interval.elapsed() <= Duration::from_secs(3) { - if force_close_button.clicked() && !self.current_frame { + if force_close_button.clicked() && !self.flags.current_frame { features::force_close::activate(&mut self.sysinfo); self.force_close = ForceClose::default(); } } else { self.force_close = ForceClose::default(); } - if self.current_frame { - self.current_frame = false; + if self.flags.current_frame { + self.flags.current_frame = false; } } @@ -245,7 +244,7 @@ impl App { if self.anti_afk.enabled { ui.add_space(8.0); ui.add_enabled_ui(false, |ui| { - ui.label(if util::is_window_focused(GTA_WINDOW_TITLE) { + ui.label(if util::win::is_window_focused(GTA_WINDOW_TITLE) { "GTA is focused." } else { "GTA is not focused!" @@ -264,7 +263,7 @@ impl App { .inner_margin(egui::vec2(4.0, 4.0)) .stroke(egui::Stroke::new(1.0, THEME.overlay1)) .show(ui, |ui| { - let response = ui.add_enabled_ui(self.elevated, |ui| { + let response = ui.add_enabled_ui(self.flags.elevated, |ui| { let label = ui.label("Game's network access"); ui.horizontal(|ui| { let available_width = label.rect.width(); @@ -382,11 +381,11 @@ fn check_debug_viewport_close_button_pressed(ctx: &egui::Context) -> bool { }) } -fn load_icon() -> eframe::egui::IconData { +fn load_icon() -> egui::IconData { let icon = include_bytes!("../assets/icon.png"); let image = image::load_from_memory(icon).unwrap().into_rgba8(); let (width, height) = image.dimensions(); - eframe::egui::IconData { + egui::IconData { rgba: image.into_raw(), width, height, diff --git a/src/util/mod.rs b/src/util/mod.rs index 8adc470..b5f99db 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,22 +1,3 @@ pub mod consts; pub mod countdown; -pub mod elevation; - -use windows::Win32::UI::{ - Input::KeyboardAndMouse::GetAsyncKeyState, - WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW}, -}; - -pub fn is_window_focused(target_title: &str) -> bool { - unsafe { - let hwnd = GetForegroundWindow(); - let mut buffer: [u16; 512] = [0; 512]; - let length = GetWindowTextW(hwnd, &mut buffer); - let current_title = String::from_utf16_lossy(&buffer[..length as usize]); - current_title == target_title - } -} - -pub fn is_key_pressed(key: i32) -> bool { - unsafe { (GetAsyncKeyState(key) as i32 & 0x8000) != 0 } -} +pub mod win; diff --git a/src/util/elevation.rs b/src/util/win.rs similarity index 54% rename from src/util/elevation.rs rename to src/util/win.rs index a318194..2861dbd 100644 --- a/src/util/elevation.rs +++ b/src/util/win.rs @@ -1,11 +1,34 @@ use windows::{ - Win32::Foundation::{CloseHandle, HANDLE}, - Win32::Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation}, - Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}, - Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_HIDE}, + Win32::{ + Foundation::{CloseHandle, HANDLE}, + Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation}, + System::Threading::{GetCurrentProcess, OpenProcessToken}, + UI::{ + Input::KeyboardAndMouse::GetAsyncKeyState, + Shell::ShellExecuteW, + WindowsAndMessaging::SW_HIDE, + WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW}, + }, + }, core::{HSTRING, PCWSTR}, }; +#[allow(clippy::cast_sign_loss)] +pub fn is_window_focused(target_title: &str) -> bool { + unsafe { + let hwnd = GetForegroundWindow(); + let mut buffer: [u16; 512] = [0; 512]; + let length = GetWindowTextW(hwnd, &mut buffer); + let current_title = String::from_utf16_lossy(&buffer[..length as usize]); + current_title == target_title + } +} + +pub fn is_any_key_pressed(keys: &[u8]) -> bool { + keys.iter() + .any(|&key| unsafe { (i32::from(GetAsyncKeyState(i32::from(key))) & 0x8000) != 0 }) +} + pub fn elevate(closing: &mut bool) { let exe = std::env::current_exe().unwrap(); unsafe {