Naive implementation of sending strings to the host
This commit is contained in:
parent
1e2d43a628
commit
3b364c64c2
|
|
@ -2,20 +2,26 @@ use core::alloc::Layout;
|
|||
use core::fmt::Debug;
|
||||
use core::slice;
|
||||
|
||||
use alloc::alloc::Allocator;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::collections::btree_map::{BTreeMap, Entry};
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_time::Instant;
|
||||
use esp_alloc::MemoryCapability;
|
||||
use esp_alloc::{EspHeap, MemoryCapability};
|
||||
use itertools::Itertools;
|
||||
use log::{debug, info, warn};
|
||||
use rmk::descriptor::KeyboardReport;
|
||||
use rmk::futures::FutureExt;
|
||||
use rmk::hid::Report;
|
||||
use rmk::types::action::{Action, KeyAction};
|
||||
use rmk::{a, k, layer};
|
||||
use rmk::{a, join_all, k, layer};
|
||||
use slint::platform::Key;
|
||||
use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, Status};
|
||||
use static_cell::StaticCell;
|
||||
use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, ModMask, Status};
|
||||
|
||||
use crate::matrix::{MATRIX_COLS, MATRIX_ROWS};
|
||||
use crate::util::DurationExt;
|
||||
|
|
@ -25,6 +31,7 @@ use crate::{KEYBOARD_REPORT_PROXY, PSRAM_ALLOCATOR};
|
|||
pub const NUM_LAYER: usize = 1;
|
||||
|
||||
pub static KEY_MESSAGE_CHANNEL: Channel<CriticalSectionRawMutex, KeyMessage, 16> = Channel::new();
|
||||
pub static OUTPUT_STRING_CHANNEL: Channel<CriticalSectionRawMutex, String, 16> = Channel::new();
|
||||
|
||||
pub struct KeyMessage {
|
||||
pub keysym: Keysym,
|
||||
|
|
@ -83,13 +90,16 @@ pub fn create_hid_report_interceptor() -> impl Future<Output = ()> {
|
|||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||
info!("Loading XKB keymap...");
|
||||
let instant_start = Instant::now();
|
||||
let keymap = xkb::Keymap::new_from_string(
|
||||
&context,
|
||||
keymap_string_buffer,
|
||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
.unwrap();
|
||||
let keymap = Arc::new_in(
|
||||
xkb::Keymap::new_from_string(
|
||||
&context,
|
||||
keymap_string_buffer,
|
||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
.unwrap(),
|
||||
&PSRAM_ALLOCATOR,
|
||||
);
|
||||
let duration = Instant::now().duration_since(instant_start);
|
||||
info!(
|
||||
"XKB keymap loaded successfully! Took {} seconds.",
|
||||
|
|
@ -97,19 +107,69 @@ pub fn create_hid_report_interceptor() -> impl Future<Output = ()> {
|
|||
);
|
||||
info!("Loading XKB compose map...");
|
||||
let instant_start = Instant::now();
|
||||
let compose_table = xkb::compose::Table::new_from_buffer(
|
||||
&context,
|
||||
COMPOSE_MAP_STRING,
|
||||
COMPOSE_MAP_LOCALE,
|
||||
xkb::compose::FORMAT_TEXT_V1,
|
||||
xkb::compose::COMPILE_NO_FLAGS,
|
||||
)
|
||||
.unwrap();
|
||||
let compose_table = Arc::new_in(
|
||||
xkb::compose::Table::new_from_buffer(
|
||||
&context,
|
||||
COMPOSE_MAP_STRING,
|
||||
COMPOSE_MAP_LOCALE,
|
||||
xkb::compose::FORMAT_TEXT_V1,
|
||||
xkb::compose::COMPILE_NO_FLAGS,
|
||||
)
|
||||
.unwrap(),
|
||||
&PSRAM_ALLOCATOR,
|
||||
);
|
||||
let duration = Instant::now().duration_since(instant_start);
|
||||
info!(
|
||||
"XKB compose map loaded successfully! Took {} seconds.",
|
||||
duration.display_as_secs()
|
||||
);
|
||||
|
||||
async move {
|
||||
join_all![
|
||||
send_keycodes_to_slint(keymap.clone(), compose_table.clone()),
|
||||
send_strings_to_host(keymap, compose_table)
|
||||
]
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_strings_to_host<A: Allocator>(
|
||||
keymap: Arc<xkb::Keymap, A>,
|
||||
compose_table: Arc<xkb::compose::Table, A>,
|
||||
) {
|
||||
loop {
|
||||
let string = OUTPUT_STRING_CHANNEL.receive().await;
|
||||
let keycodes = string_to_hid_keycodes(&keymap, &compose_table, &string);
|
||||
|
||||
warn!("keycodes for {string:?}: {keycodes:02x?}");
|
||||
|
||||
if let Ok(keycodes) = keycodes {
|
||||
for keycode_chunk in keycodes.chunks(6) {
|
||||
rmk::channel::KEYBOARD_REPORT_RECEIVER
|
||||
.send(Report::KeyboardReport(KeyboardReport {
|
||||
modifier: Default::default(), // TODO
|
||||
reserved: Default::default(), // TODO
|
||||
leds: Default::default(),
|
||||
keycodes: {
|
||||
// Send multiple keycodes at the same time.
|
||||
let mut keycodes = [0; _];
|
||||
keycodes[0..keycode_chunk.len()].copy_from_slice(keycode_chunk);
|
||||
keycodes
|
||||
},
|
||||
}))
|
||||
.await;
|
||||
rmk::channel::KEYBOARD_REPORT_RECEIVER
|
||||
.send(Report::KeyboardReport(KeyboardReport::default()))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_keycodes_to_slint<A: Allocator>(
|
||||
keymap: Arc<xkb::Keymap, A>,
|
||||
compose_table: Arc<xkb::compose::Table, A>,
|
||||
) {
|
||||
let mut state = xkb::State::new(&keymap);
|
||||
let mut previous_state = KeyboardReport::default();
|
||||
let mut compose_state = xkb::compose::State::new(&compose_table, xkb::compose::STATE_NO_FLAGS);
|
||||
|
|
@ -117,130 +177,207 @@ pub fn create_hid_report_interceptor() -> impl Future<Output = ()> {
|
|||
// This is a map from the basic keysyms (not a composed ones) to the string that should be produced.
|
||||
let mut pressed_keys_to_strings = BTreeMap::<Keysym, Option<String>>::new();
|
||||
|
||||
async move {
|
||||
loop {
|
||||
let report = KEYBOARD_REPORT_PROXY.receive().await;
|
||||
loop {
|
||||
let report = KEYBOARD_REPORT_PROXY.receive().await;
|
||||
|
||||
if let Report::KeyboardReport(report) = &report {
|
||||
const KEYCODES_LEN_MODIFIERS: usize = 8;
|
||||
const KEYCODES_LEN_REGULAR: usize = 6;
|
||||
const KEYCODES_LEN: usize = KEYCODES_LEN_MODIFIERS + KEYCODES_LEN_REGULAR;
|
||||
if let Report::KeyboardReport(report) = &report {
|
||||
const KEYCODES_LEN_MODIFIERS: usize = 8;
|
||||
const KEYCODES_LEN_REGULAR: usize = 6;
|
||||
const KEYCODES_LEN: usize = KEYCODES_LEN_MODIFIERS + KEYCODES_LEN_REGULAR;
|
||||
|
||||
let mut pressed_keys = rmk::heapless::Vec::<u8, KEYCODES_LEN>::new();
|
||||
let mut released_keys = rmk::heapless::Vec::<u8, KEYCODES_LEN>::new();
|
||||
let mut pressed_keys = rmk::heapless::Vec::<u8, KEYCODES_LEN>::new();
|
||||
let mut released_keys = rmk::heapless::Vec::<u8, KEYCODES_LEN>::new();
|
||||
|
||||
let pressed_mods_bits = !previous_state.modifier & report.modifier;
|
||||
let released_mods_bits = previous_state.modifier & !report.modifier;
|
||||
let pressed_mods_bits = !previous_state.modifier & report.modifier;
|
||||
let released_mods_bits = previous_state.modifier & !report.modifier;
|
||||
|
||||
for index in 0..KEYCODES_LEN_MODIFIERS {
|
||||
const USB_HID_LEFT_CTRL: u8 = 0xE0;
|
||||
let mod_bit = 1_u8 << index;
|
||||
let mod_keycode = USB_HID_LEFT_CTRL + index as u8;
|
||||
for index in 0..KEYCODES_LEN_MODIFIERS {
|
||||
const USB_HID_LEFT_CTRL: u8 = 0xE0;
|
||||
let mod_bit = 1_u8 << index;
|
||||
let mod_keycode = USB_HID_LEFT_CTRL + index as u8;
|
||||
|
||||
if pressed_mods_bits & mod_bit != 0 {
|
||||
pressed_keys.push(mod_keycode).unwrap();
|
||||
}
|
||||
|
||||
if released_mods_bits & mod_bit != 0 {
|
||||
released_keys.push(mod_keycode).unwrap();
|
||||
}
|
||||
if pressed_mods_bits & mod_bit != 0 {
|
||||
pressed_keys.push(mod_keycode).unwrap();
|
||||
}
|
||||
|
||||
// TODO: This currently depends on pressed keys not changing position in the array.
|
||||
// Should be made independent of that.
|
||||
for (&keycode_old, &keycode_new) in
|
||||
core::iter::zip(&previous_state.keycodes, &report.keycodes)
|
||||
if released_mods_bits & mod_bit != 0 {
|
||||
released_keys.push(mod_keycode).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This currently depends on pressed keys not changing position in the array.
|
||||
// Should be made independent of that.
|
||||
for (&keycode_old, &keycode_new) in
|
||||
core::iter::zip(&previous_state.keycodes, &report.keycodes)
|
||||
{
|
||||
if keycode_old == keycode_new {
|
||||
continue;
|
||||
}
|
||||
|
||||
if keycode_old != 0 {
|
||||
released_keys.push(keycode_old).unwrap();
|
||||
}
|
||||
|
||||
if keycode_new != 0 {
|
||||
pressed_keys.push(keycode_new).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
previous_state = *report;
|
||||
|
||||
for keycode in released_keys {
|
||||
debug!("Release: 0x{:02x} ({})", keycode, keycode);
|
||||
let keycode_xkb = hid_to_xkb_keycode(keycode);
|
||||
state.update_key(keycode_xkb, KeyDirection::Up);
|
||||
let keysym = state.key_get_one_sym(keycode_xkb);
|
||||
let string = pressed_keys_to_strings.remove(&keysym).unwrap_or_else(|| {
|
||||
warn!("Could not determine the string of a released key: keysym={keysym:?} pressed_keys_to_strings={pressed_keys_to_strings:?}");
|
||||
None
|
||||
});
|
||||
KEY_MESSAGE_CHANNEL
|
||||
.send(KeyMessage {
|
||||
keysym,
|
||||
string,
|
||||
direction: KeyDirection::Up,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
for keycode in pressed_keys {
|
||||
debug!("Pressed: 0x{:02x} ({})", keycode, keycode);
|
||||
let keycode_xkb = hid_to_xkb_keycode(keycode);
|
||||
let sym = state.key_get_one_sym(keycode_xkb);
|
||||
|
||||
let result: Option<(Keysym, Keysym, Option<String>)> = match compose_state.feed(sym)
|
||||
{
|
||||
if keycode_old == keycode_new {
|
||||
continue;
|
||||
FeedResult::Ignored => {
|
||||
let string = state.key_get_utf8(keycode_xkb);
|
||||
Some((sym, sym, Some(string)))
|
||||
}
|
||||
|
||||
if keycode_old != 0 {
|
||||
released_keys.push(keycode_old).unwrap();
|
||||
FeedResult::Accepted => {
|
||||
let status = compose_state.status();
|
||||
debug!("Compose status: {status:?}");
|
||||
match status {
|
||||
Status::Nothing => {
|
||||
let string = state.key_get_utf8(keycode_xkb);
|
||||
Some((sym, sym, Some(string)))
|
||||
}
|
||||
Status::Composing => None,
|
||||
Status::Composed => {
|
||||
let composed_sym = compose_state.keysym().unwrap_or_default();
|
||||
let string = compose_state.utf8().unwrap_or_default();
|
||||
Some((sym, composed_sym, Some(string)))
|
||||
}
|
||||
Status::Cancelled => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if keycode_new != 0 {
|
||||
pressed_keys.push(keycode_new).unwrap();
|
||||
}
|
||||
}
|
||||
if let Some((basic_keysym, composed_keysym, string)) = result {
|
||||
// Change `Some("")` into `None`.
|
||||
let string = string.filter(|string| !string.is_empty());
|
||||
|
||||
previous_state = *report;
|
||||
info!(
|
||||
"Basic keysym: {basic_keysym:?}, composed keysym: {composed_keysym:?}, string: {:?}",
|
||||
string.as_ref()
|
||||
);
|
||||
|
||||
for keycode in released_keys {
|
||||
debug!("Release: 0x{:02x} ({})", keycode, keycode);
|
||||
let keycode_xkb = into_xkb_keycode(keycode);
|
||||
state.update_key(keycode_xkb, KeyDirection::Up);
|
||||
let keysym = state.key_get_one_sym(keycode_xkb);
|
||||
let string = pressed_keys_to_strings.remove(&keysym).unwrap_or_else(|| {
|
||||
warn!("Could not determine the string of a released key: keysym={keysym:?} pressed_keys_to_strings={pressed_keys_to_strings:?}");
|
||||
None
|
||||
});
|
||||
pressed_keys_to_strings.insert(basic_keysym, string.clone());
|
||||
KEY_MESSAGE_CHANNEL
|
||||
.send(KeyMessage {
|
||||
keysym,
|
||||
keysym: composed_keysym,
|
||||
string,
|
||||
direction: KeyDirection::Up,
|
||||
direction: KeyDirection::Down,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
for keycode in pressed_keys {
|
||||
debug!("Pressed: 0x{:02x} ({})", keycode, keycode);
|
||||
let keycode_xkb = into_xkb_keycode(keycode);
|
||||
let sym = state.key_get_one_sym(keycode_xkb);
|
||||
|
||||
let result: Option<(Keysym, Keysym, Option<String>)> = match compose_state
|
||||
.feed(sym)
|
||||
{
|
||||
FeedResult::Ignored => {
|
||||
let string = state.key_get_utf8(keycode_xkb);
|
||||
Some((sym, sym, Some(string)))
|
||||
}
|
||||
FeedResult::Accepted => {
|
||||
let status = compose_state.status();
|
||||
debug!("Compose status: {status:?}");
|
||||
match status {
|
||||
Status::Nothing => {
|
||||
let string = state.key_get_utf8(keycode_xkb);
|
||||
Some((sym, sym, Some(string)))
|
||||
}
|
||||
Status::Composing => None,
|
||||
Status::Composed => {
|
||||
let composed_sym = compose_state.keysym().unwrap_or_default();
|
||||
let string = compose_state.utf8().unwrap_or_default();
|
||||
Some((sym, composed_sym, Some(string)))
|
||||
}
|
||||
Status::Cancelled => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some((basic_keysym, composed_keysym, string)) = result {
|
||||
// Change `Some("")` into `None`.
|
||||
let string = string.filter(|string| !string.is_empty());
|
||||
|
||||
info!(
|
||||
"Basic keysym: {basic_keysym:?}, composed keysym: {composed_keysym:?}, string: {:?}",
|
||||
string.as_ref()
|
||||
);
|
||||
|
||||
pressed_keys_to_strings.insert(basic_keysym, string.clone());
|
||||
KEY_MESSAGE_CHANNEL
|
||||
.send(KeyMessage {
|
||||
keysym: composed_keysym,
|
||||
string,
|
||||
direction: KeyDirection::Down,
|
||||
})
|
||||
.await;
|
||||
state.update_key(keycode_xkb, KeyDirection::Down);
|
||||
}
|
||||
state.update_key(keycode_xkb, KeyDirection::Down);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_xkb_keycode(rmk_keycode: u8) -> xkb::Keycode {
|
||||
pub fn keysym_to_xkb_keycode() {}
|
||||
|
||||
/// Based on https://github.com/xkbcommon/libxkbcommon/blob/6c67e3d41d3215ab1edd4406de215c7bf1f20c74/tools/how-to-type.c#L389
|
||||
/// Fields reordered for `Ord` based on https://github.com/xkbcommon/libxkbcommon/blob/6c67e3d41d3215ab1edd4406de215c7bf1f20c74/tools/how-to-type.c#L404
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct KeysymEntry {
|
||||
layout: xkb::LayoutIndex,
|
||||
level: xkb::LevelIndex,
|
||||
keycode: xkb::Keycode,
|
||||
mask: xkb::ModMask,
|
||||
}
|
||||
|
||||
type KeysymEntries = Vec<KeysymEntry, &'static EspHeap>;
|
||||
type KeysymMap = BTreeMap<Keysym, KeysymEntries, &'static EspHeap>;
|
||||
|
||||
/// Based on https://github.com/xkbcommon/libxkbcommon/blob/6c67e3d41d3215ab1edd4406de215c7bf1f20c74/tools/how-to-type.c#L434
|
||||
fn lookup_keysym_entries(
|
||||
entries: &mut KeysymMap,
|
||||
keysym: xkb::Keysym,
|
||||
insert: bool,
|
||||
) -> Option<&mut KeysymEntries> {
|
||||
match entries.entry(keysym) {
|
||||
Entry::Occupied(occupied) => Some(occupied.into_mut()),
|
||||
Entry::Vacant(vacant) if insert => Some(vacant.insert(Vec::new_in(&PSRAM_ALLOCATOR))),
|
||||
Entry::Vacant(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_to_hid_keycodes(
|
||||
keymap: &xkb::Keymap,
|
||||
compose_table: &xkb::compose::Table,
|
||||
string: &str,
|
||||
) -> Result<Vec<u8, &'static EspHeap>, char> {
|
||||
let mut hid_keycodes = Vec::new_in(&PSRAM_ALLOCATOR);
|
||||
|
||||
for character in string.chars() {
|
||||
let keysym = xkbcommon::xkb::utf32_to_keysym(character as u32);
|
||||
let mut found_keycode = None;
|
||||
|
||||
keymap.key_for_each(|keymap, keycode| {
|
||||
if found_keycode.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
for layout_index in 0..keymap.num_layouts_for_key(keycode) {
|
||||
for level_index in 0..keymap.num_levels_for_key(keycode, layout_index) {
|
||||
let [current_keysym] =
|
||||
keymap.key_get_syms_by_level(keycode, layout_index, level_index)
|
||||
else {
|
||||
// Multi-keysym levels are currently not handled.
|
||||
continue;
|
||||
};
|
||||
|
||||
if current_keysym == &keysym {
|
||||
let mut masks: [ModMask; 32 /* TODO: Re-evaluate the appropriate size */] = Default::default();
|
||||
let masks_len = keymap.key_get_mods_for_level(
|
||||
keycode,
|
||||
layout_index,
|
||||
level_index,
|
||||
&mut masks,
|
||||
);
|
||||
let masks = &mut masks[0..masks_len];
|
||||
warn!("string_to_hid_keycodes: char = {character:?}, sym = {keysym:?}, code = {keycode:?}, layout = {layout_index:?}, level = {level_index:?}, masks = {masks:?}");
|
||||
found_keycode = Some(keycode);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let Some(found_keycode) = found_keycode else {
|
||||
return Err(character);
|
||||
};
|
||||
let hid_keycode = xkb_to_hid_keycode(found_keycode);
|
||||
|
||||
hid_keycodes.push(hid_keycode);
|
||||
}
|
||||
|
||||
Ok(hid_keycodes)
|
||||
}
|
||||
|
||||
const fn hid_to_xkb_keycode_inner(rmk_keycode: u8) -> u8 {
|
||||
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/hid/hid-input.c?id=refs/tags/v6.18#n27
|
||||
const UNK: u8 = 240;
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -267,7 +404,48 @@ fn into_xkb_keycode(rmk_keycode: u8) -> xkb::Keycode {
|
|||
|
||||
// TODO: The combination of these two operations should be precomputed
|
||||
// in a const expr into a single look-up table.
|
||||
xkb::Keycode::new((HID_KEYBOARD[rmk_keycode as usize] + MIN_KEYCODE) as u32)
|
||||
HID_KEYBOARD[rmk_keycode as usize] + MIN_KEYCODE
|
||||
// xkb::Keycode::new((HID_KEYBOARD[rmk_keycode as usize] + MIN_KEYCODE) as u32)
|
||||
}
|
||||
|
||||
pub const fn hid_to_xkb_keycode(hid_keycode: u8) -> xkb::Keycode {
|
||||
const HID_TO_XKB: [u8; 256] = {
|
||||
let mut hid_to_xkb = [0_u8; _];
|
||||
let mut hid: u8 = 0;
|
||||
|
||||
loop {
|
||||
hid_to_xkb[hid as usize] = hid_to_xkb_keycode_inner(hid);
|
||||
let overflow;
|
||||
(hid, overflow) = hid.overflowing_add(1);
|
||||
if overflow {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hid_to_xkb
|
||||
};
|
||||
|
||||
xkb::Keycode::new(HID_TO_XKB[hid_keycode as usize] as u32)
|
||||
}
|
||||
|
||||
pub const fn xkb_to_hid_keycode(xkb_keycode: xkb::Keycode) -> u8 {
|
||||
const XKB_TO_HID: [u8; 256] = {
|
||||
let mut xkb_to_hid = [0_u8; _];
|
||||
let mut hid: u8 = 0;
|
||||
|
||||
loop {
|
||||
xkb_to_hid[hid_to_xkb_keycode_inner(hid as u8) as usize] = hid;
|
||||
let overflow;
|
||||
(hid, overflow) = hid.overflowing_add(1);
|
||||
if overflow {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xkb_to_hid
|
||||
};
|
||||
|
||||
XKB_TO_HID[xkb_keycode.raw() as usize]
|
||||
}
|
||||
|
||||
pub trait TryFromKeysym: Sized {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ use alloc::{
|
|||
rc::Rc,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use embassy_time::Instant;
|
||||
use hex::FromHexError;
|
||||
use i_slint_core::model::{ModelChangeListener, ModelChangeListenerContainer};
|
||||
use log::{error, info, warn};
|
||||
use password_hash::Key;
|
||||
use rmk::futures::TryFutureExt;
|
||||
use slint::{
|
||||
Model, ModelExt, ModelNotify, ModelRc, ModelTracker, SharedString, StandardListViewItem,
|
||||
VecModel,
|
||||
|
|
@ -30,10 +32,11 @@ use crate::FRAME_DURATION_MIN;
|
|||
use crate::{
|
||||
PSRAM_ALLOCATOR, SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER,
|
||||
db::{
|
||||
AcidDatabase, DbKey, DbPathSpectreUserSites, DbPathSpectreUsers, PartitionAcid,
|
||||
ReadTransactionExt,
|
||||
AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers,
|
||||
PartitionAcid, ReadTransactionExt,
|
||||
},
|
||||
ffi::{alloc::__spre_free, crypto::ACTIVE_ENCRYPTED_USER_KEY},
|
||||
keymap::OUTPUT_STRING_CHANNEL,
|
||||
ui::{
|
||||
backend::SlintBackend,
|
||||
messages::{
|
||||
|
|
@ -189,6 +192,13 @@ impl State {
|
|||
}
|
||||
});
|
||||
|
||||
main.on_login_test_string_accepted(|string| {
|
||||
slint::spawn_local(async move {
|
||||
OUTPUT_STRING_CHANNEL.send(string.to_string()).await;
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
main.on_users_edit_user({
|
||||
let state = state.clone();
|
||||
move |username, new| {
|
||||
|
|
@ -754,6 +764,32 @@ impl AppViewTrait for StateUserSites {
|
|||
spectre_derive_site_password(&user_sites.user_key, &site_name_c);
|
||||
|
||||
warn!("Site password: {site_password:?}");
|
||||
|
||||
for character in site_password.chars() {
|
||||
let keysym = xkbcommon::xkb::utf32_to_keysym(character as u32);
|
||||
}
|
||||
|
||||
slint::spawn_local({
|
||||
let username = user_sites.username.clone();
|
||||
let db = state.db.clone();
|
||||
async move {
|
||||
let mut write = db.write_transaction().await;
|
||||
let key = DbKey::new(DbPathSpectreUserSite {
|
||||
user_sites: DbPathSpectreUserSites {
|
||||
username: Cow::Borrowed(&username),
|
||||
},
|
||||
site: Cow::Borrowed(&site_name),
|
||||
});
|
||||
let site = SpectreSiteConfig::default();
|
||||
let site_bytes =
|
||||
postcard::to_extend(&site, Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR))
|
||||
.unwrap();
|
||||
|
||||
write.write(&key, &site_bytes).await.unwrap();
|
||||
write.commit().await.unwrap();
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export component LoginView inherits VerticalLayout {
|
|||
in property <[string]> usernames <=> combo_box_username.model;
|
||||
callback users_clicked();
|
||||
callback pw_accepted(int, string, string);
|
||||
callback test_string_accepted <=> line_edit_test.accepted;
|
||||
Rectangle { }
|
||||
|
||||
HorizontalLayout {
|
||||
|
|
@ -39,5 +40,9 @@ export component LoginView inherits VerticalLayout {
|
|||
}
|
||||
}
|
||||
|
||||
line_edit_test := LineEdit {
|
||||
placeholder-text: "Text to send to host.";
|
||||
}
|
||||
|
||||
Rectangle { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export component AppWindow inherits Window {
|
|||
// Login View
|
||||
in property <[string]> login_usernames <=> login_view.usernames;
|
||||
callback login_pw_accepted <=> login_view.pw_accepted;
|
||||
callback login_test_string_accepted <=> login_view.test_string_accepted;
|
||||
// Users View
|
||||
callback users_edit_user <=> users_view.edit_user;
|
||||
// User Edit View
|
||||
|
|
|
|||
Loading…
Reference in a new issue