Merge branch 'release/V0.2.0_Alpha' into stable
This commit is contained in:
commit
8a6c9f34d5
30 changed files with 2229 additions and 62 deletions
|
@ -1,4 +0,0 @@
|
||||||
[target.x86_64-unknown-linux-gnu]
|
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-Wl,-rpath,$ORIGIN",
|
|
||||||
]
|
|
27
.gitignore
vendored
27
.gitignore
vendored
|
@ -1,16 +1,13 @@
|
||||||
# Generated by Cargo
|
*
|
||||||
# will have compiled files and executables
|
!*/
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
!.gitignore
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
!src/**
|
||||||
Cargo.lock
|
!res/HarmonyLinkLogo.ico
|
||||||
|
!Resources/**
|
||||||
# These are backup files generated by rustfmt
|
!.github/**
|
||||||
**/*.rs.bk
|
!Build.rs
|
||||||
|
!Cargo.toml
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
!Cargo.lock
|
||||||
*.pdb
|
!LICENSE
|
||||||
|
!README.md
|
||||||
!Resources/*
|
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"rust-analyzer.linkedProjects": [
|
|
||||||
".\\Cargo.toml",
|
|
||||||
".\\Cargo.toml"
|
|
||||||
]
|
|
||||||
}
|
|
1589
Cargo.lock
generated
Normal file
1589
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
26
Cargo.toml
26
Cargo.toml
|
@ -1,7 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "harmony_link_server"
|
name = "harmony_link_server"
|
||||||
version = "1.0.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["Jordon jordon@jordongamedev.co.uk"]
|
||||||
|
homepage = "https://jordongamedev.co.uk"
|
||||||
|
repository = "https://github.com/Jordonbc/HarmonyLinkServer"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -12,9 +16,27 @@ lto = true # Enables link to optimizations
|
||||||
opt-level = "z" # Optimize for binary size
|
opt-level = "z" # Optimize for binary size
|
||||||
strip = true # Remove debug symbols
|
strip = true # Remove debug symbols
|
||||||
|
|
||||||
|
[package.metadata.winres]
|
||||||
|
LegalCopyright = "Copyright © 2023 Jordon Brooks"
|
||||||
|
ProductName = "HarmonyLink: Server"
|
||||||
|
FileDescription = "Optimized games for your handheld!"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "harmony_link_server"
|
name = "harmony_link_server"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
vergen = { version = "8.2.1", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] }
|
||||||
|
winres = "0.1.12"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libloading = "0.8.0"
|
actix-web = "4.3.1"
|
||||||
|
env_logger = "0.10.0"
|
||||||
|
log = "0.4.18"
|
||||||
|
serde = {version = "1.0.163", features = ["derive"]}
|
||||||
|
serde_json = "1.0.96"
|
||||||
|
sysinfo = "0.29.0"
|
||||||
|
os_info = "3.0"
|
||||||
|
battery = "0.7.8"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
rusb = "0.9.2"
|
||||||
|
|
11
Resources/dock_models.json
Normal file
11
Resources/dock_models.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"brand": "JSAUX",
|
||||||
|
"model": "HB0603",
|
||||||
|
"usb_ids": [
|
||||||
|
[ 4826, 21521 ],
|
||||||
|
[ 4826, 1041 ],
|
||||||
|
[ 4826, 33139 ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Binary file not shown.
Binary file not shown.
53
build.rs
53
build.rs
|
@ -1,14 +1,21 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
fn main() {
|
use vergen::EmitBuilder;
|
||||||
|
|
||||||
|
fn copy_resources() -> Result<(), Box<dyn Error>> {
|
||||||
// The directory of the Cargo manifest of the package that is currently being built.
|
// The directory of the Cargo manifest of the package that is currently being built.
|
||||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not defined");
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not defined");
|
||||||
|
|
||||||
// The directory where the final binaries will be placed.
|
// The directory where the final binaries will be placed.
|
||||||
let profile = env::var("PROFILE").expect("PROFILE is not defined");
|
let profile = env::var("PROFILE")?;
|
||||||
let out_dir = Path::new(&manifest_dir).join("target").join(profile);
|
let out_dir = Path::new(&manifest_dir).join("target").join(profile).join("Resources");
|
||||||
|
|
||||||
|
if !out_dir.exists() {
|
||||||
|
fs::create_dir(&out_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
// The Resources directory.
|
// The Resources directory.
|
||||||
let resources_dir = Path::new(&manifest_dir).join("Resources");
|
let resources_dir = Path::new(&manifest_dir).join("Resources");
|
||||||
|
@ -19,17 +26,47 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over each entry in the Resources directory.
|
// Iterate over each entry in the Resources directory.
|
||||||
for entry in fs::read_dir(resources_dir).expect("read_dir call failed") {
|
for entry in fs::read_dir(resources_dir)? {
|
||||||
let entry = entry.expect("entry is invalid");
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
// The destination path is the output directory plus the file name.
|
// The destination path is the output directory plus the file name.
|
||||||
let dest_path = out_dir.join(path.file_name().expect("file has no name"));
|
let dest_path = out_dir.join(path.file_name().expect("file has no name"));
|
||||||
// Copy the file.
|
// Copy the file.
|
||||||
fs::copy(&path, &dest_path).expect("copy failed");
|
fs::copy(&path, &dest_path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rustc-env=RUSTFLAGS=-C link-arg=-Wl,-rpath,$ORIGIN");
|
Ok(())
|
||||||
println!("cargo:rerun-if-changed=src/main.rs");
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn windows_resource() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut res = winres::WindowsResource::new();
|
||||||
|
res.set_icon("res/HarmonyLinkLogo.ico");
|
||||||
|
res.compile()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn windows_resource() -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
// Emit the instructions
|
||||||
|
EmitBuilder::builder()
|
||||||
|
.all_build()
|
||||||
|
.all_cargo()
|
||||||
|
.all_git()
|
||||||
|
.all_rustc()
|
||||||
|
.all_sysinfo()
|
||||||
|
.emit()?;
|
||||||
|
|
||||||
|
// Copy the Resources folder
|
||||||
|
copy_resources()?;
|
||||||
|
|
||||||
|
windows_resource()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
BIN
res/HarmonyLinkLogo.ico
Normal file
BIN
res/HarmonyLinkLogo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
14
src/api/api.rs
Normal file
14
src/api/api.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use actix_web::{HttpResponse, get, web};
|
||||||
|
|
||||||
|
use crate::version::info::Version;
|
||||||
|
|
||||||
|
#[get("/supported_versions")]
|
||||||
|
pub async fn versions() -> HttpResponse {
|
||||||
|
let version = Version::get();
|
||||||
|
HttpResponse::Ok().json(&version.supported_api_versions)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(versions);
|
||||||
|
// Register other version 1 handlers here...
|
||||||
|
}
|
99
src/api/endpoints_v1.rs
Normal file
99
src/api/endpoints_v1.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use actix_web::web;
|
||||||
|
use actix_web::{HttpResponse, get};
|
||||||
|
|
||||||
|
use crate::v1::{docking, os, all_info, battery};
|
||||||
|
use crate::version;
|
||||||
|
|
||||||
|
#[get("/are_you_there")]
|
||||||
|
pub async fn heartbeat() -> HttpResponse {
|
||||||
|
HttpResponse::Ok().body("yes")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/all_info")]
|
||||||
|
pub async fn get_all_info() -> HttpResponse {
|
||||||
|
match all_info::stats::get_all_info() {
|
||||||
|
Ok(info) => {
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
println!("Successfully got all info: {}", &info.clone().to_string());
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().json(&info)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to get all info: {}", err);
|
||||||
|
HttpResponse::InternalServerError().body(format!("Failed to get device info: {}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/dock_info")]
|
||||||
|
pub async fn get_dock_info() -> HttpResponse {
|
||||||
|
match docking::stats::get_dock() {
|
||||||
|
Ok(info) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
println!("Successfully got dock info: {}", &info.clone().to_string());
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().json(&info)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to get dock info: {}", err);
|
||||||
|
HttpResponse::InternalServerError().body(format!("Failed to get dock info: {}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[get("/os_info")]
|
||||||
|
pub async fn get_os_info() -> HttpResponse {
|
||||||
|
match os::stats::get_os() {
|
||||||
|
Ok(info) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
println!("Successfully got os info: {}", &info.clone().to_string());
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().json(&info)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to get os info: {}", err);
|
||||||
|
HttpResponse::InternalServerError().body(format!("Failed to get OS info: {}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/battery_info")]
|
||||||
|
pub async fn get_battery_info() -> HttpResponse {
|
||||||
|
match battery::stats::get_battery_info() {
|
||||||
|
Ok(info) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
println!("Successfully got battery info: {}", &info.clone().to_string());
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().json(&info)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to get battery info: {}", err);
|
||||||
|
HttpResponse::InternalServerError().body(format!("Failed to get battery info: {}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/version_info")]
|
||||||
|
pub async fn get_version_info() -> HttpResponse {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
println!("Successfully got version info: {}", version::info::Version::get().to_string());
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().json(&version::info::Version::get())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(heartbeat);
|
||||||
|
cfg.service(get_all_info);
|
||||||
|
cfg.service(get_dock_info);
|
||||||
|
cfg.service(get_os_info);
|
||||||
|
cfg.service(get_battery_info);
|
||||||
|
cfg.service(get_version_info);
|
||||||
|
// Register other version 1 handlers here...
|
||||||
|
}
|
3
src/api/mod.rs
Normal file
3
src/api/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod server;
|
||||||
|
mod endpoints_v1;
|
||||||
|
mod api;
|
28
src/api/server.rs
Normal file
28
src/api/server.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use actix_web::{HttpServer, web};
|
||||||
|
|
||||||
|
use crate::api::endpoints_v1;
|
||||||
|
use crate::api::api;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub async fn stop_actix_web(server: actix_web::dev::Server) -> std::io::Result<()> {
|
||||||
|
println!("Stopping server.");
|
||||||
|
server.handle().stop(true).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_actix_web(port: u16) -> std::io::Result<actix_web::dev::Server> {
|
||||||
|
|
||||||
|
println!("Starting webserver on 127.0.0.1:{}", port);
|
||||||
|
|
||||||
|
let server = HttpServer::new(move || {
|
||||||
|
let logger = actix_web::middleware::Logger::default();
|
||||||
|
actix_web::App::new()
|
||||||
|
.wrap(logger)
|
||||||
|
.service(web::scope("/api").configure(api::configure))
|
||||||
|
.service(web::scope("/v1").configure(endpoints_v1::configure))
|
||||||
|
})
|
||||||
|
.bind(("127.0.0.1", port))?
|
||||||
|
.run();
|
||||||
|
|
||||||
|
Ok(server)
|
||||||
|
}
|
57
src/main.rs
57
src/main.rs
|
@ -1,34 +1,37 @@
|
||||||
extern crate libloading;
|
mod v1;
|
||||||
|
|
||||||
|
mod version;
|
||||||
|
use version::info::Version;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
|
||||||
|
static PORT: u16 = 9000;
|
||||||
|
|
||||||
|
static USE_FALLBACK_DOCK_DETECTION: bool = false;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Use `cfg!` macro to detect OS
|
|
||||||
let lib_path = if cfg!(target_os = "windows") {
|
|
||||||
"harmony_link_core.dll"
|
|
||||||
} else if cfg!(target_os = "linux") {
|
|
||||||
"libharmony_link_core.so"
|
|
||||||
} else {
|
|
||||||
eprintln!("Unsupported OS");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let lib = unsafe { match libloading::Library::new(lib_path) {
|
//#[cfg(debug_assertions)]
|
||||||
Ok(lib) => lib,
|
{
|
||||||
Err(err) => {
|
let version_info = Version::get();
|
||||||
eprintln!("Error loading dynamic library: {}", err);
|
println!("Version: {}", version_info.version);
|
||||||
return;
|
println!("Build Timestamp: {}", version_info.build_timestamp);
|
||||||
},
|
println!("Git Branch: {}", version_info.git_branch);
|
||||||
}
|
println!("Git Describe: {}", version_info.git_describe);
|
||||||
};
|
println!("Git Commit Timestamp: {}", version_info.git_commit_timestamp);
|
||||||
|
println!("Debug Build: {}", version_info.debug);
|
||||||
|
println!("API versions: {}", version_info.supported_api_versions_to_string());
|
||||||
|
|
||||||
unsafe {
|
println!("\n\n");
|
||||||
let func: libloading::Symbol<unsafe extern "C" fn()> = match lib.get(b"start") {
|
|
||||||
Ok(func) => func,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Error finding function in dynamic library: {}", err);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
func();
|
println!("HarmonyLink ©️ Jordon Brooks 2023");
|
||||||
}
|
|
||||||
|
|
||||||
|
let sys = actix_web::rt::System::new();
|
||||||
|
sys.block_on(async {
|
||||||
|
let result = api::server::start_actix_web(PORT).expect("err");
|
||||||
|
|
||||||
|
let _ = result.await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
2
src/v1/all_info/mod.rs
Normal file
2
src/v1/all_info/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod stats;
|
||||||
|
pub mod structs;
|
18
src/v1/all_info/stats.rs
Normal file
18
src/v1/all_info/stats.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::v1::docking;
|
||||||
|
use crate::v1::battery;
|
||||||
|
use crate::v1::os;
|
||||||
|
use crate::version;
|
||||||
|
|
||||||
|
use super::structs::Allinfo;
|
||||||
|
|
||||||
|
/* This will query all the modules and return all the combined data */
|
||||||
|
pub fn get_all_info() -> Result<Allinfo, Box<dyn std::error::Error>> {
|
||||||
|
let mut all_info = Allinfo::new();
|
||||||
|
|
||||||
|
all_info.dock = docking::stats::get_dock_info()?;
|
||||||
|
all_info.battery = battery::stats::get_battery_info()?;
|
||||||
|
all_info.os = os::stats::get_os()?;
|
||||||
|
all_info.version = version::info::Version::get();
|
||||||
|
|
||||||
|
Ok(all_info)
|
||||||
|
}
|
24
src/v1/all_info/structs.rs
Normal file
24
src/v1/all_info/structs.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use crate::{v1::{os, battery, docking::{self, structs::DockInfo}}, version};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Allinfo {
|
||||||
|
pub os: os::structs::OSInfo,
|
||||||
|
pub battery: battery::structs::BatteryInfo,
|
||||||
|
pub dock: docking::structs::DockInfo,
|
||||||
|
pub version: version::info::Version
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Allinfo {
|
||||||
|
pub fn new() -> Allinfo {
|
||||||
|
Allinfo { os: os::structs::OSInfo::new(),
|
||||||
|
battery: battery::structs::BatteryInfo::new(),
|
||||||
|
dock: DockInfo::new(),
|
||||||
|
version: version::info::Version::get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
}
|
2
src/v1/battery/mod.rs
Normal file
2
src/v1/battery/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod stats;
|
||||||
|
pub mod structs;
|
38
src/v1/battery/stats.rs
Normal file
38
src/v1/battery/stats.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::v1::battery::structs::ChargingStatus;
|
||||||
|
|
||||||
|
use super::structs::BatteryInfo;
|
||||||
|
|
||||||
|
pub fn get_battery_info() -> Result<BatteryInfo, Box<dyn std::error::Error>> {
|
||||||
|
let mut battery_info = BatteryInfo { has_battery: false, battery_percent: 0, charging_status: ChargingStatus::UNKNOWN };
|
||||||
|
|
||||||
|
let manager = battery::Manager::new().unwrap();
|
||||||
|
|
||||||
|
battery_info.has_battery = manager.batteries().unwrap().count() > 0;
|
||||||
|
|
||||||
|
if !battery_info.has_battery {
|
||||||
|
return Ok(battery_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx, battery) in manager.batteries()?.enumerate() {
|
||||||
|
let battery = battery?;
|
||||||
|
let state = battery.state();
|
||||||
|
let energy = battery.energy();
|
||||||
|
let full_energy = battery.energy_full();
|
||||||
|
let state_of_charge = (energy / full_energy).get::<battery::units::ratio::percent>();
|
||||||
|
|
||||||
|
println!("Battery #{}:", idx);
|
||||||
|
println!("Charging status: {:?}", state);
|
||||||
|
println!("Charge level: {:.2}%", state_of_charge);
|
||||||
|
|
||||||
|
battery_info.battery_percent = state_of_charge.round() as i8;
|
||||||
|
battery_info.charging_status = match state {
|
||||||
|
battery::State::Charging => ChargingStatus::Charging,
|
||||||
|
battery::State::Discharging => ChargingStatus::Battery,
|
||||||
|
battery::State::Empty => ChargingStatus::Battery,
|
||||||
|
battery::State::Full => ChargingStatus::Battery,
|
||||||
|
_ => ChargingStatus::UNKNOWN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(battery_info)
|
||||||
|
}
|
28
src/v1/battery/structs.rs
Normal file
28
src/v1/battery/structs.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Clone)]
|
||||||
|
pub enum ChargingStatus {
|
||||||
|
Charging,
|
||||||
|
Battery,
|
||||||
|
UNKNOWN,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct BatteryInfo {
|
||||||
|
pub has_battery: bool,
|
||||||
|
pub battery_percent: i8,
|
||||||
|
pub charging_status: ChargingStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BatteryInfo {
|
||||||
|
pub fn new() -> BatteryInfo {
|
||||||
|
BatteryInfo {
|
||||||
|
has_battery: false,
|
||||||
|
battery_percent: 0,
|
||||||
|
charging_status: ChargingStatus::UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
}
|
2
src/v1/docking/mod.rs
Normal file
2
src/v1/docking/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod stats;
|
||||||
|
pub mod structs;
|
90
src/v1/docking/stats.rs
Normal file
90
src/v1/docking/stats.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use std::{io::BufReader, fs::File};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::{v1::{battery::{stats::get_battery_info, structs::ChargingStatus}}, USE_FALLBACK_DOCK_DETECTION};
|
||||||
|
|
||||||
|
use super::structs::{Dock, DockInfo};
|
||||||
|
|
||||||
|
/* This will get the current dock info. */
|
||||||
|
pub fn get_dock_info() -> Result<DockInfo, Box<dyn std::error::Error>> {
|
||||||
|
let mut dock = DockInfo::new();
|
||||||
|
dock.dock_info = get_dock()?;
|
||||||
|
|
||||||
|
dock.is_docked = dock.dock_info.brand == String::new() && dock.dock_info.model == String::new();
|
||||||
|
|
||||||
|
/*
|
||||||
|
This code will manually detect a dock if it wasn't automatically picked up by get_dock().
|
||||||
|
The code currently doesnt work. To manually detect a dock we will detect the presence of
|
||||||
|
a battery, charging of the handheld, and eventually when I find a cross-platform create,
|
||||||
|
it will also detect the presence of an external monitor (will most likely only work on a
|
||||||
|
Steam Deck for now)
|
||||||
|
*/
|
||||||
|
if USE_FALLBACK_DOCK_DETECTION {
|
||||||
|
if !dock.is_docked {
|
||||||
|
dock.fallback_detection = true;
|
||||||
|
|
||||||
|
let battery_info = get_battery_info()?;
|
||||||
|
|
||||||
|
if battery_info.has_battery && battery_info.charging_status == ChargingStatus::Charging {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(dock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads the dock_models.json file and returns a vector of structs with the data */
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn read_dock_models_from_file() -> Result<Vec<Dock>, Box<dyn std::error::Error>> {
|
||||||
|
let file = File::open("Resources/dock_models.json")?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let dock_models: Vec<Dock> = serde_json::from_reader(reader)?;
|
||||||
|
Ok(dock_models)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
/* This will detect the dock model and brand. */
|
||||||
|
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
|
||||||
|
let devices = rusb::devices()?;
|
||||||
|
let dock_models = read_dock_models_from_file()?;
|
||||||
|
|
||||||
|
for dock_model in dock_models {
|
||||||
|
let mut found_components = HashSet::new();
|
||||||
|
|
||||||
|
for device in devices.iter() {
|
||||||
|
let device_desc = device.device_descriptor()?;
|
||||||
|
let device_id = (device_desc.vendor_id(), device_desc.product_id());
|
||||||
|
|
||||||
|
// Check if the device is one of the components of the dock.
|
||||||
|
if dock_model.usb_ids.contains(&[device_desc.vendor_id(), device_desc.product_id()]) {
|
||||||
|
found_components.insert(device_id);
|
||||||
|
println!("(get_dock) Detected: {}", serde_json::to_string_pretty(&device_id)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found_components.len() == dock_model.usb_ids.len() {
|
||||||
|
println!("(get_dock) All components detected for {}", serde_json::to_string_pretty(&dock_model)?);
|
||||||
|
return Ok(Dock {
|
||||||
|
model: dock_model.model.clone(),
|
||||||
|
brand: dock_model.brand.clone(),
|
||||||
|
usb_ids: dock_model.usb_ids.clone()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Dock::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
/* This will detect the dock model and brand. */
|
||||||
|
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
|
||||||
|
Ok(Dock::new())
|
||||||
|
//Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Incorrect OS")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This will detect the dock model and brand. */
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn get_dock() -> Result<Dock, Box<dyn std::error::Error>> {
|
||||||
|
Ok(Dock::new())
|
||||||
|
}
|
39
src/v1/docking/structs.rs
Normal file
39
src/v1/docking/structs.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Dock {
|
||||||
|
pub brand: String, // ex: JSAUX
|
||||||
|
pub model: String, // ex: HB0603
|
||||||
|
pub usb_ids: Vec<[u16; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dock {
|
||||||
|
pub fn new() -> Dock {
|
||||||
|
Dock { brand: String::new(),
|
||||||
|
model: String::new(),
|
||||||
|
usb_ids: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct DockInfo {
|
||||||
|
pub dock_info: Dock,
|
||||||
|
pub is_docked: bool,
|
||||||
|
pub fallback_detection: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DockInfo {
|
||||||
|
pub fn new() -> DockInfo {
|
||||||
|
DockInfo { dock_info: Dock::new(),
|
||||||
|
is_docked: false,
|
||||||
|
fallback_detection: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
}
|
4
src/v1/mod.rs
Normal file
4
src/v1/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod battery;
|
||||||
|
pub mod docking;
|
||||||
|
pub mod os;
|
||||||
|
pub mod all_info;
|
2
src/v1/os/mod.rs
Normal file
2
src/v1/os/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod stats;
|
||||||
|
pub mod structs;
|
45
src/v1/os/stats.rs
Normal file
45
src/v1/os/stats.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use super::structs::{OSInfo, Platform, Architecture};
|
||||||
|
|
||||||
|
pub fn get_platform() -> Platform {
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
Platform::WINDOWS
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
Platform::MAC
|
||||||
|
} else if cfg!(target_os = "linux") {
|
||||||
|
Platform::LINUX
|
||||||
|
} else {
|
||||||
|
Platform::UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_os() -> Result<OSInfo, Box<dyn std::error::Error>> {
|
||||||
|
let mut temp = OSInfo::new();
|
||||||
|
let info = os_info::get();
|
||||||
|
|
||||||
|
temp.platform = get_platform();
|
||||||
|
|
||||||
|
temp.name = match info.codename() {
|
||||||
|
Some(s) => s.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
temp.version = info.version().to_string();
|
||||||
|
|
||||||
|
if temp.name == String::new() {
|
||||||
|
temp.name = match info.edition() {
|
||||||
|
Some(s) => s.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.bits = match info.bitness()
|
||||||
|
{
|
||||||
|
os_info::Bitness::X32 => Architecture::X86,
|
||||||
|
os_info::Bitness::X64 => Architecture::X86_64,
|
||||||
|
_ => Architecture::UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(temp)
|
||||||
|
|
||||||
|
}
|
38
src/v1/os/structs.rs
Normal file
38
src/v1/os/structs.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Clone)]
|
||||||
|
pub enum Platform {
|
||||||
|
WINDOWS = 0,
|
||||||
|
LINUX = 1,
|
||||||
|
MAC = 2,
|
||||||
|
UNKNOWN = 255
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub enum Architecture {
|
||||||
|
X86 = 0,
|
||||||
|
X86_64 = 1,
|
||||||
|
UNKNOWN = 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct OSInfo {
|
||||||
|
pub platform: Platform, // Windows, Mac, Linux
|
||||||
|
pub name: String, // "Windows 11 2306", "Ubuntu 22.04 LTS"
|
||||||
|
pub version: String, // 2306, 22.04
|
||||||
|
pub bits: Architecture // 32, 64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OSInfo {
|
||||||
|
pub fn new() -> OSInfo {
|
||||||
|
OSInfo {
|
||||||
|
platform: Platform::UNKNOWN,
|
||||||
|
name: String::new(),
|
||||||
|
version: String::new(),
|
||||||
|
bits: Architecture::UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
}
|
41
src/version/info.rs
Normal file
41
src/version/info.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Version {
|
||||||
|
pub build_timestamp: String,
|
||||||
|
pub git_branch: String,
|
||||||
|
pub git_describe: String,
|
||||||
|
pub git_commit_timestamp: String,
|
||||||
|
pub debug: bool,
|
||||||
|
pub version: String,
|
||||||
|
pub version_major: i32,
|
||||||
|
pub version_minor: i32,
|
||||||
|
pub version_patch: i32,
|
||||||
|
pub version_pre: String,
|
||||||
|
pub supported_api_versions: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
pub fn get() -> Version {
|
||||||
|
Version {
|
||||||
|
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
|
||||||
|
git_branch: env!("VERGEN_GIT_BRANCH").to_string(),
|
||||||
|
git_describe: env!("VERGEN_GIT_DESCRIBE").to_string(),
|
||||||
|
git_commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string(),
|
||||||
|
debug: env!("VERGEN_CARGO_DEBUG").parse().unwrap(),
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
version_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
|
||||||
|
version_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
|
||||||
|
version_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
|
||||||
|
version_pre: "Alpha".to_string(),
|
||||||
|
supported_api_versions: vec!["v1".to_string()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Failed to parse into string")
|
||||||
|
}
|
||||||
|
pub fn supported_api_versions_to_string(self) -> String {
|
||||||
|
self.supported_api_versions.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
src/version/mod.rs
Normal file
1
src/version/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod info;
|
Loading…
Add table
Add a link
Reference in a new issue