Merge branch 'vecset' into 'master'
Move event handling to Rust See merge request Librem5/squeekboard!223
This commit is contained in:
@ -14,7 +14,7 @@ use std::vec::Vec;
|
||||
use xkbcommon::xkb;
|
||||
|
||||
use ::keyboard::{
|
||||
KeyState,
|
||||
KeyState, PressType,
|
||||
generate_keymap, generate_keycodes, FormattingError
|
||||
};
|
||||
use ::resources;
|
||||
@ -309,7 +309,7 @@ impl Layout {
|
||||
(
|
||||
name.into(),
|
||||
KeyState {
|
||||
pressed: false,
|
||||
pressed: PressType::Released,
|
||||
locked: false,
|
||||
keycodes,
|
||||
action,
|
||||
@ -370,6 +370,8 @@ impl Layout {
|
||||
CString::new(keymap_str)
|
||||
.expect("Invalid keymap string generated")
|
||||
},
|
||||
locked_keys: HashSet::new(),
|
||||
pressed_keys: HashSet::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,13 +9,4 @@ struct squeek_key;
|
||||
|
||||
uint32_t squeek_key_is_pressed(struct squeek_key *key);
|
||||
uint32_t squeek_key_is_locked(struct squeek_key *key);
|
||||
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
|
||||
uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1);
|
||||
|
||||
enum key_press {
|
||||
KEY_RELEASE = 0,
|
||||
KEY_PRESS = 1,
|
||||
};
|
||||
|
||||
void squeek_key_press(struct squeek_key *key, struct zwp_virtual_keyboard_v1*, enum key_press press, uint32_t timestamp);
|
||||
#endif
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/*! State of the emulated keyboard and keys */
|
||||
/*! State of the emulated keyboard and keys.
|
||||
* Regards the keyboard as if it was composed of switches. */
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use ::action::Action;
|
||||
@ -17,34 +17,10 @@ pub mod c {
|
||||
use super::*;
|
||||
use ::util::c;
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
pub type CKeyState = c::Wrapped<KeyState>;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ZwpVirtualKeyboardV1(*const c_void);
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" {
|
||||
/// Checks if point falls within bounds,
|
||||
/// which are relative to origin and rotated by angle (I think)
|
||||
pub fn eek_virtual_keyboard_v1_key(
|
||||
virtual_keyboard: *mut ZwpVirtualKeyboardV1,
|
||||
timestamp: u32,
|
||||
keycode: u32,
|
||||
press: u32,
|
||||
);
|
||||
}
|
||||
|
||||
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
|
||||
|
||||
/// Compares pointers to the data
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_key_equal(key: CKeyState, key2: CKeyState) -> u32 {
|
||||
return Rc::ptr_eq(&key.clone_ref(), &key2.clone_ref()) as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_key_is_pressed(key: CKeyState) -> u32 {
|
||||
@ -57,59 +33,17 @@ pub mod c {
|
||||
fn squeek_key_is_locked(key: CKeyState) -> u32 {
|
||||
return key.clone_owned().locked as u32;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
|
||||
let key = key.clone_ref();
|
||||
let mut key = key.borrow_mut();
|
||||
key.locked = locked != 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_key_press(
|
||||
key: CKeyState,
|
||||
virtual_keyboard: *mut ZwpVirtualKeyboardV1,
|
||||
press: u32,
|
||||
timestamp: u32,
|
||||
) {
|
||||
let key = key.clone_ref();
|
||||
let mut key = key.borrow_mut();
|
||||
key.pressed = press != 0;
|
||||
|
||||
let keycodes_count = key.keycodes.len();
|
||||
for keycode in key.keycodes.iter() {
|
||||
let keycode = keycode - 8;
|
||||
match (key.pressed, keycodes_count) {
|
||||
// Pressing a key made out of a single keycode is simple:
|
||||
// press on press, release on release.
|
||||
(_, 1) => unsafe {
|
||||
eek_virtual_keyboard_v1_key(
|
||||
virtual_keyboard, timestamp, keycode, press
|
||||
);
|
||||
},
|
||||
// A key made of multiple keycodes
|
||||
// has to submit them one after the other
|
||||
(true, _) => unsafe {
|
||||
eek_virtual_keyboard_v1_key(
|
||||
virtual_keyboard, timestamp, keycode, 1
|
||||
);
|
||||
eek_virtual_keyboard_v1_key(
|
||||
virtual_keyboard, timestamp, keycode, 0
|
||||
);
|
||||
},
|
||||
// Design choice here: submit multiple all at press time
|
||||
// and do nothing at release time
|
||||
(false, _) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PressType {
|
||||
Released = 0,
|
||||
Pressed = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeyState {
|
||||
pub pressed: bool,
|
||||
pub pressed: PressType,
|
||||
pub locked: bool,
|
||||
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
|
||||
pub keycodes: Vec<u32>,
|
||||
@ -245,7 +179,7 @@ mod tests {
|
||||
},
|
||||
keycodes: vec!(9, 10),
|
||||
locked: false,
|
||||
pressed: false,
|
||||
pressed: PressType::Released,
|
||||
},
|
||||
}).unwrap();
|
||||
|
||||
|
||||
37
src/layout.h
37
src/layout.h
@ -4,30 +4,31 @@
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
#include "eek/eek-element.h"
|
||||
#include "eek/eek-gtk-keyboard.h"
|
||||
#include "eek/eek-types.h"
|
||||
#include "src/keyboard.h"
|
||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||
|
||||
struct squeek_button;
|
||||
struct squeek_row;
|
||||
struct squeek_view;
|
||||
struct squeek_layout;
|
||||
|
||||
/// Represents the path to the button within a view
|
||||
struct button_place {
|
||||
const struct squeek_row *row;
|
||||
const struct squeek_button *button;
|
||||
};
|
||||
|
||||
int32_t squeek_row_get_angle(const struct squeek_row*);
|
||||
|
||||
EekBounds squeek_row_get_bounds(const struct squeek_row*);
|
||||
|
||||
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
|
||||
|
||||
struct button_place squeek_view_find_key(struct squeek_view*, struct squeek_key *state);
|
||||
|
||||
|
||||
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
|
||||
void squeek_row_foreach(struct squeek_row*,
|
||||
ButtonCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void squeek_row_free(struct squeek_row*);
|
||||
|
||||
uint32_t squeek_button_get_oref(const struct squeek_button*);
|
||||
EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
||||
const char *squeek_button_get_label(const struct squeek_button*);
|
||||
const char *squeek_button_get_icon_name(const struct squeek_button*);
|
||||
@ -47,19 +48,23 @@ void squeek_view_foreach(struct squeek_view*,
|
||||
RowCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
|
||||
struct squeek_button *button);
|
||||
|
||||
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
|
||||
|
||||
|
||||
void
|
||||
squeek_layout_place_contents(struct squeek_layout*);
|
||||
struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*);
|
||||
void squeek_layout_set_state_from_press(struct squeek_layout* layout, LevelKeyboard *keyboard, struct squeek_key* key);
|
||||
|
||||
|
||||
struct squeek_layout *squeek_load_layout(const char *type);
|
||||
const char *squeek_layout_get_keymap(const struct squeek_layout*);
|
||||
void squeek_layout_free(struct squeek_layout*);
|
||||
|
||||
void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
|
||||
void squeek_layout_release_all_only(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp);
|
||||
void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
|
||||
double x_widget, double y_widget,
|
||||
struct transformation widget_to_layout,
|
||||
uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
|
||||
void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
|
||||
double x_widget, double y_widget,
|
||||
struct transformation widget_to_layout,
|
||||
uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
|
||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekGtkKeyboard *ui_keyboard);
|
||||
#endif
|
||||
|
||||
735
src/layout.rs
735
src/layout.rs
@ -18,16 +18,17 @@
|
||||
*/
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{ HashMap, HashSet };
|
||||
use std::ffi::CString;
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use ::action::Action;
|
||||
use ::float_ord::FloatOrd;
|
||||
use ::keyboard::*;
|
||||
use ::keyboard::{ KeyState, PressType };
|
||||
use ::submission::{ Timestamp, VirtualKeyboard };
|
||||
|
||||
use ::util::CloneOwned;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
pub mod c {
|
||||
@ -42,6 +43,10 @@ pub mod c {
|
||||
#[repr(transparent)]
|
||||
pub struct UserData(*const c_void);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct EekGtkKeyboard(*const c_void);
|
||||
|
||||
/// Defined in eek-types.h
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug)]
|
||||
@ -101,14 +106,6 @@ pub mod c {
|
||||
None => panic!("Row doesn't have any bounds yet"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set bounds by consuming the value
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_row_set_bounds(row: *mut ::layout::Row, bounds: Bounds) {
|
||||
let row = unsafe { &mut *row };
|
||||
row.bounds = Some(bounds);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
@ -123,12 +120,6 @@ pub mod c {
|
||||
unsafe { callback(button, data) };
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_row_free(row: *mut ::layout::Row) {
|
||||
unsafe { Box::from_raw(row) }; // gets dropped
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
@ -187,18 +178,6 @@ pub mod c {
|
||||
let button = unsafe { &*button };
|
||||
button.outline_name.as_ptr()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_button_has_key(
|
||||
button: *const ::layout::Button,
|
||||
state: ::keyboard::c::CKeyState,
|
||||
) -> u32 {
|
||||
let button = unsafe { &*button };
|
||||
let state = state.clone_ref();
|
||||
let equal = Rc::ptr_eq(&button.state, &state);
|
||||
equal as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
@ -229,19 +208,48 @@ pub mod c {
|
||||
fn squeek_layout_free(layout: *mut Layout) {
|
||||
unsafe { Box::from_raw(layout) };
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Entry points for more complex procedures and algoithms which span multiple modules
|
||||
pub mod procedures {
|
||||
use super::*;
|
||||
|
||||
use ::submission::c::ZwpVirtualKeyboardV1;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ButtonPlace {
|
||||
pub struct CButtonPlace {
|
||||
row: *const Row,
|
||||
button: *const Button,
|
||||
}
|
||||
|
||||
impl<'a> From<ButtonPlace<'a>> for CButtonPlace {
|
||||
fn from(value: ButtonPlace<'a>) -> CButtonPlace {
|
||||
CButtonPlace {
|
||||
row: value.row as *const Row,
|
||||
button: value.button as *const Button,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Scale + translate
|
||||
#[repr(C)]
|
||||
pub struct Transformation {
|
||||
origin_x: f64,
|
||||
origin_y: f64,
|
||||
scale: f64,
|
||||
}
|
||||
|
||||
impl Transformation {
|
||||
fn forward(&self, p: Point) -> Point {
|
||||
Point {
|
||||
x: (p.x - self.origin_x) / self.scale,
|
||||
y: (p.y - self.origin_y) / self.scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is constructed only in C, no need for warnings
|
||||
#[allow(dead_code)]
|
||||
#[repr(transparent)]
|
||||
pub struct LevelKeyboard(*const c_void);
|
||||
|
||||
@ -254,55 +262,34 @@ pub mod c {
|
||||
origin: Point,
|
||||
angle: i32
|
||||
) -> u32;
|
||||
|
||||
// CKeyState is safe to pass to C as long as nothing dereferences it
|
||||
|
||||
// Button and View are safe to pass to C
|
||||
// as long as they don't outlive the call
|
||||
// and nothing dereferences them
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn eek_keyboard_set_key_locked(
|
||||
keyboard: *mut LevelKeyboard,
|
||||
key: ::keyboard::c::CKeyState,
|
||||
pub fn eek_gtk_on_button_released(
|
||||
button: *const Button,
|
||||
view: *const View,
|
||||
keyboard: EekGtkKeyboard,
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_layout_set_state_from_press(
|
||||
layout: *mut Layout,
|
||||
keyboard: *mut LevelKeyboard,
|
||||
key: ::keyboard::c::CKeyState,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
|
||||
let view_name = match key.clone_owned().action {
|
||||
Action::SetLevel(name) => {
|
||||
Some(name.clone())
|
||||
},
|
||||
Action::LockLevel { lock, unlock } => {
|
||||
let locked = {
|
||||
let key = key.clone_ref();
|
||||
let mut key = key.borrow_mut();
|
||||
key.locked ^= true;
|
||||
key.locked
|
||||
};
|
||||
|
||||
if locked {
|
||||
unsafe {
|
||||
eek_keyboard_set_key_locked(
|
||||
keyboard,
|
||||
key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Some(if locked { lock } else { unlock }.clone())
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(view_name) = view_name {
|
||||
if let Err(_e) = layout.set_view(view_name.clone()) {
|
||||
eprintln!("No such view: {}, ignoring switch", view_name)
|
||||
};
|
||||
};
|
||||
// Button and View inside CButtonPlace are safe to pass to C
|
||||
// as long as they don't outlive the call
|
||||
// and nothing dereferences them
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn eek_gtk_on_button_pressed(
|
||||
place: CButtonPlace,
|
||||
keyboard: EekGtkKeyboard,
|
||||
);
|
||||
|
||||
// Button and View inside CButtonPlace are safe to pass to C
|
||||
// as long as they don't outlive the call
|
||||
// and nothing dereferences them
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn eek_gtk_render_locked_button(
|
||||
keyboard: EekGtkKeyboard,
|
||||
place: CButtonPlace,
|
||||
);
|
||||
}
|
||||
|
||||
/// Places each button in order, starting from 0 on the left,
|
||||
@ -325,209 +312,196 @@ pub mod c {
|
||||
}
|
||||
}
|
||||
|
||||
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
|
||||
row.buttons.iter().position(
|
||||
// TODO: wrap Button properly in Rc; this comparison is unreliable
|
||||
|button| button.as_ref() as *const ::layout::Button == needle
|
||||
).is_some()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_view_get_row(
|
||||
view: *mut View,
|
||||
needle: *const ::layout::Button,
|
||||
) -> *mut Row {
|
||||
let view = unsafe { &mut *view };
|
||||
let result = view.rows.iter_mut().find(|row| {
|
||||
squeek_row_contains(row, needle)
|
||||
});
|
||||
match result {
|
||||
Some(row) => row.as_mut() as *mut Row,
|
||||
None => ptr::null_mut(),
|
||||
fn squeek_layout_release(
|
||||
layout: *mut Layout,
|
||||
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
||||
time: u32,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||
// The list must be copied,
|
||||
// because it will be mutated in the loop
|
||||
for key in layout.pressed_keys.clone() {
|
||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||
layout.release_key(
|
||||
&virtual_keyboard,
|
||||
&mut key.clone(),
|
||||
Timestamp(time)
|
||||
);
|
||||
let view = layout.get_current_view();
|
||||
::layout::procedures::release_ui_buttons(
|
||||
&view, key, ui_keyboard,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Release all buittons but don't redraw
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_layout_release_all_only(
|
||||
layout: *mut Layout,
|
||||
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
||||
time: u32,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||
// The list must be copied,
|
||||
// because it will be mutated in the loop
|
||||
for key in layout.pressed_keys.clone() {
|
||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||
layout.release_key(
|
||||
&virtual_keyboard,
|
||||
&mut key.clone(),
|
||||
Timestamp(time)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_view_find_key(
|
||||
view: *const View,
|
||||
needle: ::keyboard::c::CKeyState,
|
||||
) -> ButtonPlace {
|
||||
let view = unsafe { &*view };
|
||||
let state = needle.clone_ref();
|
||||
fn squeek_layout_depress(
|
||||
layout: *mut Layout,
|
||||
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
||||
x_widget: f64, y_widget: f64,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let point = widget_to_layout.forward(
|
||||
Point { x: x_widget, y: y_widget }
|
||||
);
|
||||
|
||||
let paths = ::layout::procedures::find_key_paths(view, &state);
|
||||
|
||||
// Can only return 1 entry back to C
|
||||
let (row, button) = match paths.get(0) {
|
||||
Some((row, button)) => (
|
||||
row.as_ref() as *const Row,
|
||||
button.as_ref() as *const Button,
|
||||
),
|
||||
None => ( ptr::null(), ptr::null() ),
|
||||
// the immutable reference to `layout` through `view`
|
||||
// must be dropped
|
||||
// before `layout.press_key` borrows it mutably again
|
||||
let state_place = {
|
||||
let view = layout.get_current_view();
|
||||
let place = view.find_button_by_position(point);
|
||||
place.map(|place| {(
|
||||
place.button.state.clone(),
|
||||
place.into(),
|
||||
)})
|
||||
};
|
||||
ButtonPlace { row, button }
|
||||
|
||||
if let Some((mut state, c_place)) = state_place {
|
||||
layout.press_key(
|
||||
&VirtualKeyboard(virtual_keyboard),
|
||||
&mut state,
|
||||
Timestamp(time),
|
||||
);
|
||||
|
||||
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this will work funny
|
||||
// when 2 touch points are on buttons and moving one after another
|
||||
// Solution is to have separate pressed lists for each point
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_layout_drag(
|
||||
layout: *mut Layout,
|
||||
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
||||
x_widget: f64, y_widget: f64,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||
|
||||
let point = widget_to_layout.forward(
|
||||
Point { x: x_widget, y: y_widget }
|
||||
);
|
||||
|
||||
let pressed = layout.pressed_keys.clone();
|
||||
let state_place = {
|
||||
let view = layout.get_current_view();
|
||||
let place = view.find_button_by_position(point);
|
||||
place.map(|place| {(
|
||||
place.button.state.clone(),
|
||||
place.into(),
|
||||
)})
|
||||
};
|
||||
|
||||
if let Some((mut state, c_place)) = state_place {
|
||||
let mut found = false;
|
||||
for wrapped_key in pressed {
|
||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
||||
if Rc::ptr_eq(&state, &wrapped_key.0) {
|
||||
found = true;
|
||||
} else {
|
||||
layout.release_key(
|
||||
&virtual_keyboard,
|
||||
&mut key.clone(),
|
||||
Timestamp(time),
|
||||
);
|
||||
let view = layout.get_current_view();
|
||||
::layout::procedures::release_ui_buttons(
|
||||
&view, key, ui_keyboard,
|
||||
);
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
layout.press_key(
|
||||
&virtual_keyboard,
|
||||
&mut state,
|
||||
Timestamp(time),
|
||||
);
|
||||
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
|
||||
}
|
||||
} else {
|
||||
for wrapped_key in pressed {
|
||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
||||
layout.release_key(
|
||||
&virtual_keyboard,
|
||||
&mut key.clone(),
|
||||
Timestamp(time),
|
||||
);
|
||||
let view = layout.get_current_view();
|
||||
::layout::procedures::release_ui_buttons(
|
||||
&view, key, ui_keyboard,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_view_find_button_by_position(
|
||||
view: *mut View, point: Point
|
||||
) -> *mut Button {
|
||||
let view = unsafe { &mut *view };
|
||||
let result = view.find_button_by_position(point);
|
||||
match result {
|
||||
Some(button) => button.as_mut() as *mut Button,
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use super::super::test::*;
|
||||
|
||||
#[test]
|
||||
fn row_has_button() {
|
||||
let state = make_state();
|
||||
let button = make_button_with_state(
|
||||
"test".into(),
|
||||
state.clone()
|
||||
);
|
||||
let button_ptr = button_as_raw(&button);
|
||||
let mut row = Row::new(0);
|
||||
row.buttons.push(button);
|
||||
assert_eq!(squeek_row_contains(&row, button_ptr), true);
|
||||
let shared_button = make_button_with_state(
|
||||
"test2".into(),
|
||||
state
|
||||
);
|
||||
let shared_button_ptr = button_as_raw(&shared_button);
|
||||
row.buttons.push(shared_button);
|
||||
assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
|
||||
let row = Row::new(0);
|
||||
assert_eq!(squeek_row_contains(&row, button_ptr), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_has_button() {
|
||||
let state = make_state();
|
||||
let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
|
||||
|
||||
let button = make_button_with_state("1".into(), state);
|
||||
let button_ptr = button.as_ref() as *const Button;
|
||||
|
||||
let row = Box::new(Row {
|
||||
buttons: vec!(button),
|
||||
angle: 0,
|
||||
bounds: None
|
||||
});
|
||||
let row_ptr = row.as_ref() as *const Row;
|
||||
|
||||
let view = View {
|
||||
bounds: Bounds {
|
||||
x: 0f64, y: 0f64,
|
||||
width: 0f64, height: 0f64
|
||||
},
|
||||
rows: vec!(row),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
squeek_view_find_key(
|
||||
&view as *const View,
|
||||
state_clone.clone(),
|
||||
),
|
||||
ButtonPlace {
|
||||
row: row_ptr,
|
||||
button: button_ptr,
|
||||
fn squeek_layout_draw_all_changed(
|
||||
layout: *mut Layout,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
|
||||
for row in &layout.get_current_view().rows {
|
||||
for button in &row.buttons {
|
||||
let c_place = CButtonPlace::from(
|
||||
ButtonPlace { row, button }
|
||||
);
|
||||
let state = RefCell::borrow(&button.state);
|
||||
match (state.pressed, state.locked) {
|
||||
(PressType::Released, false) => {}
|
||||
(PressType::Pressed, _) => unsafe {
|
||||
eek_gtk_on_button_pressed(c_place, ui_keyboard)
|
||||
},
|
||||
(_, true) => unsafe {
|
||||
eek_gtk_render_locked_button(ui_keyboard, c_place)
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
let view = View {
|
||||
bounds: Bounds {
|
||||
x: 0f64, y: 0f64,
|
||||
width: 0f64, height: 0f64
|
||||
},
|
||||
rows: Vec::new(),
|
||||
};
|
||||
assert_eq!(
|
||||
squeek_view_find_key(
|
||||
&view as *const View,
|
||||
state_clone.clone()
|
||||
),
|
||||
ButtonPlace {
|
||||
row: ptr::null(),
|
||||
button: ptr::null(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use ::keyboard::c::CKeyState;
|
||||
}
|
||||
|
||||
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
|
||||
Rc::new(RefCell::new(::keyboard::KeyState {
|
||||
pressed: false,
|
||||
locked: false,
|
||||
keycodes: Vec::new(),
|
||||
action: Action::SetLevel("default".into()),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn make_button_with_state(
|
||||
name: String,
|
||||
state: Rc<RefCell<::keyboard::KeyState>>,
|
||||
) -> Box<Button> {
|
||||
Box::new(Button {
|
||||
name: CString::new(name.clone()).unwrap(),
|
||||
bounds: c::Bounds {
|
||||
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||
},
|
||||
outline_name: CString::new("test").unwrap(),
|
||||
label: Label::Text(CString::new(name).unwrap()),
|
||||
state: state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn button_as_raw(button: &Box<Button>) -> *const Button {
|
||||
button.as_ref() as *const Button
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn button_has_key() {
|
||||
let state = make_state();
|
||||
let button = make_button_with_state("1".into(), state.clone());
|
||||
assert_eq!(
|
||||
squeek_button_has_key(
|
||||
button_as_raw(&button),
|
||||
CKeyState::wrap(state.clone())
|
||||
),
|
||||
1
|
||||
);
|
||||
let other_state = make_state();
|
||||
let other_button = make_button_with_state("1".into(), other_state);
|
||||
assert_eq!(
|
||||
squeek_button_has_key(
|
||||
button_as_raw(&other_button),
|
||||
CKeyState::wrap(state.clone())
|
||||
),
|
||||
0
|
||||
);
|
||||
let orphan_state = CKeyState::wrap(make_state());
|
||||
assert_eq!(
|
||||
squeek_button_has_key(button_as_raw(&button), orphan_state),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
pub struct ButtonPlace<'a> {
|
||||
button: &'a Button,
|
||||
row: &'a Row,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -570,14 +544,6 @@ pub struct Row {
|
||||
}
|
||||
|
||||
impl Row {
|
||||
fn new(angle: i32) -> Row {
|
||||
Row {
|
||||
buttons: Vec::new(),
|
||||
angle: angle,
|
||||
bounds: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn last(positions: &Vec<c::Bounds>) -> Option<&c::Bounds> {
|
||||
let len = positions.len();
|
||||
match len {
|
||||
@ -615,8 +581,8 @@ impl Row {
|
||||
|
||||
/// Finds the first button that covers the specified point
|
||||
/// relative to row's position's origin
|
||||
fn find_button_by_position(&mut self, point: c::Point)
|
||||
-> Option<&mut Box<Button>>
|
||||
fn find_button_by_position(&self, point: c::Point)
|
||||
-> Option<&Box<Button>>
|
||||
{
|
||||
let row_bounds = self.bounds.as_ref().expect("Missing bounds on row");
|
||||
let origin = c::Point {
|
||||
@ -624,7 +590,7 @@ impl Row {
|
||||
y: row_bounds.y,
|
||||
};
|
||||
let angle = self.angle;
|
||||
self.buttons.iter_mut().find(|button| {
|
||||
self.buttons.iter().find(|button| {
|
||||
let bounds = button.bounds.clone();
|
||||
let point = point.clone();
|
||||
let origin = origin.clone();
|
||||
@ -704,8 +670,8 @@ impl View {
|
||||
|
||||
/// Finds the first button that covers the specified point
|
||||
/// relative to view's position's origin
|
||||
fn find_button_by_position(&mut self, point: c::Point)
|
||||
-> Option<&mut Box<Button>>
|
||||
fn find_button_by_position(&self, point: c::Point)
|
||||
-> Option<ButtonPlace>
|
||||
{
|
||||
// make point relative to the inside of the view,
|
||||
// which is the origin of all rows
|
||||
@ -714,22 +680,43 @@ impl View {
|
||||
y: point.y - self.bounds.y,
|
||||
};
|
||||
|
||||
self.rows.iter_mut().find_map(
|
||||
|row| row.find_button_by_position(point.clone())
|
||||
)
|
||||
self.rows.iter().find_map(|row| {
|
||||
row.find_button_by_position(point.clone())
|
||||
.map(|button| ButtonPlace {row, button})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: split into sth like
|
||||
// Arrangement (views) + details (keymap) + State (keys)
|
||||
/// State of the UI, contains the backend as well
|
||||
pub struct Layout {
|
||||
pub current_view: String,
|
||||
// Views own the actual buttons which have state
|
||||
// Maybe they should own UI only,
|
||||
// and keys should be owned by a dedicated non-UI-State?
|
||||
pub views: HashMap<String, Box<View>>,
|
||||
// TODO: move to ::keyboard::Keyboard
|
||||
|
||||
// Non-UI stuff
|
||||
/// xkb keymap applicable to the contained keys. Unchangeable
|
||||
pub keymap_str: CString,
|
||||
// Changeable state
|
||||
// a Vec would be enough, but who cares, this will be small & fast enough
|
||||
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
|
||||
pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
|
||||
}
|
||||
|
||||
struct NoSuchView;
|
||||
|
||||
// Unfortunately, changes are not atomic due to mutability :(
|
||||
// An error will not be recoverable
|
||||
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
|
||||
// Cloning could also be used.
|
||||
impl Layout {
|
||||
fn get_current_view(&self) -> &Box<View> {
|
||||
self.views.get(&self.current_view).expect("Selected nonexistent view")
|
||||
}
|
||||
|
||||
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
||||
if self.views.contains_key(&view) {
|
||||
self.current_view = view;
|
||||
@ -738,11 +725,91 @@ impl Layout {
|
||||
Err(NoSuchView)
|
||||
}
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
virtual_keyboard: &VirtualKeyboard,
|
||||
mut key: &mut Rc<RefCell<KeyState>>,
|
||||
time: Timestamp,
|
||||
) {
|
||||
if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
|
||||
eprintln!("Warning: key {:?} was not pressed", key);
|
||||
}
|
||||
virtual_keyboard.switch(
|
||||
&mut key.borrow_mut(),
|
||||
PressType::Released,
|
||||
time,
|
||||
);
|
||||
self.set_level_from_press(&mut key);
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
virtual_keyboard: &VirtualKeyboard,
|
||||
key: &mut Rc<RefCell<KeyState>>,
|
||||
time: Timestamp,
|
||||
) {
|
||||
if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
|
||||
eprintln!("Warning: key {:?} was already pressed", key);
|
||||
}
|
||||
virtual_keyboard.switch(
|
||||
&mut key.borrow_mut(),
|
||||
PressType::Pressed,
|
||||
time,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
||||
let keys = self.locked_keys.clone();
|
||||
for key in &keys {
|
||||
self.locked_keys.remove(key);
|
||||
self.set_state_from_press(key.borrow());
|
||||
}
|
||||
|
||||
// Don't handle the same key twice, but handle it at least once,
|
||||
// because its press is the reason we're here
|
||||
if !keys.contains(&::util::Pointer(key.clone())) {
|
||||
self.set_state_from_press(key);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
||||
// Action should not hold a reference to key,
|
||||
// because key is later borrowed for mutation. So action is cloned.
|
||||
// RefCell::borrow() is covered up by (dyn Borrow)::borrow()
|
||||
// if used like key.borrow() :(
|
||||
let action = RefCell::borrow(key).action.clone();
|
||||
let view_name = match action {
|
||||
Action::SetLevel(name) => {
|
||||
Some(name.clone())
|
||||
},
|
||||
Action::LockLevel { lock, unlock } => {
|
||||
let locked = {
|
||||
let mut key = key.borrow_mut();
|
||||
key.locked ^= true;
|
||||
key.locked
|
||||
};
|
||||
|
||||
if locked {
|
||||
self.locked_keys.insert(::util::Pointer(key.clone()));
|
||||
}
|
||||
|
||||
Some(if locked { lock } else { unlock }.clone())
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(view_name) = view_name {
|
||||
if let Err(_e) = self.set_view(view_name.clone()) {
|
||||
eprintln!("No such view: {}, ignoring switch", view_name)
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
mod procedures {
|
||||
use super::*;
|
||||
|
||||
|
||||
type Path<'v> = (&'v Box<Row>, &'v Box<Button>);
|
||||
|
||||
/// Finds all `(row, button)` paths that refer to the specified key `state`
|
||||
@ -774,4 +841,112 @@ mod procedures {
|
||||
c::procedures::eek_are_bounds_inside(bounds, point, origin, angle)
|
||||
}) == 1
|
||||
}
|
||||
|
||||
/// Switch off all UI buttons associated with the (state) key
|
||||
pub fn release_ui_buttons(
|
||||
view: &Box<View>,
|
||||
key: &Rc<RefCell<KeyState>>,
|
||||
ui_keyboard: c::EekGtkKeyboard,
|
||||
) {
|
||||
let paths = ::layout::procedures::find_key_paths(&view, key);
|
||||
for (_row, button) in paths {
|
||||
unsafe {
|
||||
c::procedures::eek_gtk_on_button_released(
|
||||
button.as_ref() as *const Button,
|
||||
view.as_ref() as *const View,
|
||||
ui_keyboard,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use ::layout::test::*;
|
||||
|
||||
/// Checks whether the path points to the same boxed instances.
|
||||
/// The instance constraint will be droppable
|
||||
/// when C stops holding references to the data
|
||||
#[test]
|
||||
fn view_has_button() {
|
||||
fn as_ptr<T>(v: &Box<T>) -> *const T {
|
||||
v.as_ref() as *const T
|
||||
}
|
||||
|
||||
let state = make_state();
|
||||
let state_clone = state.clone();
|
||||
|
||||
let button = make_button_with_state("1".into(), state);
|
||||
let button_ptr = as_ptr(&button);
|
||||
|
||||
let row = Box::new(Row {
|
||||
buttons: vec!(button),
|
||||
angle: 0,
|
||||
bounds: None
|
||||
});
|
||||
let row_ptr = as_ptr(&row);
|
||||
|
||||
let view = View {
|
||||
bounds: c::Bounds {
|
||||
x: 0f64, y: 0f64,
|
||||
width: 0f64, height: 0f64
|
||||
},
|
||||
rows: vec!(row),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
find_key_paths(&view, &state_clone.clone()).iter()
|
||||
.map(|(row, button)| { (as_ptr(row), as_ptr(button)) })
|
||||
.collect::<Vec<_>>(),
|
||||
vec!(
|
||||
(row_ptr, button_ptr)
|
||||
)
|
||||
);
|
||||
|
||||
let view = View {
|
||||
bounds: c::Bounds {
|
||||
x: 0f64, y: 0f64,
|
||||
width: 0f64, height: 0f64
|
||||
},
|
||||
rows: Vec::new(),
|
||||
};
|
||||
assert_eq!(
|
||||
find_key_paths(&view, &state_clone.clone()).is_empty(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::CString;
|
||||
|
||||
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
|
||||
Rc::new(RefCell::new(::keyboard::KeyState {
|
||||
pressed: PressType::Released,
|
||||
locked: false,
|
||||
keycodes: Vec::new(),
|
||||
action: Action::SetLevel("default".into()),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn make_button_with_state(
|
||||
name: String,
|
||||
state: Rc<RefCell<::keyboard::KeyState>>,
|
||||
) -> Box<Button> {
|
||||
Box::new(Button {
|
||||
name: CString::new(name.clone()).unwrap(),
|
||||
bounds: c::Bounds {
|
||||
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||
},
|
||||
outline_name: CString::new("test").unwrap(),
|
||||
label: Label::Text(CString::new(name).unwrap()),
|
||||
state: state,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,12 +6,13 @@ extern crate maplit;
|
||||
extern crate serde;
|
||||
extern crate xkbcommon;
|
||||
|
||||
mod action;
|
||||
pub mod data;
|
||||
pub mod float_ord;
|
||||
pub mod imservice;
|
||||
mod keyboard;
|
||||
mod layout;
|
||||
mod resources;
|
||||
mod action;
|
||||
mod submission;
|
||||
mod util;
|
||||
mod xdg;
|
||||
|
||||
68
src/submission.rs
Normal file
68
src/submission.rs
Normal file
@ -0,0 +1,68 @@
|
||||
/*! Managing the events belonging to virtual-keyboard interface. */
|
||||
|
||||
use ::keyboard::{ KeyState, PressType };
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
pub mod c {
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ZwpVirtualKeyboardV1(*const c_void);
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" {
|
||||
/// Checks if point falls within bounds,
|
||||
/// which are relative to origin and rotated by angle (I think)
|
||||
pub fn eek_virtual_keyboard_v1_key(
|
||||
virtual_keyboard: ZwpVirtualKeyboardV1,
|
||||
timestamp: u32,
|
||||
keycode: u32,
|
||||
press: u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timestamp(pub u32);
|
||||
|
||||
/// Layout-independent backend. TODO: Have one instance per program or seat
|
||||
pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
|
||||
|
||||
impl VirtualKeyboard {
|
||||
// TODO: split out keyboard state management
|
||||
pub fn switch(
|
||||
&self,
|
||||
key: &mut KeyState,
|
||||
action: PressType,
|
||||
timestamp: Timestamp,
|
||||
) {
|
||||
key.pressed = action.clone();
|
||||
|
||||
let keycodes_count = key.keycodes.len();
|
||||
for keycode in key.keycodes.iter() {
|
||||
let keycode = keycode - 8;
|
||||
match (&key.pressed, keycodes_count) {
|
||||
// Pressing a key made out of a single keycode is simple:
|
||||
// press on press, release on release.
|
||||
(_, 1) => unsafe {
|
||||
c::eek_virtual_keyboard_v1_key(
|
||||
self.0, timestamp.0, keycode, action.clone() as u32
|
||||
);
|
||||
},
|
||||
// A key made of multiple keycodes
|
||||
// has to submit them one after the other
|
||||
(PressType::Pressed, _) => unsafe {
|
||||
c::eek_virtual_keyboard_v1_key(
|
||||
self.0, timestamp.0, keycode, PressType::Pressed as u32
|
||||
);
|
||||
c::eek_virtual_keyboard_v1_key(
|
||||
self.0, timestamp.0, keycode, PressType::Released as u32
|
||||
);
|
||||
},
|
||||
// Design choice here: submit multiple all at press time
|
||||
// and do nothing at release time
|
||||
(PressType::Released, _) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/util.rs
56
src/util.rs
@ -1,6 +1,9 @@
|
||||
/*! Assorted helpers */
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::{ Hash, Hasher };
|
||||
use std::iter::FromIterator;
|
||||
|
||||
pub mod c {
|
||||
@ -109,7 +112,7 @@ pub mod c {
|
||||
|
||||
fn clone_owned(&self) -> T {
|
||||
let rc = self.clone_ref();
|
||||
let r = rc.borrow();
|
||||
let r = RefCell::borrow(&rc);
|
||||
r.to_owned()
|
||||
}
|
||||
}
|
||||
@ -129,3 +132,54 @@ pub fn hash_map_map<K, V, F, K1, V1>(map: HashMap<K, V>, mut f: F)
|
||||
map.into_iter().map(|(key, value)| f(key, value))
|
||||
)
|
||||
}
|
||||
|
||||
/// Compares pointers but not internal values of Rc
|
||||
pub struct Pointer<T>(pub Rc<T>);
|
||||
|
||||
impl<T> Pointer<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Pointer(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hash for Pointer<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(&*self.0 as *const T).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Pointer<T> {
|
||||
fn eq(&self, other: &Pointer<T>) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Pointer<T> {}
|
||||
|
||||
impl<T> Clone for Pointer<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Pointer(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Borrow<Rc<T>> for Pointer<T> {
|
||||
fn borrow(&self) -> &Rc<T> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn check_set() {
|
||||
let mut s = HashSet::new();
|
||||
let first = Rc::new(1u32);
|
||||
s.insert(Pointer(first.clone()));
|
||||
assert_eq!(s.insert(Pointer(Rc::new(2u32))), true);
|
||||
assert_eq!(s.remove(&Pointer(first)), true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user