Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62efe3f411 | ||
|
|
4bb0f151a9 | ||
|
|
1aebc6b17c | ||
|
|
861ebe97d6 | ||
|
|
53b20a437a | ||
|
|
03ca861a09 | ||
|
|
a4ebfc515c | ||
|
|
52ffb34d83 | ||
|
|
6248e8379f | ||
|
|
99bc15b5ed | ||
|
|
e84d1347fd |
Generated
+1
-1
@@ -1366,7 +1366,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"catppuccin-egui",
|
"catppuccin-egui",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ build:
|
|||||||
upx .\target\release\gta-tools.exe --best
|
upx .\target\release\gta-tools.exe --best
|
||||||
|
|
||||||
install: build
|
install: build
|
||||||
cp .\target\release\gta-tools.exe ~\.cargo\bin\
|
cp .\target\release\gta-tools.exe ~\.cargo\bin
|
||||||
cp .\target\release\gta-tools.exe ~\Documents\
|
cp .\target\release\gta-tools.exe ~\Documents
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
use crate::util::{self, consts::GTA_WINDOW_TITLE};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
||||||
KEYBD_EVENT_FLAGS, MAP_VIRTUAL_KEY_TYPE, MapVirtualKeyW, keybd_event,
|
KEYBD_EVENT_FLAGS, MAP_VIRTUAL_KEY_TYPE, MapVirtualKeyW, keybd_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const INTERVAL: Duration = Duration::from_secs(60);
|
pub const INTERVAL: Duration = Duration::from_secs(60);
|
||||||
const VK_SHIFT: u8 = 16;
|
const VK_NUMPAD4: u8 = 0x64;
|
||||||
|
const VK_NUMPAD6: u8 = 0x66;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AntiAfk {
|
pub struct AntiAfk {
|
||||||
@@ -21,6 +23,19 @@ 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)
|
||||||
|
{
|
||||||
|
send(VK_NUMPAD4);
|
||||||
|
send(VK_NUMPAD6);
|
||||||
|
}
|
||||||
|
self.interval = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send(vk_code: u8) {
|
pub fn send(vk_code: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
keybd_event(
|
keybd_event(
|
||||||
@@ -37,7 +52,3 @@ pub fn send(vk_code: u8) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate() {
|
|
||||||
send(VK_SHIFT);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use windows::Win32::{
|
|||||||
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const INTERVAL: Duration = Duration::from_secs(8);
|
pub const INTERVAL: Duration = Duration::from_secs(10);
|
||||||
const ENHANCED: &str = "GTA5_Enhanced.exe";
|
const ENHANCED: &str = "GTA5_Enhanced.exe";
|
||||||
const LEGACY: &str = "GTA5.exe";
|
const LEGACY: &str = "GTA5.exe";
|
||||||
|
|
||||||
@@ -58,7 +58,9 @@ pub fn activate(app: &mut App) {
|
|||||||
|
|
||||||
pub fn deactivate(app: &mut App) {
|
pub fn deactivate(app: &mut App) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = NtResumeProcess(app.game_handle);
|
if !app.game_handle.is_invalid() {
|
||||||
let _ = CloseHandle(app.game_handle);
|
let _ = NtResumeProcess(app.game_handle);
|
||||||
|
let _ = CloseHandle(app.game_handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{fmt::Display, path::PathBuf, process::Command};
|
use std::{fmt::Display, path::PathBuf, process::Command};
|
||||||
use winreg::{RegKey, enums::HKEY_LOCAL_MACHINE};
|
use winreg::{RegKey, enums::HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Platform {
|
pub enum Platform {
|
||||||
#[default]
|
#[default]
|
||||||
Steam,
|
Steam,
|
||||||
|
|||||||
+105
-69
@@ -7,7 +7,8 @@ use crate::{
|
|||||||
launch::{Launch, Platform},
|
launch::{Launch, Platform},
|
||||||
},
|
},
|
||||||
util::{
|
util::{
|
||||||
consts::{ENHANCED, LEGACY},
|
self,
|
||||||
|
consts::{ENHANCED, GTA_WINDOW_TITLE, LEGACY},
|
||||||
elevation,
|
elevation,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -24,11 +25,12 @@ use sysinfo::System;
|
|||||||
use windows::Win32::Foundation::HANDLE;
|
use windows::Win32::Foundation::HANDLE;
|
||||||
|
|
||||||
const THEME: catppuccin_egui::Theme = catppuccin_egui::MOCHA;
|
const THEME: catppuccin_egui::Theme = catppuccin_egui::MOCHA;
|
||||||
|
const WINDOW_SIZE: [f32; 2] = [240.0, 240.0];
|
||||||
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
let mut config_path = dirs::config_local_dir().unwrap();
|
dirs::config_local_dir()
|
||||||
config_path.push("GTA Tools");
|
.unwrap()
|
||||||
config_path.push("config.json");
|
.join("GTA Tools")
|
||||||
config_path
|
.join("config.json")
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -89,49 +91,51 @@ impl eframe::App for App {
|
|||||||
self.launch.selected = persistent_state.launcher;
|
self.launch.selected = persistent_state.launcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx.style_mut(|style| style.spacing.item_spacing = egui::vec2(4.0, 4.0));
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
}
|
}
|
||||||
self.run_timers();
|
self.run_timers();
|
||||||
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::bottom("bottom_panel")
|
||||||
ui.horizontal(|ui| {
|
.exact_height(25.0)
|
||||||
ui.selectable_value(&mut self.stage, Stage::Main, "Main");
|
.show(ctx, |ui| {
|
||||||
ui.selectable_value(&mut self.stage, Stage::About, "About");
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
ui.selectable_value(&mut self.stage, Stage::Main, "Main");
|
||||||
let button = ui
|
ui.selectable_value(&mut self.stage, Stage::About, "About");
|
||||||
.add_enabled(!self.elevated, egui::Button::new("Elevate"))
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
.on_hover_text("Relaunch ourselves as administrator.")
|
let button = ui
|
||||||
.on_disabled_hover_text("We are already running elevated.");
|
.add_enabled(!self.elevated, egui::Button::new("Elevate"))
|
||||||
if button.clicked() {
|
.on_hover_text("Relaunch ourselves as administrator.")
|
||||||
elevation::elevate(&mut self.closing);
|
.on_disabled_hover_text("We are already running elevated.");
|
||||||
}
|
if button.clicked() {
|
||||||
|
elevation::elevate(&mut self.closing);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| match self.stage {
|
||||||
|
Stage::Main => {
|
||||||
|
self.header(ui, "Game");
|
||||||
|
self.show_game(ctx, ui);
|
||||||
|
self.header(ui, "Session");
|
||||||
|
self.show_session(ctx, ui);
|
||||||
|
self.header(ui, "Network");
|
||||||
|
self.show_network(ctx, ui);
|
||||||
|
}
|
||||||
|
Stage::About => self.show_about(ctx, ui),
|
||||||
});
|
});
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
if check_debug_keycombo_pressed(ctx) {
|
||||||
egui::ScrollArea::both()
|
|
||||||
.auto_shrink([false, false])
|
|
||||||
.show(ui, |ui| match self.stage {
|
|
||||||
Stage::Main => {
|
|
||||||
self.header(ui, "Game");
|
|
||||||
self.show_game(ctx, ui);
|
|
||||||
self.header(ui, "Session");
|
|
||||||
self.show_session(ctx, ui);
|
|
||||||
self.header(ui, "Network");
|
|
||||||
self.show_network(ctx, ui);
|
|
||||||
}
|
|
||||||
Stage::About => {
|
|
||||||
self.show_about(ctx, ui);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if self.check_debug_keycombo_pressed(ctx) {
|
|
||||||
self.debug = !self.debug;
|
self.debug = !self.debug;
|
||||||
}
|
}
|
||||||
if self.check_debug_viewport_close_button_pressed(ctx) {
|
if check_debug_viewport_close_button_pressed(ctx) {
|
||||||
self.debug = false;
|
self.debug = false;
|
||||||
}
|
}
|
||||||
if self.debug {
|
if self.debug {
|
||||||
let main_rect = ctx.input(|i| i.viewport().clone().outer_rect.unwrap());
|
let main_rect = ctx.input(|i| {
|
||||||
|
i.viewport()
|
||||||
|
.clone()
|
||||||
|
.outer_rect
|
||||||
|
.unwrap_or(egui::Rect::EVERYTHING)
|
||||||
|
});
|
||||||
let position = [main_rect.right(), main_rect.min.y];
|
let position = [main_rect.right(), main_rect.min.y];
|
||||||
ctx.show_viewport_immediate(
|
ctx.show_viewport_immediate(
|
||||||
egui::ViewportId::from_hash_of("debug_viewport"),
|
egui::ViewportId::from_hash_of("debug_viewport"),
|
||||||
@@ -139,11 +143,11 @@ impl eframe::App for App {
|
|||||||
.with_title("GTA Tools Debug")
|
.with_title("GTA Tools Debug")
|
||||||
.with_minimize_button(false)
|
.with_minimize_button(false)
|
||||||
.with_maximize_button(false)
|
.with_maximize_button(false)
|
||||||
.with_inner_size([256.0, 232.0])
|
.with_inner_size(WINDOW_SIZE)
|
||||||
.with_position(position)
|
.with_position(position)
|
||||||
.with_icon(load_icon()),
|
.with_icon(load_icon()),
|
||||||
|ctx, _class| {
|
|ctx, _class| {
|
||||||
if self.check_debug_keycombo_pressed(ctx) {
|
if check_debug_keycombo_pressed(ctx) {
|
||||||
self.debug = !self.debug;
|
self.debug = !self.debug;
|
||||||
}
|
}
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
@@ -154,7 +158,7 @@ impl eframe::App for App {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
ctx.request_repaint_after(Duration::from_millis(100));
|
ctx.request_repaint_after(Duration::from_millis(100));
|
||||||
if self.closing {
|
if self.closing {
|
||||||
@@ -164,20 +168,6 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn check_debug_keycombo_pressed(&self, ctx: &egui::Context) -> bool {
|
|
||||||
ctx.input(|i| i.modifiers.all() && i.key_pressed(egui::Key::D))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_debug_viewport_close_button_pressed(&self, ctx: &egui::Context) -> bool {
|
|
||||||
ctx.input(|i| {
|
|
||||||
i.raw
|
|
||||||
.viewports
|
|
||||||
.get(&egui::ViewportId::from_hash_of("debug_viewport"))
|
|
||||||
.filter(|vp| vp.close_requested())
|
|
||||||
.is_some()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn header(&self, ui: &mut egui::Ui, text: &str) {
|
fn header(&self, ui: &mut egui::Ui, text: &str) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -212,7 +202,10 @@ impl App {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let force_close_button = ui.button(&self.force_close.button_text);
|
let force_close_button = ui.add_sized(
|
||||||
|
[104.0, 0.0],
|
||||||
|
egui::Button::new(&self.force_close.button_text),
|
||||||
|
);
|
||||||
if force_close_button.clicked() && !self.force_close.prompting {
|
if force_close_button.clicked() && !self.force_close.prompting {
|
||||||
self.force_close.prompting();
|
self.force_close.prompting();
|
||||||
self.current_frame = true;
|
self.current_frame = true;
|
||||||
@@ -243,12 +236,23 @@ impl App {
|
|||||||
ui.label(&self.empty_session.countdown.i_string);
|
ui.label(&self.empty_session.countdown.i_string);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.checkbox(&mut self.anti_afk.enabled, "Anti AFK")
|
ui.horizontal(|ui| {
|
||||||
.on_hover_text("You should be tabbed in\nwhile this is enabled.");
|
ui.checkbox(&mut self.anti_afk.enabled, "Anti AFK")
|
||||||
|
.on_hover_text("You should be tabbed in\nfor this to work.");
|
||||||
|
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) {
|
||||||
|
"GTA is focused."
|
||||||
|
} else {
|
||||||
|
"GTA is not focused!"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
if self.anti_afk.enabled && self.anti_afk.interval.elapsed() >= features::anti_afk::INTERVAL
|
if self.anti_afk.enabled && self.anti_afk.interval.elapsed() >= features::anti_afk::INTERVAL
|
||||||
{
|
{
|
||||||
features::anti_afk::activate();
|
self.anti_afk.activate();
|
||||||
self.anti_afk.interval = Instant::now();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,19 +262,28 @@ impl App {
|
|||||||
.stroke(egui::Stroke::new(1.0, THEME.overlay1))
|
.stroke(egui::Stroke::new(1.0, THEME.overlay1))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
let response = ui.add_enabled_ui(self.elevated, |ui| {
|
let response = ui.add_enabled_ui(self.elevated, |ui| {
|
||||||
ui.label("Game's network access");
|
let label = ui.label("Game's network access");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Block").clicked() {
|
let available_width = label.rect.width();
|
||||||
|
let spacing = ui.spacing().item_spacing.x;
|
||||||
|
let button_width = (available_width - spacing) / 2.0;
|
||||||
|
if ui
|
||||||
|
.add_sized([button_width, 18.0], egui::Button::new("Block"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
features::game_networking::block_all(&mut self.sysinfo);
|
features::game_networking::block_all(&mut self.sysinfo);
|
||||||
};
|
};
|
||||||
if ui.button("Unblock").clicked() {
|
if ui
|
||||||
|
.add_sized([button_width, 18.0], egui::Button::new("Unblock"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
features::game_networking::unblock_all();
|
features::game_networking::unblock_all();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
response
|
response.response.on_disabled_hover_text(
|
||||||
.response
|
"This requires administrator.\nUse the Elevate button.",
|
||||||
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.")
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,11 +304,17 @@ impl App {
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.add(egui::Image::new(egui::include_image!("../assets/icon.png")))
|
ui.add(egui::Image::new(egui::include_image!("../assets/icon.png")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_debug(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
fn show_debug(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
ui.collapsing("times", |ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"anti afk timer: {}",
|
||||||
|
self.anti_afk.interval.elapsed().as_secs()
|
||||||
|
))
|
||||||
|
});
|
||||||
ui.collapsing("sysinfo", |ui| {
|
ui.collapsing("sysinfo", |ui| {
|
||||||
if ui.button("refresh all").clicked() {
|
if ui.button("refresh all").clicked() {
|
||||||
self.sysinfo.refresh_all();
|
self.sysinfo.refresh_all();
|
||||||
@@ -311,7 +330,7 @@ impl App {
|
|||||||
);
|
);
|
||||||
ui.label(format!("gta pid: {pid}"));
|
ui.label(format!("gta pid: {pid}"));
|
||||||
});
|
});
|
||||||
ui.collapsing("app state", |ui| ui.label(format!("{:#?}", self)));
|
ui.collapsing("app state", |ui| ui.label(format!("{self:#?}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_timers(&mut self) {
|
fn run_timers(&mut self) {
|
||||||
@@ -329,8 +348,9 @@ impl App {
|
|||||||
|
|
||||||
impl Drop for App {
|
impl Drop for App {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// save any persistent state to config file
|
||||||
let persistent_state = PersistentState {
|
let persistent_state = PersistentState {
|
||||||
launcher: self.launch.selected.clone(),
|
launcher: self.launch.selected,
|
||||||
};
|
};
|
||||||
let config_path = CONFIG_PATH.as_path();
|
let config_path = CONFIG_PATH.as_path();
|
||||||
let config_path_parent = config_path.parent().unwrap();
|
let config_path_parent = config_path.parent().unwrap();
|
||||||
@@ -340,9 +360,25 @@ impl Drop for App {
|
|||||||
let mut config_file = File::create(config_path).unwrap();
|
let mut config_file = File::create(config_path).unwrap();
|
||||||
let json = serde_json::to_string_pretty(&persistent_state).unwrap();
|
let json = serde_json::to_string_pretty(&persistent_state).unwrap();
|
||||||
config_file.write_all(json.as_bytes()).unwrap();
|
config_file.write_all(json.as_bytes()).unwrap();
|
||||||
|
// make sure we are not suspending game
|
||||||
|
features::empty_session::deactivate(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_debug_keycombo_pressed(ctx: &egui::Context) -> bool {
|
||||||
|
ctx.input(|i| i.modifiers.all() && i.key_pressed(egui::Key::D))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_debug_viewport_close_button_pressed(ctx: &egui::Context) -> bool {
|
||||||
|
ctx.input(|i| {
|
||||||
|
i.raw
|
||||||
|
.viewports
|
||||||
|
.get(&egui::ViewportId::from_hash_of("debug_viewport"))
|
||||||
|
.filter(|vp| vp.close_requested())
|
||||||
|
.is_some()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn load_icon() -> eframe::egui::IconData {
|
fn load_icon() -> eframe::egui::IconData {
|
||||||
let icon = include_bytes!("../assets/icon.png");
|
let icon = include_bytes!("../assets/icon.png");
|
||||||
let image = image::load_from_memory(icon).unwrap().into_rgba8();
|
let image = image::load_from_memory(icon).unwrap().into_rgba8();
|
||||||
@@ -359,7 +395,7 @@ pub fn run() {
|
|||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
.with_resizable(false)
|
.with_resizable(false)
|
||||||
.with_maximize_button(false)
|
.with_maximize_button(false)
|
||||||
.with_inner_size([256.0, 232.0])
|
.with_inner_size(WINDOW_SIZE)
|
||||||
.with_icon(load_icon()),
|
.with_icon(load_icon()),
|
||||||
centered: true,
|
centered: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
pub const ENHANCED: &str = "GTA5_Enhanced.exe";
|
pub const ENHANCED: &str = "GTA5_Enhanced.exe";
|
||||||
pub const LEGACY: &str = "GTA5.exe";
|
pub const LEGACY: &str = "GTA5.exe";
|
||||||
|
|
||||||
|
pub const GTA_WINDOW_TITLE: &str = "Grand Theft Auto V";
|
||||||
|
|||||||
@@ -1,3 +1,22 @@
|
|||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod countdown;
|
pub mod countdown;
|
||||||
pub mod elevation;
|
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 }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user