Update subsystems; add hid sandbox.

This commit is contained in:
yukirij 2024-11-14 13:25:10 -08:00
parent b275dc9a98
commit 2594d20bcf
28 changed files with 666 additions and 1758 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
Cargo.lock

1736
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,10 @@ edition = "2021"
winit = "0.30.5"
rayon = "1.10.0"
crossbeam = "0.8.4"
hidapi = "2.6.3"
[target.'cfg(windows)'.dependencies]
windows = "0.58.0"
[target.'cfg(linux)'.dependencies]

133
src/bin/test-hid.rs Normal file
View File

@ -0,0 +1,133 @@
#![allow(dead_code)]
use hidapi::HidApi;
//use donten::hid;
struct Report {
device:usize,
interface:usize,
data:Vec<u8>,
}
struct Usage {
usage_page:u16,
usage:u16,
offset:usize,
bit_length:usize,
minimum:u32,
maximum:u32,
}
struct ReportDescriptor {
id:usize,
usages:Vec<Usage>,
data:Vec<u8>,
}
struct DeviceInterface {
reports:Vec<ReportDescriptor>,
}
struct Device {
product:String,
manufacturer:String,
serial:String,
interfaces:Vec<DeviceInterface>,
}
fn main()
{
let (_tx, _rx) = crossbeam::channel::bounded::<Vec<u8>>(100);
if let Ok(hid) = HidApi::new() {
let mut devices = Vec::<Device>::new();
println!("[Device List]");
for device_info in hid.device_list() {
let product = if let Some(s) = device_info.product_string() { s } else { "" }.to_string();
let manufacturer = if let Some(s) = device_info.manufacturer_string() { s } else { "" }.to_string();
let serial = if let Some(s) = device_info.serial_number() { s } else { "" }.to_string();
let mut index = 0;
while index < devices.len() {
if devices[index].product == product
&& devices[index].manufacturer == manufacturer
&& devices[index].serial == serial
{
break;
}
index += 1;
}
if index == devices.len() {
// Insert new device
let device = Device {
product,
manufacturer,
serial,
interfaces:Vec::new(),
};
println!(" - {} ({})",
device.product,
device.manufacturer,
);
devices.push(device);
}
/*
// Add usage to device.
let mut desc_bytes = [0u8; hidapi::MAX_REPORT_DESCRIPTOR_SIZE];
if let Ok(hwd) = device_info.open_device(&hid) {
if let Ok(desc_size) = hwd.get_report_descriptor(&mut desc_bytes) {
if let Ok(descriptor) = hid::ReportDescriptor::parse(&desc_bytes[0..desc_size]) {
let interface = DeviceInterface {
reports:Vec::new(),
};
let mut buffer_length = 0;
// Push interface to device.
let interface_index = devices[index].interfaces.len();
devices[index].interfaces.push(interface);
// Listen for input reports from device.
let _ttx = tx.clone();
std::thread::spawn(move || {
let _device_index = index;
let _interface_index = interface_index;
let buffer_length = buffer_length;
let hwd = hwd;
let mut buffer = vec![0u8; buffer_length];
while let Ok(_size) = hwd.read(&mut buffer) {
}
});
}
}
}
*/
}
// Process input reports.
/*while let Ok(_msg) = rx.recv() {
}*/
} else {
println!("failed to init hidapi.");
}
}

0
src/hid/consts.rs Normal file
View File

341
src/hid/mod.rs Normal file
View File

@ -0,0 +1,341 @@
/*
enum ByteCode {
Report = 0x00,
Collection = 0xA1,
UsagePage = 0x05,
Usage = 0x09,
UsageMin = 0x19,
UsageMax = 0x29,
LogicalMin = 0x15,
LogicalMax = 0x25,
PhysicalMin = 0x100,
PhysicalMax = 0x101,
ReportSize = 0x75,
ReportCount = 0x95,
}
const F_TYPE :u16 = 0x03;
const TYPE_INPUT :u16 = 0x01;
const TYPE_OUTPUT :u16 = 0x02;
const TYPE_FEATURE :u16 = 0x03;
const F_DC :u16 = 0x40; // Data / Const
const F_RA :u16 = 0x80; // Relative / Absolute
struct _Usage {
usage_page:u16,
usage:u16,
offset:u16,
size:u16,
fields:u16,
}
*/
// Main Items
const MN_INPUT :u8 = 0b1000_00_00;
const MN_OUTPUT :u8 = 0b1001_00_00;
const MN_FEATURE :u8 = 0b1011_00_00;
const MN_COLLECTION_BEGIN :u8 = 0b1010_00_00;
const MN_COLLECTION_END :u8 = 0b1100_00_00;
const MN_EXTENDED :u8 = 0b1111_11_00;
// Global Items
const GB_USAGE_PAGE :u8 = 0b0000_01_00;
const GB_LOGICAL_MIN :u8 = 0b0001_01_00;
const GB_LOGICAL_MAX :u8 = 0b0010_01_00;
const GB_PHYSICAL_MIN :u8 = 0b0011_01_00;
const GB_PHYSICAL_MAX :u8 = 0b0100_01_00;
const GB_UNIT_EXPONENT :u8 = 0b0101_01_00;
const GB_UNIT :u8 = 0b0110_01_00;
const GB_REPORT_SIZE :u8 = 0b0111_01_00;
const GB_REPORT_ID :u8 = 0b1000_01_00;
const GB_REPORT_COUNT :u8 = 0b1001_01_00;
const GB_PUSH :u8 = 0b1010_01_00;
const GB_POP :u8 = 0b1011_01_00;
// Local Items
const LC_USAGE :u8 = 0b0000_10_00;
const LC_USAGE_MIN :u8 = 0b0001_10_00;
const LC_USAGE_MAX :u8 = 0b0010_10_00;
const LC_DESIGNATOR_INDEX :u8 = 0b0011_10_00;
const LC_DESIGNATOR_MIN :u8 = 0b0100_10_00;
const LC_DESIGNATOR_MAX :u8 = 0b0101_10_00;
const LC_STRING_INDEX :u8 = 0b0111_10_00;
const LC_STRING_MIN :u8 = 0b1000_10_00;
const LC_STRING_MAX :u8 = 0b1001_10_00;
const LC_DELIMITER :u8 = 0b1010_10_00;
struct Context {
usage_page:u16,
usages:Vec<u16>,
logical_min:i32,
logical_max:i32,
physical_min:Option<i32>,
physical_max:Option<i32>,
report_count:u16,
report_size:u16,
}
struct GlobalState {
usage_page:u32,
logical_min:i32,
logical_max:i32,
physical_min:i32,
physical_max:i32,
unit_exponent:i32,
unit:u32,
report_id:u32,
report_size:u32,
report_count:u32,
}
impl GlobalState {
pub fn new() -> Self
{
Self {
usage_page:0,
logical_min:0,
logical_max:0,
physical_min:0,
physical_max:0,
unit_exponent:0,
unit:0,
report_id:0,
report_size:0,
report_count:0,
}
}
}
pub struct ReportDescriptor {
}
impl ReportDescriptor {
pub fn parse(bytes:&[u8]) -> Result<Self,()>
{
// Output
let mut descriptor = Self {
};
// Global State
let mut global_state = Vec::<GlobalState>::new();
global_state.push(GlobalState::new());
// Local State
let usages = Vec::<[u16;2]>::new();
// Tracking
let mut collection_index = 0;
let mut offset = 0;
let mut report_id = 0;
let mut index = 0;
while index < bytes.len() {
let mut sz = bytes[index] & 0x03;
let mut tg = bytes[index] & 0xFC;
// Get byte size from index
sz = [ 0, 1, 2, 4 ][sz as usize];
index += 1;
if bytes.len() - index >= sz as usize {
// Handle long tag
if sz == 2 && tg == MN_EXTENDED {
sz = bytes[index + 1];
tg = bytes[index + 2];
if bytes.len() - index < sz as usize {
return Err(());
}
}
if sz <= 4 {
match tg {
MN_INPUT => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
** 2: Absolute(0), Relative(1)
** 3: NoWrap(0), Wrap(1)
** 4: Linear(0), NonLinear(1)
** 5: PreferredState(0), NoPreferred(1)
** 6: NoNull(0), NullState(1)
** 7: reserved
** 8: BitField(0), BufferedBytes(1)
** _: reserved
*/
let mut is_const = false;
let mut is_var = false;
let mut is_rel = false;
let mut is_wrap = false;
let mut is_linear = false;
let mut is_pref = false;
let mut has_null = false;
let mut is_volatile = false;
let mut is_buffered = false;
if sz >= 1 {
is_const = (bytes[index + 1] & 0x01) != 0;
is_var = (bytes[index + 1] & 0x02) != 0;
is_rel = (bytes[index + 1] & 0x04) != 0;
}
if sz >= 2 {
}
}
MN_OUTPUT => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
** 2: Absolute(0), Relative(1)
** 3: NoWrap(0), Wrap(1)
** 4: Linear(0), NonLinear(1)
** 5: PreferredState(0), NoPreferred(1)
** 6: NoNull(0), NullState(1)
** 7: NonVolatile(0), Volatile(1)
** 8: BitField(0), BufferedBytes(1)
** _: reserved
*/
}
MN_FEATURE => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
** 2: Absolute(0), Relative(1)
** 3: NoWrap(0), Wrap(1)
** 4: Linear(0), NonLinear(1)
** 5: PreferredState(0), NoPreferred(1)
** 6: NoNull(0), NullState(1)
** 7: NonVolatile(0), Volatile(1)
** 8: BitField(0), BufferedBytes(1)
** _: reserved
*/
}
MN_COLLECTION_BEGIN => {
/*
** 00: Physical
** 01: Application
** 02: Logical
** 03: Report
** 04: NamedArray
** 05: UsageSwitch
** 06: UsageModifier
** 07-7F: reserved
** 80-FF: vendor-defined
*/
}
MN_COLLECTION_END => {
}
GB_USAGE_PAGE => {
}
GB_LOGICAL_MIN => {
}
GB_LOGICAL_MAX => {
}
GB_PHYSICAL_MIN => {
}
GB_PHYSICAL_MAX => {
}
GB_UNIT_EXPONENT => {
}
GB_UNIT => {
}
GB_REPORT_SIZE => {
}
GB_REPORT_ID => {
}
GB_REPORT_COUNT => {
}
GB_PUSH => {
}
GB_POP => {
}
LC_USAGE => {
}
LC_USAGE_MIN => {
}
LC_USAGE_MAX => {
}
LC_DESIGNATOR_INDEX => {
}
LC_DESIGNATOR_MIN => {
}
LC_DESIGNATOR_MAX => {
}
LC_STRING_INDEX => {
}
LC_STRING_MIN => {
}
LC_STRING_MAX => {
}
LC_DELIMITER => {
}
_ => { return Err(()); }
}
}
index += sz as usize;
}
}
Ok(descriptor)
}
}

View File

@ -1,12 +1,15 @@
#![allow(dead_code, unused_imports)]
use crossbeam::channel::Sender;
mod util;
mod error; pub use error::Error;
mod config; pub use config::Config;
use system::{Subsystem, Query};
mod system;
mod platform;
pub mod hid;
pub struct Donten {
channel:Sender<Query>,
}
@ -14,7 +17,7 @@ impl Donten {
pub fn init(_config:Config) -> Result<Self, Error>
{
// Initialize supervisor.
let channel = system::Supervisor::start()?;
let channel = system::Supervisor::start(None)?;
// Parse config and push instructions to supervisor.

View File

@ -9,5 +9,8 @@ impl APIManager {
}
impl Subsystem for APIManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -9,5 +9,8 @@ impl AudioManager {
}
impl Subsystem for AudioManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -9,5 +9,8 @@ impl ControlManager {
}
impl Subsystem for ControlManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -0,0 +1,51 @@
use crate::system::*;
mod platform;
mod xinput;
pub struct Internal {
supervisor:Sender<Query>,
sys_window:Option<Sender<Query>>,
}
impl Internal {
pub fn thread(supervisor:Sender<Query>, _tx:Sender<Query>, rx:Receiver<Query>)
{
let _sv = Internal {
supervisor,
sys_window:None,
};
// Request interested subsystem status.
// Handle messages.
while let Ok(msg) = rx.recv() {
match msg.data {
QueryData::Supervisor(request) => match request {
SupervisorQuery::Stop {
system: _,
} => {
// Perform shutdown operations and terminate thread.
return;
}
_ => { }
}
QueryData::Window(request) => match request {
WindowQuery::Create {
} => {
}
_ => { }
}
_ => { }
}
}
}
}

View File

@ -0,0 +1,13 @@
pub trait Platform {
fn register_devices() -> Result<(),()>;
}
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::*;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
pub use linux::*;

View File

@ -0,0 +1,28 @@
use crate::system::*;
pub struct XInputSystem {
running:bool,
}
impl XInputSystem {
pub fn thread(_tx:Sender<Query>, rx:Receiver<Query>)
{
let sv = XInputSystem {
running:true,
};
while sv.running {
// Handle system queries
while let Ok(_msg) = rx.try_recv() {
}
// Poll xinput devices
// Sleep until next step
std::thread::sleep(std::time::Duration::from_millis(4));
}
}
}

View File

@ -2,6 +2,7 @@ use super::*;
mod usage; use usage::Usage;
mod device; use device::Device;
mod internal; use internal::Internal;
pub struct InputManager {
channel:Sender<Query>,
@ -12,5 +13,19 @@ impl InputManager {
}
impl Subsystem for InputManager {
fn start(supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
if let Some(supervisor) = supervisor {
let (tx, rx) = channel::bounded::<Query>(24);
let sys_tx = tx.clone();
std::thread::spawn(move || {
Internal::thread(supervisor, sys_tx, rx);
});
Ok(tx)
} else {
Err(Error::new())
}
}
}

View File

@ -15,7 +15,7 @@ pub(crate) mod storage; pub(crate) use storage::*;
pub(crate) mod api; pub(crate) use api::*;
pub(crate) trait Subsystem {
fn start() -> Result<Sender<Query>, Error> { Err(Error::new()) }
fn start(supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>;
fn shutdown() -> Result<(), Error> { Ok(()) }
}

View File

@ -9,5 +9,8 @@ impl NetworkManager {
}
impl Subsystem for NetworkManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -9,5 +9,8 @@ impl SceneManager {
}
impl Subsystem for SceneManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -9,5 +9,8 @@ impl StorageManager {
}
impl Subsystem for StorageManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -18,9 +18,11 @@ impl Internal {
SupervisorQuery::Start {
system,
} => if sv.subsystems[system as usize].is_none() {
let sv_channel = sv.subsystems[System::Supervisor as usize].clone();
if let Ok(channel) = match system {
System::Window => WindowManager::start(),
System::Window => WindowManager::start(sv_channel),
_ => { Err(Error::new()) }
} {
@ -36,6 +38,9 @@ impl Internal {
))).ok();
}
}
} else {
// Log subsystem startup error
}
}

View File

@ -12,7 +12,7 @@ impl Supervisor {
}
impl Subsystem for Supervisor {
fn start() -> Result<Sender<Query>, crate::Error>
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, crate::Error>
{
let (tx, rx) = channel::bounded::<Query>(24);

View File

@ -9,5 +9,8 @@ impl VideoManager {
}
impl Subsystem for VideoManager {
fn start(_supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, Error>
{
Err(Error::new())
}
}

View File

@ -32,4 +32,19 @@ impl ApplicationHandler for Handler {
) {
}
fn device_event(
&mut self,
_event_loop: &ActiveEventLoop,
_device_id: winit::event::DeviceId,
event: winit::event::DeviceEvent,
) {
use winit::event::DeviceEvent;
match event {
DeviceEvent::Added => { println!(" - added"); }
DeviceEvent::Removed => { println!(" - removed"); }
_ => { }
}
}
}

View File

@ -10,13 +10,13 @@ use winit::{
mod handler; use handler::Handler;
pub struct Internal {
supervisor:Sender<Query>,
}
impl Internal {
pub fn thread(tx:Sender<Query>, rx:Receiver<Query>)
pub fn thread(supervisor:Sender<Query>, tx:Sender<Query>, rx:Receiver<Query>)
{
let _sv = Internal {
supervisor,
};
// Initialize event loop.

View File

@ -25,16 +25,20 @@ impl WindowManager {
impl Subsystem for WindowManager {
fn start() -> Result<Sender<Query>, crate::Error>
fn start(supervisor:Option<Sender<Query>>) -> Result<Sender<Query>, crate::Error>
{
let (tx, rx) = channel::bounded::<Query>(24);
if let Some(supervisor) = supervisor {
let (tx, rx) = channel::bounded::<Query>(24);
let sys_tx = tx.clone();
std::thread::spawn(move || {
Internal::thread(sys_tx, rx);
});
let sys_tx = tx.clone();
std::thread::spawn(move || {
Internal::thread(supervisor, sys_tx, rx);
});
Ok(tx)
Ok(tx)
} else {
Err(Error::new())
}
}
}

1
src/util/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod pack;

6
src/util/pack.rs Normal file
View File

@ -0,0 +1,6 @@
use std::ops::{BitOr, ShlAssign};
fn unpack<T:ShlAssign + BitOr>()
{
}