Skip to content

Commit 3d5ae28

Browse files
committed
Add configuration system
1 parent 4bb93e3 commit 3d5ae28

File tree

5 files changed

+134
-36
lines changed

5 files changed

+134
-36
lines changed

gameutil-rs/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "gameutil-rs"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[dependencies]
@@ -9,6 +9,8 @@ winapi = { version = "0.3.9", features = ["processthreadsapi"] }
99
sysinfo = "0.24.3"
1010
nwg = {version = "^1.0.12", package = "native-windows-gui"}
1111
livesplit-hotkey = "0.6.0"
12+
toml = "0.5"
13+
serde = {version = "1.0.136"}
1214

1315
[target.'cfg(target_os="windows")'.build-dependencies]
1416
winres = "^0.1"
@@ -17,4 +19,5 @@ winres = "^0.1"
1719
opt-level = 3
1820
lto = true
1921
codegen-units = 1
20-
panic = "abort"
22+
panic = "abort"
23+
strip = true

gameutil-rs/src/config.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
///! Config system is currently only updated on the startup of the application and when the "start" button is pressed. This is to minimize unnecessary reads and writes to the config file.
2+
use std::{
3+
fs::File,
4+
io::{Read, Write},
5+
};
6+
7+
use serde::{Deserialize, Serialize};
8+
9+
#[derive(Clone, Deserialize, Serialize)]
10+
pub struct Config {
11+
pub kill_dwm: bool,
12+
pub kill_explorer: bool,
13+
pub disable_idle: bool,
14+
pub timer_resolution: f64,
15+
}
16+
17+
impl Config {
18+
pub fn default() -> Config {
19+
Config {
20+
kill_dwm: true,
21+
kill_explorer: false,
22+
disable_idle: false,
23+
timer_resolution: 1.0,
24+
}
25+
}
26+
27+
pub fn read() -> Config {
28+
let file = File::open("gameutil.toml");
29+
match file {
30+
Err(_) => {
31+
let config = Config::default();
32+
config.write().expect("Failed to write config file!");
33+
config
34+
},
35+
Ok(mut file) => {
36+
let mut contents = String::new();
37+
file.read_to_string(&mut contents)
38+
.expect("Failed to read config file!");
39+
let mut config: Config = toml::from_str(&contents).expect("Failed to parse config file!");
40+
// Prevent both from being true, dwm kills explorer already
41+
if config.kill_dwm && config.kill_explorer {
42+
config.kill_explorer = false;
43+
}
44+
config
45+
}
46+
}
47+
}
48+
49+
pub fn write(&self) -> Result<(), Box<dyn std::error::Error>> {
50+
let mut file = File::create("gameutil.toml")?;
51+
let serialized = toml::to_string(&self)?;
52+
file.write_all(serialized.as_bytes())?;
53+
Ok(())
54+
}
55+
}

gameutil-rs/src/gui.rs

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ use std::rc::Rc;
22

33
use nwg::{CheckBoxState, NativeUi};
44

5+
use crate::config::Config;
6+
57
pub mod sys;
68

79
#[derive(Default)]
810
pub struct GameUtil {
911
window: nwg::Window,
1012
layout: nwg::GridLayout,
11-
timerresval: nwg::TextInput,
13+
timer_resolution: nwg::TextInput,
1214
start_button: nwg::Button,
1315
clean_button: nwg::Button,
1416
disableidle_button: nwg::CheckBox,
1517
kill_dwm: nwg::CheckBox,
16-
kill_exp: nwg::CheckBox,
18+
kill_explorer: nwg::CheckBox,
1719
timerresval_label: nwg::Label,
1820
timer_tooltip: nwg::Tooltip,
1921
idle_tooltip: nwg::Tooltip,
@@ -34,6 +36,8 @@ mod app_gui {
3436
fn build_ui(mut data: GameUtil) -> Result<GameUtilUi, nwg::NwgError> {
3537
use nwg::Event as E;
3638

39+
let config: Config = Config::read();
40+
3741
let mut font = nwg::Font::default();
3842
nwg::Font::builder()
3943
.family("MS Shell Dlg 2")
@@ -60,16 +64,17 @@ mod app_gui {
6064
.build(&mut data.timerresval_label)?;
6165

6266
nwg::TextInput::builder()
63-
.text("0.5")
67+
.text(&format!("{}", config.timer_resolution))
6468
.limit(3)
6569
.parent(&data.window)
66-
.build(&mut data.timerresval)?;
70+
.build(&mut data.timer_resolution)?;
6771

6872
// Disable Idle Option
6973
nwg::CheckBox::builder()
7074
.text("Disable Idle")
7175
//.position((10, 80))
7276
.parent(&data.window)
77+
.check_state(bool_to_checkbox_state(config.disable_idle))
7378
.build(&mut data.disableidle_button)?;
7479

7580
nwg::Button::builder()
@@ -80,16 +85,16 @@ mod app_gui {
8085
// radio button for kill type
8186
nwg::CheckBox::builder()
8287
.text("Kill DWM")
83-
.check_state(CheckBoxState::Checked)
88+
.check_state(bool_to_checkbox_state(config.kill_dwm))
8489
.parent(&data.window)
8590
.build(&mut data.kill_dwm)?;
8691

8792
// radio button for kill type
8893
nwg::CheckBox::builder()
8994
.text("Kill Explorer")
90-
.check_state(CheckBoxState::Unchecked)
95+
.check_state(bool_to_checkbox_state(config.kill_explorer))
9196
.parent(&data.window)
92-
.build(&mut data.kill_exp)?;
97+
.build(&mut data.kill_explorer)?;
9398

9499
nwg::Button::builder()
95100
.text("Clean Memory")
@@ -98,7 +103,7 @@ mod app_gui {
98103

99104
nwg::Tooltip::builder()
100105
.register(
101-
&*&mut data.timerresval,
106+
&*&mut data.timer_resolution,
102107
"Has no effect on Windows 2004+, 0.0 to disable.",
103108
)
104109
.build(&mut data.timer_tooltip)?;
@@ -108,7 +113,7 @@ mod app_gui {
108113
.build(&mut data.idle_tooltip)?;
109114

110115
nwg::Tooltip::builder()
111-
.register(&*&mut data.clean_button, "Hotkey: F4. Cleans the working set of all processes. Can cause a slight stutter after clicking so if in-game run it when you are safe.")
116+
.register(&*&mut data.clean_button, "Hotkey: F4. Cleans the working set of all processes. Can cause a slight stutter after clicking so if using in-game run it when you are safe.")
112117
.build(&mut data.clean_tooltip)?;
113118

114119
// Wrap-up
@@ -149,11 +154,11 @@ mod app_gui {
149154
}
150155
if handle == ui.kill_dwm {
151156
if ui.kill_dwm.check_state() == CheckBoxState::Checked {
152-
ui.kill_exp.set_check_state(CheckBoxState::Unchecked);
157+
ui.kill_explorer.set_check_state(CheckBoxState::Unchecked);
153158
}
154159
}
155-
if handle == ui.kill_exp {
156-
if ui.kill_exp.check_state() == CheckBoxState::Checked {
160+
if handle == ui.kill_explorer {
161+
if ui.kill_explorer.check_state() == CheckBoxState::Checked {
157162
ui.kill_dwm.set_check_state(CheckBoxState::Unchecked);
158163
}
159164
}
@@ -167,23 +172,23 @@ mod app_gui {
167172
}
168173
}
169174
E::OnTextInput => {
170-
if handle == ui.timerresval {
175+
if handle == ui.timer_resolution {
171176
// make sure numbers only
172177
#[allow(unused_variables)]
173178
// don't check for incorrect types if input is empty
174-
if !ui.timerresval.text().is_empty() {
175-
if let Err(num) = ui.timerresval.text().parse::<f32>() {
179+
if !ui.timer_resolution.text().is_empty() {
180+
if let Err(num) = ui.timer_resolution.text().parse::<f32>() {
176181
// warning message
177-
//nwg::modal_info_message(&ui.window, "Error", &format!("{} is not a valid number", ui.timerresval.text()));
182+
//nwg::modal_info_message(&ui.window, "Error", &format!("{} is not a valid number", ui.timer_resolution.text()));
178183

179184
// filter to only numbers
180-
ui.timerresval.set_text(
181-
&ui.timerresval
182-
.text()
183-
.chars()
184-
.filter(|&c| c.is_numeric() || c == '.')
185-
.collect::<String>(),
186-
);
185+
let timer_resolution: String = ui
186+
.timer_resolution
187+
.text()
188+
.chars()
189+
.filter(|&c| c.is_numeric() || c == '.')
190+
.collect::<String>();
191+
ui.timer_resolution.set_text(&timer_resolution);
187192
}
188193
}
189194
}
@@ -201,11 +206,11 @@ mod app_gui {
201206
.parent(&ui.window)
202207
.spacing(1)
203208
.child(0, 0, &ui.timerresval_label)
204-
.child(1, 0, &ui.timerresval)
209+
.child(1, 0, &ui.timer_resolution)
205210
.child(0, 1, &ui.disableidle_button)
206211
.child(1, 3, &ui.clean_button)
207212
.child(0, 2, &ui.kill_dwm)
208-
.child(1, 2, &ui.kill_exp)
213+
.child(1, 2, &ui.kill_explorer)
209214
.child_item(nwg::GridLayoutItem::new(&ui.start_button, 0, 3, 1, 1))
210215
.build(&ui.layout)?;
211216
return Ok(ui);
@@ -240,34 +245,56 @@ fn restore(ui: Rc<GameUtil>) {
240245
ui.start_button.set_text("Start");
241246
// disallow chaning settings while running
242247
ui.kill_dwm.set_enabled(true);
243-
ui.kill_exp.set_enabled(true);
248+
ui.kill_explorer.set_enabled(true);
244249
if ui.disableidle_button.check_state() == nwg::CheckBoxState::Checked {
245250
sys::idle(0);
246251
}
247252
if ui.kill_dwm.check_state() == CheckBoxState::Checked {
248253
sys::resumeproc("winlogon.exe");
249254
sys::startproc("explorer.exe");
250-
} else if ui.kill_exp.check_state() == CheckBoxState::Checked {
255+
} else if ui.kill_explorer.check_state() == CheckBoxState::Checked {
251256
sys::startproc("explorer.exe");
252257
}
253-
ui.timerresval.set_readonly(false);
258+
ui.timer_resolution.set_readonly(false);
254259
}
255260

256261
fn start(ui: Rc<GameUtil>) {
262+
let mut config: Config = Config {
263+
kill_dwm: false,
264+
kill_explorer: false,
265+
disable_idle: false,
266+
timer_resolution: 1.0,
267+
};
257268
// rename button to Restore
258269
ui.start_button.set_text("Restore");
270+
// Lock settings so there is proper restoration
259271
ui.kill_dwm.set_enabled(false);
260-
ui.kill_exp.set_enabled(false);
272+
ui.kill_explorer.set_enabled(false);
261273
if ui.disableidle_button.check_state() == nwg::CheckBoxState::Checked {
262274
sys::idle(1);
275+
config.disable_idle = true;
263276
}
264277
if ui.kill_dwm.check_state() == CheckBoxState::Checked {
265278
sys::killdwm();
266-
} else if ui.kill_exp.check_state() == CheckBoxState::Checked {
279+
config.kill_dwm = true;
280+
config.kill_explorer = false;
281+
} else if ui.kill_explorer.check_state() == CheckBoxState::Checked {
267282
sys::taskkill("explorer.exe");
283+
config.kill_explorer = true;
284+
config.kill_dwm = false;
268285
}
269-
if ui.timerresval.text().parse::<f32>().unwrap() != 0.0 {
270-
sys::timerres((ui.timerresval.text().parse::<f32>().unwrap() * 10000.0) as u32);
271-
ui.timerresval.set_readonly(true);
286+
if ui.timer_resolution.text().parse::<f32>().unwrap() != 0.0 {
287+
let resolution: f64 = ui.timer_resolution.text().parse::<f64>().unwrap();
288+
sys::timerres((resolution * 10000.0) as u32);
289+
config.timer_resolution = resolution;
290+
ui.timer_resolution.set_readonly(true);
272291
}
292+
config.write().unwrap();
273293
}
294+
295+
fn bool_to_checkbox_state(b: bool) -> CheckBoxState {
296+
match b {
297+
true => CheckBoxState::Checked,
298+
false => CheckBoxState::Unchecked,
299+
}
300+
}

gameutil-rs/src/gui/sys.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{ffi::c_void, process::Command};
2-
use sysinfo::{ProcessExt, System, SystemExt, PidExt};
2+
use sysinfo::{PidExt, ProcessExt, System, SystemExt};
33
use winapi::um::processthreadsapi::OpenProcess;
44
use windows_dll::dll;
55

gameutil-rs/src/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
#![windows_subsystem = "windows"]
22

3+
mod config;
34
mod gui;
45

6+
use std::fs::File;
7+
8+
use crate::config::Config;
9+
510
fn main() {
11+
let file = File::open("gameutil.toml");
12+
match file {
13+
Err(_) => {
14+
let config = Config::default();
15+
config.write().expect("Failed to write config file!");
16+
}
17+
_ => {}
18+
}
619
let hotkey = gui::sys::hotkey();
720
gui::gui_init();
821
hotkey

0 commit comments

Comments
 (0)