598 lines
23 KiB
Rust
598 lines
23 KiB
Rust
use std::collections::VecDeque;
|
|
use std::env;
|
|
use std::fs::File;
|
|
use std::io::Write;
|
|
use std::os::unix::io::AsRawFd;
|
|
|
|
use bitflags::bitflags;
|
|
use orbclient::KeyEvent as OrbKeyEvent;
|
|
use redox_log::{OutputBuilder, RedoxLogger};
|
|
use xhcid_interface::{ConfigureEndpointsReq, DevDesc, PortReqRecipient, XhciClientHandle};
|
|
|
|
mod keymap;
|
|
mod report_desc;
|
|
mod reqs;
|
|
mod usage_tables;
|
|
|
|
use report_desc::{LocalItemsState, MainCollectionFlags, MainItem, MainItemFlags, GlobalItemsState, ReportFlatIter, ReportItem, ReportIter, ReportIterItem, REPORT_DESC_TY};
|
|
use reqs::ReportTy;
|
|
|
|
fn div_round_up(num: u32, denom: u32) -> u32 {
|
|
if num % denom == 0 { num / denom } else { num / denom + 1 }
|
|
}
|
|
|
|
struct BinaryView<'a> {
|
|
data: &'a [u8],
|
|
offset: usize,
|
|
len: usize,
|
|
}
|
|
impl<'a> BinaryView<'a> {
|
|
pub fn new(data: &'a [u8], offset: usize, len: usize) -> Self {
|
|
Self {
|
|
data,
|
|
offset,
|
|
len,
|
|
}
|
|
}
|
|
pub fn get(&self, bit_index: usize) -> Option<bool> {
|
|
let bit_index = self.offset + bit_index;
|
|
|
|
if bit_index >= self.offset + self.len { return None }
|
|
|
|
let byte_index = bit_index / 8;
|
|
let bits_from_first = bit_index % 8;
|
|
let byte = self.data.get(byte_index)?;
|
|
Some(byte & (1 << bits_from_first) != 0)
|
|
}
|
|
pub fn read_u8(&self, bit_index: usize) -> Option<u8> {
|
|
let bit_index = self.offset + bit_index;
|
|
|
|
if bit_index >= self.offset + self.len { return None }
|
|
|
|
let first = bit_index / 8;
|
|
let bits_from_first = bit_index % 8;
|
|
let first_byte = self.data.get(first)?;
|
|
let lo = first_byte >> bits_from_first;
|
|
|
|
let hi = if bits_from_first > 0 {
|
|
let hi = self.data.get(first + 1)? & ((1 << bits_from_first) - 1);
|
|
let bits_to_next = 8 - bits_from_first;
|
|
hi << bits_to_next
|
|
} else { 0 };
|
|
|
|
|
|
Some(lo | hi)
|
|
}
|
|
}
|
|
|
|
fn setup_logging() -> Option<&'static RedoxLogger> {
|
|
let mut logger = RedoxLogger::new()
|
|
.with_output(
|
|
OutputBuilder::stderr()
|
|
.with_filter(log::LevelFilter::Info) // limit global output to important info
|
|
.with_ansi_escape_codes()
|
|
.flush_on_newline(true)
|
|
.build()
|
|
);
|
|
|
|
#[cfg(target_os = "redox")]
|
|
match OutputBuilder::in_redox_logging_scheme("usb", "device", "hid.log") {
|
|
Ok(b) => logger = logger.with_output(
|
|
// TODO: Add a configuration file for this
|
|
b.with_filter(log::LevelFilter::Info)
|
|
.flush_on_newline(true)
|
|
.build()
|
|
),
|
|
Err(error) => eprintln!("Failed to create hid.log: {}", error),
|
|
}
|
|
|
|
#[cfg(target_os = "redox")]
|
|
match OutputBuilder::in_redox_logging_scheme("usb", "device", "hid.ansi.log") {
|
|
Ok(b) => logger = logger.with_output(
|
|
b.with_filter(log::LevelFilter::Info)
|
|
.with_ansi_escape_codes()
|
|
.flush_on_newline(true)
|
|
.build()
|
|
),
|
|
Err(error) => eprintln!("Failed to create hid.ansi.log: {}", error),
|
|
}
|
|
|
|
match logger.enable() {
|
|
Ok(logger_ref) => {
|
|
eprintln!("usbhidd: enabled logger");
|
|
Some(logger_ref)
|
|
}
|
|
Err(error) => {
|
|
eprintln!("usbhidd: failed to set default logger: {}", error);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn send_key_event(display: &mut File, usage_page: u32, usage: u8, pressed: bool, shift_opt: Option<bool>) {
|
|
let scancode = match usage_page {
|
|
0x07 => match usage {
|
|
0x04 => orbclient::K_A,
|
|
0x05 => orbclient::K_B,
|
|
0x06 => orbclient::K_C,
|
|
0x07 => orbclient::K_D,
|
|
0x08 => orbclient::K_E,
|
|
0x09 => orbclient::K_F,
|
|
0x0A => orbclient::K_G,
|
|
0x0B => orbclient::K_H,
|
|
0x0C => orbclient::K_I,
|
|
0x0D => orbclient::K_J,
|
|
0x0E => orbclient::K_K,
|
|
0x0F => orbclient::K_L,
|
|
0x10 => orbclient::K_M,
|
|
0x11 => orbclient::K_N,
|
|
0x12 => orbclient::K_O,
|
|
0x13 => orbclient::K_P,
|
|
0x14 => orbclient::K_Q,
|
|
0x15 => orbclient::K_R,
|
|
0x16 => orbclient::K_S,
|
|
0x17 => orbclient::K_T,
|
|
0x18 => orbclient::K_U,
|
|
0x19 => orbclient::K_V,
|
|
0x1A => orbclient::K_W,
|
|
0x1B => orbclient::K_X,
|
|
0x1C => orbclient::K_Y,
|
|
0x1D => orbclient::K_Z,
|
|
0x1E => orbclient::K_1,
|
|
0x1F => orbclient::K_2,
|
|
0x20 => orbclient::K_3,
|
|
0x21 => orbclient::K_4,
|
|
0x22 => orbclient::K_5,
|
|
0x23 => orbclient::K_6,
|
|
0x24 => orbclient::K_7,
|
|
0x25 => orbclient::K_8,
|
|
0x26 => orbclient::K_9,
|
|
0x27 => orbclient::K_0,
|
|
0x28 => orbclient::K_ENTER,
|
|
0x29 => orbclient::K_ESC,
|
|
0x2A => orbclient::K_BKSP,
|
|
0x2B => orbclient::K_TAB,
|
|
0x2C => orbclient::K_SPACE,
|
|
0x2D => orbclient::K_MINUS,
|
|
0x2E => orbclient::K_EQUALS,
|
|
0x2F => orbclient::K_BRACE_OPEN,
|
|
0x30 => orbclient::K_BRACE_CLOSE,
|
|
0x31 => orbclient::K_BACKSLASH,
|
|
// 0x32 non-us # and ~
|
|
0x33 => orbclient::K_SEMICOLON,
|
|
0x34 => orbclient::K_QUOTE,
|
|
0x35 => orbclient::K_TICK,
|
|
0x36 => orbclient::K_COMMA,
|
|
0x37 => orbclient::K_PERIOD,
|
|
0x38 => orbclient::K_SLASH,
|
|
0x39 => orbclient::K_CAPS,
|
|
0x3A => orbclient::K_F1,
|
|
0x3B => orbclient::K_F2,
|
|
0x3C => orbclient::K_F3,
|
|
0x3D => orbclient::K_F4,
|
|
0x3E => orbclient::K_F5,
|
|
0x3F => orbclient::K_F6,
|
|
0x40 => orbclient::K_F7,
|
|
0x41 => orbclient::K_F8,
|
|
0x42 => orbclient::K_F9,
|
|
0x43 => orbclient::K_F10,
|
|
0x44 => orbclient::K_F11,
|
|
0x45 => orbclient::K_F12,
|
|
// 0x46 print screen
|
|
// 0x47 scroll lock
|
|
// 0x48 pause
|
|
// 0x49 insert
|
|
0x4A => orbclient::K_HOME,
|
|
0x4B => orbclient::K_PGUP,
|
|
0x4C => orbclient::K_DEL,
|
|
0x4D => orbclient::K_END,
|
|
0x4E => orbclient::K_PGDN,
|
|
0x4F => orbclient::K_RIGHT,
|
|
0x50 => orbclient::K_LEFT,
|
|
0x51 => orbclient::K_DOWN,
|
|
0x52 => orbclient::K_UP,
|
|
// 0x53 num lock
|
|
// 0x54 num /
|
|
// 0x55 num *
|
|
// 0x56 num -
|
|
// 0x57 num +
|
|
// 0x58 num enter
|
|
0x59 => orbclient::K_NUM_1,
|
|
0x5A => orbclient::K_NUM_2,
|
|
0x5B => orbclient::K_NUM_3,
|
|
0x5C => orbclient::K_NUM_4,
|
|
0x5D => orbclient::K_NUM_5,
|
|
0x5E => orbclient::K_NUM_6,
|
|
0x5F => orbclient::K_NUM_7,
|
|
0x60 => orbclient::K_NUM_8,
|
|
0x61 => orbclient::K_NUM_9,
|
|
0x62 => orbclient::K_NUM_0,
|
|
// 0x62 num .
|
|
// 0x64 non-us \ and |
|
|
// 0x64 app
|
|
// 0x66 power
|
|
// 0x67 num =
|
|
// unmapped values
|
|
0xE0 => orbclient::K_CTRL, // TODO: left control
|
|
0xE1 => orbclient::K_LEFT_SHIFT,
|
|
0xE2 => orbclient::K_ALT,
|
|
0xE3 => 0x5B, // left super
|
|
0xE4 => orbclient::K_CTRL, // TODO: right control
|
|
0xE5 => orbclient::K_RIGHT_SHIFT,
|
|
0xE6 => orbclient::K_ALT_GR,
|
|
// 0xE7 right super
|
|
// reserved values
|
|
_ => {
|
|
log::warn!("unknown usage_page {:#x} usage {:#x}", usage_page, usage);
|
|
return;
|
|
},
|
|
},
|
|
_ => {
|
|
log::warn!("unknown usage_page {:#x}", usage_page);
|
|
return;
|
|
},
|
|
};
|
|
|
|
//TODO: other keymaps
|
|
let character = if let Some(shift) = shift_opt {
|
|
keymap::us::get_char(scancode, shift)
|
|
} else {
|
|
'\0'
|
|
};
|
|
|
|
let key_event = OrbKeyEvent {
|
|
character,
|
|
scancode,
|
|
pressed,
|
|
};
|
|
|
|
match display.write(&key_event.to_event()) {
|
|
Ok(_) => (),
|
|
Err(err) => {
|
|
log::warn!("failed to send key event to orbital: {}", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let _logger_ref = setup_logging();
|
|
|
|
let mut args = env::args().skip(1);
|
|
|
|
const USAGE: &'static str = "usbhidd <scheme> <port> <protocol>";
|
|
|
|
let scheme = args.next().expect(USAGE);
|
|
let port = args
|
|
.next()
|
|
.expect(USAGE)
|
|
.parse::<usize>()
|
|
.expect("Expected integer as input of port");
|
|
let protocol = args.next().expect(USAGE);
|
|
|
|
log::info!(
|
|
"USB HID driver spawned with scheme `{}`, port {}, protocol {}",
|
|
scheme, port, protocol
|
|
);
|
|
|
|
let handle = XhciClientHandle::new(scheme, port);
|
|
let dev_desc: DevDesc = handle
|
|
.get_standard_descs()
|
|
.expect("Failed to get standard descriptors");
|
|
let hid_desc = dev_desc.config_descs[0].interface_descs[0].hid_descs[0];
|
|
|
|
// TODO: Currently it's assumed that config 0 and interface 0 are used.
|
|
|
|
let interface_num = 0;
|
|
let report_desc_len = hid_desc.desc_len;
|
|
assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY);
|
|
|
|
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
|
handle
|
|
.get_descriptor(
|
|
PortReqRecipient::Interface,
|
|
REPORT_DESC_TY,
|
|
0,
|
|
interface_num,
|
|
&mut report_desc_bytes,
|
|
)
|
|
.expect("Failed to retrieve report descriptor");
|
|
|
|
let report_desc = ReportIter::new(ReportFlatIter::new(&report_desc_bytes)).collect::<Vec<_>>();
|
|
|
|
for item in &report_desc {
|
|
log::info!("{:?}", item);
|
|
}
|
|
|
|
handle.configure_endpoints(&ConfigureEndpointsReq { config_desc: 0, interface_desc: None, alternate_setting: None }).expect("Failed to configure endpoints");
|
|
|
|
let (mut global_state, mut local_state, mut stack) = (GlobalItemsState::default(), LocalItemsState::default(), Vec::new());
|
|
|
|
let (_, application_collection, application_global_state, application_local_state) = report_desc.iter().filter_map(|item: &ReportIterItem| {
|
|
log::trace!("1: {:?}", item);
|
|
match item {
|
|
&ReportIterItem::Item(ref item) => {
|
|
report_desc::update_global_state(&mut global_state, &mut stack, item).unwrap();
|
|
report_desc::update_local_state(&mut local_state, item);
|
|
None
|
|
}
|
|
&ReportIterItem::Collection(n, ref collection) => {
|
|
let lc_state = std::mem::replace(&mut local_state, LocalItemsState::default());
|
|
Some((n, collection, global_state, lc_state))
|
|
}
|
|
}
|
|
}).find(|&(n, _, _, _)| n == MainCollectionFlags::Application as u8).expect("Failed to find application collection");
|
|
|
|
// Get all main items, and their global item options.
|
|
{
|
|
let mut collections = VecDeque::new();
|
|
collections.push_back(application_collection);
|
|
let mut items = Vec::new();
|
|
while let Some(collection) = collections.pop_front() {
|
|
for item in collection {
|
|
log::trace!("2: {:?}", item);
|
|
match item {
|
|
ReportIterItem::Item(item) => match item {
|
|
ReportItem::Global(_) => {
|
|
report_desc::update_global_state(&mut global_state, &mut stack, item).unwrap();
|
|
}
|
|
ReportItem::Main(m) => {
|
|
let lc_state = std::mem::replace(&mut local_state, LocalItemsState::default());
|
|
items.push((global_state, lc_state, m));
|
|
}
|
|
ReportItem::Local(_) => {
|
|
report_desc::update_local_state(&mut local_state, item);
|
|
},
|
|
},
|
|
//TODO: does local state need to be different for inner collections?
|
|
ReportIterItem::Collection(_, collection) => {
|
|
collections.push_back(collection);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
let mut bit_offset = 0;
|
|
let inputs = items.iter().filter_map(|(global_state, local_state, item)| {
|
|
log::trace!("3: {:?}", item);
|
|
|
|
if let &MainItem::Input(flags) = item {
|
|
let report_size = match global_state.report_size {
|
|
Some(s) => s,
|
|
None => return None,
|
|
};
|
|
let report_count = match global_state.report_count {
|
|
Some(c) => c,
|
|
None => return None,
|
|
};
|
|
|
|
let bit_length = report_size * report_count;
|
|
let offset = bit_offset;
|
|
bit_offset += bit_length;
|
|
|
|
Some((bit_length, offset, global_state, local_state, MainItemFlags::from_bits_truncate(*flags)))
|
|
} else {
|
|
None
|
|
}
|
|
}).collect::<Vec<_>>();
|
|
let total_bit_length = inputs.iter().map(|(bit_length, _, _, _, _)| bit_length).sum();
|
|
|
|
let total_byte_length = div_round_up(total_bit_length, 8);
|
|
|
|
let mut report_buffer = vec! [0u8; total_byte_length as usize];
|
|
let mut last_buffer = report_buffer.clone();
|
|
let report_ty = ReportTy::Input;
|
|
let report_id = 0;
|
|
|
|
let mut display = File::open("display:input").expect("Failed to open orbital input socket");
|
|
|
|
//TODO: get dynamically
|
|
let mut display_width = 0;
|
|
let mut display_height = 0;
|
|
{
|
|
let mut buf = [0; 4096];
|
|
if let Ok(count) = syscall::fpath(display.as_raw_fd() as usize, &mut buf) {
|
|
let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
|
|
let res = path.split(":").nth(1).unwrap_or("");
|
|
display_width = res.split("/").nth(1).unwrap_or("").parse::<u32>().unwrap_or(0);
|
|
display_height = res.split("/").nth(2).unwrap_or("").parse::<u32>().unwrap_or(0);
|
|
}
|
|
}
|
|
|
|
let mut pressed_keys = Vec::<(u32, u8)>::new();
|
|
let mut last_pressed_keys = pressed_keys.clone();
|
|
let mut last_buttons = (false, false, false);
|
|
|
|
loop {
|
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
|
|
std::mem::swap(&mut report_buffer, &mut last_buffer);
|
|
reqs::get_report(&handle, report_ty, report_id, interface_num, &mut report_buffer).expect("Failed to get report");
|
|
|
|
if report_buffer == last_buffer {
|
|
continue
|
|
}
|
|
|
|
for &(bit_length, bit_offset, global_state, local_state, input) in &inputs {
|
|
let report_size = global_state.report_size.unwrap();
|
|
let report_count = global_state.report_count.unwrap();
|
|
|
|
log::trace!(
|
|
"size {} count {} at {} length {}",
|
|
report_size,
|
|
report_count,
|
|
bit_offset,
|
|
bit_length
|
|
);
|
|
|
|
// TODO: For now, the dynamic value usages cannot overlap with selector usages...
|
|
// for now.
|
|
|
|
if local_state.usage_min == Some(224) && local_state.usage_max == Some(231) {
|
|
// The usages that this descriptor references are all dynamic values.
|
|
} else {
|
|
// The usages are selectors.
|
|
}
|
|
|
|
if input.contains(MainItemFlags::VARIABLE) {
|
|
// The item is a variable.
|
|
|
|
let binary_view = BinaryView::new(&report_buffer, bit_offset as usize, bit_length as usize);
|
|
|
|
if report_count == 8 && report_size == 1 && global_state.usage_page == Some(7) && local_state.usage_min == Some(224) && local_state.usage_max == Some(231) && global_state.logical_min == Some(0) && global_state.logical_max == Some(1) {
|
|
let bits = binary_view.read_u8(0).expect("Failed to read array item");
|
|
for bit in 0..8 {
|
|
if bits & (1 << bit) > 0 {
|
|
pressed_keys.push((0x07, 0xE0 + bit));
|
|
}
|
|
}
|
|
log::trace!("Report variable {:#x?}", bits);
|
|
} else if report_count == 2 && report_size == 16 && global_state.usage_page == Some(1) {
|
|
//TODO: Make this less hard-coded
|
|
let raw_x =
|
|
binary_view.read_u8(0).expect("Failed to read array item") as u16 |
|
|
(binary_view.read_u8(8).expect("Failed to read array item") as u16) << 8;
|
|
let raw_y =
|
|
binary_view.read_u8(16).expect("Failed to read array item") as u16 |
|
|
(binary_view.read_u8(24).expect("Failed to read array item") as u16) << 8;
|
|
|
|
let x = ((raw_x as u32) * display_width) / 32767;
|
|
let y = ((raw_y as u32) * display_height) / 32767;
|
|
|
|
log::trace!("Touchscreen {}, {} => {}, {}", raw_x, raw_y, x, y);
|
|
if x != 0 || y != 0 {
|
|
let mouse_event = orbclient::event::MouseEvent {
|
|
x: x as i32,
|
|
y: y as i32,
|
|
};
|
|
|
|
match display.write(&mouse_event.to_event()) {
|
|
Ok(_) => (),
|
|
Err(err) => {
|
|
log::warn!("failed to send mouse event to orbital: {}", err);
|
|
}
|
|
}
|
|
}
|
|
} else if report_count == 3 && report_size == 8 && global_state.usage_page == Some(1) {
|
|
//TODO: Make this less hard-coded
|
|
let dx = binary_view.read_u8(0).expect("Failed to read array item") as i8;
|
|
let dy = binary_view.read_u8(8).expect("Failed to read array item") as i8;
|
|
let dz = binary_view.read_u8(16).expect("Failed to read array item") as i8;
|
|
log::trace!("Mouse {}, {}, {}", dx, dy, dz);
|
|
if dx != 0 || dy != 0 {
|
|
let mouse_event = orbclient::event::MouseRelativeEvent {
|
|
dx: dx as i32,
|
|
dy: dy as i32,
|
|
};
|
|
|
|
match display.write(&mouse_event.to_event()) {
|
|
Ok(_) => (),
|
|
Err(err) => {
|
|
log::warn!("failed to send mouse event to orbital: {}", err);
|
|
}
|
|
}
|
|
}
|
|
if dz != 0 {
|
|
let scroll_event = orbclient::event::ScrollEvent {
|
|
x: dz as i32,
|
|
y: 0,
|
|
};
|
|
|
|
match display.write(&scroll_event.to_event()) {
|
|
Ok(_) => (),
|
|
Err(err) => {
|
|
log::warn!("failed to send scroll event to orbital: {}", err);
|
|
}
|
|
}
|
|
}
|
|
} else if report_count == 3 && report_size == 1 && global_state.usage_page == Some(9) {
|
|
//TODO: Make this less hard-coded
|
|
let left = binary_view.get(0).expect("Failed to read array item");
|
|
let right = binary_view.get(1).expect("Failed to read array item");
|
|
let middle = binary_view.get(2).expect("Failed to read array item");
|
|
log::trace!("Left {}, Right {}, Middle {}", left, right, middle);
|
|
if last_buttons != (left, right, middle) {
|
|
last_buttons = (left, right, middle);
|
|
|
|
let button_event = orbclient::event::ButtonEvent {
|
|
left,
|
|
right,
|
|
middle,
|
|
};
|
|
|
|
match display.write(&button_event.to_event()) {
|
|
Ok(_) => (),
|
|
Err(err) => {
|
|
log::warn!("failed to send button event to orbital: {}", err);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
log::trace!("Unknown report variable item: size {} count {} at {}", report_size, report_count, bit_offset);
|
|
}
|
|
} else {
|
|
// The item is an array.
|
|
|
|
log::trace!("INPUT FLAGS: {:?}", input);
|
|
if report_size == 8 {
|
|
for report_index in 0..report_count as usize {
|
|
let binary_view = BinaryView::new(&report_buffer, bit_offset as usize + report_index * report_size as usize, report_size as usize);
|
|
let usage = binary_view.read_u8(0).expect("Failed to read array item");
|
|
if usage != 0 {
|
|
pressed_keys.push((global_state.usage_page.unwrap_or(0), usage));
|
|
}
|
|
log::trace!("Report index array {}: {:#x}", report_index, usage);
|
|
}
|
|
} else {
|
|
log::trace!("Unknown report array item: size {} count {} at {}", report_size, report_count, bit_offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for &(usage_page, usage) in last_pressed_keys.iter() {
|
|
if ! pressed_keys.contains(&(usage_page, usage)) {
|
|
log::debug!("Released {:#x},{:#x}", usage_page, usage);
|
|
send_key_event(&mut display, usage_page, usage, false, None);
|
|
}
|
|
}
|
|
|
|
for &(usage_page, usage) in pressed_keys.iter() {
|
|
if ! last_pressed_keys.contains(&(usage_page, usage)) {
|
|
log::debug!("Pressed {:#x},{:#x}", usage_page, usage);
|
|
send_key_event(&mut display, usage_page, usage, true, Some(
|
|
pressed_keys.contains(&(0x07, 0xE1)) || pressed_keys.contains(&(0x07, 0xE5))
|
|
));
|
|
}
|
|
}
|
|
|
|
std::mem::swap(&mut pressed_keys, &mut last_pressed_keys);
|
|
pressed_keys.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn binary_view() {
|
|
// 0000 1000 1100 0111
|
|
// E S
|
|
let view = super::BinaryView::new(&[0xC7, 0x08], 3, 11);
|
|
assert_eq!(view.get(0), Some(false));
|
|
assert_eq!(view.get(2), Some(false));
|
|
assert_eq!(view.get(3), Some(true));
|
|
assert_eq!(view.get(7), Some(false));
|
|
assert_eq!(view.get(17), None);
|
|
|
|
assert_eq!(view.read_u8(0), Some(0b0001_1000));
|
|
assert_eq!(view.read_u8(1), Some(0b1000_1100));
|
|
assert_eq!(view.read_u8(2), Some(0b0100_0110));
|
|
assert_eq!(view.read_u8(3), Some(0b0010_0011));
|
|
assert_eq!(view.read_u8(7), None);
|
|
|
|
// 0000 1000 1100 0111
|
|
// E S
|
|
let view = super::BinaryView::new(&[0xC7, 0x08], 8, 8);
|
|
assert_eq!(view.read_u8(0), Some(0b0000_1000));
|
|
}
|
|
}
|