Merge pull request #14 from vberger/keyboard
keyboard: first draft of xkbcommon handling
This commit is contained in:
commit
1a3e560ae1
|
@ -5,12 +5,14 @@ authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wayland-server = "0.8.6"
|
wayland-server = "0.9.1"
|
||||||
nix = "0.7.0"
|
nix = "0.7.0"
|
||||||
glutin = { version = "~0.7.4", optional = true }
|
xkbcommon = "0.2.1"
|
||||||
glium = { version = "~0.16.0", optional = true }
|
tempfile = "2.1.5"
|
||||||
slog = { version = "~1.5.2", features = ["max_level_trace", "release_max_level_info"] }
|
slog = { version = "~1.5.2", features = ["max_level_trace", "release_max_level_info"] }
|
||||||
slog-stdlog = "~1.1.0"
|
slog-stdlog = "~1.1.0"
|
||||||
|
glutin = { version = "~0.7.4", optional = true }
|
||||||
|
glium = { version = "~0.16.0", optional = true }
|
||||||
clippy = { version = "*", optional = true }
|
clippy = { version = "*", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -218,7 +218,9 @@ impl InputBackend for GlutinInputBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
|
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
|
||||||
self.handler.as_mut().map(|handler| handler as &mut InputHandler<Self>)
|
self.handler
|
||||||
|
.as_mut()
|
||||||
|
.map(|handler| handler as &mut InputHandler<Self>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_handler(&mut self) {
|
fn clear_handler(&mut self) {
|
||||||
|
@ -233,7 +235,8 @@ impl InputBackend for GlutinInputBackend {
|
||||||
|
|
||||||
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> {
|
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> {
|
||||||
if let Some((win_x, win_y)) = self.window.get_position() {
|
if let Some((win_x, win_y)) = self.window.get_position() {
|
||||||
self.window.set_cursor_position(win_x + x as i32, win_y + y as i32)
|
self.window
|
||||||
|
.set_cursor_position(win_x + x as i32, win_y + y as i32)
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
@ -304,7 +307,11 @@ impl InputBackend for GlutinInputBackend {
|
||||||
Event::MouseInput(state, button) => {
|
Event::MouseInput(state, button) => {
|
||||||
handler.on_pointer_button(&self.seat, self.time_counter, button.into(), state.into())
|
handler.on_pointer_button(&self.seat, self.time_counter, button.into(), state.into())
|
||||||
}
|
}
|
||||||
Event::Touch(Touch { phase: TouchPhase::Started, location: (x, y), id }) => {
|
Event::Touch(Touch {
|
||||||
|
phase: TouchPhase::Started,
|
||||||
|
location: (x, y),
|
||||||
|
id,
|
||||||
|
}) => {
|
||||||
handler.on_touch(&self.seat,
|
handler.on_touch(&self.seat,
|
||||||
self.time_counter,
|
self.time_counter,
|
||||||
TouchEvent::Down {
|
TouchEvent::Down {
|
||||||
|
@ -313,7 +320,11 @@ impl InputBackend for GlutinInputBackend {
|
||||||
y: y,
|
y: y,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Event::Touch(Touch { phase: TouchPhase::Moved, location: (x, y), id }) => {
|
Event::Touch(Touch {
|
||||||
|
phase: TouchPhase::Moved,
|
||||||
|
location: (x, y),
|
||||||
|
id,
|
||||||
|
}) => {
|
||||||
handler.on_touch(&self.seat,
|
handler.on_touch(&self.seat,
|
||||||
self.time_counter,
|
self.time_counter,
|
||||||
TouchEvent::Motion {
|
TouchEvent::Motion {
|
||||||
|
@ -322,7 +333,11 @@ impl InputBackend for GlutinInputBackend {
|
||||||
y: y,
|
y: y,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Event::Touch(Touch { phase: TouchPhase::Ended, location: (x, y), id }) => {
|
Event::Touch(Touch {
|
||||||
|
phase: TouchPhase::Ended,
|
||||||
|
location: (x, y),
|
||||||
|
id,
|
||||||
|
}) => {
|
||||||
handler.on_touch(&self.seat,
|
handler.on_touch(&self.seat,
|
||||||
self.time_counter,
|
self.time_counter,
|
||||||
TouchEvent::Motion {
|
TouchEvent::Motion {
|
||||||
|
@ -334,7 +349,11 @@ impl InputBackend for GlutinInputBackend {
|
||||||
self.time_counter,
|
self.time_counter,
|
||||||
TouchEvent::Up { slot: Some(TouchSlot::new(id as u32)) });
|
TouchEvent::Up { slot: Some(TouchSlot::new(id as u32)) });
|
||||||
}
|
}
|
||||||
Event::Touch(Touch { phase: TouchPhase::Cancelled, id, .. }) => {
|
Event::Touch(Touch {
|
||||||
|
phase: TouchPhase::Cancelled,
|
||||||
|
id,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
handler.on_touch(&self.seat,
|
handler.on_touch(&self.seat,
|
||||||
self.time_counter,
|
self.time_counter,
|
||||||
TouchEvent::Cancel { slot: Some(TouchSlot::new(id as u32)) })
|
TouchEvent::Cancel { slot: Some(TouchSlot::new(id as u32)) })
|
||||||
|
|
|
@ -0,0 +1,281 @@
|
||||||
|
//! Utilities for keyboard handling
|
||||||
|
//!
|
||||||
|
//! This module provides utilities for keyboardand keymap handling: keymap interpretation
|
||||||
|
//! and forwarding keystrokes to clients using xkbcommon.
|
||||||
|
//!
|
||||||
|
//! You can first create a `KbdHandle` using the `create_keyboard_handler` function in this module.
|
||||||
|
//! The handle you obtained can be cloned to access this keyboard state from different places. It is
|
||||||
|
//! expected that such a context is created for each keyboard the compositor has access to.
|
||||||
|
//!
|
||||||
|
//! This handle gives you 3 main ways to interact with the keymap handling:
|
||||||
|
//!
|
||||||
|
//! - send the keymap information to a client using the `KbdHandle::send_keymap` method.
|
||||||
|
//! - set the current focus for this keyboard: designing the client that will receive the key inputs
|
||||||
|
//! using the `KbdHandle::set_focus` method.
|
||||||
|
//! - process key inputs from the input backend, allowing them to be catched at the compositor-level
|
||||||
|
//! or forwarded to the client. See the documentation of the `KbdHandle::input` method for
|
||||||
|
//! details.
|
||||||
|
|
||||||
|
|
||||||
|
use backend::input::KeyState;
|
||||||
|
use std::io::{Error as IoError, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use tempfile::tempfile;
|
||||||
|
|
||||||
|
use wayland_server::{Liveness, Resource};
|
||||||
|
use wayland_server::protocol::{wl_keyboard, wl_surface};
|
||||||
|
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
pub use xkbcommon::xkb::{Keysym, keysyms};
|
||||||
|
|
||||||
|
/// Represents the current state of the keyboard modifiers
|
||||||
|
///
|
||||||
|
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||||
|
///
|
||||||
|
/// For some modifiers, this means that the key is currently pressed, others are toggled
|
||||||
|
/// (like caps lock).
|
||||||
|
#[derive(Copy,Clone,Debug)]
|
||||||
|
pub struct ModifiersState {
|
||||||
|
/// The "control" key
|
||||||
|
pub ctrl: bool,
|
||||||
|
/// The "alt" key
|
||||||
|
pub alt: bool,
|
||||||
|
/// The "shift" key
|
||||||
|
pub shift: bool,
|
||||||
|
/// The "Caps lock" key
|
||||||
|
pub caps_lock: bool,
|
||||||
|
/// The "logo" key
|
||||||
|
///
|
||||||
|
/// Also known as the "windows" key on most keyboards
|
||||||
|
pub logo: bool,
|
||||||
|
/// The "Num lock" key
|
||||||
|
pub num_lock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifiersState {
|
||||||
|
fn new() -> ModifiersState {
|
||||||
|
ModifiersState {
|
||||||
|
ctrl: false,
|
||||||
|
alt: false,
|
||||||
|
shift: false,
|
||||||
|
caps_lock: false,
|
||||||
|
logo: false,
|
||||||
|
num_lock: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_with(&mut self, state: &xkb::State) {
|
||||||
|
self.ctrl = state.mod_name_is_active(&xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
self.alt = state.mod_name_is_active(&xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
self.shift = state.mod_name_is_active(&xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
self.caps_lock = state.mod_name_is_active(&xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
self.logo = state.mod_name_is_active(&xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
self.num_lock = state.mod_name_is_active(&xkb::MOD_NAME_NUM, xkb::STATE_MODS_EFFECTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KbdInternal {
|
||||||
|
focus: Option<(wl_surface::WlSurface, wl_keyboard::WlKeyboard)>,
|
||||||
|
pressed_keys: Vec<u32>,
|
||||||
|
mods_state: ModifiersState,
|
||||||
|
keymap: xkb::Keymap,
|
||||||
|
state: xkb::State,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KbdInternal {
|
||||||
|
fn new(rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>)
|
||||||
|
-> Result<KbdInternal, ()> {
|
||||||
|
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
|
||||||
|
// so confining it inside the KbdInternal allows us to use Rusts mutability rules to make
|
||||||
|
// sure nothing goes wrong.
|
||||||
|
//
|
||||||
|
// FIXME: This is an issue with the xkbcommon-rs crate that does not reflect this
|
||||||
|
// non-threadsafety properly.
|
||||||
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
let keymap = xkb::Keymap::new_from_names(&context,
|
||||||
|
&rules,
|
||||||
|
&model,
|
||||||
|
&layout,
|
||||||
|
&variant,
|
||||||
|
options,
|
||||||
|
xkb::KEYMAP_COMPILE_NO_FLAGS)
|
||||||
|
.ok_or(())?;
|
||||||
|
let state = xkb::State::new(&keymap);
|
||||||
|
Ok(KbdInternal {
|
||||||
|
focus: None,
|
||||||
|
pressed_keys: Vec::new(),
|
||||||
|
mods_state: ModifiersState::new(),
|
||||||
|
keymap: keymap,
|
||||||
|
state: state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if modifier state has changed
|
||||||
|
fn key_input(&mut self, keycode: u32, state: KeyState) -> bool {
|
||||||
|
// track pressed keys as xkbcommon does not seem to expose it :(
|
||||||
|
let direction = match state {
|
||||||
|
KeyState::Pressed => {
|
||||||
|
self.pressed_keys.push(keycode);
|
||||||
|
xkb::KeyDirection::Down
|
||||||
|
}
|
||||||
|
KeyState::Released => {
|
||||||
|
self.pressed_keys.retain(|&k| k != keycode);
|
||||||
|
xkb::KeyDirection::Up
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// update state
|
||||||
|
let state_components = self.state.update_key(keycode, direction);
|
||||||
|
if state_components != 0 {
|
||||||
|
self.mods_state.update_with(&self.state);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_modifiers(&self) -> (u32, u32, u32, u32) {
|
||||||
|
let mods_depressed = self.state.serialize_mods(xkb::STATE_MODS_DEPRESSED);
|
||||||
|
let mods_latched = self.state.serialize_mods(xkb::STATE_MODS_LATCHED);
|
||||||
|
let mods_locked = self.state.serialize_mods(xkb::STATE_MODS_LOCKED);
|
||||||
|
let layout_locked = self.state.serialize_layout(xkb::STATE_LAYOUT_LOCKED);
|
||||||
|
|
||||||
|
return (mods_depressed, mods_latched, mods_locked, layout_locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_pressed_keys(&self) -> Vec<u8> {
|
||||||
|
let serialized = unsafe {
|
||||||
|
::std::slice::from_raw_parts(self.pressed_keys.as_ptr() as *const u8,
|
||||||
|
self.pressed_keys.len() * 4)
|
||||||
|
};
|
||||||
|
serialized.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can be encountered when creating a keyboard handler
|
||||||
|
pub enum Error {
|
||||||
|
/// libxkbcommon could not load the specified keymap
|
||||||
|
BadKeymap,
|
||||||
|
/// Smithay could not create a tempfile to share the keymap with clients
|
||||||
|
IoError(IoError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a keyboard handler from a set of RMLVO rules
|
||||||
|
pub fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str,
|
||||||
|
options: Option<String>)
|
||||||
|
-> Result<KbdHandle, Error> {
|
||||||
|
let internal = KbdInternal::new(rules, model, layout, variant, options)
|
||||||
|
.map_err(|_| Error::BadKeymap)?;
|
||||||
|
|
||||||
|
// prepare a tempfile with the keymap, to send it to clients
|
||||||
|
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
||||||
|
let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
|
||||||
|
keymap_file
|
||||||
|
.write_all(keymap_data.as_bytes())
|
||||||
|
.map_err(Error::IoError)?;
|
||||||
|
keymap_file.flush().map_err(Error::IoError)?;
|
||||||
|
|
||||||
|
Ok(KbdHandle {
|
||||||
|
internal: Arc::new((Mutex::new(internal), (keymap_file, keymap_data.as_bytes().len() as u32))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An handle to a keyboard handler
|
||||||
|
///
|
||||||
|
/// It can be cloned and all clones manipulate the same internal state. Clones
|
||||||
|
/// can also be sent across threads.
|
||||||
|
///
|
||||||
|
/// See module-level documentation for details of its use.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KbdHandle {
|
||||||
|
internal: Arc<(Mutex<KbdInternal>, (::std::fs::File, u32))>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KbdHandle {
|
||||||
|
/// Handle a keystroke
|
||||||
|
///
|
||||||
|
/// All keystrokes from the input backend should be fed _in order_ to this method of the
|
||||||
|
/// keyboard handler. It will internally track the state of the keymap.
|
||||||
|
///
|
||||||
|
/// The `filter` argument is expected to be a closure which will peek at the generated input
|
||||||
|
/// as interpreted by the keymap befor it is forwarded to the focused client. If this closure
|
||||||
|
/// returns false, the input will not be sent to the client. This mechanism can be used to
|
||||||
|
/// implement compositor-level key bindings for example.
|
||||||
|
///
|
||||||
|
/// The module `smithay::keyboard::keysyms` exposes definitions of all possible keysyms
|
||||||
|
/// to be compared against. This includes non-characted keysyms, such as XF86 special keys.
|
||||||
|
pub fn input<F>(&self, keycode: u32, state: KeyState, serial: u32, filter: F)
|
||||||
|
where F: FnOnce(&ModifiersState, Keysym) -> bool
|
||||||
|
{
|
||||||
|
let mut guard = self.internal.0.lock().unwrap();
|
||||||
|
let mods_changed = guard.key_input(keycode, state);
|
||||||
|
|
||||||
|
if !filter(&guard.mods_state, guard.state.key_get_one_sym(keycode)) {
|
||||||
|
// the filter returned false, we do not forward to client
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward to client if no keybinding is triggered
|
||||||
|
if let Some((_, ref kbd)) = guard.focus {
|
||||||
|
if mods_changed {
|
||||||
|
let (dep, la, lo, gr) = guard.serialize_modifiers();
|
||||||
|
kbd.modifiers(serial, dep, la, lo, gr);
|
||||||
|
}
|
||||||
|
let wl_state = match state {
|
||||||
|
KeyState::Pressed => wl_keyboard::KeyState::Pressed,
|
||||||
|
KeyState::Released => wl_keyboard::KeyState::Released,
|
||||||
|
};
|
||||||
|
kbd.key(serial, 0, keycode, wl_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the current focus of this keyboard
|
||||||
|
///
|
||||||
|
/// Any previous focus will be sent a `wl_keyboard::leave` event, and if the new focus
|
||||||
|
/// is not `None`, a `wl_keyboard::enter` event will be sent.
|
||||||
|
pub fn set_focus(&self, focus: Option<(wl_surface::WlSurface, wl_keyboard::WlKeyboard)>, serial: u32) {
|
||||||
|
// TODO: check surface and keyboard are from the same client
|
||||||
|
|
||||||
|
let mut guard = self.internal.0.lock().unwrap();
|
||||||
|
|
||||||
|
// remove current focus
|
||||||
|
let old_kbd = if let Some((old_surface, old_kbd)) = guard.focus.take() {
|
||||||
|
if old_surface.status() != Liveness::Dead {
|
||||||
|
old_kbd.leave(serial, &old_surface);
|
||||||
|
}
|
||||||
|
Some(old_kbd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// set new focus
|
||||||
|
if let Some((surface, kbd)) = focus {
|
||||||
|
if surface.status() != Liveness::Dead {
|
||||||
|
// send new mods status if client instance changed
|
||||||
|
match old_kbd {
|
||||||
|
Some(ref okbd) if okbd.equals(&kbd) => {}
|
||||||
|
_ => {
|
||||||
|
let (dep, la, lo, gr) = guard.serialize_modifiers();
|
||||||
|
kbd.modifiers(serial, dep, la, lo, gr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// send enter event
|
||||||
|
kbd.enter(serial, &surface, guard.serialize_pressed_keys());
|
||||||
|
}
|
||||||
|
guard.focus = Some((surface, kbd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the keymap to this keyboard
|
||||||
|
///
|
||||||
|
/// This should be done first, before anything else is done with this keyboard.
|
||||||
|
pub fn send_keymap(&self, kbd: &wl_keyboard::WlKeyboard) {
|
||||||
|
let keymap_data = &self.internal.1;
|
||||||
|
kbd.keymap(wl_keyboard::KeymapFormat::XkbV1,
|
||||||
|
keymap_data.0.as_raw_fd(),
|
||||||
|
keymap_data.1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate wayland_server;
|
extern crate wayland_server;
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
extern crate tempfile;
|
||||||
|
|
||||||
#[cfg(feature = "backend_glutin")]
|
#[cfg(feature = "backend_glutin")]
|
||||||
extern crate glutin;
|
extern crate glutin;
|
||||||
|
@ -19,3 +21,4 @@ extern crate slog_stdlog;
|
||||||
|
|
||||||
pub mod shm;
|
pub mod shm;
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
pub mod keyboard;
|
||||||
|
|
|
@ -94,7 +94,9 @@ impl ShmGlobal {
|
||||||
where L: Into<Option<::slog::Logger>>
|
where L: Into<Option<::slog::Logger>>
|
||||||
{
|
{
|
||||||
use slog::DrainExt;
|
use slog::DrainExt;
|
||||||
let log = logger.into().unwrap_or_else(|| ::slog::Logger::root(::slog_stdlog::StdLog.fuse(), o!()));
|
let log = logger
|
||||||
|
.into()
|
||||||
|
.unwrap_or_else(|| ::slog::Logger::root(::slog_stdlog::StdLog.fuse(), o!()));
|
||||||
|
|
||||||
// always add the mandatory formats
|
// always add the mandatory formats
|
||||||
formats.push(wl_shm::Format::Argb8888);
|
formats.push(wl_shm::Format::Argb8888);
|
||||||
|
@ -157,7 +159,9 @@ impl ShmGlobalToken {
|
||||||
}
|
}
|
||||||
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) };
|
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) };
|
||||||
|
|
||||||
if data.pool.with_data_slice(|slice| f(slice, data.data)).is_err() {
|
if data.pool
|
||||||
|
.with_data_slice(|slice| f(slice, data.data))
|
||||||
|
.is_err() {
|
||||||
// SIGBUS error occured
|
// SIGBUS error occured
|
||||||
buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into());
|
buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into());
|
||||||
return Err(BufferAccessError::BadMap);
|
return Err(BufferAccessError::BadMap);
|
||||||
|
|
|
@ -60,13 +60,13 @@ impl Pool {
|
||||||
|
|
||||||
// Prepare the access
|
// Prepare the access
|
||||||
SIGBUS_GUARD.with(|guard| {
|
SIGBUS_GUARD.with(|guard| {
|
||||||
let (p, _) = guard.get();
|
let (p, _) = guard.get();
|
||||||
if !p.is_null() {
|
if !p.is_null() {
|
||||||
// Recursive call of this method is not supported
|
// Recursive call of this method is not supported
|
||||||
panic!("Recursive access to a SHM pool content is not supported.");
|
panic!("Recursive access to a SHM pool content is not supported.");
|
||||||
}
|
}
|
||||||
guard.set((&*pool_guard as *const MemMap, false))
|
guard.set((&*pool_guard as *const MemMap, false))
|
||||||
});
|
});
|
||||||
|
|
||||||
let slice = pool_guard.get_slice();
|
let slice = pool_guard.get_slice();
|
||||||
f(slice);
|
f(slice);
|
||||||
|
|
Loading…
Reference in New Issue