#[macro_use]
extern crate log;
#[cfg(windows)]
#[macro_use]
extern crate windows_service;
#[cfg(windows)]
use hid_io_core::RUNNING;
#[cfg(windows)]
use std::sync::atomic::Ordering;
use clap::Command;
use hid_io_core::api;
use hid_io_core::built_info;
use hid_io_core::device;
use hid_io_core::logging;
use hid_io_core::mailbox;
use hid_io_core::module;
use std::sync::Arc;
#[cfg(windows)]
fn main() -> Result<(), std::io::Error> {
let args: Vec<_> = std::env::args().collect();
if args.len() > 1 && args[1] == "-d" {
info!("-------------------------- HID-IO Core starting! --------------------------");
match service::run() {
Ok(_) => (),
Err(_e) => panic!("Service failed"),
}
} else {
logging::setup_logging()?;
start();
}
Ok(())
}
#[cfg(not(windows))]
fn main() -> Result<(), std::io::Error> {
logging::setup_logging()?;
start();
Ok(())
}
fn start() {
let rt: Arc<tokio::runtime::Runtime> = Arc::new(
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap(),
);
rt.block_on(async {
let version_info = format!(
"{}{} - {}",
built_info::PKG_VERSION,
built_info::GIT_VERSION.map_or_else(|| "".to_owned(), |v| format!(" (git {v})")),
built_info::PROFILE,
);
info!("Version: {}", version_info);
let after_info = format!(
"{} ({}) -> {} ({})",
built_info::RUSTC_VERSION,
built_info::HOST,
built_info::TARGET,
built_info::BUILT_TIME_UTC,
);
info!("Build: {}", after_info);
Command::new(built_info::PKG_NAME.to_string())
.version(version_info.as_str())
.author(built_info::PKG_AUTHORS)
.about(format!("\n{}", built_info::PKG_DESCRIPTION).as_str())
.after_help(after_info.as_str())
.get_matches();
info!("Initializing HID-IO daemon...");
let mailbox = mailbox::Mailbox::new(rt.clone());
let (_, _, _) = tokio::join!(
module::initialize(mailbox.clone()),
device::initialize(mailbox.clone()),
api::initialize(mailbox),
);
info!("-------------------------- HID-IO Core exiting! --------------------------");
});
}
#[cfg(windows)]
fn stop() {
info!("Stopping!");
let r = RUNNING.clone();
r.store(false, Ordering::SeqCst);
}
#[cfg(windows)]
mod service {
use flexi_logger::{opt_format, FileSpec, Logger};
use hid_io_core::built_info;
use windows_service::service_dispatcher;
const SERVICE_NAME: &str = built_info::PKG_NAME;
define_windows_service!(ffi_service_main, my_service_main);
use std::ffi::OsString;
use std::time::Duration;
use windows_service::service::{
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
ServiceType,
};
use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
pub fn run() -> windows_service::Result<()> {
service_dispatcher::start(SERVICE_NAME, ffi_service_main)
}
fn my_service_main(arguments: Vec<OsString>) {
Logger::try_with_env()
.unwrap()
.log_to_file(FileSpec::default().directory("log_files"))
.format(opt_format)
.start()
.unwrap_or_else(|e| panic!("Logger initialization failed {}", e));
info!("Running as service!");
if let Err(_e) = run_service(arguments) {
}
}
fn run_service(_arguments: Vec<OsString>) -> windows_service::Result<()> {
let event_handler = move |control_event| -> ServiceControlHandlerResult {
info!("EVENT: {:?}", control_event);
match control_event {
ServiceControl::Stop => {
crate::stop();
ServiceControlHandlerResult::NoError
}
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
_ => ServiceControlHandlerResult::NotImplemented,
}
};
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
let next_status = ServiceStatus {
service_type: ServiceType::OWN_PROCESS,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
};
status_handle.set_service_status(next_status)?;
crate::start();
status_handle.set_service_status(ServiceStatus {
service_type: ServiceType::OWN_PROCESS,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None, })?;
Ok(())
}
}