Major overhaul of input and communication systems

* Add input manager

* WIP lifetime hell

* Hell yeah, dark lifetime magic

* Replace events with actions in tools

* Fix borrow in GlobalEventHandler

* Fix typo in response-handler

* Distribute dispatch structure

* Add translation from events to actions

* Port default key actions to input_mapper

* Split actions macro

* Add handling for Ambiguous Mouse events

* Fix warnings and clippy lints

* Add actions macro

* WIP rework

* Add AsMessage macro

* Add implementation for derived enums

* Add macro implementation for top level message

* Add #[child] attribute to indicate derivation

* Replace some mentions of Actions and Responses with Message

* It compiles !!!

* Add functionality to some message handlers

* Add document rendering

* ICE

* Rework the previous code while keeping basic functionality

* Reduce parent-top-level macro args to only two

* Add workaround for ICE

* Fix cyclic reference in document.rs

* Make derive_transitive_child a bit more powerful

This addresses the todo that was left,
enabling arbitrary expressions to be passed as the last
parameter of a #[parent] attribute

* Adapt frontend message format

* Make responses use VecDeque

Our responses are a queue so we should use a queue type for them

* Move traits to sensible location

* Are we rectangle yet?

* Simplify, improve & document `derive_discriminant`

* Change `child` to `sub_discriminant`

This only applies to `ToDiscriminant`.
Code using `#[impl_message]` continues to work.

* Add docs for `derive_transitive_child`

* Finish docs and improve macros

The improvements are that impl_message now uses trait
resolution to obtain the parent's discriminant
and that derive_as_message now allows for non-unit
variants (which we don't use but it's nice to have,
just in case)

* Remove logging call

* Move files around and cleanup structure

* Fix proc macro doc tests

* Improve actions_fn!() macro

* Add ellipse tool

* Pass populated actions list to the input mapper

* Add KeyState bitvector

* Merge mouse buttons into "keyboard"

* Add macro for initialization of key mapper table

* Add syntactic sugar for the macro

* Implement mapping function

* Translate the remaining tools

* Fix shape tool

* Add keybindings for line and pen tool

* Fix modifiers

* Cleanup

* Add doc comments for the actions macro

* Fix formatting

* Rename MouseMove to PointerMove

* Add keybinds for tools

* Apply review suggestions

* Rename KeyMappings -> KeyMappingEntries

* Apply review changes

Co-authored-by: T0mstone <realt0mstone@gmail.com>
Co-authored-by: Paul Kupper <kupper.pa@gmail.com>
This commit is contained in:
TrueDoctor
2021-05-23 01:26:24 +02:00
committed by Keavon Chambers
parent c08b2d4b31
commit 4b19a459b7
72 changed files with 3028 additions and 1856 deletions

View File

@@ -0,0 +1,191 @@
use crate::message_prelude::*;
use crate::tool::ToolType;
use super::{
keyboard::{Key, KeyStates, NUMBER_OF_KEYS},
InputPreprocessor,
};
#[impl_message(Message, InputMapper)]
#[derive(PartialEq, Clone, Debug)]
pub enum InputMapperMessage {
PointerMove,
KeyUp(Key),
KeyDown(Key),
}
#[derive(PartialEq, Clone, Debug)]
struct MappingEntry {
trigger: InputMapperMessage,
modifiers: KeyStates,
action: Message,
}
#[derive(Debug, Clone, Default)]
struct KeyMappingEntries(Vec<MappingEntry>);
impl KeyMappingEntries {
fn match_mapping(&self, keys: &KeyStates, actions: ActionList) -> Option<Message> {
for entry in self.0.iter() {
let all_required_modifiers_pressed = ((*keys & entry.modifiers) ^ entry.modifiers).is_empty();
if all_required_modifiers_pressed && actions.iter().flatten().any(|action| entry.action.to_discriminant() == *action) {
return Some(entry.action.clone());
}
}
None
}
fn push(&mut self, entry: MappingEntry) {
self.0.push(entry)
}
}
#[derive(Debug, Clone)]
struct Mapping {
up: [KeyMappingEntries; NUMBER_OF_KEYS],
down: [KeyMappingEntries; NUMBER_OF_KEYS],
pointer_move: KeyMappingEntries,
}
macro_rules! modifiers {
($($m:ident),*) => {{
#[allow(unused_mut)]
let mut state = KeyStates::new();
$(
state.set(Key::$m as usize);
),*
state
}};
}
macro_rules! entry {
{action=$action:expr, key_down=$key:ident $(, modifiers=[$($m:ident),* $(,)?])?} => {{
entry!{action=$action, message=InputMapperMessage::KeyDown(Key::$key) $(, modifiers=[$($m),*])?}
}};
{action=$action:expr, key_up=$key:ident $(, modifiers=[$($m:ident),* $(,)?])?} => {{
entry!{action=$action, message=InputMapperMessage::KeyUp(Key::$key) $(, modifiers=[$($m),* ])?}
}};
{action=$action:expr, message=$message:expr $(, modifiers=[$($m:ident),* $(,)?])?} => {{
MappingEntry {trigger: $message, modifiers: modifiers!($($($m),*)?), action: $action.into()}
}};
}
macro_rules! mapping {
//[$(<action=$action:expr; message=$key:expr; $(modifiers=[$($m:ident),* $(,)?];)?>)*] => {{
[$($entry:expr),* $(,)?] => {{
let mut up: [KeyMappingEntries; NUMBER_OF_KEYS] = Default::default();
let mut down: [KeyMappingEntries; NUMBER_OF_KEYS] = Default::default();
let mut pointer_move: KeyMappingEntries = Default::default();
$(
let arr = match $entry.trigger {
InputMapperMessage::KeyDown(key) => &mut down[key as usize],
InputMapperMessage::KeyUp(key) => &mut up[key as usize],
InputMapperMessage::PointerMove => &mut pointer_move,
};
arr.push($entry);
)*
(up, down, pointer_move)
}};
}
impl Default for Mapping {
fn default() -> Self {
let (up, down, pointer_move) = mapping![
// Rectangle
entry! {action=RectangleMessage::Center, key_down=KeyAlt},
entry! {action=RectangleMessage::UnCenter, key_up=KeyAlt},
entry! {action=RectangleMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=RectangleMessage::DragStart, key_down=Lmb},
entry! {action=RectangleMessage::DragStop, key_up=Lmb},
entry! {action=RectangleMessage::Abort, key_down=Rmb},
entry! {action=RectangleMessage::Abort, key_down=KeyEscape},
entry! {action=RectangleMessage::LockAspectRatio, key_down=KeyShift},
entry! {action=RectangleMessage::UnlockAspectRatio, key_up=KeyShift},
entry! {action=RectangleMessage::LockAspectRatio, key_down=KeyCaps},
entry! {action=RectangleMessage::UnlockAspectRatio, key_up=KeyCaps},
// Ellipse
entry! {action=EllipseMessage::Center, key_down=KeyAlt},
entry! {action=EllipseMessage::UnCenter, key_up=KeyAlt},
entry! {action=EllipseMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=EllipseMessage::DragStart, key_down=Lmb},
entry! {action=EllipseMessage::DragStop, key_up=Lmb},
entry! {action=EllipseMessage::Abort, key_down=Rmb},
entry! {action=EllipseMessage::Abort, key_down=KeyEscape},
entry! {action=EllipseMessage::LockAspectRatio, key_down=KeyShift},
entry! {action=EllipseMessage::UnlockAspectRatio, key_up=KeyShift},
entry! {action=EllipseMessage::LockAspectRatio, key_down=KeyCaps},
entry! {action=EllipseMessage::UnlockAspectRatio, key_up=KeyCaps},
// Shape
entry! {action=ShapeMessage::Center, key_down=KeyAlt},
entry! {action=ShapeMessage::UnCenter, key_up=KeyAlt},
entry! {action=ShapeMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=ShapeMessage::DragStart, key_down=Lmb},
entry! {action=ShapeMessage::DragStop, key_up=Lmb},
entry! {action=ShapeMessage::Abort, key_down=Rmb},
entry! {action=ShapeMessage::Abort, key_down=KeyEscape},
entry! {action=ShapeMessage::LockAspectRatio, key_down=KeyShift},
entry! {action=ShapeMessage::UnlockAspectRatio, key_up=KeyShift},
entry! {action=ShapeMessage::LockAspectRatio, key_down=KeyCaps},
entry! {action=ShapeMessage::UnlockAspectRatio, key_up=KeyCaps},
// Line
entry! {action=LineMessage::Center, key_down=KeyAlt},
entry! {action=LineMessage::UnCenter, key_up=KeyAlt},
entry! {action=LineMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=LineMessage::DragStart, key_down=Lmb},
entry! {action=LineMessage::DragStop, key_up=Lmb},
entry! {action=LineMessage::Abort, key_down=Rmb},
entry! {action=LineMessage::Abort, key_down=KeyEscape},
entry! {action=LineMessage::LockAngle, key_down=KeyControl},
entry! {action=LineMessage::UnlockAngle, key_up=KeyControl},
entry! {action=LineMessage::SnapToAngle, key_down=KeyShift},
entry! {action=LineMessage::UnSnapToAngle, key_up=KeyShift},
entry! {action=LineMessage::SnapToAngle, key_down=KeyCaps},
entry! {action=LineMessage::UnSnapToAngle, key_up=KeyCaps},
// Pen
entry! {action=PenMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=PenMessage::DragStart, key_down=Lmb},
entry! {action=PenMessage::DragStop, key_up=Lmb},
entry! {action=PenMessage::Confirm, key_down=Rmb},
entry! {action=PenMessage::Confirm, key_down=KeyEscape},
entry! {action=PenMessage::Confirm, key_down=KeyEnter},
// Document Actions
entry! {action=DocumentMessage::Undo, key_down=KeyZ, modifiers=[KeyControl]},
// Tool Actions
entry! {action=ToolMessage::SelectTool(ToolType::Rectangle), key_down=KeyM},
entry! {action=ToolMessage::SelectTool(ToolType::Ellipse), key_down=KeyE},
entry! {action=ToolMessage::SelectTool(ToolType::Select), key_down=KeyV},
entry! {action=ToolMessage::SelectTool(ToolType::Line), key_down=KeyL},
entry! {action=ToolMessage::SelectTool(ToolType::Shape), key_down=KeyY},
entry! {action=ToolMessage::SwapColors, key_down=KeyX, modifiers=[KeyShift]},
// Global Actions
entry! {action=GlobalMessage::LogInfo, key_down=Key1},
entry! {action=GlobalMessage::LogDebug, key_down=Key2},
entry! {action=GlobalMessage::LogTrace, key_down=Key3},
];
Self { up, down, pointer_move }
}
}
impl Mapping {
fn match_message(&self, message: InputMapperMessage, keys: &KeyStates, actions: ActionList) -> Option<Message> {
use InputMapperMessage::*;
let list = match message {
KeyDown(key) => &self.down[key as usize],
KeyUp(key) => &self.up[key as usize],
PointerMove => &self.pointer_move,
};
list.match_mapping(keys, actions)
}
}
#[derive(Debug, Default)]
pub struct InputMapper {
mapping: Mapping,
}
impl MessageHandler<InputMapperMessage, (&InputPreprocessor, ActionList)> for InputMapper {
fn process_action(&mut self, message: InputMapperMessage, data: (&InputPreprocessor, ActionList), responses: &mut VecDeque<Message>) {
let (input, actions) = data;
if let Some(message) = self.mapping.match_message(message, &input.keyboard, actions) {
responses.push_back(message);
}
}
advertise_actions!();
}

View File

@@ -0,0 +1,74 @@
use super::keyboard::{Key, KeyStates};
use super::mouse::{MouseKeys, MouseState, ViewportPosition};
use crate::message_prelude::*;
#[doc(inline)]
pub use document_core::DocumentResponse;
#[impl_message(Message, InputPreprocessor)]
#[derive(PartialEq, Clone, Debug)]
pub enum InputPreprocessorMessage {
MouseDown(MouseState),
MouseUp(MouseState),
MouseMove(ViewportPosition),
KeyUp(Key),
KeyDown(Key),
}
#[derive(Debug, Default)]
pub struct InputPreprocessor {
pub keyboard: KeyStates,
pub mouse: MouseState,
}
enum KeyPosition {
Pressed,
Released,
}
impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessor {
fn process_action(&mut self, message: InputPreprocessorMessage, _data: (), responses: &mut VecDeque<Message>) {
let response = match message {
InputPreprocessorMessage::MouseMove(pos) => {
self.mouse.position = pos;
InputMapperMessage::PointerMove.into()
}
InputPreprocessorMessage::MouseDown(state) => self.translate_mouse_event(state, KeyPosition::Pressed),
InputPreprocessorMessage::MouseUp(state) => self.translate_mouse_event(state, KeyPosition::Released),
InputPreprocessorMessage::KeyDown(key) => {
self.keyboard.set(key as usize);
InputMapperMessage::KeyDown(key).into()
}
InputPreprocessorMessage::KeyUp(key) => {
self.keyboard.unset(key as usize);
InputMapperMessage::KeyUp(key).into()
}
};
responses.push_back(response)
}
// clean user input and if possible reconstruct it
// store the changes in the keyboard if it is a key event
// transform canvas coordinates to document coordinates
advertise_actions!();
}
impl InputPreprocessor {
fn translate_mouse_event(&mut self, new_state: MouseState, position: KeyPosition) -> Message {
// Calculate the difference between the two key states (binary xor)
let diff = self.mouse.mouse_keys ^ new_state.mouse_keys;
self.mouse = new_state;
let key = match diff {
MouseKeys::LEFT => Key::Lmb,
MouseKeys::RIGHT => Key::Rmb,
MouseKeys::MIDDLE => Key::Mmb,
_ => {
log::warn!("The number of buttons modified at the same time was not equal to 1. Modification: {:#010b}", diff);
Key::UnknownKey
}
};
match position {
KeyPosition::Pressed => InputMapperMessage::KeyDown(key).into(),
KeyPosition::Released => InputMapperMessage::KeyUp(key).into(),
}
}
}

View File

@@ -0,0 +1,143 @@
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
// Edit this to specify the storage type used
// TODO: Increase size of type
pub type StorageType = u8;
const STORAGE_SIZE: u32 = std::mem::size_of::<usize>() as u32 * 8 + 2 - std::mem::size_of::<StorageType>().leading_zeros();
const STORAGE_SIZE_BITS: usize = 1 << STORAGE_SIZE;
const KEY_MASK_STORAGE_LENGTH: usize = (NUMBER_OF_KEYS + STORAGE_SIZE_BITS - 1) >> STORAGE_SIZE;
pub type KeyStates = BitVector<KEY_MASK_STORAGE_LENGTH>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
UnknownKey,
// MouseKeys
Lmb,
Rmb,
Mmb,
// Keyboard keys
KeyR,
KeyM,
KeyE,
KeyL,
KeyP,
KeyV,
KeyX,
KeyZ,
KeyY,
KeyEnter,
Key0,
Key1,
Key2,
Key3,
Key4,
Key5,
Key6,
Key7,
Key8,
Key9,
KeyShift,
KeyCaps,
KeyControl,
KeyAlt,
KeyEscape,
// This has to be the last element in the enum.
NumKeys,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BitVector<const LENGTH: usize>([StorageType; LENGTH]);
use std::{
fmt::{Display, Formatter},
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign},
usize,
};
impl<const LENGTH: usize> BitVector<LENGTH> {
#[inline]
fn convert_index(bitvector_index: usize) -> (usize, StorageType) {
let bit = 1 << (bitvector_index & (STORAGE_SIZE_BITS as StorageType - 1) as usize);
let offset = bitvector_index >> STORAGE_SIZE;
(offset, bit)
}
pub const fn new() -> Self {
Self([0; LENGTH])
}
pub fn set(&mut self, bitvector_index: usize) {
let (offset, bit) = Self::convert_index(bitvector_index);
self.0[offset] |= bit;
}
pub fn unset(&mut self, bitvector_index: usize) {
let (offset, bit) = Self::convert_index(bitvector_index);
self.0[offset] &= !bit;
}
pub fn toggle(&mut self, bitvector_index: usize) {
let (offset, bit) = Self::convert_index(bitvector_index);
self.0[offset] ^= bit;
}
pub fn is_empty(&self) -> bool {
let mut result = 0;
for storage in self.0.iter() {
result |= storage;
}
result == 0
}
}
impl<const LENGTH: usize> Default for BitVector<LENGTH> {
fn default() -> Self {
Self::new()
}
}
impl<const LENGTH: usize> Display for BitVector<LENGTH> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for storage in self.0.iter().rev() {
write!(f, "{:0width$b}", storage, width = STORAGE_SIZE_BITS)?;
}
Ok(())
}
}
macro_rules! bit_ops {
($(($op:ident, $func:ident)),* $(,)?) => {
$(
impl<const LENGTH: usize> $op for BitVector<LENGTH> {
type Output = Self;
fn $func(self, right: Self) -> Self::Output {
let mut result = Self::new();
for ((left, right), new) in self.0.iter().zip(right.0.iter()).zip(result.0.iter_mut()) {
*new = $op::$func(left, right);
}
result
}
}
impl<const LENGTH: usize> $op for &BitVector<LENGTH> {
type Output = BitVector<LENGTH>;
fn $func(self, right: Self) -> Self::Output {
let mut result = BitVector::<LENGTH>::new();
for ((left, right), new) in self.0.iter().zip(right.0.iter()).zip(result.0.iter_mut()) {
*new = $op::$func(left, right);
}
result
}
}
)*
};
}
macro_rules! bit_ops_assign {
($(($op:ident, $func:ident)),* $(,)?) => {
$(impl<const LENGTH: usize> $op for BitVector<LENGTH> {
fn $func(&mut self, right: Self) {
for (left, right) in self.0.iter_mut().zip(right.0.iter()) {
$op::$func(left, right);
}
}
})*
};
}
bit_ops!((BitAnd, bitand), (BitOr, bitor), (BitXor, bitxor));
bit_ops_assign!((BitAndAssign, bitand_assign), (BitOrAssign, bitor_assign), (BitXorAssign, bitxor_assign));

View File

@@ -0,0 +1,9 @@
pub mod input_mapper;
pub mod input_preprocessor;
pub mod keyboard;
pub mod mouse;
pub use {
input_mapper::{InputMapper, InputMapperMessage, InputMapperMessageDiscriminant},
input_preprocessor::{InputPreprocessor, InputPreprocessorMessage, InputPreprocessorMessageDiscriminant},
};

View File

@@ -0,0 +1,48 @@
use bitflags::bitflags;
// origin is top left
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct ViewportPosition {
pub x: u32,
pub y: u32,
}
impl ViewportPosition {
pub fn distance(&self, other: &Self) -> f64 {
let x_diff = other.x as i64 - self.x as i64;
let y_diff = other.y as i64 - self.y as i64;
f64::sqrt((x_diff * x_diff + y_diff * y_diff) as f64)
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct MouseState {
pub position: ViewportPosition,
pub mouse_keys: MouseKeys,
}
impl MouseState {
pub fn new() -> MouseState {
Self::default()
}
pub fn from_pos(x: u32, y: u32) -> MouseState {
MouseState {
position: ViewportPosition { x, y },
mouse_keys: MouseKeys::default(),
}
}
pub fn from_u8_pos(keys: u8, position: ViewportPosition) -> Self {
let mouse_keys = MouseKeys::from_bits(keys).expect("invalid modifier keys");
Self { position, mouse_keys }
}
}
bitflags! {
#[derive(Default)]
#[repr(transparent)]
pub struct MouseKeys: u8 {
const LEFT = 0b0000_0001;
const RIGHT = 0b0000_0010;
const MIDDLE = 0b0000_0100;
}
}