nothing works

This commit is contained in:
galister
2023-11-09 22:27:05 +09:00
commit f193f33f4e
42 changed files with 9992 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
use std::{collections::VecDeque, time::Instant};
use glam::{Affine3A, Vec2, Vec3};
use crate::state::AppState;
pub const HAND_LEFT: usize = 0;
pub const HAND_RIGHT: usize = 1;
pub const POINTER_NORM: u16 = 0;
pub const POINTER_SHIFT: u16 = 1;
pub const POINTER_ALT: u16 = 2;
pub trait InteractionHandler {
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit);
fn on_left(&mut self, app: &mut AppState, hand: usize);
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32);
}
// --- Dummies & plumbing below ---
impl Default for PointerState {
fn default() -> Self {
Self {
click: false,
grab: false,
show_hide: false,
scroll: 0.,
}
}
}
pub struct DummyInteractionHandler;
impl InteractionHandler for DummyInteractionHandler {
fn on_left(&mut self, _app: &mut AppState, _hand: usize) {}
fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) {}
fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {}
fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {}
}

354
src/overlays/keyboard.rs Normal file
View File

@@ -0,0 +1,354 @@
use std::{
collections::HashMap,
env::var,
fs,
io::Cursor,
path::PathBuf,
process::{Child, Command},
str::FromStr,
sync::Arc,
};
use crate::{
gui::{color_parse, CanvasBuilder, Control},
input::{KeyModifier, VirtualKey, KEYS_TO_MODS},
state::AppState,
};
use glam::{vec2, vec3};
use log::error;
use once_cell::sync::Lazy;
use regex::Regex;
use rodio::{Decoder, OutputStream, Source};
use serde::{Deserialize, Serialize};
use super::OverlayData;
const PIXELS_PER_UNIT: f32 = 80.;
const BUTTON_PADDING: f32 = 4.;
pub fn create_keyboard(app: &AppState) -> OverlayData {
let size = vec2(
LAYOUT.row_size * PIXELS_PER_UNIT,
(LAYOUT.main_layout.len() as f32) * PIXELS_PER_UNIT,
);
let data = KeyboardData {
modifiers: 0,
processes: vec![],
audio_stream: None,
};
let mut canvas = CanvasBuilder::new(
size.x as _,
size.y as _,
app.graphics.clone(),
app.format,
data,
);
canvas.bg_color = color_parse("#101010");
canvas.panel(0., 0., size.x, size.y);
canvas.font_size = 18;
canvas.bg_color = color_parse("#202020");
let unit_size = size.x / LAYOUT.row_size;
let h = unit_size - 2. * BUTTON_PADDING;
for row in 0..LAYOUT.key_sizes.len() {
let y = unit_size * (row as f32) + BUTTON_PADDING;
let mut sum_size = 0f32;
for col in 0..LAYOUT.key_sizes[row].len() {
let my_size = LAYOUT.key_sizes[row][col];
let x = unit_size * sum_size + BUTTON_PADDING;
let w = unit_size * my_size - 2. * BUTTON_PADDING;
if let Some(key) = LAYOUT.main_layout[row][col].as_ref() {
let mut maybe_state: Option<KeyButtonData> = None;
if let Ok(vk) = VirtualKey::from_str(key) {
if let Some(mods) = KEYS_TO_MODS.get(vk) {
maybe_state = Some(KeyButtonData::Modifier {
modifier: *mods,
sticky: false,
pressed: false,
});
} else {
maybe_state = Some(KeyButtonData::Key { vk, pressed: false });
}
} else if let Some(macro_verbs) = LAYOUT.macros.get(key) {
maybe_state = Some(KeyButtonData::Macro {
verbs: key_events_for_macro(macro_verbs),
});
} else if let Some(exec_args) = LAYOUT.exec_commands.get(key) {
maybe_state = Some(KeyButtonData::Exec {
program: exec_args.first().unwrap().clone(),
args: exec_args.iter().skip(1).cloned().collect(),
});
} else {
error!("Unknown key: {}", key);
}
if let Some(state) = maybe_state {
let label = LAYOUT.label_for_key(key);
let button = canvas.key_button(x, y, w, h, &label);
button.state = Some(state);
button.on_press = Some(key_press);
button.on_release = Some(key_release);
button.test_highlight = Some(test_highlight);
}
}
sum_size += my_size;
}
}
let canvas = canvas.build();
OverlayData {
name: Arc::from("Kbd"),
show_hide: true,
width: LAYOUT.row_size * 0.05,
size: (size.x as _, size.y as _),
grabbable: true,
spawn_point: vec3(0., -0.5, -1.),
backend: Box::new(canvas),
..Default::default()
}
}
fn key_press(
control: &mut Control<KeyboardData, KeyButtonData>,
data: &mut KeyboardData,
app: &mut AppState,
) {
match control.state.as_mut() {
Some(KeyButtonData::Key { vk, pressed }) => {
data.key_click();
app.input.send_key(*vk as _, true);
*pressed = true;
}
Some(KeyButtonData::Modifier {
modifier,
sticky,
pressed,
}) => {
*sticky = data.modifiers & *modifier == 0;
data.modifiers |= *modifier;
data.key_click();
app.input.set_modifiers(data.modifiers);
*pressed = true;
}
Some(KeyButtonData::Macro { verbs }) => {
data.key_click();
for (vk, press) in verbs {
app.input.send_key(*vk as _, *press);
}
}
Some(KeyButtonData::Exec { program, args }) => {
// Reap previous processes
data.processes
.retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_))));
data.key_click();
if let Ok(child) = Command::new(program).args(args).spawn() {
data.processes.push(child);
}
}
None => {}
}
}
fn key_release(
control: &mut Control<KeyboardData, KeyButtonData>,
data: &mut KeyboardData,
app: &mut AppState,
) {
match control.state.as_mut() {
Some(KeyButtonData::Key { vk, pressed }) => {
app.input.send_key(*vk as _, false);
*pressed = false;
}
Some(KeyButtonData::Modifier {
modifier,
sticky,
pressed,
}) => {
if !*sticky {
data.modifiers &= !*modifier;
app.input.set_modifiers(data.modifiers);
*pressed = false;
}
}
_ => {}
}
}
fn test_highlight(
control: &Control<KeyboardData, KeyButtonData>,
_data: &mut KeyboardData,
_app: &mut AppState,
) -> bool {
match control.state.as_ref() {
Some(KeyButtonData::Key { pressed, .. }) => *pressed,
Some(KeyButtonData::Modifier { pressed, .. }) => *pressed,
_ => false,
}
}
struct KeyboardData {
modifiers: KeyModifier,
processes: Vec<Child>,
audio_stream: Option<OutputStream>,
}
impl KeyboardData {
fn key_click(&mut self) {
let wav = include_bytes!("../res/421581.wav");
let cursor = Cursor::new(wav);
let source = Decoder::new_wav(cursor).unwrap();
self.audio_stream = None;
if let Ok((stream, handle)) = OutputStream::try_default() {
let _ = handle.play_raw(source.convert_samples());
self.audio_stream = Some(stream);
} else {
error!("Failed to play key click");
}
}
}
enum KeyButtonData {
Key {
vk: VirtualKey,
pressed: bool,
},
Modifier {
modifier: KeyModifier,
sticky: bool,
pressed: bool,
},
Macro {
verbs: Vec<(VirtualKey, bool)>,
},
Exec {
program: String,
args: Vec<String>,
},
}
static KEYBOARD_YAML: Lazy<PathBuf> = Lazy::new(|| {
let home = &var("HOME").unwrap();
[home, ".config/wlxoverlay/keyboard.yaml"].iter().collect() //TODO other paths
});
static LAYOUT: Lazy<Layout> = Lazy::new(Layout::load_from_disk);
static MACRO_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^([A-Za-z0-1_-]+)(?: +(UP|DOWN))?$").unwrap());
#[derive(Debug, Deserialize, Serialize)]
struct Layout {
name: String,
row_size: f32,
key_sizes: Vec<Vec<f32>>,
main_layout: Vec<Vec<Option<String>>>,
exec_commands: HashMap<String, Vec<String>>,
macros: HashMap<String, Vec<String>>,
labels: HashMap<String, Vec<String>>,
}
impl Layout {
fn load_from_disk() -> Layout {
let mut yaml = fs::read_to_string(KEYBOARD_YAML.as_path()).ok();
if yaml.is_none() {
yaml = Some(include_str!("../res/keyboard.yaml").to_string());
}
let mut layout: Layout =
serde_yaml::from_str(&yaml.unwrap()).expect("Failed to parse keyboard.yaml");
layout.post_load();
layout
}
fn post_load(&mut self) {
for i in 0..self.key_sizes.len() {
let row = &self.key_sizes[i];
let width: f32 = row.iter().sum();
if (width - self.row_size).abs() > 0.001 {
panic!(
"Row {} has a width of {}, but the row size is {}",
i, width, self.row_size
);
}
}
for i in 0..self.main_layout.len() {
let row = &self.main_layout[i];
let width = row.len();
if width != self.key_sizes[i].len() {
panic!(
"Row {} has {} keys, needs to have {} according to key_sizes",
i,
width,
self.key_sizes[i].len()
);
}
}
}
fn label_for_key(&self, key: &str) -> Vec<String> {
if let Some(label) = self.labels.get(key) {
return label.clone();
}
if key.is_empty() {
return vec![];
}
if key.len() == 1 {
return vec![key.to_string().to_lowercase()];
}
let mut key = key;
if key.starts_with("KP_") {
key = &key[3..];
}
if key.contains('_') {
key = key.split('_').next().unwrap();
}
vec![format!(
"{}{}",
key.chars().next().unwrap().to_uppercase(),
&key[1..].to_lowercase()
)]
}
}
fn key_events_for_macro(macro_verbs: &Vec<String>) -> Vec<(VirtualKey, bool)> {
let mut key_events = vec![];
for verb in macro_verbs {
if let Some(caps) = MACRO_REGEX.captures(verb) {
if let Ok(virtual_key) = VirtualKey::from_str(&caps[1]) {
if let Some(state) = caps.get(2) {
if state.as_str() == "UP" {
key_events.push((virtual_key, false));
} else if state.as_str() == "DOWN" {
key_events.push((virtual_key, true));
} else {
error!(
"Unknown key state in macro: {}, looking for UP or DOWN.",
state.as_str()
);
return vec![];
}
} else {
key_events.push((virtual_key, true));
key_events.push((virtual_key, false));
}
} else {
error!("Unknown virtual key: {}", &caps[1]);
return vec![];
}
}
}
key_events
}

145
src/overlays/mod.rs Normal file
View File

@@ -0,0 +1,145 @@
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use glam::{Affine3A, Quat, Vec3};
use vulkano::image::ImageViewAbstract;
use crate::state::AppState;
use self::interactions::{DummyInteractionHandler, InteractionHandler, PointerHit};
pub mod interactions;
pub mod keyboard;
pub mod watch;
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
pub enum RelativeTo {
None,
Head,
Hand(usize),
}
pub trait OverlayBackend: OverlayRenderer + InteractionHandler {}
pub struct OverlayData {
pub id: usize,
pub name: Arc<str>,
pub width: f32,
pub size: (i32, i32),
pub want_visible: bool,
pub show_hide: bool,
pub grabbable: bool,
pub transform: Affine3A,
pub spawn_point: Vec3,
pub spawn_rotation: Quat,
pub relative_to: RelativeTo,
pub interaction_transform: Affine3A,
pub backend: Box<dyn OverlayBackend>,
pub primary_pointer: Option<usize>,
}
impl Default for OverlayData {
fn default() -> OverlayData {
OverlayData {
id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed),
name: Arc::from(""),
width: 1.,
size: (0, 0),
want_visible: false,
show_hide: false,
grabbable: false,
relative_to: RelativeTo::None,
spawn_point: Vec3::NEG_Z,
spawn_rotation: Quat::IDENTITY,
transform: Affine3A::IDENTITY,
interaction_transform: Affine3A::IDENTITY,
backend: Box::new(SplitOverlayBackend::default()),
primary_pointer: None,
}
}
}
impl OverlayData {
pub fn reset(&mut self, app: &mut AppState) {
todo!()
}
pub fn init(&mut self, app: &mut AppState) {
self.backend.init(app);
}
pub fn render(&mut self, app: &mut AppState) {
self.backend.render(app);
}
pub fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
self.backend.view()
}
}
pub trait OverlayRenderer {
fn init(&mut self, app: &mut AppState);
fn pause(&mut self, app: &mut AppState);
fn resume(&mut self, app: &mut AppState);
fn render(&mut self, app: &mut AppState);
fn view(&mut self) -> Arc<dyn ImageViewAbstract>;
}
pub struct FallbackRenderer;
impl OverlayRenderer for FallbackRenderer {
fn init(&mut self, _app: &mut AppState) {}
fn pause(&mut self, _app: &mut AppState) {}
fn resume(&mut self, _app: &mut AppState) {}
fn render(&mut self, _app: &mut AppState) {}
fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
unimplemented!()
}
}
// Boilerplate and dummies
pub struct SplitOverlayBackend {
pub renderer: Box<dyn OverlayRenderer>,
pub interaction: Box<dyn InteractionHandler>,
}
impl Default for SplitOverlayBackend {
fn default() -> SplitOverlayBackend {
SplitOverlayBackend {
renderer: Box::new(FallbackRenderer),
interaction: Box::new(DummyInteractionHandler),
}
}
}
impl OverlayBackend for SplitOverlayBackend {}
impl OverlayRenderer for SplitOverlayBackend {
fn init(&mut self, app: &mut AppState) {
self.renderer.init(app);
}
fn pause(&mut self, app: &mut AppState) {
self.renderer.pause(app);
}
fn resume(&mut self, app: &mut AppState) {
self.renderer.resume(app);
}
fn render(&mut self, app: &mut AppState) {
self.renderer.render(app);
}
fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
self.renderer.view()
}
}
impl InteractionHandler for SplitOverlayBackend {
fn on_left(&mut self, app: &mut AppState, hand: usize) {
self.interaction.on_left(app, hand);
}
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) {
self.interaction.on_hover(app, hit);
}
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
self.interaction.on_scroll(app, hit, delta);
}
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
self.interaction.on_pointer(app, hit, pressed);
}
}

31
src/overlays/screen.rs Normal file
View File

@@ -0,0 +1,31 @@
pub struct ScreenInteractionData {
next_scroll: Instant,
next_move: Instant,
mouse_transform: Affine2,
}
impl ScreenInteractionData {
fn new(pos: Vec2, size: Vec2, transform: Transform) -> ScreenInteractionHandler {
let transform = match transform {
Transform::_90 | Transform::Flipped90 =>
Affine2::from_cols(vec2(0., size.y), vec2(-size.x, 0.), vec2(pos.x + size.x, pos.y)),
Transform::_180 | Transform::Flipped180 =>
Affine2::from_cols(vec2(-size.x, 0.), vec2(0., -size.y), vec2(pos.x + size.x, pos.y + size.y)),
Transform::_270 | Transform::Flipped270 =>
Affine2::from_cols(vec2(0., -size.y), vec2(size.x, 0.), vec2(pos.x, pos.y + size.y)),
_ =>
Affine2::from_cols(vec2(size.x, 0.), vec2(0., size.y), pos),
};
ScreenInteractionHandler {
next_scroll: Instant::now(),
next_move: Instant::now(),
mouse_transform: transform,
}
}
}
struct ScreenInteractionHandler {
}

170
src/overlays/watch.rs Normal file
View File

@@ -0,0 +1,170 @@
use std::{sync::Arc, time::Instant};
use chrono::Local;
use glam::{Quat, Vec3};
use crate::{
gui::{color_parse, CanvasBuilder},
state::AppState,
};
use super::{OverlayData, RelativeTo};
pub const WATCH_DEFAULT_POS: Vec3 = Vec3::new(0., 0., 0.15);
pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(0.7071066, 0., 0.7071066, 0.0007963);
pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> OverlayData {
let mut canvas = CanvasBuilder::new(400, 200, state.graphics.clone(), state.format, ());
let empty_str: Arc<str> = Arc::from("");
// Background
canvas.bg_color = color_parse("#353535");
canvas.panel(0., 0., 400., 200.);
// Time display
canvas.font_size = 46;
let clock = canvas.label(19., 100., 200., 50., empty_str.clone());
clock.on_update = Some(|control, _data, _app| {
let date = Local::now();
control.set_text(&format!("{}", &date.format("%H:%M")));
});
canvas.font_size = 14;
let date = canvas.label(20., 125., 200., 50., empty_str.clone());
date.on_update = Some(|control, _data, _app| {
let date = Local::now();
control.set_text(&format!("{}", &date.format("%x")));
});
let day_of_week = canvas.label(20., 150., 200., 50., empty_str);
day_of_week.on_update = Some(|control, _data, _app| {
let date = Local::now();
control.set_text(&format!("{}", &date.format("%A")));
});
// Volume controls
canvas.bg_color = color_parse("#222222");
canvas.fg_color = color_parse("#AAAAAA");
canvas.font_size = 14;
canvas.bg_color = color_parse("#303030");
canvas.fg_color = color_parse("#353535");
let vol_up = canvas.button(327., 116., 46., 32., "+".into());
vol_up.on_press = Some(|_control, _data, _app| {
println!("Volume up!"); //TODO
});
let vol_dn = canvas.button(327., 52., 46., 32., "-".into());
vol_dn.on_press = Some(|_control, _data, _app| {
println!("Volume down!"); //TODO
});
canvas.bg_color = color_parse("#303030");
canvas.fg_color = color_parse("#353535");
let settings = canvas.button(2., 162., 36., 36., "".into());
settings.on_press = Some(|_control, _data, _app| {
println!("Settings!"); //TODO
});
canvas.fg_color = color_parse("#CCBBAA");
canvas.bg_color = color_parse("#406050");
// Bottom row
let num_buttons = screens.len() + 1;
let button_width = 360. / num_buttons as f32;
let mut button_x = 40.;
let keyboard = canvas.button(button_x + 2., 162., button_width - 4., 36., "Kbd".into());
keyboard.state = Some(WatchButtonState {
pressed_at: Instant::now(),
scr_idx: 0,
});
keyboard.on_press = Some(|control, _data, _app| {
if let Some(state) = control.state.as_mut() {
state.pressed_at = Instant::now();
}
});
keyboard.on_release = Some(|control, _data, app| {
if let Some(state) = control.state.as_ref() {
if Instant::now()
.saturating_duration_since(state.pressed_at)
.as_millis()
< 2000
{
app.tasks.push_back(Box::new(|_app, o| {
for overlay in o {
if &*overlay.name == "Kbd" {
overlay.want_visible = !overlay.want_visible;
return;
}
}
}));
} else {
app.tasks.push_back(Box::new(|app, o| {
for overlay in o {
if &*overlay.name == "Kbd" {
overlay.reset(app);
}
}
}));
}
}
});
button_x += button_width;
canvas.bg_color = color_parse("#405060");
for (scr_idx, scr_name) in screens.into_iter() {
let button = canvas.button(button_x + 2., 162., button_width - 4., 36., scr_name);
button.state = Some(WatchButtonState {
pressed_at: Instant::now(),
scr_idx,
});
button.on_press = Some(|control, _data, _app| {
if let Some(state) = control.state.as_mut() {
state.pressed_at = Instant::now();
}
});
button.on_release = Some(|control, _data, app| {
if let Some(state) = control.state.as_ref() {
let scr_idx = state.scr_idx;
if Instant::now()
.saturating_duration_since(state.pressed_at)
.as_millis()
< 2000
{
app.tasks.push_back(Box::new(move |_app, o| {
o[scr_idx].want_visible = !o[scr_idx].want_visible;
}));
} else {
app.tasks.push_back(Box::new(move |app, o| {
o[scr_idx].reset(app);
}));
}
}
});
button_x += button_width;
}
let relative_to = RelativeTo::Hand(state.session.watch_hand);
OverlayData {
name: "Watch".into(),
size: (400, 200),
width: 0.065,
backend: Box::new(canvas.build()),
want_visible: true,
relative_to,
spawn_point: state.session.watch_pos,
spawn_rotation: state.session.watch_rot,
..Default::default()
}
}
struct WatchButtonState {
pressed_at: Instant,
scr_idx: usize,
}