diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..98736cc
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,28 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug selected unit test",
+ "cargo": {
+ "args": [
+ "test",
+ // "--no-run",
+ "test::${selectedText}"
+ ],
+ // "filter": {
+ // "name": "vk_layer_rs",
+ // "kind": "lib"
+ // }
+ },
+ "cwd": "${workspaceFolder}",
+ "env": {
+ "RFACTOR_HUD_LOG": "1"
+ }
+ },
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index d3c1b4d..e3f2351 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ I would consider the following elements as working:
# How to enable
### Archlinux based
-Simply use the PKGBUILD from the pkgbuild directory ([How to use it](https://wiki.archlinux.org/title/Makepkg))
+Simply use the PKGBUILD from the resources/pkgbuild directory ([How to use it](https://wiki.archlinux.org/title/Makepkg))
### Manual installation
1) Build this repository `cargo build --release`
@@ -30,7 +30,7 @@ You need to have rFactor2 memory plugin shared file enabled ([rF2SharedMemoryMap
### Debugging
-Add `RFACTOR_HUD_LOG=1` to the launch options of the game. Then a file log file will be created (`$HOME/rf2_vk_hud.log`). The logs may seem super random and they are super random, since I didn't have (or took) the time to unify my logging output.
+Add `RFACTOR_HUD_LOG=1` to the launch options of the game. Then a file log file will be created (`$HOME/rf2_vk_hud.log`). The logs may seem super random and they are super random, since I didn't have (or took) the time to unify my logging output.
I would not recommend to activate logging while playing a longer session, it can get quite big (about 15 minutes are roundabout 1 million lines).
# Resources
diff --git a/font.png b/resources/font.png
similarity index 100%
rename from font.png
rename to resources/font.png
diff --git a/pkgbuild/PKGBUILD b/resources/pkgbuild/PKGBUILD
similarity index 82%
rename from pkgbuild/PKGBUILD
rename to resources/pkgbuild/PKGBUILD
index 890cef1..2b8f2f9 100644
--- a/pkgbuild/PKGBUILD
+++ b/resources/pkgbuild/PKGBUILD
@@ -25,5 +25,5 @@ package() {
install -Dm755 ${_pkgbase}/target/release/libvk_layer_rs.so "${pkgdir}"/usr/lib/libvk_layer_rs.so
# copy layer discovery info file
- install -Dm644 ${_pkgbase}/rFactorOverlay.json "${pkgdir}"/usr/share/vulkan/implicit_layer.d/rFactorOverlay.json
+ install -Dm644 ${_pkgbase}/resources/rFactorOverlay.json "${pkgdir}"/usr/share/vulkan/implicit_layer.d/rFactorOverlay.json
}
diff --git a/pkgbuild/README.md b/resources/pkgbuild/README.md
similarity index 100%
rename from pkgbuild/README.md
rename to resources/pkgbuild/README.md
diff --git a/rFactorOverlay.json b/resources/rFactorOverlay.json
similarity index 100%
rename from rFactorOverlay.json
rename to resources/rFactorOverlay.json
diff --git a/src/lib.rs b/src/lib.rs
index 57198a2..74ac621 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ pub(crate) fn logging() -> bool {
macro_rules! write_log {
($msg:expr) => {
+ println!("{}", $msg);
if crate::logging() {
crate::log($msg);
}
@@ -31,18 +32,25 @@ fn get_config(home: &str) -> OverlayConfig {
let config_path = Path::new(&home).join(".config/rFactorHUD/config.json");
if config_path.exists() {
- fs::read_to_string(&config_path)
- .map(|s| {
- serde_json::from_str(&s).unwrap_or({
- write_log!("failed to deserialize config file");
- OverlayConfig::new()
- })
- })
- .unwrap_or({
- write_log!(format!("failed to open config file: {:?}", config_path));
+ match fs::read_to_string(&config_path) {
+ Ok(s) => serde_json::from_str(&s).unwrap_or_else(|err| {
+ write_log!(format!(
+ "failed to deserialize config file from {} \n({:?})",
+ s, err
+ ));
OverlayConfig::new()
- })
+ }),
+ Err(err) => {
+ write_log!(format!(
+ "failed to open config file: {:?} due to {:?}",
+ config_path, err
+ ));
+ OverlayConfig::new()
+ }
+ }
} else {
+ write_log!("create parent dir structure");
+
if let Err(err) = std::fs::create_dir_all(config_path.parent().unwrap()) {
write_log!(format!("failed to create dirs for config file: {:?}", err));
}
@@ -52,6 +60,8 @@ fn get_config(home: &str) -> OverlayConfig {
match File::create(config_path) {
Ok(mut file) => match serde_json::to_string_pretty(&config) {
Ok(conf_str) => {
+ write_log!("create config file with default values");
+
if let Err(err) = file.write_all(conf_str.as_bytes()) {
write_log!(format!("failed to write to config file: {:?}", err));
}
@@ -69,6 +79,31 @@ fn get_config(home: &str) -> OverlayConfig {
}
}
+pub fn check_logging(home: &str) {
+ let log_enabled = match std::env::var("RFACTOR_HUD_LOG") {
+ Ok(var) => {
+ let i: u32 = var.parse().unwrap_or(0);
+
+ i == 1
+ }
+ Err(_) => false,
+ };
+
+ unsafe { LOG_ENABLED = log_enabled };
+
+ if logging() {
+ unsafe {
+ LOG_FILE = format!("{}/rf2_vk_hud.log", home);
+ }
+
+ if let Err(_) = File::create(unsafe { &LOG_FILE }) {}
+
+ write_log!(" ==================================================================");
+ write_log!(" ======================= New Negotiation ==========================");
+ write_log!(" ==================================================================");
+ }
+}
+
pub fn log(msg: impl ToString) {
assert!(logging());
@@ -83,11 +118,14 @@ pub fn log(msg: impl ToString) {
#[cfg(test)]
mod test {
- use crate::get_config;
+ use crate::{check_logging, get_config};
#[test]
fn config() {
let home = std::env::var("HOME").unwrap();
- get_config(&home);
+ check_logging(&home);
+ let config = get_config(&home);
+
+ println!("{:#?}", config);
}
}
diff --git a/src/overlay/elements/leaderboard/deltaboard_grid.xml b/src/overlay/elements/leaderboard/deltaboard_grid.xml
index 3cb0254..7934c2c 100644
--- a/src/overlay/elements/leaderboard/deltaboard_grid.xml
+++ b/src/overlay/elements/leaderboard/deltaboard_grid.xml
@@ -1,6 +1,6 @@
-
\ No newline at end of file
diff --git a/src/overlay/elements/leaderboard/leaderboard_entry.rs b/src/overlay/elements/leaderboard/leaderboard_entry.rs
index d45cdc5..e8fec60 100644
--- a/src/overlay/elements/leaderboard/leaderboard_entry.rs
+++ b/src/overlay/elements/leaderboard/leaderboard_entry.rs
@@ -108,7 +108,26 @@ impl LeaderBoardEntry {
pub fn change_name(&mut self, name: String) -> Result<()> {
self.name = name;
- self.name_label.set_text(&self.name)
+ self.name_label.set_text(Self::check_string(&self.name))
+ }
+
+ fn check_string(s: &str) -> String {
+ std::str::from_utf8(
+ &s.as_bytes()
+ .iter()
+ .filter(|&&b| {
+ (b > 96 && b < 123) // small letters
+ || (b > 64 && b < 91) // big letters
+ || (b > 47 && b < 58) // numbers
+ || b == 45 // dash
+ || b == 95 // underscore
+ || b == 32 // whitespace
+ })
+ .map(|&b| b)
+ .collect::>(),
+ )
+ .unwrap()
+ .to_string()
}
pub fn snippet(&self) -> Arc {
@@ -145,40 +164,41 @@ impl LeaderBoardEntry {
self.time_label.set_text("---")
}
- pub fn force_display_behind_leader(&mut self) -> Result<()> {
- match self.behind {
+ fn split_minute(time: f64) -> (f64, f64) {
+ let full_minutes = (time / 60.0).floor();
+ let remainder = time - (full_minutes * 60.0);
+
+ (full_minutes, remainder)
+ }
+
+ fn calculate_behind_leader(behind: BehindLeader) -> String {
+ match behind {
BehindLeader::Time(time_behind) => {
// check if we are leader
if time_behind <= 0.0 {
- self.time_label.set_text("---")?;
+ "---".to_string()
} else {
- let text = if time_behind > 60.0 {
- let full_minutes = (self.best_lap / 60.0).floor();
- let remainder = self.best_lap - (full_minutes * 60.0);
+ if time_behind > 60.0 {
+ let (full_minutes, remainder) = Self::split_minute(time_behind);
format!("+{:.0}:{:.0}", full_minutes, remainder)
} else {
format!("+{:.3}", time_behind)
- };
-
- self.time_label.set_text(text)?;
+ }
}
}
BehindLeader::Laps(laps_behind) => {
- self.time_label.set_text(format!("+{}", laps_behind))?;
- }
- BehindLeader::DNF => {
- self.time_label.set_text("DNF")?;
- }
- BehindLeader::DSQ => {
- self.time_label.set_text("DSQ")?;
- }
- BehindLeader::PITS => {
- self.time_label.set_text("PIT")?;
+ format!("+{}", laps_behind)
}
+ BehindLeader::DNF => "DNF".to_string(),
+ BehindLeader::DSQ => "DSQ".to_string(),
+ BehindLeader::PITS => "PIT".to_string(),
}
+ }
- Ok(())
+ pub fn force_display_behind_leader(&self) -> Result<()> {
+ self.time_label
+ .set_text(Self::calculate_behind_leader(self.behind))
}
pub fn update_time_behind_leader(&mut self, behind: BehindLeader) -> Result<()> {
@@ -191,23 +211,23 @@ impl LeaderBoardEntry {
Ok(())
}
- pub fn force_display_best_lap(&mut self) -> Result<()> {
- if self.best_lap <= 0.0 {
- self.time_label.set_text("---")?;
+ fn calculuate_best_lap(best_lap: f64) -> String {
+ if best_lap <= 0.0 {
+ "---".to_string()
} else {
- let text = if self.best_lap > 60.0 {
- let full_minutes = (self.best_lap / 60.0).floor();
- let remainder = self.best_lap - (full_minutes * 60.0);
+ if best_lap > 60.0 {
+ let (full_minutes, remainder) = Self::split_minute(best_lap);
format!("{:.0}:{:.3}", full_minutes, remainder)
} else {
- format!("{:.3}", self.best_lap)
- };
-
- self.time_label.set_text(text)?;
+ format!("{:.3}", best_lap)
+ }
}
+ }
- Ok(())
+ pub fn force_display_best_lap(&mut self) -> Result<()> {
+ self.time_label
+ .set_text(Self::calculuate_best_lap(self.best_lap))
}
pub fn update_best_lap(&mut self, time: f64) -> Result<()> {
@@ -220,19 +240,21 @@ impl LeaderBoardEntry {
Ok(())
}
- pub fn force_display_behind_next(&mut self) -> Result<()> {
- let text = if self.time_behind_next <= 0.0 {
+ fn calculate_behind_next(behind_next: f64) -> String {
+ if behind_next <= 0.0 {
"---".to_string()
- } else if self.time_behind_next > 60.0 {
- let full_minutes = (self.time_behind_next / 60.0).floor();
- let remainder = self.time_behind_next - (full_minutes * 60.0);
+ } else if behind_next > 60.0 {
+ let (full_minutes, remainder) = Self::split_minute(behind_next);
- format!("+{:.0}:{:.3}", full_minutes, remainder)
+ format!("+{:.0}:{:.0}", full_minutes, remainder)
} else {
- format!("+{:.3}", self.time_behind_next)
- };
+ format!("+{:.3}", behind_next)
+ }
+ }
- self.time_label.set_text(text)
+ pub fn force_display_behind_next(&mut self) -> Result<()> {
+ self.time_label
+ .set_text(Self::calculate_behind_next(self.time_behind_next))
}
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
@@ -253,3 +275,37 @@ impl LeaderBoardEntry {
self.place_updated = false;
}
}
+
+#[cfg(test)]
+mod test {
+ use super::{BehindLeader, LeaderBoardEntry};
+
+ #[test]
+ fn test_string_replacement() {
+ let s_orig = "blaĆ¼";
+ let s = super::LeaderBoardEntry::check_string(s_orig);
+ println!("{} {:?}", s_orig, s);
+
+ for b in s.as_bytes() {
+ println!("{}", b)
+ }
+
+ println!();
+
+ let s2_orig = "az AZ 09 # - _";
+ let s2 = super::LeaderBoardEntry::check_string(s2_orig);
+ println!("{} {:?}", s2_orig, s2);
+
+ for b in s2.as_bytes() {
+ println!("{}", *b);
+ }
+ }
+
+ #[test]
+ #[allow(unused)]
+ fn entry_display_tests() {
+ let behind_leader = LeaderBoardEntry::calculate_behind_leader(BehindLeader::Time(85.42));
+ let behind_next = LeaderBoardEntry::calculate_behind_next(150.213423);
+ let best_lap = LeaderBoardEntry::calculuate_best_lap(97.23436);
+ }
+}
diff --git a/src/overlay/elements/leaderboard/leaderboard_entry.xml b/src/overlay/elements/leaderboard/leaderboard_entry.xml
index 286ad0d..fd5493c 100644
--- a/src/overlay/elements/leaderboard/leaderboard_entry.xml
+++ b/src/overlay/elements/leaderboard/leaderboard_entry.xml
@@ -1,11 +1,11 @@
-
+
-
+ y_slot="0" x_size="9" text_color="black">
+
\ No newline at end of file
diff --git a/src/overlay/elements/leaderboard/leaderboard_grid.xml b/src/overlay/elements/leaderboard/leaderboard_grid.xml
index 0c3b7a5..9eca1e9 100644
--- a/src/overlay/elements/leaderboard/leaderboard_grid.xml
+++ b/src/overlay/elements/leaderboard/leaderboard_grid.xml
@@ -1,6 +1,6 @@
-
\ No newline at end of file
diff --git a/src/overlay/elements/leaderboard/mod.rs b/src/overlay/elements/leaderboard/mod.rs
index e8d534b..c5c7bbc 100644
--- a/src/overlay/elements/leaderboard/mod.rs
+++ b/src/overlay/elements/leaderboard/mod.rs
@@ -22,7 +22,7 @@ use crate::write_log;
use bg_generator::BackgroundGenerator;
-#[derive(Default, Deserialize, Serialize, Clone, Copy)]
+#[derive(Default, Deserialize, Serialize, Clone, Copy, Debug)]
pub struct LeaderBoardConfig {
first_board_color: [f32; 3],
second_board_color: [f32; 3],
diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs
index 9a69b52..e11e641 100644
--- a/src/overlay/mod.rs
+++ b/src/overlay/mod.rs
@@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize};
pub trait UiOverlay: DataReceiver {}
-#[derive(Deserialize, Serialize)]
+#[derive(Deserialize, Serialize, Debug)]
pub struct UiSelectorConfig {
pub enable_watermark: bool,
@@ -45,7 +45,7 @@ impl UiSelectorConfig {
}
}
-#[derive(Deserialize, Serialize)]
+#[derive(Deserialize, Serialize, Debug)]
pub struct OverlayConfig {
pub ui_config: UiSelectorConfig,
pub radar_config: RadarConfig,
@@ -144,7 +144,7 @@ impl Overlay {
// only font is used
let mut create_info = GuiHandlerCreateInfo::default();
- create_info.font = Font::Bytes(include_bytes!("../../font.png"));
+ create_info.font = Font::Bytes(include_bytes!("../../resources/font.png"));
// required to not crash
create_info.resource_directory = AssetPath::from("");
diff --git a/src/vk_layer/mod.rs b/src/vk_layer/mod.rs
index 4240975..3338c50 100644
--- a/src/vk_layer/mod.rs
+++ b/src/vk_layer/mod.rs
@@ -2,47 +2,27 @@ mod enums;
mod structs;
mod vk_handles;
+use crate::check_logging;
use enums::*;
use structs::*;
use vk_handles::*;
use vulkan_rs::prelude::*;
-use std::{ffi::c_void, fs::File, mem, os::raw::c_char, ptr};
+use std::{ffi::c_void, mem, os::raw::c_char, ptr};
static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR =
unsafe { mem::transmute(vkVoidFunction as *const c_void) };
-use crate::{get_config, logging, write_log, LOG_ENABLED, LOG_FILE, OVERLAY};
+use crate::{get_config, logging, write_log, OVERLAY};
#[no_mangle]
#[allow(non_snake_case)]
pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion(
pVersionStruct: *mut VkNegotiateLayerInterface,
) -> VkResult {
- let log_enabled = match std::env::var("RFACTOR_HUD_LOG") {
- Ok(var) => {
- let i: u32 = var.parse().unwrap_or(0);
-
- i == 1
- }
- Err(_) => false,
- };
-
- unsafe { LOG_ENABLED = log_enabled };
-
let home = std::env::var("HOME").unwrap();
- if logging() {
- unsafe {
- LOG_FILE = format!("{}/rf2_vk_hud.log", home);
- }
-
- if let Err(_) = File::create(unsafe { &LOG_FILE }) {}
-
- write_log!(" ==================================================================");
- write_log!(" ======================= New Negotiation ==========================");
- write_log!(" ==================================================================");
- }
+ check_logging(&home);
unsafe {
OVERLAY.set_config(get_config(&home));